From 8bb973a1aabcd666f04ec08fc295dc11797dd059 Mon Sep 17 00:00:00 2001 From: mrwhoareyou <2359146115@qq.com> Date: Sat, 20 Feb 2016 13:11:54 +0800 Subject: [PATCH] first-all --- .env | 25 + .env.example | 23 + artisan | 51 + composer.json | 52 + composer.lock | 3013 +++++++++++++++++ gulpfile.js | 16 + package.json | 10 + phpunit.xml | 27 + readme.md | 27 + server.php | 21 + .../Matcher/AbstractContextAwareMatcher.php | 65 + .../TabCompletion/Matcher/AbstractMatcher.php | 186 + .../Matcher/ClassAttributesMatcher.php | 79 + .../Matcher/ClassMethodsMatcher.php | 71 + .../Matcher/ClassNamesMatcher.php | 77 + .../TabCompletion/Matcher/CommandsMatcher.php | 114 + .../Matcher/ConstantsMatcher.php | 54 + .../Matcher/FunctionsMatcher.php | 56 + .../TabCompletion/Matcher/KeywordsMatcher.php | 85 + .../Matcher/MongoClientMatcher.php | 71 + .../Matcher/MongoDatabaseMatcher.php | 67 + .../Matcher/ObjectAttributesMatcher.php | 71 + .../Matcher/ObjectMethodsMatcher.php | 71 + .../Matcher/VariablesMatcher.php | 51 + vendor/psy/psysh/src/Psy/Util/Docblock.php | 241 ++ vendor/psy/psysh/src/Psy/Util/Json.php | 35 + vendor/psy/psysh/src/Psy/Util/Mirror.php | 94 + vendor/psy/psysh/src/Psy/Util/Str.php | 114 + vendor/psy/psysh/src/Psy/VarDumper/Cloner.php | 42 + vendor/psy/psysh/src/Psy/VarDumper/Dumper.php | 103 + .../psy/psysh/src/Psy/VarDumper/Presenter.php | 123 + .../src/Psy/VarDumper/PresenterAware.php | 26 + .../psysh/test/Psy/Test/AutoloaderTest.php | 23 + .../CodeCleaner/AbstractClassPassTest.php | 61 + .../AssignThisVariablePassTest.php | 62 + .../CallTimePassByReferencePassTest.php | 73 + .../Test/CodeCleaner/CalledClassPassTest.php | 98 + .../Test/CodeCleaner/CodeCleanerTestCase.php | 98 + .../Fixtures/ClassWithCallStatic.php | 20 + .../CodeCleaner/Fixtures/ClassWithStatic.php | 20 + .../FunctionReturnInWriteContextPassTest.php | 78 + .../CodeCleaner/ImplicitReturnPassTest.php | 38 + .../Test/CodeCleaner/InstanceOfPassTest.php | 74 + .../CodeCleaner/LeavePsyshAlonePassTest.php | 69 + .../Test/CodeCleaner/LegacyEmptyPassTest.php | 77 + .../CodeCleaner/MagicConstantsPassTest.php | 39 + .../Test/CodeCleaner/NamespacePassTest.php | 49 + .../CodeCleaner/StaticConstructorPassTest.php | 91 + .../Test/CodeCleaner/UseStatementPassTest.php | 52 + .../CodeCleaner/ValidClassNamePassTest.php | 244 ++ .../CodeCleaner/ValidConstantPassTest.php | 66 + .../CodeCleaner/ValidFunctionNamePassTest.php | 130 + .../psysh/test/Psy/Test/CodeCleanerTest.php | 95 + .../psysh/test/Psy/Test/ConfigurationTest.php | 173 + .../Psy/Test/Exception/BreakExceptionTest.php | 34 + .../Psy/Test/Exception/ErrorExceptionTest.php | 108 + .../Exception/FatalErrorExceptionTest.php | 46 + .../Exception/ParseErrorExceptionTest.php | 43 + .../Test/Exception/RuntimeExceptionTest.php | 31 + .../Psy/Test/Formatter/CodeFormatterTest.php | 61 + .../Test/Formatter/DocblockFormatterTest.php | 63 + .../Test/Formatter/SignatureFormatterTest.php | 77 + .../Psy/Test/Readline/GNUReadlineTest.php | 80 + .../test/Psy/Test/Readline/LibeditTest.php | 128 + .../test/Psy/Test/Readline/TransientTest.php | 76 + .../Reflection/ReflectionConstantTest.php | 60 + vendor/psy/psysh/test/Psy/Test/ShellTest.php | 340 ++ .../Test/TabCompletion/AutoCompleterTest.php | 145 + .../Psy/Test/TabCompletion/StaticSample.php | 27 + .../psysh/test/Psy/Test/Util/DocblockTest.php | 96 + .../psysh/test/Psy/Test/Util/MirrorTest.php | 80 + .../psy/psysh/test/Psy/Test/Util/StrTest.php | 31 + vendor/psy/psysh/test/fixtures/config.php | 18 + .../fixtures/default/.config/psysh/config.php | 1 + .../default/.config/psysh/psysh_history | 0 .../.local/share/psysh/php_manual.sqlite | 0 vendor/psy/psysh/test/fixtures/empty.php | 11 + .../psysh/test/fixtures/legacy/.psysh/history | 0 .../fixtures/legacy/.psysh/php_manual.sqlite | 0 .../psysh/test/fixtures/legacy/.psysh/rc.php | 1 + .../test/fixtures/mixed/.psysh/config.php | 1 + .../test/fixtures/mixed/.psysh/psysh_history | 0 .../psysh/test/fixtures/mixed/.psysh/rc.php | 1 + .../psysh/test/fixtures/project/.psysh.php | 15 + .../psysh/test/fixtures/unvis_fixtures.json | 1 + .../psysh/test/tools/gen_unvis_fixtures.py | 94 + vendor/psy/psysh/test/tools/vis.py | 126 + vendor/sebastian/comparator/.gitignore | 6 + vendor/sebastian/comparator/.travis.yml | 25 + vendor/sebastian/comparator/LICENSE | 33 + vendor/sebastian/comparator/README.md | 41 + vendor/sebastian/comparator/build.xml | 34 + .../sebastian/comparator/build/travis-ci.xml | 11 + vendor/sebastian/comparator/composer.json | 44 + vendor/sebastian/comparator/phpunit.xml.dist | 21 + .../comparator/src/ArrayComparator.php | 136 + .../sebastian/comparator/src/Comparator.php | 68 + .../comparator/src/ComparisonFailure.php | 129 + .../comparator/src/DOMNodeComparator.php | 110 + .../comparator/src/DateTimeComparator.php | 80 + .../comparator/src/DoubleComparator.php | 60 + .../comparator/src/ExceptionComparator.php | 51 + vendor/sebastian/comparator/src/Factory.php | 107 + .../comparator/src/MockObjectComparator.php | 45 + .../comparator/src/NumericComparator.php | 72 + .../comparator/src/ObjectComparator.php | 109 + .../comparator/src/ResourceComparator.php | 56 + .../comparator/src/ScalarComparator.php | 94 + .../src/SplObjectStorageComparator.php | 73 + .../comparator/src/TypeComparator.php | 63 + .../comparator/tests/ArrayComparatorTest.php | 163 + .../tests/DOMNodeComparatorTest.php | 162 + .../tests/DateTimeComparatorTest.php | 216 ++ .../comparator/tests/DoubleComparatorTest.php | 134 + .../tests/ExceptionComparatorTest.php | 136 + .../comparator/tests/FactoryTest.php | 115 + .../tests/MockObjectComparatorTest.php | 166 + .../tests/NumericComparatorTest.php | 122 + .../comparator/tests/ObjectComparatorTest.php | 150 + .../tests/ResourceComparatorTest.php | 120 + .../comparator/tests/ScalarComparatorTest.php | 158 + .../tests/SplObjectStorageComparatorTest.php | 137 + .../comparator/tests/TypeComparatorTest.php | 104 + .../comparator/tests/_files/Author.php | 28 + .../comparator/tests/_files/Book.php | 21 + .../tests/_files/ClassWithToString.php | 19 + .../comparator/tests/_files/SampleClass.php | 29 + .../comparator/tests/_files/Struct.php | 25 + .../comparator/tests/_files/TestClass.php | 14 + .../tests/_files/TestClassComparator.php | 14 + .../sebastian/comparator/tests/autoload.php | 38 + .../sebastian/comparator/tests/bootstrap.php | 7 + vendor/sebastian/diff/.gitignore | 10 + vendor/sebastian/diff/.php_cs | 66 + vendor/sebastian/diff/.travis.yml | 16 + vendor/sebastian/diff/LICENSE | 33 + vendor/sebastian/diff/README.md | 126 + vendor/sebastian/diff/build.xml | 26 + vendor/sebastian/diff/composer.json | 33 + vendor/sebastian/diff/phpunit.xml.dist | 17 + vendor/sebastian/diff/src/Chunk.php | 104 + vendor/sebastian/diff/src/Diff.php | 75 + vendor/sebastian/diff/src/Differ.php | 261 ++ .../diff/src/LCS/LongestCommonSubsequence.php | 27 + ...LongestCommonSubsequenceImplementation.php | 93 + ...LongestCommonSubsequenceImplementation.php | 73 + vendor/sebastian/diff/src/Line.php | 56 + vendor/sebastian/diff/src/Parser.php | 99 + vendor/sebastian/diff/tests/DifferTest.php | Bin 0 -> 11492 bytes .../LCS/TimeEfficientImplementationTest.php | 175 + vendor/sebastian/diff/tests/ParserTest.php | 62 + .../sebastian/diff/tests/fixtures/patch.txt | 9 + .../sebastian/diff/tests/fixtures/patch2.txt | 21 + vendor/sebastian/environment/.gitignore | 5 + vendor/sebastian/environment/.travis.yml | 16 + vendor/sebastian/environment/LICENSE | 33 + vendor/sebastian/environment/README.md | 72 + vendor/sebastian/environment/build.xml | 26 + vendor/sebastian/environment/composer.json | 29 + vendor/sebastian/environment/phpunit.xml.dist | 20 + vendor/sebastian/environment/src/Console.php | 81 + vendor/sebastian/environment/src/Runtime.php | 192 ++ .../environment/tests/ConsoleTest.php | 60 + .../environment/tests/RuntimeTest.php | 112 + vendor/sebastian/exporter/.gitignore | 9 + vendor/sebastian/exporter/.travis.yml | 23 + vendor/sebastian/exporter/LICENSE | 33 + vendor/sebastian/exporter/README.md | 171 + vendor/sebastian/exporter/build.xml | 27 + vendor/sebastian/exporter/composer.json | 47 + vendor/sebastian/exporter/phpunit.xml.dist | 21 + vendor/sebastian/exporter/src/Exporter.php | 296 ++ .../sebastian/exporter/tests/ExporterTest.php | 333 ++ vendor/sebastian/global-state/.gitignore | 6 + vendor/sebastian/global-state/.travis.yml | 20 + vendor/sebastian/global-state/LICENSE | 33 + vendor/sebastian/global-state/README.md | 15 + vendor/sebastian/global-state/build.xml | 33 + vendor/sebastian/global-state/composer.json | 37 + .../sebastian/global-state/phpunit.xml.dist | 21 + .../sebastian/global-state/src/Blacklist.php | 149 + .../global-state/src/CodeExporter.php | 93 + .../sebastian/global-state/src/Exception.php | 17 + .../sebastian/global-state/src/Restorer.php | 141 + .../global-state/src/RuntimeException.php | 17 + .../sebastian/global-state/src/Snapshot.php | 423 +++ .../global-state/tests/BlacklistTest.php | 113 + .../global-state/tests/SnapshotTest.php | 119 + .../tests/_fixture/BlacklistedChildClass.php | 17 + .../tests/_fixture/BlacklistedClass.php | 18 + .../tests/_fixture/BlacklistedImplementor.php | 18 + .../tests/_fixture/BlacklistedInterface.php | 17 + .../tests/_fixture/SnapshotClass.php | 37 + .../tests/_fixture/SnapshotDomDocument.php | 19 + .../tests/_fixture/SnapshotFunctions.php | 15 + .../tests/_fixture/SnapshotTrait.php | 17 + vendor/sebastian/recursion-context/.gitignore | 9 + .../sebastian/recursion-context/.travis.yml | 21 + vendor/sebastian/recursion-context/LICENSE | 33 + vendor/sebastian/recursion-context/README.md | 13 + vendor/sebastian/recursion-context/build.xml | 27 + .../sebastian/recursion-context/composer.json | 36 + .../recursion-context/phpunit.xml.dist | 20 + .../recursion-context/src/Context.php | 153 + .../recursion-context/src/Exception.php | 17 + .../src/InvalidArgumentException.php | 17 + .../recursion-context/tests/ContextTest.php | 144 + vendor/sebastian/version/.gitattributes | 1 + vendor/sebastian/version/.gitignore | 1 + vendor/sebastian/version/LICENSE | 33 + vendor/sebastian/version/README.md | 37 + vendor/sebastian/version/composer.json | 21 + vendor/sebastian/version/src/Version.php | 82 + vendor/swiftmailer/swiftmailer/.gitattributes | 9 + vendor/swiftmailer/swiftmailer/.gitignore | 4 + vendor/swiftmailer/swiftmailer/.travis.yml | 25 + vendor/swiftmailer/swiftmailer/CHANGES | 206 ++ vendor/swiftmailer/swiftmailer/LICENSE | 19 + vendor/swiftmailer/swiftmailer/README | 16 + vendor/swiftmailer/swiftmailer/VERSION | 1 + vendor/swiftmailer/swiftmailer/composer.json | 31 + .../swiftmailer/swiftmailer/doc/headers.rst | 742 ++++ .../swiftmailer/doc/help-resources.rst | 44 + .../swiftmailer/doc/including-the-files.rst | 46 + vendor/swiftmailer/swiftmailer/doc/index.rst | 16 + .../swiftmailer/doc/installing.rst | 89 + .../swiftmailer/doc/introduction.rst | 135 + .../swiftmailer/swiftmailer/doc/japanese.rst | 22 + .../swiftmailer/swiftmailer/doc/messages.rst | 1057 ++++++ .../swiftmailer/swiftmailer/doc/overview.rst | 161 + .../swiftmailer/swiftmailer/doc/plugins.rst | 385 +++ .../swiftmailer/swiftmailer/doc/sending.rst | 607 ++++ .../swiftmailer/doc/uml/Encoders.graffle | Bin 0 -> 3503 bytes .../swiftmailer/doc/uml/Mime.graffle | Bin 0 -> 5575 bytes .../swiftmailer/doc/uml/Transports.graffle | Bin 0 -> 3061 bytes .../swiftmailer/lib/classes/Swift.php | 80 + .../lib/classes/Swift/Attachment.php | 71 + .../AbstractFilterableInputStream.php | 179 + .../Swift/ByteStream/ArrayByteStream.php | 184 + .../Swift/ByteStream/FileByteStream.php | 229 ++ .../ByteStream/TemporaryFileByteStream.php | 42 + .../lib/classes/Swift/CharacterReader.php | 67 + .../GenericFixedWidthReader.php | 97 + .../Swift/CharacterReader/UsAsciiReader.php | 84 + .../Swift/CharacterReader/Utf8Reader.php | 179 + .../classes/Swift/CharacterReaderFactory.php | 26 + .../SimpleCharacterReaderFactory.php | 124 + .../lib/classes/Swift/CharacterStream.php | 89 + .../CharacterStream/ArrayCharacterStream.php | 293 ++ .../CharacterStream/NgCharacterStream.php | 275 ++ .../lib/classes/Swift/ConfigurableSpool.php | 63 + .../lib/classes/Swift/DependencyContainer.php | 373 ++ .../lib/classes/Swift/DependencyException.php | 27 + .../lib/classes/Swift/EmbeddedFile.php | 69 + .../swiftmailer/lib/classes/Swift/Encoder.php | 28 + .../classes/Swift/Encoder/Base64Encoder.php | 58 + .../lib/classes/Swift/Encoder/QpEncoder.php | 289 ++ .../classes/Swift/Encoder/Rfc2231Encoder.php | 92 + .../lib/classes/Swift/Encoding.php | 64 + .../lib/classes/Swift/Events/CommandEvent.php | 65 + .../classes/Swift/Events/CommandListener.php | 24 + .../lib/classes/Swift/Events/Event.php | 38 + .../classes/Swift/Events/EventDispatcher.php | 83 + .../classes/Swift/Events/EventListener.php | 18 + .../lib/classes/Swift/Events/EventObject.php | 63 + .../classes/Swift/Events/ResponseEvent.php | 65 + .../classes/Swift/Events/ResponseListener.php | 24 + .../lib/classes/Swift/Events/SendEvent.php | 129 + .../lib/classes/Swift/Events/SendListener.php | 31 + .../Swift/Events/SimpleEventDispatcher.php | 156 + .../Swift/Events/TransportChangeEvent.php | 27 + .../Swift/Events/TransportChangeListener.php | 45 + .../Swift/Events/TransportExceptionEvent.php | 46 + .../Events/TransportExceptionListener.php | 24 + .../lib/classes/Swift/FailoverTransport.php | 45 + .../lib/classes/Swift/FileSpool.php | 208 ++ .../lib/classes/Swift/FileStream.php | 24 + .../lib/classes/Swift/Filterable.php | 32 + .../swiftmailer/lib/classes/Swift/Image.php | 61 + .../lib/classes/Swift/InputByteStream.php | 75 + .../lib/classes/Swift/IoException.php | 29 + .../lib/classes/Swift/KeyCache.php | 105 + .../classes/Swift/KeyCache/ArrayKeyCache.php | 206 ++ .../classes/Swift/KeyCache/DiskKeyCache.php | 324 ++ .../Swift/KeyCache/KeyCacheInputStream.php | 51 + .../classes/Swift/KeyCache/NullKeyCache.php | 115 + .../KeyCache/SimpleKeyCacheInputStream.php | 127 + .../classes/Swift/LoadBalancedTransport.php | 45 + .../lib/classes/Swift/MailTransport.php | 45 + .../swiftmailer/lib/classes/Swift/Mailer.php | 114 + .../Swift/Mailer/ArrayRecipientIterator.php | 55 + .../Swift/Mailer/RecipientIterator.php | 32 + .../lib/classes/Swift/MemorySpool.php | 84 + .../swiftmailer/lib/classes/Swift/Message.php | 291 ++ .../lib/classes/Swift/Mime/Attachment.php | 153 + .../classes/Swift/Mime/CharsetObserver.php | 24 + .../lib/classes/Swift/Mime/ContentEncoder.php | 34 + .../ContentEncoder/Base64ContentEncoder.php | 104 + .../ContentEncoder/NativeQpContentEncoder.php | 123 + .../ContentEncoder/PlainContentEncoder.php | 162 + .../Mime/ContentEncoder/QpContentEncoder.php | 123 + .../ContentEncoder/QpContentEncoderProxy.php | 97 + .../Mime/ContentEncoder/RawContentEncoder.php | 64 + .../lib/classes/Swift/Mime/EmbeddedFile.php | 45 + .../classes/Swift/Mime/EncodingObserver.php | 24 + .../lib/classes/Swift/Mime/Grammar.php | 176 + .../lib/classes/Swift/Mime/Header.php | 93 + .../lib/classes/Swift/Mime/HeaderEncoder.php | 24 + .../HeaderEncoder/Base64HeaderEncoder.php | 55 + .../Mime/HeaderEncoder/QpHeaderEncoder.php | 65 + .../lib/classes/Swift/Mime/HeaderFactory.php | 78 + .../lib/classes/Swift/Mime/HeaderSet.php | 169 + .../Swift/Mime/Headers/AbstractHeader.php | 503 +++ .../classes/Swift/Mime/Headers/DateHeader.php | 125 + .../Mime/Headers/IdentificationHeader.php | 180 + .../Swift/Mime/Headers/MailboxHeader.php | 354 ++ .../Swift/Mime/Headers/OpenDKIMHeader.php | 137 + .../Mime/Headers/ParameterizedHeader.php | 260 ++ .../classes/Swift/Mime/Headers/PathHeader.php | 143 + .../Swift/Mime/Headers/UnstructuredHeader.php | 112 + .../lib/classes/Swift/Mime/Message.php | 223 ++ .../lib/classes/Swift/Mime/MimeEntity.php | 117 + .../lib/classes/Swift/Mime/MimePart.php | 214 ++ .../Swift/Mime/ParameterizedHeader.php | 34 + .../Swift/Mime/SimpleHeaderFactory.php | 198 ++ .../classes/Swift/Mime/SimpleHeaderSet.php | 396 +++ .../lib/classes/Swift/Mime/SimpleMessage.php | 649 ++++ .../classes/Swift/Mime/SimpleMimeEntity.php | 867 +++++ .../lib/classes/Swift/MimePart.php | 59 + .../lib/classes/Swift/NullTransport.php | 39 + .../lib/classes/Swift/OutputByteStream.php | 46 + .../classes/Swift/Plugins/AntiFloodPlugin.php | 141 + .../Swift/Plugins/BandwidthMonitorPlugin.php | 164 + .../Swift/Plugins/Decorator/Replacements.php | 31 + .../classes/Swift/Plugins/DecoratorPlugin.php | 207 ++ .../Swift/Plugins/ImpersonatePlugin.php | 69 + .../lib/classes/Swift/Plugins/Logger.php | 36 + .../classes/Swift/Plugins/LoggerPlugin.php | 142 + .../Swift/Plugins/Loggers/ArrayLogger.php | 72 + .../Swift/Plugins/Loggers/EchoLogger.php | 58 + .../classes/Swift/Plugins/MessageLogger.php | 74 + .../Swift/Plugins/Pop/Pop3Connection.php | 31 + .../Swift/Plugins/Pop/Pop3Exception.php | 27 + .../Swift/Plugins/PopBeforeSmtpPlugin.php | 273 ++ .../Swift/Plugins/RedirectingPlugin.php | 213 ++ .../lib/classes/Swift/Plugins/Reporter.php | 32 + .../classes/Swift/Plugins/ReporterPlugin.php | 73 + .../Swift/Plugins/Reporters/HitReporter.php | 59 + .../Swift/Plugins/Reporters/HtmlReporter.php | 39 + .../lib/classes/Swift/Plugins/Sleeper.php | 24 + .../classes/Swift/Plugins/ThrottlerPlugin.php | 200 ++ .../lib/classes/Swift/Plugins/Timer.php | 24 + .../lib/classes/Swift/Preferences.php | 103 + .../Swift/ReplacementFilterFactory.php | 27 + .../classes/Swift/RfcComplianceException.php | 27 + .../lib/classes/Swift/SendmailTransport.php | 45 + .../lib/classes/Swift/SignedMessage.php | 23 + .../swiftmailer/lib/classes/Swift/Signer.php | 20 + .../lib/classes/Swift/Signers/BodySigner.php | 33 + .../lib/classes/Swift/Signers/DKIMSigner.php | 702 ++++ .../classes/Swift/Signers/DomainKeySigner.php | 525 +++ .../classes/Swift/Signers/HeaderSigner.php | 65 + .../classes/Swift/Signers/OpenDKIMSigner.php | 189 ++ .../lib/classes/Swift/Signers/SMimeSigner.php | 437 +++ .../lib/classes/Swift/SmtpTransport.php | 58 + .../swiftmailer/lib/classes/Swift/Spool.php | 53 + .../lib/classes/Swift/SpoolTransport.php | 47 + .../lib/classes/Swift/StreamFilter.php | 35 + .../ByteArrayReplacementFilter.php | 169 + .../StreamFilters/StringReplacementFilter.php | 66 + .../StringReplacementFilterFactory.php | 45 + .../lib/classes/Swift/SwiftException.php | 29 + .../lib/classes/Swift/Transport.php | 54 + .../Swift/Transport/AbstractSmtpTransport.php | 490 +++ .../Esmtp/Auth/CramMd5Authenticator.php | 81 + .../Esmtp/Auth/LoginAuthenticator.php | 51 + .../Esmtp/Auth/NTLMAuthenticator.php | 726 ++++ .../Esmtp/Auth/PlainAuthenticator.php | 50 + .../Esmtp/Auth/XOAuth2Authenticator.php | 70 + .../Swift/Transport/Esmtp/AuthHandler.php | 263 ++ .../Swift/Transport/Esmtp/Authenticator.php | 35 + .../classes/Swift/Transport/EsmtpHandler.php | 86 + .../Swift/Transport/EsmtpTransport.php | 386 +++ .../Swift/Transport/FailoverTransport.php | 85 + .../lib/classes/Swift/Transport/IoBuffer.php | 67 + .../Swift/Transport/LoadBalancedTransport.php | 166 + .../classes/Swift/Transport/MailInvoker.php | 32 + .../classes/Swift/Transport/MailTransport.php | 237 ++ .../classes/Swift/Transport/NullTransport.php | 93 + .../Swift/Transport/SendmailTransport.php | 159 + .../Swift/Transport/SimpleMailInvoker.php | 39 + .../lib/classes/Swift/Transport/SmtpAgent.php | 36 + .../Swift/Transport/SpoolTransport.php | 117 + .../classes/Swift/Transport/StreamBuffer.php | 321 ++ .../lib/classes/Swift/TransportException.php | 29 + .../lib/classes/Swift/Validate.php | 43 + .../lib/dependency_maps/cache_deps.php | 23 + .../lib/dependency_maps/message_deps.php | 9 + .../lib/dependency_maps/mime_deps.php | 123 + .../lib/dependency_maps/transport_deps.php | 76 + .../swiftmailer/lib/mime_types.php | 1007 ++++++ .../swiftmailer/lib/preferences.php | 25 + .../swiftmailer/lib/swift_init.php | 28 + .../swiftmailer/lib/swift_required.php | 30 + .../swiftmailer/lib/swift_required_pear.php | 30 + .../lib/swiftmailer_generate_mimes_config.php | 193 ++ .../swiftmailer/swiftmailer/phpunit.xml.dist | 37 + .../tests/IdenticalBinaryConstraint.php | 62 + .../swiftmailer/tests/StreamCollector.php | 11 + .../tests/SwiftMailerSmokeTestCase.php | 46 + .../swiftmailer/tests/SwiftMailerTestCase.php | 34 + .../_samples/charsets/iso-2022-jp/one.txt | 11 + .../_samples/charsets/iso-8859-1/one.txt | 19 + .../tests/_samples/charsets/utf-8/one.txt | 22 + .../tests/_samples/charsets/utf-8/three.txt | 45 + .../tests/_samples/charsets/utf-8/two.txt | 3 + .../tests/_samples/dkim/dkim.test.priv | 15 + .../tests/_samples/dkim/dkim.test.pub | 6 + .../swiftmailer/tests/_samples/files/data.txt | 1 + .../tests/_samples/files/swiftmailer.png | Bin 0 -> 3194 bytes .../tests/_samples/files/textfile.zip | Bin 0 -> 202 bytes .../swiftmailer/tests/_samples/smime/CA.srl | 1 + .../swiftmailer/tests/_samples/smime/ca.crt | 21 + .../swiftmailer/tests/_samples/smime/ca.key | 27 + .../tests/_samples/smime/create-cert.sh | 40 + .../tests/_samples/smime/encrypt.crt | 19 + .../tests/_samples/smime/encrypt.key | 27 + .../tests/_samples/smime/encrypt2.crt | 19 + .../tests/_samples/smime/encrypt2.key | 27 + .../tests/_samples/smime/intermediate.crt | 19 + .../tests/_samples/smime/intermediate.key | 27 + .../swiftmailer/tests/_samples/smime/sign.crt | 19 + .../swiftmailer/tests/_samples/smime/sign.key | 27 + .../tests/_samples/smime/sign2.crt | 19 + .../tests/_samples/smime/sign2.key | 27 + .../tests/acceptance.conf.php.default | 44 + .../Swift/AttachmentAcceptanceTest.php | 12 + .../FileByteStreamAcceptanceTest.php | 174 + ...leCharacterReaderFactoryAcceptanceTest.php | 179 + .../DependencyContainerAcceptanceTest.php | 20 + .../Swift/EmbeddedFileAcceptanceTest.php | 12 + .../Encoder/Base64EncoderAcceptanceTest.php | 45 + .../Swift/Encoder/QpEncoderAcceptanceTest.php | 50 + .../Encoder/Rfc2231EncoderAcceptanceTest.php | 50 + .../Swift/EncodingAcceptanceTest.php | 30 + .../KeyCache/ArrayKeyCacheAcceptanceTest.php | 173 + .../KeyCache/DiskKeyCacheAcceptanceTest.php | 183 + .../Swift/MessageAcceptanceTest.php | 57 + .../Swift/Mime/AttachmentAcceptanceTest.php | 125 + .../Base64ContentEncoderAcceptanceTest.php | 56 + .../NativeQpContentEncoderAcceptanceTest.php | 86 + .../PlainContentEncoderAcceptanceTest.php | 88 + .../QpContentEncoderAcceptanceTest.php | 157 + .../Swift/Mime/EmbeddedFileAcceptanceTest.php | 137 + .../Base64HeaderEncoderAcceptanceTest.php | 32 + .../Swift/Mime/MimePartAcceptanceTest.php | 129 + .../Mime/SimpleMessageAcceptanceTest.php | 1251 +++++++ .../Swift/MimePartAcceptanceTest.php | 15 + .../AbstractStreamBufferAcceptanceTest.php | 134 + .../BasicSocketAcceptanceTest.php | 34 + .../StreamBuffer/ProcessAcceptanceTest.php | 27 + .../StreamBuffer/SocketTimeoutTest.php | 65 + .../StreamBuffer/SslSocketAcceptanceTest.php | 41 + .../StreamBuffer/TlsSocketAcceptanceTest.php | 40 + .../swiftmailer/tests/bootstrap.php | 19 + .../tests/bug/Swift/Bug111Test.php | 42 + .../tests/bug/Swift/Bug118Test.php | 20 + .../tests/bug/Swift/Bug206Test.php | 38 + .../tests/bug/Swift/Bug274Test.php | 21 + .../swiftmailer/tests/bug/Swift/Bug34Test.php | 75 + .../swiftmailer/tests/bug/Swift/Bug35Test.php | 73 + .../swiftmailer/tests/bug/Swift/Bug38Test.php | 194 ++ .../tests/bug/Swift/Bug518Test.php | 38 + .../swiftmailer/tests/bug/Swift/Bug51Test.php | 121 + .../tests/bug/Swift/Bug534Test.php | 38 + .../swiftmailer/tests/bug/Swift/Bug71Test.php | 20 + .../swiftmailer/tests/bug/Swift/Bug76Test.php | 82 + .../tests/fixtures/EsmtpTransportFixture.php | 10 + .../tests/fixtures/MimeEntityFixture.php | 59 + .../swiftmailer/tests/smoke.conf.php.default | 63 + .../smoke/Swift/Smoke/AttachmentSmokeTest.php | 30 + .../smoke/Swift/Smoke/BasicSmokeTest.php | 23 + .../Smoke/HtmlWithAttachmentSmokeTest.php | 29 + .../Swift/Smoke/InternationalSmokeTest.php | 37 + .../Swift/ByteStream/ArrayByteStreamTest.php | 204 ++ .../GenericFixedWidthReaderTest.php | 43 + .../CharacterReader/UsAsciiReaderTest.php | 52 + .../Swift/CharacterReader/Utf8ReaderTest.php | 65 + .../ArrayCharacterStreamTest.php | 360 ++ .../unit/Swift/DependencyContainerTest.php | 174 + .../unit/Swift/Encoder/Base64EncoderTest.php | 173 + .../unit/Swift/Encoder/QpEncoderTest.php | 381 +++ .../unit/Swift/Encoder/Rfc2231EncoderTest.php | 141 + .../unit/Swift/Events/CommandEventTest.php | 36 + .../unit/Swift/Events/EventObjectTest.php | 34 + .../unit/Swift/Events/ResponseEventTest.php | 40 + .../tests/unit/Swift/Events/SendEventTest.php | 99 + .../Events/SimpleEventDispatcherTest.php | 135 + .../Swift/Events/TransportChangeEventTest.php | 32 + .../Events/TransportExceptionEventTest.php | 43 + .../unit/Swift/KeyCache/ArrayKeyCacheTest.php | 242 ++ .../SimpleKeyCacheInputStreamTest.php | 73 + .../Mailer/ArrayRecipientIteratorTest.php | 42 + .../tests/unit/Swift/MailerTest.php | 152 + .../tests/unit/Swift/MessageTest.php | 130 + .../Swift/Mime/AbstractMimeEntityTest.php | 1054 ++++++ .../tests/unit/Swift/Mime/AttachmentTest.php | 320 ++ .../Base64ContentEncoderTest.php | 323 ++ .../PlainContentEncoderTest.php | 173 + .../ContentEncoder/QpContentEncoderTest.php | 496 +++ .../unit/Swift/Mime/EmbeddedFileTest.php | 57 + .../HeaderEncoder/Base64HeaderEncoderTest.php | 13 + .../HeaderEncoder/QpHeaderEncoderTest.php | 223 ++ .../Swift/Mime/Headers/DateHeaderTest.php | 69 + .../Mime/Headers/IdentificationHeaderTest.php | 189 ++ .../Swift/Mime/Headers/MailboxHeaderTest.php | 327 ++ .../Mime/Headers/ParameterizedHeaderTest.php | 400 +++ .../Swift/Mime/Headers/PathHeaderTest.php | 77 + .../Mime/Headers/UnstructuredHeaderTest.php | 355 ++ .../tests/unit/Swift/Mime/MimePartTest.php | 233 ++ .../Swift/Mime/SimpleHeaderFactoryTest.php | 168 + .../unit/Swift/Mime/SimpleHeaderSetTest.php | 734 ++++ .../unit/Swift/Mime/SimpleMessageTest.php | 829 +++++ .../unit/Swift/Mime/SimpleMimeEntityTest.php | 11 + .../Swift/Plugins/AntiFloodPluginTest.php | 95 + .../Plugins/BandwidthMonitorPluginTest.php | 127 + .../Swift/Plugins/DecoratorPluginTest.php | 269 ++ .../unit/Swift/Plugins/LoggerPluginTest.php | 190 ++ .../Swift/Plugins/Loggers/ArrayLoggerTest.php | 65 + .../Swift/Plugins/Loggers/EchoLoggerTest.php | 24 + .../Swift/Plugins/PopBeforeSmtpPluginTest.php | 103 + .../Swift/Plugins/RedirectingPluginTest.php | 185 + .../unit/Swift/Plugins/ReporterPluginTest.php | 88 + .../Plugins/Reporters/HitReporterTest.php | 64 + .../Plugins/Reporters/HtmlReporterTest.php | 54 + .../Swift/Plugins/ThrottlerPluginTest.php | 104 + .../unit/Swift/Signers/DKIMSignerTest.php | 227 ++ .../unit/Swift/Signers/OpenDKIMSignerTest.php | 45 + .../unit/Swift/Signers/SMimeSignerTest.php | 554 +++ .../ByteArrayReplacementFilterTest.php | 131 + .../StringReplacementFilterFactoryTest.php | 38 + .../StringReplacementFilterTest.php | 55 + .../AbstractSmtpEventSupportTest.php | 560 +++ .../unit/Swift/Transport/AbstractSmtpTest.php | 1249 +++++++ .../Esmtp/Auth/CramMd5AuthenticatorTest.php | 66 + .../Esmtp/Auth/LoginAuthenticatorTest.php | 66 + .../Esmtp/Auth/NTLMAuthenticatorTest.php | 235 ++ .../Esmtp/Auth/PlainAuthenticatorTest.php | 69 + .../Swift/Transport/Esmtp/AuthHandlerTest.php | 167 + .../EsmtpTransport/ExtensionSupportTest.php | 530 +++ .../Swift/Transport/EsmtpTransportTest.php | 298 ++ .../Swift/Transport/FailoverTransportTest.php | 520 +++ .../Transport/LoadBalancedTransportTest.php | 751 ++++ .../Swift/Transport/MailTransportTest.php | 311 ++ .../Swift/Transport/SendmailTransportTest.php | 152 + .../unit/Swift/Transport/StreamBufferTest.php | 45 + vendor/symfony/console/.gitignore | 3 + vendor/symfony/console/Application.php | 1088 ++++++ vendor/symfony/console/CHANGELOG.md | 68 + vendor/symfony/console/Command/Command.php | 626 ++++ .../symfony/console/Command/HelpCommand.php | 86 + .../symfony/console/Command/ListCommand.php | 90 + vendor/symfony/console/ConsoleEvents.php | 61 + .../Descriptor/ApplicationDescription.php | 160 + .../symfony/console/Descriptor/Descriptor.php | 122 + .../Descriptor/DescriptorInterface.php | 31 + .../console/Descriptor/JsonDescriptor.php | 166 + .../console/Descriptor/MarkdownDescriptor.php | 143 + .../console/Descriptor/TextDescriptor.php | 283 ++ .../console/Descriptor/XmlDescriptor.php | 263 ++ .../console/Event/ConsoleCommandEvent.php | 62 + vendor/symfony/console/Event/ConsoleEvent.php | 67 + .../console/Event/ConsoleExceptionEvent.php | 67 + .../console/Event/ConsoleTerminateEvent.php | 58 + .../Exception/CommandNotFoundException.php | 43 + .../console/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 19 + .../Exception/InvalidOptionException.php | 21 + .../console/Exception/LogicException.php | 19 + .../console/Exception/RuntimeException.php | 19 + .../console/Formatter/OutputFormatter.php | 228 ++ .../Formatter/OutputFormatterInterface.php | 69 + .../Formatter/OutputFormatterStyle.php | 221 ++ .../OutputFormatterStyleInterface.php | 64 + .../Formatter/OutputFormatterStyleStack.php | 123 + .../console/Helper/DebugFormatterHelper.php | 127 + .../console/Helper/DescriptorHelper.php | 97 + .../console/Helper/FormatterHelper.php | 82 + vendor/symfony/console/Helper/Helper.php | 117 + .../console/Helper/HelperInterface.php | 41 + vendor/symfony/console/Helper/HelperSet.php | 109 + .../console/Helper/InputAwareHelper.php | 33 + .../symfony/console/Helper/ProcessHelper.php | 144 + vendor/symfony/console/Helper/ProgressBar.php | 601 ++++ .../console/Helper/ProgressIndicator.php | 322 ++ .../symfony/console/Helper/QuestionHelper.php | 465 +++ .../console/Helper/SymfonyQuestionHelper.php | 107 + vendor/symfony/console/Helper/Table.php | 654 ++++ vendor/symfony/console/Helper/TableCell.php | 79 + .../symfony/console/Helper/TableSeparator.php | 29 + vendor/symfony/console/Helper/TableStyle.php | 258 ++ vendor/symfony/console/Input/ArgvInput.php | 358 ++ vendor/symfony/console/Input/ArrayInput.php | 223 ++ vendor/symfony/console/Input/Input.php | 236 ++ .../symfony/console/Input/InputArgument.php | 131 + .../console/Input/InputAwareInterface.php | 28 + .../symfony/console/Input/InputDefinition.php | 411 +++ .../symfony/console/Input/InputInterface.php | 154 + vendor/symfony/console/Input/InputOption.php | 212 ++ vendor/symfony/console/Input/StringInput.php | 74 + vendor/symfony/console/LICENSE | 19 + .../symfony/console/Logger/ConsoleLogger.php | 119 + .../symfony/console/Output/BufferedOutput.php | 48 + .../symfony/console/Output/ConsoleOutput.php | 156 + .../console/Output/ConsoleOutputInterface.php | 35 + vendor/symfony/console/Output/NullOutput.php | 111 + vendor/symfony/console/Output/Output.php | 165 + .../console/Output/OutputInterface.php | 119 + .../symfony/console/Output/StreamOutput.php | 101 + .../console/Question/ChoiceQuestion.php | 177 + .../console/Question/ConfirmationQuestion.php | 61 + vendor/symfony/console/Question/Question.php | 250 ++ vendor/symfony/console/README.md | 67 + .../console/Resources/bin/hiddeninput.exe | Bin 0 -> 9216 bytes vendor/symfony/console/Style/OutputStyle.php | 148 + .../symfony/console/Style/StyleInterface.php | 159 + vendor/symfony/console/Style/SymfonyStyle.php | 415 +++ .../console/Tester/ApplicationTester.php | 128 + .../symfony/console/Tester/CommandTester.php | 132 + .../symfony/console/Tests/ApplicationTest.php | 1093 ++++++ .../console/Tests/Command/CommandTest.php | 350 ++ .../console/Tests/Command/HelpCommandTest.php | 70 + .../console/Tests/Command/ListCommandTest.php | 112 + .../Descriptor/AbstractDescriptorTest.php | 106 + .../Tests/Descriptor/JsonDescriptorTest.php | 35 + .../Descriptor/MarkdownDescriptorTest.php | 27 + .../Tests/Descriptor/ObjectsProvider.php | 77 + .../Tests/Descriptor/TextDescriptorTest.php | 27 + .../Tests/Descriptor/XmlDescriptorTest.php | 27 + .../console/Tests/Fixtures/BarBucCommand.php | 11 + .../Tests/Fixtures/DescriptorApplication1.php | 18 + .../Tests/Fixtures/DescriptorApplication2.php | 24 + .../Tests/Fixtures/DescriptorCommand1.php | 27 + .../Tests/Fixtures/DescriptorCommand2.php | 32 + .../console/Tests/Fixtures/DummyOutput.php | 36 + .../console/Tests/Fixtures/Foo1Command.php | 26 + .../console/Tests/Fixtures/Foo2Command.php | 21 + .../console/Tests/Fixtures/Foo3Command.php | 29 + .../console/Tests/Fixtures/Foo4Command.php | 11 + .../console/Tests/Fixtures/Foo5Command.php | 10 + .../console/Tests/Fixtures/Foo6Command.php | 12 + .../console/Tests/Fixtures/FooCommand.php | 33 + .../Fixtures/FooSubnamespaced1Command.php | 26 + .../Fixtures/FooSubnamespaced2Command.php | 26 + .../console/Tests/Fixtures/FoobarCommand.php | 25 + .../Style/SymfonyStyle/command/command_0.php | 11 + .../Style/SymfonyStyle/command/command_1.php | 13 + .../Style/SymfonyStyle/command/command_2.php | 16 + .../Style/SymfonyStyle/command/command_3.php | 12 + .../Style/SymfonyStyle/command/command_4.php | 34 + .../Style/SymfonyStyle/command/command_5.php | 37 + .../Style/SymfonyStyle/command/command_6.php | 16 + .../Style/SymfonyStyle/command/command_7.php | 15 + .../Style/SymfonyStyle/output/output_0.txt | 3 + .../Style/SymfonyStyle/output/output_1.txt | 9 + .../Style/SymfonyStyle/output/output_2.txt | 13 + .../Style/SymfonyStyle/output/output_3.txt | 7 + .../Style/SymfonyStyle/output/output_4.txt | 32 + .../Style/SymfonyStyle/output/output_5.txt | 15 + .../Style/SymfonyStyle/output/output_6.txt | 6 + .../Style/SymfonyStyle/output/output_7.txt | 5 + .../console/Tests/Fixtures/TestCommand.php | 28 + .../console/Tests/Fixtures/application_1.json | 1 + .../console/Tests/Fixtures/application_1.md | 181 + .../console/Tests/Fixtures/application_1.txt | 17 + .../console/Tests/Fixtures/application_1.xml | 104 + .../console/Tests/Fixtures/application_2.json | 1 + .../console/Tests/Fixtures/application_2.md | 376 ++ .../console/Tests/Fixtures/application_2.txt | 22 + .../console/Tests/Fixtures/application_2.xml | 184 + .../Tests/Fixtures/application_astext1.txt | 20 + .../Tests/Fixtures/application_astext2.txt | 16 + .../Tests/Fixtures/application_gethelp.txt | 1 + .../Fixtures/application_renderexception1.txt | 6 + .../Fixtures/application_renderexception2.txt | 8 + .../Fixtures/application_renderexception3.txt | 18 + .../application_renderexception3decorated.txt | 18 + .../Fixtures/application_renderexception4.txt | 7 + ...plication_renderexception_doublewidth1.txt | 8 + ..._renderexception_doublewidth1decorated.txt | 8 + ...plication_renderexception_doublewidth2.txt | 9 + .../Tests/Fixtures/application_run1.txt | 17 + .../Tests/Fixtures/application_run2.txt | 28 + .../Tests/Fixtures/application_run3.txt | 26 + .../Tests/Fixtures/application_run4.txt | 1 + .../console/Tests/Fixtures/command_1.json | 1 + .../console/Tests/Fixtures/command_1.md | 11 + .../console/Tests/Fixtures/command_1.txt | 7 + .../console/Tests/Fixtures/command_1.xml | 12 + .../console/Tests/Fixtures/command_2.json | 1 + .../console/Tests/Fixtures/command_2.md | 33 + .../console/Tests/Fixtures/command_2.txt | 13 + .../console/Tests/Fixtures/command_2.xml | 21 + .../console/Tests/Fixtures/command_astext.txt | 18 + .../console/Tests/Fixtures/command_asxml.txt | 38 + .../Tests/Fixtures/definition_astext.txt | 11 + .../Tests/Fixtures/definition_asxml.txt | 39 + .../Tests/Fixtures/input_argument_1.json | 1 + .../Tests/Fixtures/input_argument_1.md | 7 + .../Tests/Fixtures/input_argument_1.txt | 1 + .../Tests/Fixtures/input_argument_1.xml | 5 + .../Tests/Fixtures/input_argument_2.json | 1 + .../Tests/Fixtures/input_argument_2.md | 7 + .../Tests/Fixtures/input_argument_2.txt | 1 + .../Tests/Fixtures/input_argument_2.xml | 5 + .../Tests/Fixtures/input_argument_3.json | 1 + .../Tests/Fixtures/input_argument_3.md | 7 + .../Tests/Fixtures/input_argument_3.txt | 1 + .../Tests/Fixtures/input_argument_3.xml | 7 + .../Tests/Fixtures/input_argument_4.json | 1 + .../Tests/Fixtures/input_argument_4.md | 8 + .../Tests/Fixtures/input_argument_4.txt | 2 + .../Tests/Fixtures/input_argument_4.xml | 6 + .../Tests/Fixtures/input_definition_1.json | 1 + .../Tests/Fixtures/input_definition_1.md | 0 .../Tests/Fixtures/input_definition_1.txt | 0 .../Tests/Fixtures/input_definition_1.xml | 5 + .../Tests/Fixtures/input_definition_2.json | 1 + .../Tests/Fixtures/input_definition_2.md | 9 + .../Tests/Fixtures/input_definition_2.txt | 2 + .../Tests/Fixtures/input_definition_2.xml | 10 + .../Tests/Fixtures/input_definition_3.json | 1 + .../Tests/Fixtures/input_definition_3.md | 11 + .../Tests/Fixtures/input_definition_3.txt | 2 + .../Tests/Fixtures/input_definition_3.xml | 9 + .../Tests/Fixtures/input_definition_4.json | 1 + .../Tests/Fixtures/input_definition_4.md | 21 + .../Tests/Fixtures/input_definition_4.txt | 5 + .../Tests/Fixtures/input_definition_4.xml | 14 + .../Tests/Fixtures/input_option_1.json | 1 + .../console/Tests/Fixtures/input_option_1.md | 9 + .../console/Tests/Fixtures/input_option_1.txt | 1 + .../console/Tests/Fixtures/input_option_1.xml | 4 + .../Tests/Fixtures/input_option_2.json | 1 + .../console/Tests/Fixtures/input_option_2.md | 9 + .../console/Tests/Fixtures/input_option_2.txt | 1 + .../console/Tests/Fixtures/input_option_2.xml | 7 + .../Tests/Fixtures/input_option_3.json | 1 + .../console/Tests/Fixtures/input_option_3.md | 9 + .../console/Tests/Fixtures/input_option_3.txt | 1 + .../console/Tests/Fixtures/input_option_3.xml | 5 + .../Tests/Fixtures/input_option_4.json | 1 + .../console/Tests/Fixtures/input_option_4.md | 9 + .../console/Tests/Fixtures/input_option_4.txt | 1 + .../console/Tests/Fixtures/input_option_4.xml | 5 + .../Tests/Fixtures/input_option_5.json | 1 + .../console/Tests/Fixtures/input_option_5.md | 10 + .../console/Tests/Fixtures/input_option_5.txt | 2 + .../console/Tests/Fixtures/input_option_5.xml | 6 + .../Tests/Fixtures/input_option_6.json | 1 + .../console/Tests/Fixtures/input_option_6.md | 9 + .../console/Tests/Fixtures/input_option_6.txt | 1 + .../console/Tests/Fixtures/input_option_6.xml | 5 + .../OutputFormatterStyleStackTest.php | 70 + .../Formatter/OutputFormatterStyleTest.php | 99 + .../Tests/Formatter/OutputFormatterTest.php | 273 ++ .../Tests/Helper/FormatterHelperTest.php | 92 + .../console/Tests/Helper/HelperSetTest.php | 133 + .../Tests/Helper/ProcessHelperTest.php | 117 + .../console/Tests/Helper/ProgressBarTest.php | 664 ++++ .../Tests/Helper/ProgressIndicatorTest.php | 179 + .../Tests/Helper/QuestionHelperTest.php | 415 +++ .../console/Tests/Helper/TableStyleTest.php | 27 + .../console/Tests/Helper/TableTest.php | 639 ++++ .../console/Tests/Input/ArgvInputTest.php | 336 ++ .../console/Tests/Input/ArrayInputTest.php | 159 + .../console/Tests/Input/InputArgumentTest.php | 111 + .../Tests/Input/InputDefinitionTest.php | 402 +++ .../console/Tests/Input/InputOptionTest.php | 204 ++ .../symfony/console/Tests/Input/InputTest.php | 132 + .../console/Tests/Input/StringInputTest.php | 86 + .../Tests/Logger/ConsoleLoggerTest.php | 58 + .../Tests/Output/ConsoleOutputTest.php | 25 + .../console/Tests/Output/NullOutputTest.php | 39 + .../console/Tests/Output/OutputTest.php | 175 + .../console/Tests/Output/StreamOutputTest.php | 60 + .../console/Tests/Style/SymfonyStyleTest.php | 89 + .../Tests/Tester/ApplicationTesterTest.php | 69 + .../Tests/Tester/CommandTesterTest.php | 84 + vendor/symfony/console/composer.json | 44 + vendor/symfony/console/phpunit.xml.dist | 29 + vendor/symfony/css-selector/.gitignore | 3 + vendor/symfony/css-selector/CHANGELOG.md | 13 + .../css-selector/CssSelectorConverter.php | 65 + .../Exception/ExceptionInterface.php | 24 + .../Exception/ExpressionErrorException.php | 24 + .../Exception/InternalErrorException.php | 24 + .../css-selector/Exception/ParseException.php | 24 + .../Exception/SyntaxErrorException.php | 73 + vendor/symfony/css-selector/LICENSE | 19 + .../css-selector/Node/AbstractNode.php | 42 + .../css-selector/Node/AttributeNode.php | 126 + .../symfony/css-selector/Node/ClassNode.php | 77 + .../Node/CombinedSelectorNode.php | 94 + .../symfony/css-selector/Node/ElementNode.php | 79 + .../css-selector/Node/FunctionNode.php | 98 + vendor/symfony/css-selector/Node/HashNode.php | 77 + .../css-selector/Node/NegationNode.php | 77 + .../css-selector/Node/NodeInterface.php | 46 + .../symfony/css-selector/Node/PseudoNode.php | 77 + .../css-selector/Node/SelectorNode.php | 77 + .../symfony/css-selector/Node/Specificity.php | 105 + .../Parser/Handler/CommentHandler.php | 48 + .../Parser/Handler/HandlerInterface.php | 36 + .../Parser/Handler/HashHandler.php | 69 + .../Parser/Handler/IdentifierHandler.php | 69 + .../Parser/Handler/NumberHandler.php | 60 + .../Parser/Handler/StringHandler.php | 88 + .../Parser/Handler/WhitespaceHandler.php | 46 + vendor/symfony/css-selector/Parser/Parser.php | 401 +++ .../css-selector/Parser/ParserInterface.php | 36 + vendor/symfony/css-selector/Parser/Reader.php | 127 + .../Parser/Shortcut/ClassParser.php | 51 + .../Parser/Shortcut/ElementParser.php | 47 + .../Parser/Shortcut/EmptyStringParser.php | 46 + .../Parser/Shortcut/HashParser.php | 51 + vendor/symfony/css-selector/Parser/Token.php | 162 + .../css-selector/Parser/TokenStream.php | 184 + .../Parser/Tokenizer/Tokenizer.php | 80 + .../Parser/Tokenizer/TokenizerEscaping.php | 84 + .../Parser/Tokenizer/TokenizerPatterns.php | 162 + vendor/symfony/css-selector/README.md | 84 + .../Tests/CssSelectorConverterTest.php | 75 + .../Tests/Node/AbstractNodeTest.php | 33 + .../Tests/Node/AttributeNodeTest.php | 37 + .../css-selector/Tests/Node/ClassNodeTest.php | 33 + .../Tests/Node/CombinedSelectorNodeTest.php | 35 + .../Tests/Node/ElementNodeTest.php | 35 + .../Tests/Node/FunctionNodeTest.php | 47 + .../css-selector/Tests/Node/HashNodeTest.php | 33 + .../Tests/Node/NegationNodeTest.php | 33 + .../Tests/Node/PseudoNodeTest.php | 32 + .../Tests/Node/SelectorNodeTest.php | 34 + .../Tests/Node/SpecificityTest.php | 62 + .../Parser/Handler/AbstractHandlerTest.php | 69 + .../Parser/Handler/CommentHandlerTest.php | 55 + .../Tests/Parser/Handler/HashHandlerTest.php | 49 + .../Parser/Handler/IdentifierHandlerTest.php | 49 + .../Parser/Handler/NumberHandlerTest.php | 50 + .../Parser/Handler/StringHandlerTest.php | 50 + .../Parser/Handler/WhitespaceHandlerTest.php | 44 + .../css-selector/Tests/Parser/ParserTest.php | 248 ++ .../css-selector/Tests/Parser/ReaderTest.php | 101 + .../Tests/Parser/Shortcut/ClassParserTest.php | 44 + .../Parser/Shortcut/ElementParserTest.php | 43 + .../Parser/Shortcut/EmptyStringParserTest.php | 35 + .../Tests/Parser/Shortcut/HashParserTest.php | 44 + .../Tests/Parser/TokenStreamTest.php | 95 + .../Tests/XPath/Fixtures/ids.html | 48 + .../Tests/XPath/Fixtures/lang.xml | 11 + .../Tests/XPath/Fixtures/shakespear.html | 308 ++ .../Tests/XPath/TranslatorTest.php | 324 ++ .../XPath/Extension/AbstractExtension.php | 65 + .../Extension/AttributeMatchingExtension.php | 175 + .../XPath/Extension/CombinationExtension.php | 95 + .../XPath/Extension/ExtensionInterface.php | 69 + .../XPath/Extension/FunctionExtension.php | 211 ++ .../XPath/Extension/HtmlExtension.php | 240 ++ .../XPath/Extension/NodeExtension.php | 273 ++ .../XPath/Extension/PseudoClassExtension.php | 164 + .../symfony/css-selector/XPath/Translator.php | 301 ++ .../XPath/TranslatorInterface.php | 47 + .../symfony/css-selector/XPath/XPathExpr.php | 142 + vendor/symfony/css-selector/composer.json | 37 + vendor/symfony/css-selector/phpunit.xml.dist | 29 + vendor/symfony/debug/.gitignore | 3 + vendor/symfony/debug/BufferingLogger.php | 37 + vendor/symfony/debug/CHANGELOG.md | 47 + vendor/symfony/debug/Debug.php | 63 + vendor/symfony/debug/DebugClassLoader.php | 295 ++ vendor/symfony/debug/ErrorHandler.php | 662 ++++ .../Exception/ClassNotFoundException.php | 33 + .../debug/Exception/ContextErrorException.php | 36 + .../debug/Exception/FatalErrorException.php | 82 + .../debug/Exception/FatalThrowableError.php | 44 + .../debug/Exception/FlattenException.php | 256 ++ .../debug/Exception/OutOfMemoryException.php | 21 + .../Exception/UndefinedFunctionException.php | 33 + .../Exception/UndefinedMethodException.php | 33 + vendor/symfony/debug/ExceptionHandler.php | 414 +++ .../ClassNotFoundFatalErrorHandler.php | 206 ++ .../FatalErrorHandlerInterface.php | 32 + .../UndefinedFunctionFatalErrorHandler.php | 84 + .../UndefinedMethodFatalErrorHandler.php | 60 + vendor/symfony/debug/LICENSE | 19 + vendor/symfony/debug/README.md | 42 + vendor/symfony/debug/Resources/ext/README.md | 134 + vendor/symfony/debug/Resources/ext/config.m4 | 63 + vendor/symfony/debug/Resources/ext/config.w32 | 13 + .../debug/Resources/ext/php_symfony_debug.h | 60 + .../debug/Resources/ext/symfony_debug.c | 283 ++ .../debug/Resources/ext/tests/001.phpt | 151 + .../debug/Resources/ext/tests/002.phpt | 64 + .../debug/Resources/ext/tests/002_1.phpt | 47 + .../debug/Resources/ext/tests/003.phpt | 85 + .../debug/Tests/DebugClassLoaderTest.php | 316 ++ .../symfony/debug/Tests/ErrorHandlerTest.php | 491 +++ .../Tests/Exception/FlattenExceptionTest.php | 270 ++ .../debug/Tests/ExceptionHandlerTest.php | 132 + .../ClassNotFoundFatalErrorHandlerTest.php | 178 + ...UndefinedFunctionFatalErrorHandlerTest.php | 80 + .../UndefinedMethodFatalErrorHandlerTest.php | 66 + .../debug/Tests/Fixtures/ClassAlias.php | 3 + .../debug/Tests/Fixtures/DeprecatedClass.php | 12 + .../Tests/Fixtures/DeprecatedInterface.php | 12 + .../Tests/Fixtures/NonDeprecatedInterface.php | 7 + .../debug/Tests/Fixtures/PEARClass.php | 5 + .../debug/Tests/Fixtures/ToStringThrower.php | 24 + .../debug/Tests/Fixtures/casemismatch.php | 7 + .../debug/Tests/Fixtures/notPsr0Bis.php | 7 + .../Tests/Fixtures/psr4/Psr4CaseMismatch.php | 7 + .../debug/Tests/Fixtures/reallyNotPsr0.php | 7 + .../debug/Tests/Fixtures2/RequiredTwice.php | 7 + vendor/symfony/debug/Tests/HeaderMock.php | 38 + .../debug/Tests/MockExceptionHandler.php | 24 + vendor/symfony/debug/composer.json | 41 + vendor/symfony/debug/phpunit.xml.dist | 31 + vendor/symfony/dom-crawler/.gitignore | 3 + vendor/symfony/dom-crawler/CHANGELOG.md | 45 + vendor/symfony/dom-crawler/Crawler.php | 1035 ++++++ .../dom-crawler/Field/ChoiceFormField.php | 319 ++ .../dom-crawler/Field/FileFormField.php | 108 + .../symfony/dom-crawler/Field/FormField.php | 114 + .../dom-crawler/Field/InputFormField.php | 45 + .../dom-crawler/Field/TextareaFormField.php | 37 + vendor/symfony/dom-crawler/Form.php | 471 +++ .../symfony/dom-crawler/FormFieldRegistry.php | 222 ++ vendor/symfony/dom-crawler/LICENSE | 19 + vendor/symfony/dom-crawler/Link.php | 224 ++ vendor/symfony/dom-crawler/README.md | 36 + .../symfony/dom-crawler/Tests/CrawlerTest.php | 1097 ++++++ .../Tests/Field/ChoiceFormFieldTest.php | 388 +++ .../Tests/Field/FileFormFieldTest.php | 114 + .../dom-crawler/Tests/Field/FormFieldTest.php | 38 + .../Tests/Field/FormFieldTestCase.php | 27 + .../Tests/Field/InputFormFieldTest.php | 49 + .../Tests/Field/TextareaFormFieldTest.php | 46 + .../dom-crawler/Tests/Fixtures/no-extension | 1 + .../Tests/Fixtures/windows-1250.html | 8 + vendor/symfony/dom-crawler/Tests/FormTest.php | 973 ++++++ vendor/symfony/dom-crawler/Tests/LinkTest.php | 160 + vendor/symfony/dom-crawler/composer.json | 40 + vendor/symfony/dom-crawler/phpunit.xml.dist | 29 + vendor/symfony/event-dispatcher/.gitignore | 3 + vendor/symfony/event-dispatcher/CHANGELOG.md | 32 + .../ContainerAwareEventDispatcher.php | 195 ++ .../Debug/TraceableEventDispatcher.php | 368 ++ .../TraceableEventDispatcherInterface.php | 34 + .../Debug/WrappedListener.php | 71 + .../RegisterListenersPass.php | 110 + vendor/symfony/event-dispatcher/Event.php | 58 + .../event-dispatcher/EventDispatcher.php | 188 + .../EventDispatcherInterface.php | 100 + .../EventSubscriberInterface.php | 46 + .../symfony/event-dispatcher/GenericEvent.php | 186 + .../ImmutableEventDispatcher.php | 101 + vendor/symfony/event-dispatcher/LICENSE | 19 + vendor/symfony/event-dispatcher/README.md | 27 + .../Tests/AbstractEventDispatcherTest.php | 373 ++ .../ContainerAwareEventDispatcherTest.php | 207 ++ .../Debug/TraceableEventDispatcherTest.php | 216 ++ .../RegisterListenersPassTest.php | 200 ++ .../Tests/EventDispatcherTest.php | 22 + .../event-dispatcher/Tests/EventTest.php | 54 + .../Tests/GenericEventTest.php | 139 + .../Tests/ImmutableEventDispatcherTest.php | 105 + vendor/symfony/event-dispatcher/composer.json | 44 + .../symfony/event-dispatcher/phpunit.xml.dist | 29 + vendor/symfony/finder/.gitignore | 3 + vendor/symfony/finder/CHANGELOG.md | 44 + .../symfony/finder/Comparator/Comparator.php | 98 + .../finder/Comparator/DateComparator.php | 53 + .../finder/Comparator/NumberComparator.php | 81 + .../Exception/AccessDeniedException.php | 19 + .../finder/Exception/ExceptionInterface.php | 23 + vendor/symfony/finder/Finder.php | 716 ++++ vendor/symfony/finder/Glob.php | 104 + .../finder/Iterator/CustomFilterIterator.php | 63 + .../Iterator/DateRangeFilterIterator.php | 60 + .../Iterator/DepthRangeFilterIterator.php | 47 + .../ExcludeDirectoryFilterIterator.php | 85 + .../Iterator/FileTypeFilterIterator.php | 55 + .../Iterator/FilecontentFilterIterator.php | 58 + .../Iterator/FilenameFilterIterator.php | 47 + .../finder/Iterator/FilterIterator.php | 51 + .../Iterator/MultiplePcreFilterIterator.php | 114 + .../finder/Iterator/PathFilterIterator.php | 56 + .../Iterator/RecursiveDirectoryIterator.php | 151 + .../Iterator/SizeRangeFilterIterator.php | 59 + .../finder/Iterator/SortableIterator.php | 82 + vendor/symfony/finder/LICENSE | 19 + vendor/symfony/finder/README.md | 53 + vendor/symfony/finder/SplFileInfo.php | 81 + .../Tests/Comparator/ComparatorTest.php | 64 + .../Tests/Comparator/DateComparatorTest.php | 63 + .../Tests/Comparator/NumberComparatorTest.php | 107 + vendor/symfony/finder/Tests/FinderTest.php | 680 ++++ .../finder/Tests/Fixtures/A/B/C/abc.dat | 0 .../symfony/finder/Tests/Fixtures/A/B/ab.dat | 0 vendor/symfony/finder/Tests/Fixtures/A/a.dat | 0 .../Tests/Fixtures/copy/A/B/C/abc.dat.copy | 0 .../Tests/Fixtures/copy/A/B/ab.dat.copy | 0 .../finder/Tests/Fixtures/copy/A/a.dat.copy | 0 .../symfony/finder/Tests/Fixtures/dolor.txt | 2 + .../symfony/finder/Tests/Fixtures/ipsum.txt | 2 + .../symfony/finder/Tests/Fixtures/lorem.txt | 2 + vendor/symfony/finder/Tests/Fixtures/one/a | 0 .../finder/Tests/Fixtures/one/b/c.neon | 0 .../finder/Tests/Fixtures/one/b/d.neon | 0 .../Fixtures/r+e.gex[c]a(r)s/dir/bar.dat | 0 .../finder/Tests/Fixtures/with space/foo.txt | 0 vendor/symfony/finder/Tests/GlobTest.php | 25 + .../Iterator/CustomFilterIteratorTest.php | 46 + .../Iterator/DateRangeFilterIteratorTest.php | 74 + .../Iterator/DepthRangeFilterIteratorTest.php | 83 + .../ExcludeDirectoryFilterIteratorTest.php | 66 + .../Iterator/FileTypeFilterIteratorTest.php | 73 + .../FilecontentFilterIteratorTest.php | 86 + .../Iterator/FilenameFilterIteratorTest.php | 54 + .../Tests/Iterator/FilterIteratorTest.php | 51 + .../finder/Tests/Iterator/Iterator.php | 55 + .../Tests/Iterator/IteratorTestCase.php | 98 + .../Tests/Iterator/MockFileListIterator.php | 21 + .../finder/Tests/Iterator/MockSplFileInfo.php | 134 + .../MultiplePcreFilterIteratorTest.php | 70 + .../Tests/Iterator/PathFilterIteratorTest.php | 83 + .../Tests/Iterator/RealIteratorTestCase.php | 110 + .../RecursiveDirectoryIteratorTest.php | 59 + .../Iterator/SizeRangeFilterIteratorTest.php | 69 + .../Tests/Iterator/SortableIteratorTest.php | 183 + vendor/symfony/finder/composer.json | 33 + vendor/symfony/finder/phpunit.xml.dist | 28 + vendor/symfony/http-foundation/.gitignore | 3 + .../symfony/http-foundation/AcceptHeader.php | 172 + .../http-foundation/AcceptHeaderItem.php | 226 ++ .../symfony/http-foundation/ApacheRequest.php | 43 + .../http-foundation/BinaryFileResponse.php | 326 ++ vendor/symfony/http-foundation/CHANGELOG.md | 133 + vendor/symfony/http-foundation/Cookie.php | 190 ++ .../ExpressionRequestMatcher.php | 47 + .../File/Exception/AccessDeniedException.php | 30 + .../File/Exception/FileException.php | 21 + .../File/Exception/FileNotFoundException.php | 30 + .../Exception/UnexpectedTypeException.php | 20 + .../File/Exception/UploadException.php | 21 + vendor/symfony/http-foundation/File/File.php | 136 + .../File/MimeType/ExtensionGuesser.php | 96 + .../MimeType/ExtensionGuesserInterface.php | 27 + .../MimeType/FileBinaryMimeTypeGuesser.php | 87 + .../File/MimeType/FileinfoMimeTypeGuesser.php | 71 + .../MimeType/MimeTypeExtensionGuesser.php | 807 +++++ .../File/MimeType/MimeTypeGuesser.php | 144 + .../MimeType/MimeTypeGuesserInterface.php | 35 + .../http-foundation/File/UploadedFile.php | 293 ++ vendor/symfony/http-foundation/FileBag.php | 145 + vendor/symfony/http-foundation/HeaderBag.php | 324 ++ vendor/symfony/http-foundation/IpUtils.php | 129 + .../symfony/http-foundation/JsonResponse.php | 172 + vendor/symfony/http-foundation/LICENSE | 19 + .../symfony/http-foundation/ParameterBag.php | 240 ++ vendor/symfony/http-foundation/README.md | 44 + .../http-foundation/RedirectResponse.php | 102 + vendor/symfony/http-foundation/Request.php | 1920 +++++++++++ .../http-foundation/RequestMatcher.php | 178 + .../RequestMatcherInterface.php | 29 + .../symfony/http-foundation/RequestStack.php | 103 + vendor/symfony/http-foundation/Response.php | 1179 +++++++ .../http-foundation/ResponseHeaderBag.php | 304 ++ vendor/symfony/http-foundation/ServerBag.php | 102 + .../Session/Attribute/AttributeBag.php | 157 + .../Attribute/AttributeBagInterface.php | 72 + .../Attribute/NamespacedAttributeBag.php | 160 + .../Session/Flash/AutoExpireFlashBag.php | 175 + .../Session/Flash/FlashBag.php | 166 + .../Session/Flash/FlashBagInterface.php | 93 + .../http-foundation/Session/Session.php | 249 ++ .../Session/SessionBagInterface.php | 48 + .../Session/SessionInterface.php | 182 + .../Handler/MemcacheSessionHandler.php | 119 + .../Handler/MemcachedSessionHandler.php | 125 + .../Storage/Handler/MongoDbSessionHandler.php | 188 + .../Handler/NativeFileSessionHandler.php | 58 + .../Storage/Handler/NativeSessionHandler.php | 21 + .../Storage/Handler/NullSessionHandler.php | 70 + .../Storage/Handler/PdoSessionHandler.php | 695 ++++ .../Handler/WriteCheckSessionHandler.php | 91 + .../Session/Storage/MetadataBag.php | 170 + .../Storage/MockArraySessionStorage.php | 268 ++ .../Storage/MockFileSessionStorage.php | 138 + .../Session/Storage/NativeSessionStorage.php | 399 +++ .../Storage/PhpBridgeSessionStorage.php | 64 + .../Session/Storage/Proxy/AbstractProxy.php | 124 + .../Session/Storage/Proxy/NativeProxy.php | 41 + .../Storage/Proxy/SessionHandlerProxy.php | 87 + .../Storage/SessionStorageInterface.php | 139 + .../http-foundation/StreamedResponse.php | 114 + .../Tests/AcceptHeaderItemTest.php | 112 + .../Tests/AcceptHeaderTest.php | 102 + .../Tests/ApacheRequestTest.php | 92 + .../Tests/BinaryFileResponseTest.php | 261 ++ .../http-foundation/Tests/CookieTest.php | 154 + .../Tests/ExpressionRequestMatcherTest.php | 68 + .../http-foundation/Tests/File/FakeFile.php | 45 + .../http-foundation/Tests/File/FileTest.php | 179 + .../Tests/File/Fixtures/.unknownextension | 1 + .../Tests/File/Fixtures/directory/.empty | 0 .../Tests/File/Fixtures/other-file.example | 0 .../http-foundation/Tests/File/Fixtures/test | Bin 0 -> 35 bytes .../Tests/File/Fixtures/test.gif | Bin 0 -> 35 bytes .../Tests/File/MimeType/MimeTypeTest.php | 89 + .../Tests/File/UploadedFileTest.php | 272 ++ .../http-foundation/Tests/FileBagTest.php | 148 + .../http-foundation/Tests/HeaderBagTest.php | 195 ++ .../http-foundation/Tests/IpUtilsTest.php | 81 + .../Tests/JsonResponseTest.php | 225 ++ .../Tests/ParameterBagTest.php | 193 ++ .../Tests/RedirectResponseTest.php | 83 + .../Tests/RequestMatcherTest.php | 150 + .../Tests/RequestStackTest.php | 69 + .../http-foundation/Tests/RequestTest.php | 1870 ++++++++++ .../Tests/ResponseHeaderBagTest.php | 297 ++ .../http-foundation/Tests/ResponseTest.php | 893 +++++ .../Tests/ResponseTestCase.php | 88 + .../http-foundation/Tests/ServerBagTest.php | 169 + .../Session/Attribute/AttributeBagTest.php | 188 + .../Attribute/NamespacedAttributeBagTest.php | 184 + .../Session/Flash/AutoExpireFlashBagTest.php | 155 + .../Tests/Session/Flash/FlashBagTest.php | 134 + .../Tests/Session/SessionTest.php | 221 ++ .../Handler/MemcacheSessionHandlerTest.php | 134 + .../Handler/MemcachedSessionHandlerTest.php | 134 + .../Handler/MongoDbSessionHandlerTest.php | 255 ++ .../Handler/NativeFileSessionHandlerTest.php | 76 + .../Handler/NativeSessionHandlerTest.php | 33 + .../Handler/NullSessionHandlerTest.php | 58 + .../Storage/Handler/PdoSessionHandlerTest.php | 357 ++ .../Handler/WriteCheckSessionHandlerTest.php | 94 + .../Tests/Session/Storage/MetadataBagTest.php | 138 + .../Storage/MockArraySessionStorageTest.php | 106 + .../Storage/MockFileSessionStorageTest.php | 126 + .../Storage/NativeSessionStorageTest.php | 236 ++ .../Storage/PhpBridgeSessionStorageTest.php | 95 + .../Storage/Proxy/AbstractProxyTest.php | 144 + .../Session/Storage/Proxy/NativeProxyTest.php | 35 + .../Storage/Proxy/SessionHandlerProxyTest.php | 123 + .../Tests/StreamedResponseTest.php | 112 + vendor/symfony/http-foundation/composer.json | 36 + .../symfony/http-foundation/phpunit.xml.dist | 29 + vendor/symfony/http-kernel/.gitignore | 5 + vendor/symfony/http-kernel/Bundle/Bundle.php | 221 ++ .../http-kernel/Bundle/BundleInterface.php | 84 + vendor/symfony/http-kernel/CHANGELOG.md | 104 + .../CacheClearer/CacheClearerInterface.php | 27 + .../CacheClearer/ChainCacheClearer.php | 55 + .../http-kernel/CacheWarmer/CacheWarmer.php | 32 + .../CacheWarmer/CacheWarmerAggregate.php | 74 + .../CacheWarmer/CacheWarmerInterface.php | 32 + .../CacheWarmer/WarmableInterface.php | 27 + vendor/symfony/http-kernel/Client.php | 226 ++ .../Config/EnvParametersResource.php | 95 + .../http-kernel/Config/FileLocator.php | 56 + .../Controller/ControllerReference.php | 46 + .../Controller/ControllerResolver.php | 225 ++ .../ControllerResolverInterface.php | 57 + .../TraceableControllerResolver.php | 66 + .../DataCollector/AjaxDataCollector.php | 33 + .../DataCollector/ConfigDataCollector.php | 291 ++ .../DataCollector/DataCollector.php | 58 + .../DataCollector/DataCollectorInterface.php | 39 + .../DataCollector/DumpDataCollector.php | 295 ++ .../DataCollector/EventDataCollector.php | 107 + .../DataCollector/ExceptionDataCollector.php | 104 + .../LateDataCollectorInterface.php | 25 + .../DataCollector/LoggerDataCollector.php | 216 ++ .../DataCollector/MemoryDataCollector.php | 109 + .../DataCollector/RequestDataCollector.php | 342 ++ .../DataCollector/RouterDataCollector.php | 102 + .../DataCollector/TimeDataCollector.php | 136 + .../DataCollector/Util/ValueExporter.php | 78 + .../Debug/TraceableEventDispatcher.php | 82 + .../AddClassesToCachePass.php | 46 + .../ConfigurableExtension.php | 45 + .../DependencyInjection/Extension.php | 44 + .../FragmentRendererPass.php | 65 + .../LazyLoadingFragmentHandler.php | 65 + .../MergeExtensionConfigurationPass.php | 41 + .../Event/FilterControllerEvent.php | 63 + .../http-kernel/Event/FilterResponseEvent.php | 62 + .../http-kernel/Event/FinishRequestEvent.php | 21 + .../http-kernel/Event/GetResponseEvent.php | 65 + .../GetResponseForControllerResultEvent.php | 61 + .../Event/GetResponseForExceptionEvent.php | 67 + .../symfony/http-kernel/Event/KernelEvent.php | 94 + .../http-kernel/Event/PostResponseEvent.php | 46 + .../AddRequestFormatsListener.php | 57 + .../EventListener/DebugHandlersListener.php | 136 + .../EventListener/DumpListener.php | 55 + .../EventListener/ExceptionListener.php | 116 + .../EventListener/FragmentListener.php | 96 + .../EventListener/LocaleListener.php | 85 + .../EventListener/ProfilerListener.php | 135 + .../EventListener/ResponseListener.php | 58 + .../EventListener/RouterListener.php | 138 + .../EventListener/SaveSessionListener.php | 66 + .../EventListener/SessionListener.php | 53 + .../StreamedResponseListener.php | 51 + .../EventListener/SurrogateListener.php | 58 + .../EventListener/TestSessionListener.php | 83 + .../EventListener/TranslatorListener.php | 69 + .../Exception/AccessDeniedHttpException.php | 33 + .../Exception/BadRequestHttpException.php | 32 + .../Exception/ConflictHttpException.php | 32 + .../Exception/GoneHttpException.php | 32 + .../http-kernel/Exception/HttpException.php | 41 + .../Exception/HttpExceptionInterface.php | 34 + .../Exception/LengthRequiredHttpException.php | 32 + .../MethodNotAllowedHttpException.php | 35 + .../Exception/NotAcceptableHttpException.php | 32 + .../Exception/NotFoundHttpException.php | 32 + .../PreconditionFailedHttpException.php | 32 + .../PreconditionRequiredHttpException.php | 34 + .../ServiceUnavailableHttpException.php | 38 + .../TooManyRequestsHttpException.php | 40 + .../Exception/UnauthorizedHttpException.php | 35 + .../UnprocessableEntityHttpException.php | 32 + .../UnsupportedMediaTypeHttpException.php | 32 + .../AbstractSurrogateFragmentRenderer.php | 95 + .../Fragment/EsiFragmentRenderer.php | 28 + .../http-kernel/Fragment/FragmentHandler.php | 118 + .../Fragment/FragmentRendererInterface.php | 42 + .../Fragment/HIncludeFragmentRenderer.php | 160 + .../Fragment/InlineFragmentRenderer.php | 152 + .../Fragment/RoutableFragmentRenderer.php | 90 + .../Fragment/SsiFragmentRenderer.php | 28 + vendor/symfony/http-kernel/HttpCache/Esi.php | 249 ++ .../http-kernel/HttpCache/HttpCache.php | 684 ++++ .../HttpCache/ResponseCacheStrategy.php | 85 + .../ResponseCacheStrategyInterface.php | 41 + vendor/symfony/http-kernel/HttpCache/Ssi.php | 194 ++ .../symfony/http-kernel/HttpCache/Store.php | 452 +++ .../http-kernel/HttpCache/StoreInterface.php | 96 + .../HttpCache/SurrogateInterface.php | 103 + vendor/symfony/http-kernel/HttpKernel.php | 285 ++ .../http-kernel/HttpKernelInterface.php | 43 + vendor/symfony/http-kernel/Kernel.php | 736 ++++ vendor/symfony/http-kernel/KernelEvents.php | 115 + .../symfony/http-kernel/KernelInterface.php | 164 + vendor/symfony/http-kernel/LICENSE | 19 + .../http-kernel/Log/DebugLoggerInterface.php | 38 + .../Profiler/FileProfilerStorage.php | 284 ++ .../symfony/http-kernel/Profiler/Profile.php | 292 ++ .../symfony/http-kernel/Profiler/Profiler.php | 264 ++ .../Profiler/ProfilerStorageInterface.php | 59 + vendor/symfony/http-kernel/README.md | 99 + .../http-kernel/TerminableInterface.php | 35 + .../http-kernel/Tests/Bundle/BundleTest.php | 54 + .../CacheClearer/ChainCacheClearerTest.php | 57 + .../CacheWarmer/CacheWarmerAggregateTest.php | 100 + .../Tests/CacheWarmer/CacheWarmerTest.php | 67 + .../symfony/http-kernel/Tests/ClientTest.php | 179 + .../Config/EnvParametersResourceTest.php | 106 + .../Tests/Config/FileLocatorTest.php | 47 + .../Controller/ControllerResolverTest.php | 261 ++ .../DataCollector/ConfigDataCollectorTest.php | 81 + .../DataCollector/DumpDataCollectorTest.php | 119 + .../ExceptionDataCollectorTest.php | 39 + .../DataCollector/LoggerDataCollectorTest.php | 95 + .../DataCollector/MemoryDataCollectorTest.php | 58 + .../RequestDataCollectorTest.php | 222 ++ .../DataCollector/TimeDataCollectorTest.php | 54 + .../DataCollector/Util/ValueExporterTest.php | 39 + .../Debug/TraceableEventDispatcherTest.php | 117 + .../FragmentRendererPassTest.php | 111 + .../LazyLoadingFragmentHandlerTest.php | 40 + .../MergeExtensionConfigurationPassTest.php | 57 + .../AddRequestFormatsListenerTest.php | 83 + .../DebugHandlersListenerTest.php | 106 + .../Tests/EventListener/DumpListenerTest.php | 82 + .../EventListener/ExceptionListenerTest.php | 148 + .../EventListener/FragmentListenerTest.php | 96 + .../EventListener/LocaleListenerTest.php | 102 + .../EventListener/ProfilerListenerTest.php | 70 + .../EventListener/ResponseListenerTest.php | 94 + .../EventListener/RouterListenerTest.php | 160 + .../EventListener/SurrogateListenerTest.php | 66 + .../EventListener/TestSessionListenerTest.php | 132 + .../EventListener/TranslatorListenerTest.php | 117 + .../Fixtures/BaseBundle/Resources/foo.txt | 0 .../Fixtures/BaseBundle/Resources/hide.txt | 0 .../Fixtures/Bundle1Bundle/Resources/foo.txt | 0 .../Tests/Fixtures/Bundle1Bundle/bar.txt | 0 .../Tests/Fixtures/Bundle1Bundle/foo.txt | 0 .../Tests/Fixtures/Bundle2Bundle/foo.txt | 0 .../Fixtures/ChildBundle/Resources/foo.txt | 0 .../Fixtures/ChildBundle/Resources/hide.txt | 0 .../ExtensionAbsentBundle.php | 18 + .../ExtensionLoadedExtension.php | 22 + .../ExtensionLoadedBundle.php | 18 + .../ExtensionNotValidExtension.php | 20 + .../ExtensionNotValidBundle.php | 18 + .../Command/BarCommand.php | 18 + .../Command/FooCommand.php | 22 + .../ExtensionPresentExtension.php | 22 + .../ExtensionPresentBundle.php | 18 + .../Tests/Fixtures/KernelForOverrideName.php | 28 + .../Tests/Fixtures/KernelForTest.php | 37 + .../Fixtures/Resources/BaseBundle/hide.txt | 0 .../Fixtures/Resources/Bundle1Bundle/foo.txt | 0 .../Fixtures/Resources/ChildBundle/foo.txt | 0 .../Fixtures/Resources/FooBundle/foo.txt | 0 .../http-kernel/Tests/Fixtures/TestClient.php | 31 + .../Tests/Fixtures/TestEventDispatcher.php | 28 + .../Fragment/EsiFragmentRendererTest.php | 97 + .../Tests/Fragment/FragmentHandlerTest.php | 98 + .../Fragment/HIncludeFragmentRendererTest.php | 88 + .../Fragment/InlineFragmentRendererTest.php | 210 ++ .../Fragment/RoutableFragmentRendererTest.php | 93 + .../http-kernel/Tests/HttpCache/EsiTest.php | 247 ++ .../Tests/HttpCache/HttpCacheTest.php | 1227 +++++++ .../Tests/HttpCache/HttpCacheTestCase.php | 177 + .../http-kernel/Tests/HttpCache/SsiTest.php | 214 ++ .../http-kernel/Tests/HttpCache/StoreTest.php | 269 ++ .../Tests/HttpCache/TestHttpKernel.php | 91 + .../HttpCache/TestMultipleHttpKernel.php | 80 + .../http-kernel/Tests/HttpKernelTest.php | 308 ++ .../symfony/http-kernel/Tests/KernelTest.php | 806 +++++ vendor/symfony/http-kernel/Tests/Logger.php | 88 + .../Profiler/FileProfilerStorageTest.php | 335 ++ .../Tests/Profiler/ProfilerTest.php | 82 + .../http-kernel/Tests/TestHttpKernel.php | 41 + .../http-kernel/Tests/UriSignerTest.php | 52 + vendor/symfony/http-kernel/UriSigner.php | 109 + vendor/symfony/http-kernel/composer.json | 66 + vendor/symfony/http-kernel/phpunit.xml.dist | 38 + vendor/symfony/polyfill-mbstring/LICENSE | 19 + vendor/symfony/polyfill-mbstring/Mbstring.php | 604 ++++ vendor/symfony/polyfill-mbstring/README.md | 13 + .../Resources/unidata/lowerCase.ser | 1 + .../Resources/unidata/upperCase.ser | 1 + .../symfony/polyfill-mbstring/bootstrap.php | 51 + .../symfony/polyfill-mbstring/composer.json | 34 + vendor/symfony/polyfill-php56/LICENSE | 19 + vendor/symfony/polyfill-php56/Php56.php | 127 + vendor/symfony/polyfill-php56/README.md | 15 + vendor/symfony/polyfill-php56/bootstrap.php | 38 + vendor/symfony/polyfill-php56/composer.json | 32 + vendor/symfony/polyfill-util/Binary.php | 18 + .../polyfill-util/BinaryNoFuncOverload.php | 65 + .../polyfill-util/BinaryOnFuncOverload.php | 67 + vendor/symfony/polyfill-util/LICENSE | 19 + vendor/symfony/polyfill-util/README.md | 13 + vendor/symfony/polyfill-util/TestListener.php | 154 + vendor/symfony/polyfill-util/composer.json | 30 + vendor/symfony/process/.gitignore | 3 + vendor/symfony/process/CHANGELOG.md | 40 + .../process/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 21 + .../process/Exception/LogicException.php | 21 + .../Exception/ProcessFailedException.php | 54 + .../Exception/ProcessTimedOutException.php | 69 + .../process/Exception/RuntimeException.php | 21 + vendor/symfony/process/ExecutableFinder.php | 90 + vendor/symfony/process/LICENSE | 19 + .../symfony/process/PhpExecutableFinder.php | 90 + vendor/symfony/process/PhpProcess.php | 78 + .../symfony/process/Pipes/AbstractPipes.php | 74 + .../symfony/process/Pipes/PipesInterface.php | 60 + vendor/symfony/process/Pipes/UnixPipes.php | 214 ++ vendor/symfony/process/Pipes/WindowsPipes.php | 253 ++ vendor/symfony/process/Process.php | 1468 ++++++++ vendor/symfony/process/ProcessBuilder.php | 284 ++ vendor/symfony/process/ProcessUtils.php | 107 + vendor/symfony/process/README.md | 65 + .../process/Tests/ExecutableFinderTest.php | 132 + .../process/Tests/NonStopableProcess.php | 47 + .../process/Tests/PhpExecutableFinderTest.php | 71 + .../symfony/process/Tests/PhpProcessTest.php | 49 + .../PipeStdinInStdoutStdErrStreamSelect.php | 72 + .../process/Tests/ProcessBuilderTest.php | 225 ++ .../Tests/ProcessFailedExceptionTest.php | 146 + vendor/symfony/process/Tests/ProcessTest.php | 1218 +++++++ .../process/Tests/ProcessUtilsTest.php | 48 + .../symfony/process/Tests/SignalListener.php | 21 + vendor/symfony/process/composer.json | 33 + vendor/symfony/process/phpunit.xml.dist | 28 + vendor/symfony/routing/.gitignore | 3 + vendor/symfony/routing/Annotation/Route.php | 146 + vendor/symfony/routing/CHANGELOG.md | 184 + vendor/symfony/routing/CompiledRoute.php | 166 + .../routing/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidParameterException.php | 21 + .../Exception/MethodNotAllowedException.php | 44 + .../MissingMandatoryParametersException.php | 22 + .../Exception/ResourceNotFoundException.php | 23 + .../Exception/RouteNotFoundException.php | 21 + .../ConfigurableRequirementsInterface.php | 55 + .../Generator/Dumper/GeneratorDumper.php | 45 + .../Dumper/GeneratorDumperInterface.php | 39 + .../Generator/Dumper/PhpGeneratorDumper.php | 123 + .../routing/Generator/UrlGenerator.php | 332 ++ .../Generator/UrlGeneratorInterface.php | 84 + vendor/symfony/routing/LICENSE | 19 + .../routing/Loader/AnnotationClassLoader.php | 265 ++ .../Loader/AnnotationDirectoryLoader.php | 81 + .../routing/Loader/AnnotationFileLoader.php | 125 + .../symfony/routing/Loader/ClosureLoader.php | 46 + .../ServiceRouterLoader.php | 40 + .../routing/Loader/DirectoryLoader.php | 58 + .../routing/Loader/ObjectRouteLoader.php | 93 + .../symfony/routing/Loader/PhpFileLoader.php | 66 + .../symfony/routing/Loader/XmlFileLoader.php | 241 ++ .../symfony/routing/Loader/YamlFileLoader.php | 207 ++ .../Loader/schema/routing/routing-1.0.xsd | 64 + .../Matcher/Dumper/DumperCollection.php | 161 + .../Matcher/Dumper/DumperPrefixCollection.php | 107 + .../routing/Matcher/Dumper/DumperRoute.php | 66 + .../routing/Matcher/Dumper/MatcherDumper.php | 45 + .../Matcher/Dumper/MatcherDumperInterface.php | 39 + .../Matcher/Dumper/PhpMatcherDumper.php | 409 +++ .../Matcher/RedirectableUrlMatcher.php | 65 + .../RedirectableUrlMatcherInterface.php | 31 + .../Matcher/RequestMatcherInterface.php | 39 + .../routing/Matcher/TraceableUrlMatcher.php | 131 + vendor/symfony/routing/Matcher/UrlMatcher.php | 251 ++ .../routing/Matcher/UrlMatcherInterface.php | 39 + vendor/symfony/routing/README.md | 36 + vendor/symfony/routing/RequestContext.php | 344 ++ .../routing/RequestContextAwareInterface.php | 29 + vendor/symfony/routing/Route.php | 588 ++++ vendor/symfony/routing/RouteCollection.php | 277 ++ .../routing/RouteCollectionBuilder.php | 372 ++ vendor/symfony/routing/RouteCompiler.php | 229 ++ .../routing/RouteCompilerInterface.php | 32 + vendor/symfony/routing/Router.php | 378 +++ vendor/symfony/routing/RouterInterface.php | 32 + .../routing/Tests/Annotation/RouteTest.php | 49 + .../routing/Tests/CompiledRouteTest.php | 26 + .../AnnotatedClasses/AbstractClass.php | 16 + .../Fixtures/AnnotatedClasses/BarClass.php | 19 + .../Fixtures/AnnotatedClasses/FooClass.php | 16 + .../Tests/Fixtures/CustomXmlFileLoader.php | 26 + .../OtherAnnotatedClasses/VariadicClass.php | 19 + .../Tests/Fixtures/RedirectableUrlMatcher.php | 30 + .../routing/Tests/Fixtures/annotated.php | 0 .../routing/Tests/Fixtures/bad_format.yml | 3 + .../Fixtures/directory/recurse/routes1.yml | 2 + .../Fixtures/directory/recurse/routes2.yml | 2 + .../Tests/Fixtures/directory/routes3.yml | 2 + .../Fixtures/directory_import/import.yml | 3 + .../Tests/Fixtures/dumper/url_matcher1.apache | 163 + .../Tests/Fixtures/dumper/url_matcher1.php | 312 ++ .../Tests/Fixtures/dumper/url_matcher2.apache | 7 + .../Tests/Fixtures/dumper/url_matcher2.php | 344 ++ .../Tests/Fixtures/dumper/url_matcher3.php | 50 + .../symfony/routing/Tests/Fixtures/empty.yml | 0 vendor/symfony/routing/Tests/Fixtures/foo.xml | 0 .../symfony/routing/Tests/Fixtures/foo1.xml | 0 .../routing/Tests/Fixtures/incomplete.yml | 2 + .../routing/Tests/Fixtures/missing_id.xml | 8 + .../routing/Tests/Fixtures/missing_path.xml | 8 + .../Tests/Fixtures/namespaceprefix.xml | 13 + .../Fixtures/nonesense_resource_plus_path.yml | 3 + .../nonesense_type_without_resource.yml | 3 + .../routing/Tests/Fixtures/nonvalid.xml | 10 + .../routing/Tests/Fixtures/nonvalid.yml | 1 + .../routing/Tests/Fixtures/nonvalid2.yml | 1 + .../routing/Tests/Fixtures/nonvalidkeys.yml | 3 + .../routing/Tests/Fixtures/nonvalidnode.xml | 8 + .../routing/Tests/Fixtures/nonvalidroute.xml | 12 + .../routing/Tests/Fixtures/null_values.xml | 12 + .../Tests/Fixtures/special_route_name.yml | 2 + .../routing/Tests/Fixtures/validpattern.php | 18 + .../routing/Tests/Fixtures/validpattern.xml | 15 + .../routing/Tests/Fixtures/validpattern.yml | 13 + .../routing/Tests/Fixtures/validresource.php | 18 + .../routing/Tests/Fixtures/validresource.xml | 13 + .../routing/Tests/Fixtures/validresource.yml | 8 + .../Fixtures/with_define_path_variable.php | 5 + .../routing/Tests/Fixtures/withdoctype.xml | 3 + .../Dumper/PhpGeneratorDumperTest.php | 180 + .../Tests/Generator/UrlGeneratorTest.php | 643 ++++ .../Loader/AbstractAnnotationLoaderTest.php | 31 + .../Loader/AnnotationClassLoaderTest.php | 188 + .../Loader/AnnotationDirectoryLoaderTest.php | 53 + .../Tests/Loader/AnnotationFileLoaderTest.php | 61 + .../Tests/Loader/ClosureLoaderTest.php | 48 + .../Tests/Loader/DirectoryLoaderTest.php | 74 + .../Tests/Loader/ObjectRouteLoaderTest.php | 116 + .../Tests/Loader/PhpFileLoaderTest.php | 82 + .../Tests/Loader/XmlFileLoaderTest.php | 132 + .../Tests/Loader/YamlFileLoaderTest.php | 110 + .../Matcher/Dumper/DumperCollectionTest.php | 33 + .../Dumper/DumperPrefixCollectionTest.php | 123 + .../Matcher/Dumper/PhpMatcherDumperTest.php | 287 ++ .../Matcher/RedirectableUrlMatcherTest.php | 71 + .../Tests/Matcher/TraceableUrlMatcherTest.php | 101 + .../routing/Tests/Matcher/UrlMatcherTest.php | 419 +++ .../routing/Tests/RequestContextTest.php | 159 + .../Tests/RouteCollectionBuilderTest.php | 324 ++ .../routing/Tests/RouteCollectionTest.php | 304 ++ .../routing/Tests/RouteCompilerTest.php | 267 ++ vendor/symfony/routing/Tests/RouteTest.php | 239 ++ vendor/symfony/routing/Tests/RouterTest.php | 161 + vendor/symfony/routing/composer.json | 52 + vendor/symfony/routing/phpunit.xml.dist | 28 + vendor/symfony/translation/.gitignore | 3 + vendor/symfony/translation/CHANGELOG.md | 69 + .../Catalogue/AbstractOperation.php | 171 + .../translation/Catalogue/MergeOperation.php | 55 + .../Catalogue/OperationInterface.php | 77 + .../translation/Catalogue/TargetOperation.php | 69 + .../TranslationDataCollector.php | 150 + .../translation/DataCollectorTranslator.php | 152 + .../translation/Dumper/CsvFileDumper.php | 63 + .../translation/Dumper/DumperInterface.php | 31 + .../symfony/translation/Dumper/FileDumper.php | 123 + .../translation/Dumper/IcuResFileDumper.php | 106 + .../translation/Dumper/IniFileDumper.php | 45 + .../translation/Dumper/JsonFileDumper.php | 44 + .../translation/Dumper/MoFileDumper.php | 82 + .../translation/Dumper/PhpFileDumper.php | 38 + .../translation/Dumper/PoFileDumper.php | 61 + .../translation/Dumper/QtFileDumper.php | 50 + .../translation/Dumper/XliffFileDumper.php | 183 + .../translation/Dumper/YamlFileDumper.php | 54 + .../Exception/ExceptionInterface.php | 21 + .../Exception/InvalidResourceException.php | 21 + .../Exception/NotFoundResourceException.php | 21 + .../Extractor/AbstractFileExtractor.php | 83 + .../translation/Extractor/ChainExtractor.php | 60 + .../Extractor/ExtractorInterface.php | 38 + .../translation/IdentityTranslator.php | 65 + vendor/symfony/translation/Interval.php | 107 + vendor/symfony/translation/LICENSE | 19 + .../translation/Loader/ArrayLoader.php | 66 + .../translation/Loader/CsvFileLoader.php | 65 + .../symfony/translation/Loader/FileLoader.php | 65 + .../translation/Loader/IcuDatFileLoader.php | 62 + .../translation/Loader/IcuResFileLoader.php | 92 + .../translation/Loader/IniFileLoader.php | 28 + .../translation/Loader/JsonFileLoader.php | 64 + .../translation/Loader/LoaderInterface.php | 38 + .../translation/Loader/MoFileLoader.php | 154 + .../translation/Loader/PhpFileLoader.php | 28 + .../translation/Loader/PoFileLoader.php | 143 + .../translation/Loader/QtFileLoader.php | 77 + .../translation/Loader/XliffFileLoader.php | 313 ++ .../translation/Loader/YamlFileLoader.php | 48 + .../dic/xliff-core/xliff-core-1.2-strict.xsd | 2223 ++++++++++++ .../schema/dic/xliff-core/xliff-core-2.0.xsd | 411 +++ .../Loader/schema/dic/xliff-core/xml.xsd | 309 ++ .../symfony/translation/LoggingTranslator.php | 124 + .../symfony/translation/MessageCatalogue.php | 270 ++ .../translation/MessageCatalogueInterface.php | 142 + .../symfony/translation/MessageSelector.php | 86 + .../translation/MetadataAwareInterface.php | 54 + .../translation/PluralizationRules.php | 209 ++ vendor/symfony/translation/README.md | 37 + .../Tests/Catalogue/AbstractOperationTest.php | 73 + .../Tests/Catalogue/MergeOperationTest.php | 83 + .../Tests/Catalogue/TargetOperationTest.php | 82 + .../TranslationDataCollectorTest.php | 148 + .../Tests/DataCollectorTranslatorTest.php | 93 + .../Tests/Dumper/CsvFileDumperTest.php | 29 + .../Tests/Dumper/FileDumperTest.php | 83 + .../Tests/Dumper/IcuResFileDumperTest.php | 28 + .../Tests/Dumper/IniFileDumperTest.php | 28 + .../Tests/Dumper/JsonFileDumperTest.php | 38 + .../Tests/Dumper/MoFileDumperTest.php | 28 + .../Tests/Dumper/PhpFileDumperTest.php | 28 + .../Tests/Dumper/PoFileDumperTest.php | 28 + .../Tests/Dumper/QtFileDumperTest.php | 28 + .../Tests/Dumper/XliffFileDumperTest.php | 89 + .../Tests/Dumper/YamlFileDumperTest.php | 46 + .../Tests/IdentityTranslatorTest.php | 95 + .../translation/Tests/IntervalTest.php | 48 + .../Tests/Loader/CsvFileLoaderTest.php | 60 + .../Tests/Loader/IcuDatFileLoaderTest.php | 64 + .../Tests/Loader/IcuResFileLoaderTest.php | 51 + .../Tests/Loader/IniFileLoaderTest.php | 50 + .../Tests/Loader/JsonFileLoaderTest.php | 61 + .../Tests/Loader/LocalizedTestCase.php | 22 + .../Tests/Loader/MoFileLoaderTest.php | 71 + .../Tests/Loader/PhpFileLoaderTest.php | 49 + .../Tests/Loader/PoFileLoaderTest.php | 96 + .../Tests/Loader/QtFileLoaderTest.php | 67 + .../Tests/Loader/XliffFileLoaderTest.php | 169 + .../Tests/Loader/YamlFileLoaderTest.php | 70 + .../Tests/LoggingTranslatorTest.php | 49 + .../Tests/MessageCatalogueTest.php | 214 ++ .../translation/Tests/MessageSelectorTest.php | 130 + .../Tests/PluralizationRulesTest.php | 123 + .../translation/Tests/TranslatorCacheTest.php | 299 ++ .../translation/Tests/TranslatorTest.php | 533 +++ .../Tests/Util/ArrayConverterTest.php | 73 + .../Tests/Writer/TranslationWriterTest.php | 47 + .../Tests/fixtures/empty-translation.mo | Bin 0 -> 49 bytes .../Tests/fixtures/empty-translation.po | 3 + .../translation/Tests/fixtures/empty.csv | 0 .../translation/Tests/fixtures/empty.ini | 0 .../translation/Tests/fixtures/empty.json | 0 .../translation/Tests/fixtures/empty.mo | 0 .../translation/Tests/fixtures/empty.po | 0 .../translation/Tests/fixtures/empty.xlf | 0 .../translation/Tests/fixtures/empty.yml | 0 .../translation/Tests/fixtures/encoding.xlf | 16 + .../Tests/fixtures/escaped-id-plurals.po | 10 + .../translation/Tests/fixtures/escaped-id.po | 8 + .../Tests/fixtures/invalid-xml-resources.xlf | 23 + .../translation/Tests/fixtures/malformed.json | 3 + .../translation/Tests/fixtures/messages.yml | 3 + .../Tests/fixtures/messages_linear.yml | 2 + .../translation/Tests/fixtures/non-valid.xlf | 11 + .../translation/Tests/fixtures/non-valid.yml | 1 + .../translation/Tests/fixtures/plurals.mo | Bin 0 -> 74 bytes .../translation/Tests/fixtures/plurals.po | 5 + .../translation/Tests/fixtures/resname.xlf | 19 + .../resourcebundle/corrupted/resources.dat | 1 + .../Tests/fixtures/resourcebundle/dat/en.res | Bin 0 -> 120 bytes .../Tests/fixtures/resourcebundle/dat/en.txt | 3 + .../Tests/fixtures/resourcebundle/dat/fr.res | Bin 0 -> 124 bytes .../Tests/fixtures/resourcebundle/dat/fr.txt | 3 + .../resourcebundle/dat/packagelist.txt | 2 + .../fixtures/resourcebundle/dat/resources.dat | Bin 0 -> 352 bytes .../Tests/fixtures/resourcebundle/res/en.res | Bin 0 -> 84 bytes .../Tests/fixtures/resources-2.0-clean.xlf | 23 + .../Tests/fixtures/resources-2.0.xlf | 25 + .../Tests/fixtures/resources-clean.xlf | 25 + .../fixtures/resources-target-attributes.xlf | 14 + .../Tests/fixtures/resources-tool-info.xlf | 14 + .../translation/Tests/fixtures/resources.csv | 4 + .../Tests/fixtures/resources.dump.json | 1 + .../translation/Tests/fixtures/resources.ini | 1 + .../translation/Tests/fixtures/resources.json | 3 + .../translation/Tests/fixtures/resources.mo | Bin 0 -> 52 bytes .../translation/Tests/fixtures/resources.php | 5 + .../translation/Tests/fixtures/resources.po | 8 + .../translation/Tests/fixtures/resources.ts | 10 + .../translation/Tests/fixtures/resources.xlf | 23 + .../translation/Tests/fixtures/resources.yml | 1 + .../translation/Tests/fixtures/valid.csv | 4 + .../Tests/fixtures/with-attributes.xlf | 21 + .../Tests/fixtures/withdoctype.xlf | 12 + .../translation/Tests/fixtures/withnote.xlf | 22 + vendor/symfony/translation/Translator.php | 441 +++ .../translation/TranslatorBagInterface.php | 31 + .../translation/TranslatorInterface.php | 65 + .../translation/Util/ArrayConverter.php | 99 + .../translation/Writer/TranslationWriter.php | 89 + vendor/symfony/translation/composer.json | 48 + vendor/symfony/translation/phpunit.xml.dist | 28 + vendor/symfony/var-dumper/.gitignore | 3 + vendor/symfony/var-dumper/CHANGELOG.md | 7 + .../symfony/var-dumper/Caster/AmqpCaster.php | 164 + vendor/symfony/var-dumper/Caster/Caster.php | 116 + .../symfony/var-dumper/Caster/ConstStub.php | 28 + .../var-dumper/Caster/CutArrayStub.php | 30 + vendor/symfony/var-dumper/Caster/CutStub.php | 56 + .../symfony/var-dumper/Caster/DOMCaster.php | 302 ++ .../var-dumper/Caster/DoctrineCaster.php | 60 + vendor/symfony/var-dumper/Caster/EnumStub.php | 27 + .../var-dumper/Caster/ExceptionCaster.php | 238 ++ .../symfony/var-dumper/Caster/FrameStub.php | 30 + .../symfony/var-dumper/Caster/MongoCaster.php | 34 + .../symfony/var-dumper/Caster/PdoCaster.php | 114 + .../symfony/var-dumper/Caster/PgSqlCaster.php | 154 + .../var-dumper/Caster/ReflectionCaster.php | 310 ++ .../var-dumper/Caster/ResourceCaster.php | 67 + .../symfony/var-dumper/Caster/SplCaster.php | 203 ++ .../symfony/var-dumper/Caster/StubCaster.php | 72 + .../symfony/var-dumper/Caster/TraceStub.php | 36 + .../var-dumper/Caster/XmlResourceCaster.php | 61 + .../var-dumper/Cloner/AbstractCloner.php | 313 ++ .../var-dumper/Cloner/ClonerInterface.php | 27 + vendor/symfony/var-dumper/Cloner/Cursor.php | 41 + vendor/symfony/var-dumper/Cloner/Data.php | 218 ++ .../var-dumper/Cloner/DumperInterface.php | 60 + vendor/symfony/var-dumper/Cloner/Stub.php | 40 + .../symfony/var-dumper/Cloner/VarCloner.php | 300 ++ .../var-dumper/Dumper/AbstractDumper.php | 176 + .../symfony/var-dumper/Dumper/CliDumper.php | 471 +++ .../var-dumper/Dumper/DataDumperInterface.php | 29 + .../symfony/var-dumper/Dumper/HtmlDumper.php | 452 +++ .../Exception/ThrowingCasterException.php | 26 + vendor/symfony/var-dumper/LICENSE | 19 + vendor/symfony/var-dumper/README.md | 14 + .../var-dumper/Resources/functions/dump.php | 24 + .../var-dumper/Test/VarDumperTestTrait.php | 45 + .../var-dumper/Tests/Caster/CasterTest.php | 180 + .../var-dumper/Tests/Caster/PdoCasterTest.php | 57 + .../Tests/Caster/ReflectionCasterTest.php | 228 ++ .../var-dumper/Tests/Caster/SplCasterTest.php | 126 + .../var-dumper/Tests/CliDumperTest.php | 441 +++ .../Tests/Fixtures/GeneratorDemo.php | 21 + .../var-dumper/Tests/Fixtures/Twig.php | 34 + .../var-dumper/Tests/Fixtures/dumb-var.php | 40 + .../var-dumper/Tests/HtmlDumperTest.php | 142 + .../Tests/Test/VarDumperTestTraitTest.php | 40 + .../var-dumper/Tests/VarClonerTest.php | 187 + vendor/symfony/var-dumper/VarDumper.php | 48 + vendor/symfony/var-dumper/composer.json | 41 + vendor/symfony/var-dumper/phpunit.xml.dist | 29 + vendor/symfony/yaml/.gitignore | 3 + vendor/symfony/yaml/CHANGELOG.md | 34 + vendor/symfony/yaml/Dumper.php | 73 + vendor/symfony/yaml/Escaper.php | 99 + .../symfony/yaml/Exception/DumpException.php | 21 + .../yaml/Exception/ExceptionInterface.php | 21 + .../symfony/yaml/Exception/ParseException.php | 141 + .../yaml/Exception/RuntimeException.php | 21 + vendor/symfony/yaml/Inline.php | 563 +++ vendor/symfony/yaml/LICENSE | 19 + vendor/symfony/yaml/Parser.php | 772 +++++ vendor/symfony/yaml/README.md | 21 + vendor/symfony/yaml/Tests/DumperTest.php | 236 ++ .../yaml/Tests/Fixtures/YtsAnchorAlias.yml | 31 + .../yaml/Tests/Fixtures/YtsBasicTests.yml | 202 ++ .../yaml/Tests/Fixtures/YtsBlockMapping.yml | 51 + .../Tests/Fixtures/YtsDocumentSeparator.yml | 85 + .../yaml/Tests/Fixtures/YtsErrorTests.yml | 25 + .../Tests/Fixtures/YtsFlowCollections.yml | 60 + .../yaml/Tests/Fixtures/YtsFoldedScalars.yml | 176 + .../Tests/Fixtures/YtsNullsAndEmpties.yml | 45 + .../Fixtures/YtsSpecificationExamples.yml | 1697 ++++++++++ .../yaml/Tests/Fixtures/YtsTypeTransfers.yml | 244 ++ .../yaml/Tests/Fixtures/embededPhp.yml | 1 + .../yaml/Tests/Fixtures/escapedCharacters.yml | 155 + vendor/symfony/yaml/Tests/Fixtures/index.yml | 18 + .../yaml/Tests/Fixtures/sfComments.yml | 76 + .../symfony/yaml/Tests/Fixtures/sfCompact.yml | 159 + .../yaml/Tests/Fixtures/sfMergeKey.yml | 58 + .../symfony/yaml/Tests/Fixtures/sfObjects.yml | 11 + .../symfony/yaml/Tests/Fixtures/sfQuotes.yml | 33 + .../symfony/yaml/Tests/Fixtures/sfTests.yml | 149 + .../Tests/Fixtures/unindentedCollections.yml | 82 + vendor/symfony/yaml/Tests/InlineTest.php | 429 +++ .../symfony/yaml/Tests/ParseExceptionTest.php | 33 + vendor/symfony/yaml/Tests/ParserTest.php | 1051 ++++++ vendor/symfony/yaml/Tests/YamlTest.php | 25 + vendor/symfony/yaml/Unescaper.php | 142 + vendor/symfony/yaml/Yaml.php | 69 + vendor/symfony/yaml/composer.json | 33 + vendor/symfony/yaml/phpunit.xml.dist | 28 + vendor/vlucas/phpdotenv/LICENSE.txt | 32 + vendor/vlucas/phpdotenv/composer.json | 31 + vendor/vlucas/phpdotenv/src/Dotenv.php | 87 + .../src/Exception/ExceptionInterface.php | 11 + .../Exception/InvalidCallbackException.php | 13 + .../src/Exception/InvalidFileException.php | 13 + .../src/Exception/InvalidPathException.php | 13 + .../src/Exception/ValidationException.php | 13 + vendor/vlucas/phpdotenv/src/Loader.php | 371 ++ vendor/vlucas/phpdotenv/src/Validator.php | 130 + 1763 files changed, 195114 insertions(+) create mode 100644 .env create mode 100644 .env.example create mode 100644 artisan create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 phpunit.xml create mode 100644 readme.md create mode 100644 server.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/AbstractContextAwareMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/AbstractMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassAttributesMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassMethodsMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassNamesMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/CommandsMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ConstantsMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/FunctionsMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/KeywordsMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/MongoClientMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/MongoDatabaseMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ObjectAttributesMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ObjectMethodsMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/TabCompletion/Matcher/VariablesMatcher.php create mode 100644 vendor/psy/psysh/src/Psy/Util/Docblock.php create mode 100644 vendor/psy/psysh/src/Psy/Util/Json.php create mode 100644 vendor/psy/psysh/src/Psy/Util/Mirror.php create mode 100644 vendor/psy/psysh/src/Psy/Util/Str.php create mode 100644 vendor/psy/psysh/src/Psy/VarDumper/Cloner.php create mode 100644 vendor/psy/psysh/src/Psy/VarDumper/Dumper.php create mode 100644 vendor/psy/psysh/src/Psy/VarDumper/Presenter.php create mode 100644 vendor/psy/psysh/src/Psy/VarDumper/PresenterAware.php create mode 100644 vendor/psy/psysh/test/Psy/Test/AutoloaderTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/AbstractClassPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/AssignThisVariablePassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/CallTimePassByReferencePassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/CalledClassPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/CodeCleanerTestCase.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/Fixtures/ClassWithCallStatic.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/Fixtures/ClassWithStatic.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/FunctionReturnInWriteContextPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/ImplicitReturnPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/InstanceOfPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/LeavePsyshAlonePassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/LegacyEmptyPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/MagicConstantsPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/NamespacePassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/StaticConstructorPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/UseStatementPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidClassNamePassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidConstantPassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidFunctionNamePassTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/CodeCleanerTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/ConfigurationTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Exception/BreakExceptionTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Exception/ErrorExceptionTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Exception/FatalErrorExceptionTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Exception/ParseErrorExceptionTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Exception/RuntimeExceptionTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Formatter/CodeFormatterTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Formatter/DocblockFormatterTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Formatter/SignatureFormatterTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Readline/GNUReadlineTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Readline/LibeditTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Readline/TransientTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Reflection/ReflectionConstantTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/ShellTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/TabCompletion/AutoCompleterTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/TabCompletion/StaticSample.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Util/DocblockTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Util/MirrorTest.php create mode 100644 vendor/psy/psysh/test/Psy/Test/Util/StrTest.php create mode 100644 vendor/psy/psysh/test/fixtures/config.php create mode 100644 vendor/psy/psysh/test/fixtures/default/.config/psysh/config.php create mode 100644 vendor/psy/psysh/test/fixtures/default/.config/psysh/psysh_history create mode 100644 vendor/psy/psysh/test/fixtures/default/.local/share/psysh/php_manual.sqlite create mode 100644 vendor/psy/psysh/test/fixtures/empty.php create mode 100644 vendor/psy/psysh/test/fixtures/legacy/.psysh/history create mode 100644 vendor/psy/psysh/test/fixtures/legacy/.psysh/php_manual.sqlite create mode 100644 vendor/psy/psysh/test/fixtures/legacy/.psysh/rc.php create mode 100644 vendor/psy/psysh/test/fixtures/mixed/.psysh/config.php create mode 100644 vendor/psy/psysh/test/fixtures/mixed/.psysh/psysh_history create mode 100644 vendor/psy/psysh/test/fixtures/mixed/.psysh/rc.php create mode 100644 vendor/psy/psysh/test/fixtures/project/.psysh.php create mode 100644 vendor/psy/psysh/test/fixtures/unvis_fixtures.json create mode 100644 vendor/psy/psysh/test/tools/gen_unvis_fixtures.py create mode 100644 vendor/psy/psysh/test/tools/vis.py create mode 100644 vendor/sebastian/comparator/.gitignore create mode 100644 vendor/sebastian/comparator/.travis.yml create mode 100644 vendor/sebastian/comparator/LICENSE create mode 100644 vendor/sebastian/comparator/README.md create mode 100644 vendor/sebastian/comparator/build.xml create mode 100644 vendor/sebastian/comparator/build/travis-ci.xml create mode 100644 vendor/sebastian/comparator/composer.json create mode 100644 vendor/sebastian/comparator/phpunit.xml.dist create mode 100644 vendor/sebastian/comparator/src/ArrayComparator.php create mode 100644 vendor/sebastian/comparator/src/Comparator.php create mode 100644 vendor/sebastian/comparator/src/ComparisonFailure.php create mode 100644 vendor/sebastian/comparator/src/DOMNodeComparator.php create mode 100644 vendor/sebastian/comparator/src/DateTimeComparator.php create mode 100644 vendor/sebastian/comparator/src/DoubleComparator.php create mode 100644 vendor/sebastian/comparator/src/ExceptionComparator.php create mode 100644 vendor/sebastian/comparator/src/Factory.php create mode 100644 vendor/sebastian/comparator/src/MockObjectComparator.php create mode 100644 vendor/sebastian/comparator/src/NumericComparator.php create mode 100644 vendor/sebastian/comparator/src/ObjectComparator.php create mode 100644 vendor/sebastian/comparator/src/ResourceComparator.php create mode 100644 vendor/sebastian/comparator/src/ScalarComparator.php create mode 100644 vendor/sebastian/comparator/src/SplObjectStorageComparator.php create mode 100644 vendor/sebastian/comparator/src/TypeComparator.php create mode 100644 vendor/sebastian/comparator/tests/ArrayComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/DOMNodeComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/DateTimeComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/DoubleComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/ExceptionComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/FactoryTest.php create mode 100644 vendor/sebastian/comparator/tests/MockObjectComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/NumericComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/ObjectComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/ResourceComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/ScalarComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/SplObjectStorageComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/TypeComparatorTest.php create mode 100644 vendor/sebastian/comparator/tests/_files/Author.php create mode 100644 vendor/sebastian/comparator/tests/_files/Book.php create mode 100644 vendor/sebastian/comparator/tests/_files/ClassWithToString.php create mode 100644 vendor/sebastian/comparator/tests/_files/SampleClass.php create mode 100644 vendor/sebastian/comparator/tests/_files/Struct.php create mode 100644 vendor/sebastian/comparator/tests/_files/TestClass.php create mode 100644 vendor/sebastian/comparator/tests/_files/TestClassComparator.php create mode 100644 vendor/sebastian/comparator/tests/autoload.php create mode 100644 vendor/sebastian/comparator/tests/bootstrap.php create mode 100644 vendor/sebastian/diff/.gitignore create mode 100644 vendor/sebastian/diff/.php_cs create mode 100644 vendor/sebastian/diff/.travis.yml create mode 100644 vendor/sebastian/diff/LICENSE create mode 100644 vendor/sebastian/diff/README.md create mode 100644 vendor/sebastian/diff/build.xml create mode 100644 vendor/sebastian/diff/composer.json create mode 100644 vendor/sebastian/diff/phpunit.xml.dist create mode 100644 vendor/sebastian/diff/src/Chunk.php create mode 100644 vendor/sebastian/diff/src/Diff.php create mode 100644 vendor/sebastian/diff/src/Differ.php create mode 100644 vendor/sebastian/diff/src/LCS/LongestCommonSubsequence.php create mode 100644 vendor/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php create mode 100644 vendor/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php create mode 100644 vendor/sebastian/diff/src/Line.php create mode 100644 vendor/sebastian/diff/src/Parser.php create mode 100644 vendor/sebastian/diff/tests/DifferTest.php create mode 100644 vendor/sebastian/diff/tests/LCS/TimeEfficientImplementationTest.php create mode 100644 vendor/sebastian/diff/tests/ParserTest.php create mode 100644 vendor/sebastian/diff/tests/fixtures/patch.txt create mode 100644 vendor/sebastian/diff/tests/fixtures/patch2.txt create mode 100644 vendor/sebastian/environment/.gitignore create mode 100644 vendor/sebastian/environment/.travis.yml create mode 100644 vendor/sebastian/environment/LICENSE create mode 100644 vendor/sebastian/environment/README.md create mode 100644 vendor/sebastian/environment/build.xml create mode 100644 vendor/sebastian/environment/composer.json create mode 100644 vendor/sebastian/environment/phpunit.xml.dist create mode 100644 vendor/sebastian/environment/src/Console.php create mode 100644 vendor/sebastian/environment/src/Runtime.php create mode 100644 vendor/sebastian/environment/tests/ConsoleTest.php create mode 100644 vendor/sebastian/environment/tests/RuntimeTest.php create mode 100644 vendor/sebastian/exporter/.gitignore create mode 100644 vendor/sebastian/exporter/.travis.yml create mode 100644 vendor/sebastian/exporter/LICENSE create mode 100644 vendor/sebastian/exporter/README.md create mode 100644 vendor/sebastian/exporter/build.xml create mode 100644 vendor/sebastian/exporter/composer.json create mode 100644 vendor/sebastian/exporter/phpunit.xml.dist create mode 100644 vendor/sebastian/exporter/src/Exporter.php create mode 100644 vendor/sebastian/exporter/tests/ExporterTest.php create mode 100644 vendor/sebastian/global-state/.gitignore create mode 100644 vendor/sebastian/global-state/.travis.yml create mode 100644 vendor/sebastian/global-state/LICENSE create mode 100644 vendor/sebastian/global-state/README.md create mode 100644 vendor/sebastian/global-state/build.xml create mode 100644 vendor/sebastian/global-state/composer.json create mode 100644 vendor/sebastian/global-state/phpunit.xml.dist create mode 100644 vendor/sebastian/global-state/src/Blacklist.php create mode 100644 vendor/sebastian/global-state/src/CodeExporter.php create mode 100644 vendor/sebastian/global-state/src/Exception.php create mode 100644 vendor/sebastian/global-state/src/Restorer.php create mode 100644 vendor/sebastian/global-state/src/RuntimeException.php create mode 100644 vendor/sebastian/global-state/src/Snapshot.php create mode 100644 vendor/sebastian/global-state/tests/BlacklistTest.php create mode 100644 vendor/sebastian/global-state/tests/SnapshotTest.php create mode 100644 vendor/sebastian/global-state/tests/_fixture/BlacklistedChildClass.php create mode 100644 vendor/sebastian/global-state/tests/_fixture/BlacklistedClass.php create mode 100644 vendor/sebastian/global-state/tests/_fixture/BlacklistedImplementor.php create mode 100644 vendor/sebastian/global-state/tests/_fixture/BlacklistedInterface.php create mode 100644 vendor/sebastian/global-state/tests/_fixture/SnapshotClass.php create mode 100644 vendor/sebastian/global-state/tests/_fixture/SnapshotDomDocument.php create mode 100644 vendor/sebastian/global-state/tests/_fixture/SnapshotFunctions.php create mode 100644 vendor/sebastian/global-state/tests/_fixture/SnapshotTrait.php create mode 100644 vendor/sebastian/recursion-context/.gitignore create mode 100644 vendor/sebastian/recursion-context/.travis.yml create mode 100644 vendor/sebastian/recursion-context/LICENSE create mode 100644 vendor/sebastian/recursion-context/README.md create mode 100644 vendor/sebastian/recursion-context/build.xml create mode 100644 vendor/sebastian/recursion-context/composer.json create mode 100644 vendor/sebastian/recursion-context/phpunit.xml.dist create mode 100644 vendor/sebastian/recursion-context/src/Context.php create mode 100644 vendor/sebastian/recursion-context/src/Exception.php create mode 100644 vendor/sebastian/recursion-context/src/InvalidArgumentException.php create mode 100644 vendor/sebastian/recursion-context/tests/ContextTest.php create mode 100644 vendor/sebastian/version/.gitattributes create mode 100644 vendor/sebastian/version/.gitignore create mode 100644 vendor/sebastian/version/LICENSE create mode 100644 vendor/sebastian/version/README.md create mode 100644 vendor/sebastian/version/composer.json create mode 100644 vendor/sebastian/version/src/Version.php create mode 100644 vendor/swiftmailer/swiftmailer/.gitattributes create mode 100644 vendor/swiftmailer/swiftmailer/.gitignore create mode 100644 vendor/swiftmailer/swiftmailer/.travis.yml create mode 100644 vendor/swiftmailer/swiftmailer/CHANGES create mode 100644 vendor/swiftmailer/swiftmailer/LICENSE create mode 100644 vendor/swiftmailer/swiftmailer/README create mode 100644 vendor/swiftmailer/swiftmailer/VERSION create mode 100644 vendor/swiftmailer/swiftmailer/composer.json create mode 100644 vendor/swiftmailer/swiftmailer/doc/headers.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/help-resources.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/including-the-files.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/index.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/installing.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/introduction.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/japanese.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/messages.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/overview.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/plugins.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/sending.rst create mode 100644 vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle create mode 100644 vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle create mode 100644 vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Attachment.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/EmbeddedFile.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Base64Encoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/Event.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventObject.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/FailoverTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Filterable.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Image.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/IoException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/NullKeyCache.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/Base64ContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Grammar.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderSet.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/AbstractHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimePart.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Exception.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Preferences.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/RfcComplianceException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SendmailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpHandler.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SmtpAgent.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SpoolTransport.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/classes/Swift/Validate.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/mime_types.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/preferences.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swift_init.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swift_required.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swift_required_pear.php create mode 100644 vendor/swiftmailer/swiftmailer/lib/swiftmailer_generate_mimes_config.php create mode 100644 vendor/swiftmailer/swiftmailer/phpunit.xml.dist create mode 100644 vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/StreamCollector.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-2022-jp/one.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/iso-8859-1/one.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/one.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/three.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/charsets/utf-8/two.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.priv create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/dkim/dkim.test.pub create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/files/data.txt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/files/swiftmailer.png create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/CA.srl create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/ca.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/create-cert.sh create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/encrypt2.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/intermediate.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.crt create mode 100644 vendor/swiftmailer/swiftmailer/tests/_samples/smime/sign2.key create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance.conf.php.default create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/AttachmentAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/ByteStream/FileByteStreamAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Base64EncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bootstrap.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/fixtures/EsmtpTransportFixture.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/fixtures/MimeEntityFixture.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/AttachmentSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/AntiFloodPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php create mode 100644 vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php create mode 100644 vendor/symfony/console/.gitignore create mode 100644 vendor/symfony/console/Application.php create mode 100644 vendor/symfony/console/CHANGELOG.md create mode 100644 vendor/symfony/console/Command/Command.php create mode 100644 vendor/symfony/console/Command/HelpCommand.php create mode 100644 vendor/symfony/console/Command/ListCommand.php create mode 100644 vendor/symfony/console/ConsoleEvents.php create mode 100644 vendor/symfony/console/Descriptor/ApplicationDescription.php create mode 100644 vendor/symfony/console/Descriptor/Descriptor.php create mode 100644 vendor/symfony/console/Descriptor/DescriptorInterface.php create mode 100644 vendor/symfony/console/Descriptor/JsonDescriptor.php create mode 100644 vendor/symfony/console/Descriptor/MarkdownDescriptor.php create mode 100644 vendor/symfony/console/Descriptor/TextDescriptor.php create mode 100644 vendor/symfony/console/Descriptor/XmlDescriptor.php create mode 100644 vendor/symfony/console/Event/ConsoleCommandEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleExceptionEvent.php create mode 100644 vendor/symfony/console/Event/ConsoleTerminateEvent.php create mode 100644 vendor/symfony/console/Exception/CommandNotFoundException.php create mode 100644 vendor/symfony/console/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/console/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/console/Exception/InvalidOptionException.php create mode 100644 vendor/symfony/console/Exception/LogicException.php create mode 100644 vendor/symfony/console/Exception/RuntimeException.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatter.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterInterface.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterStyle.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php create mode 100644 vendor/symfony/console/Formatter/OutputFormatterStyleStack.php create mode 100644 vendor/symfony/console/Helper/DebugFormatterHelper.php create mode 100644 vendor/symfony/console/Helper/DescriptorHelper.php create mode 100644 vendor/symfony/console/Helper/FormatterHelper.php create mode 100644 vendor/symfony/console/Helper/Helper.php create mode 100644 vendor/symfony/console/Helper/HelperInterface.php create mode 100644 vendor/symfony/console/Helper/HelperSet.php create mode 100644 vendor/symfony/console/Helper/InputAwareHelper.php create mode 100644 vendor/symfony/console/Helper/ProcessHelper.php create mode 100644 vendor/symfony/console/Helper/ProgressBar.php create mode 100644 vendor/symfony/console/Helper/ProgressIndicator.php create mode 100644 vendor/symfony/console/Helper/QuestionHelper.php create mode 100644 vendor/symfony/console/Helper/SymfonyQuestionHelper.php create mode 100644 vendor/symfony/console/Helper/Table.php create mode 100644 vendor/symfony/console/Helper/TableCell.php create mode 100644 vendor/symfony/console/Helper/TableSeparator.php create mode 100644 vendor/symfony/console/Helper/TableStyle.php create mode 100644 vendor/symfony/console/Input/ArgvInput.php create mode 100644 vendor/symfony/console/Input/ArrayInput.php create mode 100644 vendor/symfony/console/Input/Input.php create mode 100644 vendor/symfony/console/Input/InputArgument.php create mode 100644 vendor/symfony/console/Input/InputAwareInterface.php create mode 100644 vendor/symfony/console/Input/InputDefinition.php create mode 100644 vendor/symfony/console/Input/InputInterface.php create mode 100644 vendor/symfony/console/Input/InputOption.php create mode 100644 vendor/symfony/console/Input/StringInput.php create mode 100644 vendor/symfony/console/LICENSE create mode 100644 vendor/symfony/console/Logger/ConsoleLogger.php create mode 100644 vendor/symfony/console/Output/BufferedOutput.php create mode 100644 vendor/symfony/console/Output/ConsoleOutput.php create mode 100644 vendor/symfony/console/Output/ConsoleOutputInterface.php create mode 100644 vendor/symfony/console/Output/NullOutput.php create mode 100644 vendor/symfony/console/Output/Output.php create mode 100644 vendor/symfony/console/Output/OutputInterface.php create mode 100644 vendor/symfony/console/Output/StreamOutput.php create mode 100644 vendor/symfony/console/Question/ChoiceQuestion.php create mode 100644 vendor/symfony/console/Question/ConfirmationQuestion.php create mode 100644 vendor/symfony/console/Question/Question.php create mode 100644 vendor/symfony/console/README.md create mode 100644 vendor/symfony/console/Resources/bin/hiddeninput.exe create mode 100644 vendor/symfony/console/Style/OutputStyle.php create mode 100644 vendor/symfony/console/Style/StyleInterface.php create mode 100644 vendor/symfony/console/Style/SymfonyStyle.php create mode 100644 vendor/symfony/console/Tester/ApplicationTester.php create mode 100644 vendor/symfony/console/Tester/CommandTester.php create mode 100644 vendor/symfony/console/Tests/ApplicationTest.php create mode 100644 vendor/symfony/console/Tests/Command/CommandTest.php create mode 100644 vendor/symfony/console/Tests/Command/HelpCommandTest.php create mode 100644 vendor/symfony/console/Tests/Command/ListCommandTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php create mode 100644 vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php create mode 100644 vendor/symfony/console/Tests/Fixtures/BarBucCommand.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php create mode 100644 vendor/symfony/console/Tests/Fixtures/DummyOutput.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo1Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo2Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo3Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo4Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo5Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Foo6Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/FooCommand.php create mode 100644 vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php create mode 100644 vendor/symfony/console/Tests/Fixtures/FoobarCommand.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/TestCommand.php create mode 100644 vendor/symfony/console/Tests/Fixtures/application_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/application_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/application_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/application_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/application_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/application_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/application_astext1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_astext2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_gethelp.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_run1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_run2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_run3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/application_run4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/command_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/command_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/command_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/command_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/command_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/command_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/command_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/command_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/command_astext.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/command_asxml.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/definition_astext.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/definition_asxml.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_3.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_3.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_3.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_4.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_4.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_argument_4.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_3.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_3.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_3.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_4.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_4.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_definition_4.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_1.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_1.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_1.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_1.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_2.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_2.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_2.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_2.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_3.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_3.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_3.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_3.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_4.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_4.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_4.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_4.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_5.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_5.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_5.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_5.xml create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_6.json create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_6.md create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_6.txt create mode 100644 vendor/symfony/console/Tests/Fixtures/input_option_6.xml create mode 100644 vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php create mode 100644 vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php create mode 100644 vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php create mode 100644 vendor/symfony/console/Tests/Helper/FormatterHelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/HelperSetTest.php create mode 100644 vendor/symfony/console/Tests/Helper/ProcessHelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/ProgressBarTest.php create mode 100644 vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php create mode 100644 vendor/symfony/console/Tests/Helper/QuestionHelperTest.php create mode 100644 vendor/symfony/console/Tests/Helper/TableStyleTest.php create mode 100644 vendor/symfony/console/Tests/Helper/TableTest.php create mode 100644 vendor/symfony/console/Tests/Input/ArgvInputTest.php create mode 100644 vendor/symfony/console/Tests/Input/ArrayInputTest.php create mode 100644 vendor/symfony/console/Tests/Input/InputArgumentTest.php create mode 100644 vendor/symfony/console/Tests/Input/InputDefinitionTest.php create mode 100644 vendor/symfony/console/Tests/Input/InputOptionTest.php create mode 100644 vendor/symfony/console/Tests/Input/InputTest.php create mode 100644 vendor/symfony/console/Tests/Input/StringInputTest.php create mode 100644 vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php create mode 100644 vendor/symfony/console/Tests/Output/ConsoleOutputTest.php create mode 100644 vendor/symfony/console/Tests/Output/NullOutputTest.php create mode 100644 vendor/symfony/console/Tests/Output/OutputTest.php create mode 100644 vendor/symfony/console/Tests/Output/StreamOutputTest.php create mode 100644 vendor/symfony/console/Tests/Style/SymfonyStyleTest.php create mode 100644 vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php create mode 100644 vendor/symfony/console/Tests/Tester/CommandTesterTest.php create mode 100644 vendor/symfony/console/composer.json create mode 100644 vendor/symfony/console/phpunit.xml.dist create mode 100644 vendor/symfony/css-selector/.gitignore create mode 100644 vendor/symfony/css-selector/CHANGELOG.md create mode 100644 vendor/symfony/css-selector/CssSelectorConverter.php create mode 100644 vendor/symfony/css-selector/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/css-selector/Exception/ExpressionErrorException.php create mode 100644 vendor/symfony/css-selector/Exception/InternalErrorException.php create mode 100644 vendor/symfony/css-selector/Exception/ParseException.php create mode 100644 vendor/symfony/css-selector/Exception/SyntaxErrorException.php create mode 100644 vendor/symfony/css-selector/LICENSE create mode 100644 vendor/symfony/css-selector/Node/AbstractNode.php create mode 100644 vendor/symfony/css-selector/Node/AttributeNode.php create mode 100644 vendor/symfony/css-selector/Node/ClassNode.php create mode 100644 vendor/symfony/css-selector/Node/CombinedSelectorNode.php create mode 100644 vendor/symfony/css-selector/Node/ElementNode.php create mode 100644 vendor/symfony/css-selector/Node/FunctionNode.php create mode 100644 vendor/symfony/css-selector/Node/HashNode.php create mode 100644 vendor/symfony/css-selector/Node/NegationNode.php create mode 100644 vendor/symfony/css-selector/Node/NodeInterface.php create mode 100644 vendor/symfony/css-selector/Node/PseudoNode.php create mode 100644 vendor/symfony/css-selector/Node/SelectorNode.php create mode 100644 vendor/symfony/css-selector/Node/Specificity.php create mode 100644 vendor/symfony/css-selector/Parser/Handler/CommentHandler.php create mode 100644 vendor/symfony/css-selector/Parser/Handler/HandlerInterface.php create mode 100644 vendor/symfony/css-selector/Parser/Handler/HashHandler.php create mode 100644 vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.php create mode 100644 vendor/symfony/css-selector/Parser/Handler/NumberHandler.php create mode 100644 vendor/symfony/css-selector/Parser/Handler/StringHandler.php create mode 100644 vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.php create mode 100644 vendor/symfony/css-selector/Parser/Parser.php create mode 100644 vendor/symfony/css-selector/Parser/ParserInterface.php create mode 100644 vendor/symfony/css-selector/Parser/Reader.php create mode 100644 vendor/symfony/css-selector/Parser/Shortcut/ClassParser.php create mode 100644 vendor/symfony/css-selector/Parser/Shortcut/ElementParser.php create mode 100644 vendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php create mode 100644 vendor/symfony/css-selector/Parser/Shortcut/HashParser.php create mode 100644 vendor/symfony/css-selector/Parser/Token.php create mode 100644 vendor/symfony/css-selector/Parser/TokenStream.php create mode 100644 vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.php create mode 100644 vendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php create mode 100644 vendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php create mode 100644 vendor/symfony/css-selector/README.md create mode 100644 vendor/symfony/css-selector/Tests/CssSelectorConverterTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/AbstractNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/AttributeNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/ClassNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/CombinedSelectorNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/ElementNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/FunctionNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/HashNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/NegationNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/PseudoNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/SelectorNodeTest.php create mode 100644 vendor/symfony/css-selector/Tests/Node/SpecificityTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Handler/AbstractHandlerTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Handler/CommentHandlerTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Handler/HashHandlerTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Handler/IdentifierHandlerTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Handler/NumberHandlerTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Handler/StringHandlerTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Handler/WhitespaceHandlerTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/ParserTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/ReaderTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Shortcut/ClassParserTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Shortcut/ElementParserTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Shortcut/EmptyStringParserTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/Shortcut/HashParserTest.php create mode 100644 vendor/symfony/css-selector/Tests/Parser/TokenStreamTest.php create mode 100644 vendor/symfony/css-selector/Tests/XPath/Fixtures/ids.html create mode 100644 vendor/symfony/css-selector/Tests/XPath/Fixtures/lang.xml create mode 100644 vendor/symfony/css-selector/Tests/XPath/Fixtures/shakespear.html create mode 100644 vendor/symfony/css-selector/Tests/XPath/TranslatorTest.php create mode 100644 vendor/symfony/css-selector/XPath/Extension/AbstractExtension.php create mode 100644 vendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php create mode 100644 vendor/symfony/css-selector/XPath/Extension/CombinationExtension.php create mode 100644 vendor/symfony/css-selector/XPath/Extension/ExtensionInterface.php create mode 100644 vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php create mode 100644 vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php create mode 100644 vendor/symfony/css-selector/XPath/Extension/NodeExtension.php create mode 100644 vendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.php create mode 100644 vendor/symfony/css-selector/XPath/Translator.php create mode 100644 vendor/symfony/css-selector/XPath/TranslatorInterface.php create mode 100644 vendor/symfony/css-selector/XPath/XPathExpr.php create mode 100644 vendor/symfony/css-selector/composer.json create mode 100644 vendor/symfony/css-selector/phpunit.xml.dist create mode 100644 vendor/symfony/debug/.gitignore create mode 100644 vendor/symfony/debug/BufferingLogger.php create mode 100644 vendor/symfony/debug/CHANGELOG.md create mode 100644 vendor/symfony/debug/Debug.php create mode 100644 vendor/symfony/debug/DebugClassLoader.php create mode 100644 vendor/symfony/debug/ErrorHandler.php create mode 100644 vendor/symfony/debug/Exception/ClassNotFoundException.php create mode 100644 vendor/symfony/debug/Exception/ContextErrorException.php create mode 100644 vendor/symfony/debug/Exception/FatalErrorException.php create mode 100644 vendor/symfony/debug/Exception/FatalThrowableError.php create mode 100644 vendor/symfony/debug/Exception/FlattenException.php create mode 100644 vendor/symfony/debug/Exception/OutOfMemoryException.php create mode 100644 vendor/symfony/debug/Exception/UndefinedFunctionException.php create mode 100644 vendor/symfony/debug/Exception/UndefinedMethodException.php create mode 100644 vendor/symfony/debug/ExceptionHandler.php create mode 100644 vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php create mode 100644 vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php create mode 100644 vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php create mode 100644 vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php create mode 100644 vendor/symfony/debug/LICENSE create mode 100644 vendor/symfony/debug/README.md create mode 100644 vendor/symfony/debug/Resources/ext/README.md create mode 100644 vendor/symfony/debug/Resources/ext/config.m4 create mode 100644 vendor/symfony/debug/Resources/ext/config.w32 create mode 100644 vendor/symfony/debug/Resources/ext/php_symfony_debug.h create mode 100644 vendor/symfony/debug/Resources/ext/symfony_debug.c create mode 100644 vendor/symfony/debug/Resources/ext/tests/001.phpt create mode 100644 vendor/symfony/debug/Resources/ext/tests/002.phpt create mode 100644 vendor/symfony/debug/Resources/ext/tests/002_1.phpt create mode 100644 vendor/symfony/debug/Resources/ext/tests/003.phpt create mode 100644 vendor/symfony/debug/Tests/DebugClassLoaderTest.php create mode 100644 vendor/symfony/debug/Tests/ErrorHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php create mode 100644 vendor/symfony/debug/Tests/ExceptionHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/ClassAlias.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/DeprecatedClass.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/DeprecatedInterface.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/NonDeprecatedInterface.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/PEARClass.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/ToStringThrower.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/casemismatch.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/notPsr0Bis.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/psr4/Psr4CaseMismatch.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/reallyNotPsr0.php create mode 100644 vendor/symfony/debug/Tests/Fixtures2/RequiredTwice.php create mode 100644 vendor/symfony/debug/Tests/HeaderMock.php create mode 100644 vendor/symfony/debug/Tests/MockExceptionHandler.php create mode 100644 vendor/symfony/debug/composer.json create mode 100644 vendor/symfony/debug/phpunit.xml.dist create mode 100644 vendor/symfony/dom-crawler/.gitignore create mode 100644 vendor/symfony/dom-crawler/CHANGELOG.md create mode 100644 vendor/symfony/dom-crawler/Crawler.php create mode 100644 vendor/symfony/dom-crawler/Field/ChoiceFormField.php create mode 100644 vendor/symfony/dom-crawler/Field/FileFormField.php create mode 100644 vendor/symfony/dom-crawler/Field/FormField.php create mode 100644 vendor/symfony/dom-crawler/Field/InputFormField.php create mode 100644 vendor/symfony/dom-crawler/Field/TextareaFormField.php create mode 100644 vendor/symfony/dom-crawler/Form.php create mode 100644 vendor/symfony/dom-crawler/FormFieldRegistry.php create mode 100644 vendor/symfony/dom-crawler/LICENSE create mode 100644 vendor/symfony/dom-crawler/Link.php create mode 100644 vendor/symfony/dom-crawler/README.md create mode 100644 vendor/symfony/dom-crawler/Tests/CrawlerTest.php create mode 100644 vendor/symfony/dom-crawler/Tests/Field/ChoiceFormFieldTest.php create mode 100644 vendor/symfony/dom-crawler/Tests/Field/FileFormFieldTest.php create mode 100644 vendor/symfony/dom-crawler/Tests/Field/FormFieldTest.php create mode 100644 vendor/symfony/dom-crawler/Tests/Field/FormFieldTestCase.php create mode 100644 vendor/symfony/dom-crawler/Tests/Field/InputFormFieldTest.php create mode 100644 vendor/symfony/dom-crawler/Tests/Field/TextareaFormFieldTest.php create mode 100644 vendor/symfony/dom-crawler/Tests/Fixtures/no-extension create mode 100644 vendor/symfony/dom-crawler/Tests/Fixtures/windows-1250.html create mode 100644 vendor/symfony/dom-crawler/Tests/FormTest.php create mode 100644 vendor/symfony/dom-crawler/Tests/LinkTest.php create mode 100644 vendor/symfony/dom-crawler/composer.json create mode 100644 vendor/symfony/dom-crawler/phpunit.xml.dist create mode 100644 vendor/symfony/event-dispatcher/.gitignore create mode 100644 vendor/symfony/event-dispatcher/CHANGELOG.md create mode 100644 vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/Debug/WrappedListener.php create mode 100644 vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php create mode 100644 vendor/symfony/event-dispatcher/Event.php create mode 100644 vendor/symfony/event-dispatcher/EventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/EventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/EventSubscriberInterface.php create mode 100644 vendor/symfony/event-dispatcher/GenericEvent.php create mode 100644 vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/LICENSE create mode 100644 vendor/symfony/event-dispatcher/README.md create mode 100644 vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/EventTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/GenericEventTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/composer.json create mode 100644 vendor/symfony/event-dispatcher/phpunit.xml.dist create mode 100644 vendor/symfony/finder/.gitignore create mode 100644 vendor/symfony/finder/CHANGELOG.md create mode 100644 vendor/symfony/finder/Comparator/Comparator.php create mode 100644 vendor/symfony/finder/Comparator/DateComparator.php create mode 100644 vendor/symfony/finder/Comparator/NumberComparator.php create mode 100644 vendor/symfony/finder/Exception/AccessDeniedException.php create mode 100644 vendor/symfony/finder/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/finder/Finder.php create mode 100644 vendor/symfony/finder/Glob.php create mode 100644 vendor/symfony/finder/Iterator/CustomFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/DateRangeFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/FileTypeFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/FilecontentFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/FilenameFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/FilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/PathFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php create mode 100644 vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/SortableIterator.php create mode 100644 vendor/symfony/finder/LICENSE create mode 100644 vendor/symfony/finder/README.md create mode 100644 vendor/symfony/finder/SplFileInfo.php create mode 100644 vendor/symfony/finder/Tests/Comparator/ComparatorTest.php create mode 100644 vendor/symfony/finder/Tests/Comparator/DateComparatorTest.php create mode 100644 vendor/symfony/finder/Tests/Comparator/NumberComparatorTest.php create mode 100644 vendor/symfony/finder/Tests/FinderTest.php create mode 100644 vendor/symfony/finder/Tests/Fixtures/A/B/C/abc.dat create mode 100644 vendor/symfony/finder/Tests/Fixtures/A/B/ab.dat create mode 100644 vendor/symfony/finder/Tests/Fixtures/A/a.dat create mode 100644 vendor/symfony/finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy create mode 100644 vendor/symfony/finder/Tests/Fixtures/copy/A/B/ab.dat.copy create mode 100644 vendor/symfony/finder/Tests/Fixtures/copy/A/a.dat.copy create mode 100644 vendor/symfony/finder/Tests/Fixtures/dolor.txt create mode 100644 vendor/symfony/finder/Tests/Fixtures/ipsum.txt create mode 100644 vendor/symfony/finder/Tests/Fixtures/lorem.txt create mode 100644 vendor/symfony/finder/Tests/Fixtures/one/a create mode 100644 vendor/symfony/finder/Tests/Fixtures/one/b/c.neon create mode 100644 vendor/symfony/finder/Tests/Fixtures/one/b/d.neon create mode 100644 vendor/symfony/finder/Tests/Fixtures/r+e.gex[c]a(r)s/dir/bar.dat create mode 100644 vendor/symfony/finder/Tests/Fixtures/with space/foo.txt create mode 100644 vendor/symfony/finder/Tests/GlobTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/CustomFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/DateRangeFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/DepthRangeFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/FileTypeFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/FilecontentFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/FilenameFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/FilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/Iterator.php create mode 100644 vendor/symfony/finder/Tests/Iterator/IteratorTestCase.php create mode 100644 vendor/symfony/finder/Tests/Iterator/MockFileListIterator.php create mode 100644 vendor/symfony/finder/Tests/Iterator/MockSplFileInfo.php create mode 100644 vendor/symfony/finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/PathFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/RealIteratorTestCase.php create mode 100644 vendor/symfony/finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/SizeRangeFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/SortableIteratorTest.php create mode 100644 vendor/symfony/finder/composer.json create mode 100644 vendor/symfony/finder/phpunit.xml.dist create mode 100644 vendor/symfony/http-foundation/.gitignore create mode 100644 vendor/symfony/http-foundation/AcceptHeader.php create mode 100644 vendor/symfony/http-foundation/AcceptHeaderItem.php create mode 100644 vendor/symfony/http-foundation/ApacheRequest.php create mode 100644 vendor/symfony/http-foundation/BinaryFileResponse.php create mode 100644 vendor/symfony/http-foundation/CHANGELOG.md create mode 100644 vendor/symfony/http-foundation/Cookie.php create mode 100644 vendor/symfony/http-foundation/ExpressionRequestMatcher.php create mode 100644 vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/FileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/UploadException.php create mode 100644 vendor/symfony/http-foundation/File/File.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php create mode 100644 vendor/symfony/http-foundation/File/UploadedFile.php create mode 100644 vendor/symfony/http-foundation/FileBag.php create mode 100644 vendor/symfony/http-foundation/HeaderBag.php create mode 100644 vendor/symfony/http-foundation/IpUtils.php create mode 100644 vendor/symfony/http-foundation/JsonResponse.php create mode 100644 vendor/symfony/http-foundation/LICENSE create mode 100644 vendor/symfony/http-foundation/ParameterBag.php create mode 100644 vendor/symfony/http-foundation/README.md create mode 100644 vendor/symfony/http-foundation/RedirectResponse.php create mode 100644 vendor/symfony/http-foundation/Request.php create mode 100644 vendor/symfony/http-foundation/RequestMatcher.php create mode 100644 vendor/symfony/http-foundation/RequestMatcherInterface.php create mode 100644 vendor/symfony/http-foundation/RequestStack.php create mode 100644 vendor/symfony/http-foundation/Response.php create mode 100644 vendor/symfony/http-foundation/ResponseHeaderBag.php create mode 100644 vendor/symfony/http-foundation/ServerBag.php create mode 100644 vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php create mode 100644 vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php create mode 100644 vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php create mode 100644 vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php create mode 100644 vendor/symfony/http-foundation/Session/Flash/FlashBag.php create mode 100644 vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php create mode 100644 vendor/symfony/http-foundation/Session/Session.php create mode 100644 vendor/symfony/http-foundation/Session/SessionBagInterface.php create mode 100644 vendor/symfony/http-foundation/Session/SessionInterface.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/MetadataBag.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php create mode 100644 vendor/symfony/http-foundation/StreamedResponse.php create mode 100644 vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php create mode 100644 vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ApacheRequestTest.php create mode 100644 vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php create mode 100644 vendor/symfony/http-foundation/Tests/CookieTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php create mode 100644 vendor/symfony/http-foundation/Tests/File/FakeFile.php create mode 100644 vendor/symfony/http-foundation/Tests/File/FileTest.php create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/test create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif create mode 100644 vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php create mode 100644 vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php create mode 100644 vendor/symfony/http-foundation/Tests/FileBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/HeaderBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/IpUtilsTest.php create mode 100644 vendor/symfony/http-foundation/Tests/JsonResponseTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ParameterBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/RedirectResponseTest.php create mode 100644 vendor/symfony/http-foundation/Tests/RequestMatcherTest.php create mode 100644 vendor/symfony/http-foundation/Tests/RequestStackTest.php create mode 100644 vendor/symfony/http-foundation/Tests/RequestTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ResponseTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ResponseTestCase.php create mode 100644 vendor/symfony/http-foundation/Tests/ServerBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/SessionTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php create mode 100644 vendor/symfony/http-foundation/Tests/StreamedResponseTest.php create mode 100644 vendor/symfony/http-foundation/composer.json create mode 100644 vendor/symfony/http-foundation/phpunit.xml.dist create mode 100644 vendor/symfony/http-kernel/.gitignore create mode 100644 vendor/symfony/http-kernel/Bundle/Bundle.php create mode 100644 vendor/symfony/http-kernel/Bundle/BundleInterface.php create mode 100644 vendor/symfony/http-kernel/CHANGELOG.md create mode 100644 vendor/symfony/http-kernel/CacheClearer/CacheClearerInterface.php create mode 100644 vendor/symfony/http-kernel/CacheClearer/ChainCacheClearer.php create mode 100644 vendor/symfony/http-kernel/CacheWarmer/CacheWarmer.php create mode 100644 vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php create mode 100644 vendor/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.php create mode 100644 vendor/symfony/http-kernel/CacheWarmer/WarmableInterface.php create mode 100644 vendor/symfony/http-kernel/Client.php create mode 100644 vendor/symfony/http-kernel/Config/EnvParametersResource.php create mode 100644 vendor/symfony/http-kernel/Config/FileLocator.php create mode 100644 vendor/symfony/http-kernel/Controller/ControllerReference.php create mode 100644 vendor/symfony/http-kernel/Controller/ControllerResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ControllerResolverInterface.php create mode 100644 vendor/symfony/http-kernel/Controller/TraceableControllerResolver.php create mode 100644 vendor/symfony/http-kernel/DataCollector/AjaxDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/ConfigDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/DataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/DataCollectorInterface.php create mode 100644 vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/EventDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/ExceptionDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/LateDataCollectorInterface.php create mode 100644 vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/MemoryDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/RequestDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/RouterDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/TimeDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/Util/ValueExporter.php create mode 100644 vendor/symfony/http-kernel/Debug/TraceableEventDispatcher.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/AddClassesToCachePass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/Extension.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php create mode 100644 vendor/symfony/http-kernel/Event/FilterControllerEvent.php create mode 100644 vendor/symfony/http-kernel/Event/FilterResponseEvent.php create mode 100644 vendor/symfony/http-kernel/Event/FinishRequestEvent.php create mode 100644 vendor/symfony/http-kernel/Event/GetResponseEvent.php create mode 100644 vendor/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php create mode 100644 vendor/symfony/http-kernel/Event/GetResponseForExceptionEvent.php create mode 100644 vendor/symfony/http-kernel/Event/KernelEvent.php create mode 100644 vendor/symfony/http-kernel/Event/PostResponseEvent.php create mode 100644 vendor/symfony/http-kernel/EventListener/AddRequestFormatsListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/DumpListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/ExceptionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/FragmentListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/LocaleListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/ProfilerListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/ResponseListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/RouterListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/SaveSessionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/SessionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/StreamedResponseListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/SurrogateListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/TestSessionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/TranslatorListener.php create mode 100644 vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/BadRequestHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/ConflictHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/GoneHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/HttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/HttpExceptionInterface.php create mode 100644 vendor/symfony/http-kernel/Exception/LengthRequiredHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/MethodNotAllowedHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/NotAcceptableHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/NotFoundHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/PreconditionFailedHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/PreconditionRequiredHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/TooManyRequestsHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/UnauthorizedHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/UnprocessableEntityHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.php create mode 100644 vendor/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/EsiFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/FragmentHandler.php create mode 100644 vendor/symfony/http-kernel/Fragment/FragmentRendererInterface.php create mode 100644 vendor/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/SsiFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/HttpCache/Esi.php create mode 100644 vendor/symfony/http-kernel/HttpCache/HttpCache.php create mode 100644 vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php create mode 100644 vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php create mode 100644 vendor/symfony/http-kernel/HttpCache/Ssi.php create mode 100644 vendor/symfony/http-kernel/HttpCache/Store.php create mode 100644 vendor/symfony/http-kernel/HttpCache/StoreInterface.php create mode 100644 vendor/symfony/http-kernel/HttpCache/SurrogateInterface.php create mode 100644 vendor/symfony/http-kernel/HttpKernel.php create mode 100644 vendor/symfony/http-kernel/HttpKernelInterface.php create mode 100644 vendor/symfony/http-kernel/Kernel.php create mode 100644 vendor/symfony/http-kernel/KernelEvents.php create mode 100644 vendor/symfony/http-kernel/KernelInterface.php create mode 100644 vendor/symfony/http-kernel/LICENSE create mode 100644 vendor/symfony/http-kernel/Log/DebugLoggerInterface.php create mode 100644 vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php create mode 100644 vendor/symfony/http-kernel/Profiler/Profile.php create mode 100644 vendor/symfony/http-kernel/Profiler/Profiler.php create mode 100644 vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.php create mode 100644 vendor/symfony/http-kernel/README.md create mode 100644 vendor/symfony/http-kernel/TerminableInterface.php create mode 100644 vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php create mode 100644 vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php create mode 100644 vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/ClientTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Config/EnvParametersResourceTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/ExceptionDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/LoggerDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/MemoryDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/RequestDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/TimeDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/Util/ValueExporterTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Debug/TraceableEventDispatcherTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/DumpListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/ExceptionListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/FragmentListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/LocaleListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/ProfilerListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/ResponseListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/SurrogateListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/TranslatorListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/hide.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/bar.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Bundle2Bundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/hide.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/FooCommand.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Resources/BaseBundle/hide.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Resources/Bundle1Bundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Resources/ChildBundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Resources/FooBundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/TestClient.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/TestEventDispatcher.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/EsiFragmentRendererTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/FragmentHandlerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/HIncludeFragmentRendererTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/InlineFragmentRendererTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/RoutableFragmentRendererTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/EsiTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTestCase.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/SsiTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/StoreTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpKernelTest.php create mode 100644 vendor/symfony/http-kernel/Tests/KernelTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Logger.php create mode 100644 vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/TestHttpKernel.php create mode 100644 vendor/symfony/http-kernel/Tests/UriSignerTest.php create mode 100644 vendor/symfony/http-kernel/UriSigner.php create mode 100644 vendor/symfony/http-kernel/composer.json create mode 100644 vendor/symfony/http-kernel/phpunit.xml.dist create mode 100644 vendor/symfony/polyfill-mbstring/LICENSE create mode 100644 vendor/symfony/polyfill-mbstring/Mbstring.php create mode 100644 vendor/symfony/polyfill-mbstring/README.md create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.ser create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.ser create mode 100644 vendor/symfony/polyfill-mbstring/bootstrap.php create mode 100644 vendor/symfony/polyfill-mbstring/composer.json create mode 100644 vendor/symfony/polyfill-php56/LICENSE create mode 100644 vendor/symfony/polyfill-php56/Php56.php create mode 100644 vendor/symfony/polyfill-php56/README.md create mode 100644 vendor/symfony/polyfill-php56/bootstrap.php create mode 100644 vendor/symfony/polyfill-php56/composer.json create mode 100644 vendor/symfony/polyfill-util/Binary.php create mode 100644 vendor/symfony/polyfill-util/BinaryNoFuncOverload.php create mode 100644 vendor/symfony/polyfill-util/BinaryOnFuncOverload.php create mode 100644 vendor/symfony/polyfill-util/LICENSE create mode 100644 vendor/symfony/polyfill-util/README.md create mode 100644 vendor/symfony/polyfill-util/TestListener.php create mode 100644 vendor/symfony/polyfill-util/composer.json create mode 100644 vendor/symfony/process/.gitignore create mode 100644 vendor/symfony/process/CHANGELOG.md create mode 100644 vendor/symfony/process/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/process/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/process/Exception/LogicException.php create mode 100644 vendor/symfony/process/Exception/ProcessFailedException.php create mode 100644 vendor/symfony/process/Exception/ProcessTimedOutException.php create mode 100644 vendor/symfony/process/Exception/RuntimeException.php create mode 100644 vendor/symfony/process/ExecutableFinder.php create mode 100644 vendor/symfony/process/LICENSE create mode 100644 vendor/symfony/process/PhpExecutableFinder.php create mode 100644 vendor/symfony/process/PhpProcess.php create mode 100644 vendor/symfony/process/Pipes/AbstractPipes.php create mode 100644 vendor/symfony/process/Pipes/PipesInterface.php create mode 100644 vendor/symfony/process/Pipes/UnixPipes.php create mode 100644 vendor/symfony/process/Pipes/WindowsPipes.php create mode 100644 vendor/symfony/process/Process.php create mode 100644 vendor/symfony/process/ProcessBuilder.php create mode 100644 vendor/symfony/process/ProcessUtils.php create mode 100644 vendor/symfony/process/README.md create mode 100644 vendor/symfony/process/Tests/ExecutableFinderTest.php create mode 100644 vendor/symfony/process/Tests/NonStopableProcess.php create mode 100644 vendor/symfony/process/Tests/PhpExecutableFinderTest.php create mode 100644 vendor/symfony/process/Tests/PhpProcessTest.php create mode 100644 vendor/symfony/process/Tests/PipeStdinInStdoutStdErrStreamSelect.php create mode 100644 vendor/symfony/process/Tests/ProcessBuilderTest.php create mode 100644 vendor/symfony/process/Tests/ProcessFailedExceptionTest.php create mode 100644 vendor/symfony/process/Tests/ProcessTest.php create mode 100644 vendor/symfony/process/Tests/ProcessUtilsTest.php create mode 100644 vendor/symfony/process/Tests/SignalListener.php create mode 100644 vendor/symfony/process/composer.json create mode 100644 vendor/symfony/process/phpunit.xml.dist create mode 100644 vendor/symfony/routing/.gitignore create mode 100644 vendor/symfony/routing/Annotation/Route.php create mode 100644 vendor/symfony/routing/CHANGELOG.md create mode 100644 vendor/symfony/routing/CompiledRoute.php create mode 100644 vendor/symfony/routing/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/routing/Exception/InvalidParameterException.php create mode 100644 vendor/symfony/routing/Exception/MethodNotAllowedException.php create mode 100644 vendor/symfony/routing/Exception/MissingMandatoryParametersException.php create mode 100644 vendor/symfony/routing/Exception/ResourceNotFoundException.php create mode 100644 vendor/symfony/routing/Exception/RouteNotFoundException.php create mode 100644 vendor/symfony/routing/Generator/ConfigurableRequirementsInterface.php create mode 100644 vendor/symfony/routing/Generator/Dumper/GeneratorDumper.php create mode 100644 vendor/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php create mode 100644 vendor/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php create mode 100644 vendor/symfony/routing/Generator/UrlGenerator.php create mode 100644 vendor/symfony/routing/Generator/UrlGeneratorInterface.php create mode 100644 vendor/symfony/routing/LICENSE create mode 100644 vendor/symfony/routing/Loader/AnnotationClassLoader.php create mode 100644 vendor/symfony/routing/Loader/AnnotationDirectoryLoader.php create mode 100644 vendor/symfony/routing/Loader/AnnotationFileLoader.php create mode 100644 vendor/symfony/routing/Loader/ClosureLoader.php create mode 100644 vendor/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php create mode 100644 vendor/symfony/routing/Loader/DirectoryLoader.php create mode 100644 vendor/symfony/routing/Loader/ObjectRouteLoader.php create mode 100644 vendor/symfony/routing/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/routing/Loader/XmlFileLoader.php create mode 100644 vendor/symfony/routing/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd create mode 100644 vendor/symfony/routing/Matcher/Dumper/DumperCollection.php create mode 100644 vendor/symfony/routing/Matcher/Dumper/DumperPrefixCollection.php create mode 100644 vendor/symfony/routing/Matcher/Dumper/DumperRoute.php create mode 100644 vendor/symfony/routing/Matcher/Dumper/MatcherDumper.php create mode 100644 vendor/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php create mode 100644 vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php create mode 100644 vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php create mode 100644 vendor/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php create mode 100644 vendor/symfony/routing/Matcher/RequestMatcherInterface.php create mode 100644 vendor/symfony/routing/Matcher/TraceableUrlMatcher.php create mode 100644 vendor/symfony/routing/Matcher/UrlMatcher.php create mode 100644 vendor/symfony/routing/Matcher/UrlMatcherInterface.php create mode 100644 vendor/symfony/routing/README.md create mode 100644 vendor/symfony/routing/RequestContext.php create mode 100644 vendor/symfony/routing/RequestContextAwareInterface.php create mode 100644 vendor/symfony/routing/Route.php create mode 100644 vendor/symfony/routing/RouteCollection.php create mode 100644 vendor/symfony/routing/RouteCollectionBuilder.php create mode 100644 vendor/symfony/routing/RouteCompiler.php create mode 100644 vendor/symfony/routing/RouteCompilerInterface.php create mode 100644 vendor/symfony/routing/Router.php create mode 100644 vendor/symfony/routing/RouterInterface.php create mode 100644 vendor/symfony/routing/Tests/Annotation/RouteTest.php create mode 100644 vendor/symfony/routing/Tests/CompiledRouteTest.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BarClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/RedirectableUrlMatcher.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/annotated.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/bad_format.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes1.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes2.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.apache create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.apache create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/empty.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/foo.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/foo1.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/incomplete.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/missing_id.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/missing_path.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/namespaceprefix.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonesense_resource_plus_path.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonesense_type_without_resource.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalid.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalid.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalid2.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalidkeys.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/null_values.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/special_route_name.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/validpattern.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/validpattern.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/validpattern.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/validresource.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/validresource.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/validresource.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/with_define_path_variable.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/withdoctype.xml create mode 100644 vendor/symfony/routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php create mode 100644 vendor/symfony/routing/Tests/Generator/UrlGeneratorTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/AbstractAnnotationLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/AnnotationClassLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/ClosureLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/DirectoryLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/Dumper/DumperCollectionTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/Dumper/DumperPrefixCollectionTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/TraceableUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/UrlMatcherTest.php create mode 100644 vendor/symfony/routing/Tests/RequestContextTest.php create mode 100644 vendor/symfony/routing/Tests/RouteCollectionBuilderTest.php create mode 100644 vendor/symfony/routing/Tests/RouteCollectionTest.php create mode 100644 vendor/symfony/routing/Tests/RouteCompilerTest.php create mode 100644 vendor/symfony/routing/Tests/RouteTest.php create mode 100644 vendor/symfony/routing/Tests/RouterTest.php create mode 100644 vendor/symfony/routing/composer.json create mode 100644 vendor/symfony/routing/phpunit.xml.dist create mode 100644 vendor/symfony/translation/.gitignore create mode 100644 vendor/symfony/translation/CHANGELOG.md create mode 100644 vendor/symfony/translation/Catalogue/AbstractOperation.php create mode 100644 vendor/symfony/translation/Catalogue/MergeOperation.php create mode 100644 vendor/symfony/translation/Catalogue/OperationInterface.php create mode 100644 vendor/symfony/translation/Catalogue/TargetOperation.php create mode 100644 vendor/symfony/translation/DataCollector/TranslationDataCollector.php create mode 100644 vendor/symfony/translation/DataCollectorTranslator.php create mode 100644 vendor/symfony/translation/Dumper/CsvFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/DumperInterface.php create mode 100644 vendor/symfony/translation/Dumper/FileDumper.php create mode 100644 vendor/symfony/translation/Dumper/IcuResFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/IniFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/JsonFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/MoFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/PhpFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/PoFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/QtFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/XliffFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/YamlFileDumper.php create mode 100644 vendor/symfony/translation/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/translation/Exception/InvalidResourceException.php create mode 100644 vendor/symfony/translation/Exception/NotFoundResourceException.php create mode 100644 vendor/symfony/translation/Extractor/AbstractFileExtractor.php create mode 100644 vendor/symfony/translation/Extractor/ChainExtractor.php create mode 100644 vendor/symfony/translation/Extractor/ExtractorInterface.php create mode 100644 vendor/symfony/translation/IdentityTranslator.php create mode 100644 vendor/symfony/translation/Interval.php create mode 100644 vendor/symfony/translation/LICENSE create mode 100644 vendor/symfony/translation/Loader/ArrayLoader.php create mode 100644 vendor/symfony/translation/Loader/CsvFileLoader.php create mode 100644 vendor/symfony/translation/Loader/FileLoader.php create mode 100644 vendor/symfony/translation/Loader/IcuDatFileLoader.php create mode 100644 vendor/symfony/translation/Loader/IcuResFileLoader.php create mode 100644 vendor/symfony/translation/Loader/IniFileLoader.php create mode 100644 vendor/symfony/translation/Loader/JsonFileLoader.php create mode 100644 vendor/symfony/translation/Loader/LoaderInterface.php create mode 100644 vendor/symfony/translation/Loader/MoFileLoader.php create mode 100644 vendor/symfony/translation/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/translation/Loader/PoFileLoader.php create mode 100644 vendor/symfony/translation/Loader/QtFileLoader.php create mode 100644 vendor/symfony/translation/Loader/XliffFileLoader.php create mode 100644 vendor/symfony/translation/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd create mode 100644 vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd create mode 100644 vendor/symfony/translation/Loader/schema/dic/xliff-core/xml.xsd create mode 100644 vendor/symfony/translation/LoggingTranslator.php create mode 100644 vendor/symfony/translation/MessageCatalogue.php create mode 100644 vendor/symfony/translation/MessageCatalogueInterface.php create mode 100644 vendor/symfony/translation/MessageSelector.php create mode 100644 vendor/symfony/translation/MetadataAwareInterface.php create mode 100644 vendor/symfony/translation/PluralizationRules.php create mode 100644 vendor/symfony/translation/README.md create mode 100644 vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php create mode 100644 vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php create mode 100644 vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php create mode 100644 vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php create mode 100644 vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/FileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/IniFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/JsonFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/MoFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/PhpFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/IdentityTranslatorTest.php create mode 100644 vendor/symfony/translation/Tests/IntervalTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/CsvFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/IniFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/JsonFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/LocalizedTestCase.php create mode 100644 vendor/symfony/translation/Tests/Loader/MoFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/PhpFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/PoFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/YamlFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/LoggingTranslatorTest.php create mode 100644 vendor/symfony/translation/Tests/MessageCatalogueTest.php create mode 100644 vendor/symfony/translation/Tests/MessageSelectorTest.php create mode 100644 vendor/symfony/translation/Tests/PluralizationRulesTest.php create mode 100644 vendor/symfony/translation/Tests/TranslatorCacheTest.php create mode 100644 vendor/symfony/translation/Tests/TranslatorTest.php create mode 100644 vendor/symfony/translation/Tests/Util/ArrayConverterTest.php create mode 100644 vendor/symfony/translation/Tests/Writer/TranslationWriterTest.php create mode 100644 vendor/symfony/translation/Tests/fixtures/empty-translation.mo create mode 100644 vendor/symfony/translation/Tests/fixtures/empty-translation.po create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.csv create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.ini create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.json create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.mo create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.po create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/encoding.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/escaped-id-plurals.po create mode 100644 vendor/symfony/translation/Tests/fixtures/escaped-id.po create mode 100644 vendor/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/malformed.json create mode 100644 vendor/symfony/translation/Tests/fixtures/messages.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/messages_linear.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/non-valid.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/non-valid.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/plurals.mo create mode 100644 vendor/symfony/translation/Tests/fixtures/plurals.po create mode 100644 vendor/symfony/translation/Tests/fixtures/resname.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/res/en.res create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-2.0.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-clean.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-target-attributes.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-tool-info.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.csv create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.dump.json create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.ini create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.json create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.mo create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.php create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.po create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.ts create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/valid.csv create mode 100644 vendor/symfony/translation/Tests/fixtures/with-attributes.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/withdoctype.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/withnote.xlf create mode 100644 vendor/symfony/translation/Translator.php create mode 100644 vendor/symfony/translation/TranslatorBagInterface.php create mode 100644 vendor/symfony/translation/TranslatorInterface.php create mode 100644 vendor/symfony/translation/Util/ArrayConverter.php create mode 100644 vendor/symfony/translation/Writer/TranslationWriter.php create mode 100644 vendor/symfony/translation/composer.json create mode 100644 vendor/symfony/translation/phpunit.xml.dist create mode 100644 vendor/symfony/var-dumper/.gitignore create mode 100644 vendor/symfony/var-dumper/CHANGELOG.md create mode 100644 vendor/symfony/var-dumper/Caster/AmqpCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/Caster.php create mode 100644 vendor/symfony/var-dumper/Caster/ConstStub.php create mode 100644 vendor/symfony/var-dumper/Caster/CutArrayStub.php create mode 100644 vendor/symfony/var-dumper/Caster/CutStub.php create mode 100644 vendor/symfony/var-dumper/Caster/DOMCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/DoctrineCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/EnumStub.php create mode 100644 vendor/symfony/var-dumper/Caster/ExceptionCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/FrameStub.php create mode 100644 vendor/symfony/var-dumper/Caster/MongoCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/PdoCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/PgSqlCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ReflectionCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/ResourceCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/SplCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/StubCaster.php create mode 100644 vendor/symfony/var-dumper/Caster/TraceStub.php create mode 100644 vendor/symfony/var-dumper/Caster/XmlResourceCaster.php create mode 100644 vendor/symfony/var-dumper/Cloner/AbstractCloner.php create mode 100644 vendor/symfony/var-dumper/Cloner/ClonerInterface.php create mode 100644 vendor/symfony/var-dumper/Cloner/Cursor.php create mode 100644 vendor/symfony/var-dumper/Cloner/Data.php create mode 100644 vendor/symfony/var-dumper/Cloner/DumperInterface.php create mode 100644 vendor/symfony/var-dumper/Cloner/Stub.php create mode 100644 vendor/symfony/var-dumper/Cloner/VarCloner.php create mode 100644 vendor/symfony/var-dumper/Dumper/AbstractDumper.php create mode 100644 vendor/symfony/var-dumper/Dumper/CliDumper.php create mode 100644 vendor/symfony/var-dumper/Dumper/DataDumperInterface.php create mode 100644 vendor/symfony/var-dumper/Dumper/HtmlDumper.php create mode 100644 vendor/symfony/var-dumper/Exception/ThrowingCasterException.php create mode 100644 vendor/symfony/var-dumper/LICENSE create mode 100644 vendor/symfony/var-dumper/README.md create mode 100644 vendor/symfony/var-dumper/Resources/functions/dump.php create mode 100644 vendor/symfony/var-dumper/Test/VarDumperTestTrait.php create mode 100644 vendor/symfony/var-dumper/Tests/Caster/CasterTest.php create mode 100644 vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php create mode 100644 vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php create mode 100644 vendor/symfony/var-dumper/Tests/Caster/SplCasterTest.php create mode 100644 vendor/symfony/var-dumper/Tests/CliDumperTest.php create mode 100644 vendor/symfony/var-dumper/Tests/Fixtures/GeneratorDemo.php create mode 100644 vendor/symfony/var-dumper/Tests/Fixtures/Twig.php create mode 100644 vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php create mode 100644 vendor/symfony/var-dumper/Tests/HtmlDumperTest.php create mode 100644 vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php create mode 100644 vendor/symfony/var-dumper/Tests/VarClonerTest.php create mode 100644 vendor/symfony/var-dumper/VarDumper.php create mode 100644 vendor/symfony/var-dumper/composer.json create mode 100644 vendor/symfony/var-dumper/phpunit.xml.dist create mode 100644 vendor/symfony/yaml/.gitignore create mode 100644 vendor/symfony/yaml/CHANGELOG.md create mode 100644 vendor/symfony/yaml/Dumper.php create mode 100644 vendor/symfony/yaml/Escaper.php create mode 100644 vendor/symfony/yaml/Exception/DumpException.php create mode 100644 vendor/symfony/yaml/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/yaml/Exception/ParseException.php create mode 100644 vendor/symfony/yaml/Exception/RuntimeException.php create mode 100644 vendor/symfony/yaml/Inline.php create mode 100644 vendor/symfony/yaml/LICENSE create mode 100644 vendor/symfony/yaml/Parser.php create mode 100644 vendor/symfony/yaml/README.md create mode 100644 vendor/symfony/yaml/Tests/DumperTest.php create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsAnchorAlias.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsBasicTests.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsBlockMapping.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsDocumentSeparator.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsErrorTests.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsFlowCollections.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsFoldedScalars.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsNullsAndEmpties.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsSpecificationExamples.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/YtsTypeTransfers.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/embededPhp.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/escapedCharacters.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/index.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/sfComments.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/sfCompact.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/sfMergeKey.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/sfObjects.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/sfQuotes.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/sfTests.yml create mode 100644 vendor/symfony/yaml/Tests/Fixtures/unindentedCollections.yml create mode 100644 vendor/symfony/yaml/Tests/InlineTest.php create mode 100644 vendor/symfony/yaml/Tests/ParseExceptionTest.php create mode 100644 vendor/symfony/yaml/Tests/ParserTest.php create mode 100644 vendor/symfony/yaml/Tests/YamlTest.php create mode 100644 vendor/symfony/yaml/Unescaper.php create mode 100644 vendor/symfony/yaml/Yaml.php create mode 100644 vendor/symfony/yaml/composer.json create mode 100644 vendor/symfony/yaml/phpunit.xml.dist create mode 100644 vendor/vlucas/phpdotenv/LICENSE.txt create mode 100644 vendor/vlucas/phpdotenv/composer.json create mode 100644 vendor/vlucas/phpdotenv/src/Dotenv.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/InvalidCallbackException.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/InvalidPathException.php create mode 100644 vendor/vlucas/phpdotenv/src/Exception/ValidationException.php create mode 100644 vendor/vlucas/phpdotenv/src/Loader.php create mode 100644 vendor/vlucas/phpdotenv/src/Validator.php diff --git a/.env b/.env new file mode 100644 index 0000000..a879363 --- /dev/null +++ b/.env @@ -0,0 +1,25 @@ +APP_ENV=local +APP_DEBUG=true +APP_KEY=zg6oqlVLuXBNmVS7Oi6xcsDl4XKTEItu + +DB_HOST=localhost +DB_DATABASE=blog +DB_USERNAME=root +DB_PASSWORD=root + +CACHE_DRIVER=file +SESSION_DRIVER=file +QUEUE_DRIVER=sync + +REDIS_HOST=localhost +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=smtp +MAIL_HOST=smtp.126.com +MAIL_PORT=465 +MAIL_USERNAME=mrwhoareyou@126.com +MAIL_PASSWORD=hohutntxbifwdycl +MAIL_ENCRYPTION=ssl + + \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..031862b --- /dev/null +++ b/.env.example @@ -0,0 +1,23 @@ +APP_ENV=local +APP_DEBUG=true +APP_KEY=SomeRandomString + +DB_HOST=localhost +DB_DATABASE=homestead +DB_USERNAME=homestead +DB_PASSWORD=secret + +CACHE_DRIVER=file +SESSION_DRIVER=file +QUEUE_DRIVER=sync + +REDIS_HOST=localhost +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=smtp +MAIL_HOST=mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null diff --git a/artisan b/artisan new file mode 100644 index 0000000..df630d0 --- /dev/null +++ b/artisan @@ -0,0 +1,51 @@ +#!/usr/bin/env php +make(Illuminate\Contracts\Console\Kernel::class); + +$status = $kernel->handle( + $input = new Symfony\Component\Console\Input\ArgvInput, + new Symfony\Component\Console\Output\ConsoleOutput +); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once Artisan has finished running. We will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$kernel->terminate($input, $status); + +exit($status); diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..d216ea3 --- /dev/null +++ b/composer.json @@ -0,0 +1,52 @@ +{ + "name": "laravel/laravel", + "description": "The Laravel Framework.", + "keywords": ["framework", "laravel"], + "license": "MIT", + "type": "project", + "require": { + "php": ">=5.5.9", + "laravel/framework": "5.2.*" + }, + "require-dev": { + "fzaninotto/faker": "~1.4", + "mockery/mockery": "0.9.*", + "phpunit/phpunit": "~4.0", + "symfony/css-selector": "2.8.*|3.0.*", + "symfony/dom-crawler": "2.8.*|3.0.*" + }, + "autoload": { + "classmap": [ + "database" + ], + "psr-4": { + "App\\": "app/" + } + }, + "autoload-dev": { + "classmap": [ + "tests/TestCase.php" + ] + }, + "scripts": { + "post-root-package-install": [ + "php -r \"copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "php artisan key:generate" + ], + "post-install-cmd": [ + "php artisan clear-compiled", + "php artisan optimize" + ], + "pre-update-cmd": [ + "php artisan clear-compiled" + ], + "post-update-cmd": [ + "php artisan optimize" + ] + }, + "config": { + "preferred-install": "dist" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..42d4a40 --- /dev/null +++ b/composer.lock @@ -0,0 +1,3013 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "ea1fee525914154e6d6bbf071697b5a1", + "content-hash": "8b1485987e7c5949da82435d403e52e8", + "packages": [ + { + "name": "classpreloader/classpreloader", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/ClassPreloader/ClassPreloader.git", + "reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/9b10b913c2bdf90c3d2e0d726b454fb7f77c552a", + "reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^1.0|^2.0", + "php": ">=5.5.9" + }, + "require-dev": { + "phpunit/phpunit": "^4.8|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "ClassPreloader\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com" + } + ], + "description": "Helps class loading performance by generating a single PHP file containing all of the autoloaded files for a specific use case", + "keywords": [ + "autoload", + "class", + "preload" + ], + "time": "2015-11-09 22:51:51" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "0.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/265b8593498b997dc2d31e75b89f053b5cc9621a", + "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "@stable" + }, + "type": "project", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "time": "2014-10-24 07:27:01" + }, + { + "name": "doctrine/inflector", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Inflector\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ], + "time": "2015-11-06 14:35:42" + }, + { + "name": "jakub-onderka/php-console-color", + "version": "0.1", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Color.git", + "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Color/zipball/e0b393dacf7703fc36a4efc3df1435485197e6c1", + "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "jakub-onderka/php-code-style": "1.0", + "jakub-onderka/php-parallel-lint": "0.*", + "jakub-onderka/php-var-dump-check": "0.*", + "phpunit/phpunit": "3.7.*", + "squizlabs/php_codesniffer": "1.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "JakubOnderka\\PhpConsoleColor": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "jakub.onderka@gmail.com", + "homepage": "http://www.acci.cz" + } + ], + "time": "2014-04-08 15:00:19" + }, + { + "name": "jakub-onderka/php-console-highlighter", + "version": "v0.3.2", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git", + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/7daa75df45242c8d5b75a22c00a201e7954e4fb5", + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5", + "shasum": "" + }, + "require": { + "jakub-onderka/php-console-color": "~0.1", + "php": ">=5.3.0" + }, + "require-dev": { + "jakub-onderka/php-code-style": "~1.0", + "jakub-onderka/php-parallel-lint": "~0.5", + "jakub-onderka/php-var-dump-check": "~0.1", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JakubOnderka\\PhpConsoleHighlighter": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "acci@acci.cz", + "homepage": "http://www.acci.cz/" + } + ], + "time": "2015-04-20 18:58:01" + }, + { + "name": "jeremeamia/SuperClosure", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/jeremeamia/super_closure.git", + "reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/29a88be2a4846d27c1613aed0c9071dfad7b5938", + "reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^1.2|^2.0", + "php": ">=5.4", + "symfony/polyfill-php56": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "SuperClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia", + "role": "Developer" + } + ], + "description": "Serialize Closure objects, including their context and binding", + "homepage": "https://github.com/jeremeamia/super_closure", + "keywords": [ + "closure", + "function", + "lambda", + "parser", + "serializable", + "serialize", + "tokenizer" + ], + "time": "2015-12-05 17:17:57" + }, + { + "name": "laravel/framework", + "version": "v5.2.14", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "ce63ba53d4891bdbb5e1aaeced1b5dd34edbc86f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/ce63ba53d4891bdbb5e1aaeced1b5dd34edbc86f", + "reference": "ce63ba53d4891bdbb5e1aaeced1b5dd34edbc86f", + "shasum": "" + }, + "require": { + "classpreloader/classpreloader": "~3.0", + "doctrine/inflector": "~1.0", + "ext-mbstring": "*", + "ext-openssl": "*", + "jeremeamia/superclosure": "~2.2", + "league/flysystem": "~1.0", + "monolog/monolog": "~1.11", + "mtdowling/cron-expression": "~1.0", + "nesbot/carbon": "~1.20", + "paragonie/random_compat": "~1.1", + "php": ">=5.5.9", + "psy/psysh": "0.6.*", + "swiftmailer/swiftmailer": "~5.1", + "symfony/console": "2.8.*|3.0.*", + "symfony/debug": "2.8.*|3.0.*", + "symfony/finder": "2.8.*|3.0.*", + "symfony/http-foundation": "2.8.*|3.0.*", + "symfony/http-kernel": "2.8.*|3.0.*", + "symfony/polyfill-php56": "~1.0", + "symfony/process": "2.8.*|3.0.*", + "symfony/routing": "2.8.*|3.0.*", + "symfony/translation": "2.8.*|3.0.*", + "symfony/var-dumper": "2.8.*|3.0.*", + "vlucas/phpdotenv": "~2.2" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/exception": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/mail": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version" + }, + "require-dev": { + "aws/aws-sdk-php": "~3.0", + "mockery/mockery": "~0.9.2", + "pda/pheanstalk": "~3.0", + "phpunit/phpunit": "~4.1", + "predis/predis": "~1.0", + "symfony/css-selector": "2.8.*|3.0.*", + "symfony/dom-crawler": "2.8.*|3.0.*" + }, + "suggest": { + "aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (~3.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.4).", + "fzaninotto/faker": "Required to use the eloquent factory builder (~1.4).", + "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (~5.3|~6.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).", + "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (~3.0).", + "predis/predis": "Required to use the redis cache and queue drivers (~1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0).", + "symfony/css-selector": "Required to use some of the crawler integration testing tools (2.8.*|3.0.*).", + "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (2.8.*|3.0.*)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/Illuminate/Queue/IlluminateQueueClosure.php" + ], + "files": [ + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylorotwell@gmail.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "http://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "time": "2016-02-03 14:19:04" + }, + { + "name": "league/flysystem", + "version": "1.0.16", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "183e1a610664baf6dcd6fceda415baf43cbdc031" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/183e1a610664baf6dcd6fceda415baf43cbdc031", + "reference": "183e1a610664baf6dcd6fceda415baf43cbdc031", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "ext-fileinfo": "*", + "mockery/mockery": "~0.9", + "phpspec/phpspec": "^2.2", + "phpspec/prophecy-phpunit": "~1.0", + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-copy": "Allows you to use Copy.com storage", + "league/flysystem-dropbox": "Allows you to use Dropbox storage", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "time": "2015-12-19 20:16:43" + }, + { + "name": "monolog/monolog", + "version": "1.17.2", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bee7f0dc9c3e0b69a6039697533dca1e845c8c24", + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "raven/raven": "^0.13", + "ruflin/elastica": ">=0.90 <3.0", + "swiftmailer/swiftmailer": "~5.3", + "videlalvaro/php-amqplib": "~2.4" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "raven/raven": "Allow sending log messages to a Sentry server", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.16.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2015-10-14 12:51:02" + }, + { + "name": "mtdowling/cron-expression", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/mtdowling/cron-expression.git", + "reference": "c9ee7886f5a12902b225a1a12f36bb45f9ab89e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mtdowling/cron-expression/zipball/c9ee7886f5a12902b225a1a12f36bb45f9ab89e5", + "reference": "c9ee7886f5a12902b225a1a12f36bb45f9ab89e5", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0|~5.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Cron": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "time": "2016-01-26 21:23:30" + }, + { + "name": "nesbot/carbon", + "version": "1.21.0", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7b08ec6f75791e130012f206e3f7b0e76e18e3d7", + "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "symfony/translation": "~2.6|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0|~5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "http://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "time": "2015-11-04 20:07:17" + }, + { + "name": "nikic/php-parser", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "c542e5d86a9775abd1021618eb2430278bfc1e01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c542e5d86a9775abd1021618eb2430278bfc1e01", + "reference": "c542e5d86a9775abd1021618eb2430278bfc1e01", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2015-12-04 15:28:43" + }, + { + "name": "paragonie/random_compat", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "b0e69d10852716b2ccbdff69c75c477637220790" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/b0e69d10852716b2ccbdff69c75c477637220790", + "reference": "b0e69d10852716b2ccbdff69c75c477637220790", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2016-02-06 03:52:05" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, + { + "name": "psy/psysh", + "version": "v0.6.1", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "0f04df0b23663799a8941fae13cd8e6299bde3ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/0f04df0b23663799a8941fae13cd8e6299bde3ed", + "reference": "0f04df0b23663799a8941fae13cd8e6299bde3ed", + "shasum": "" + }, + "require": { + "dnoegel/php-xdg-base-dir": "0.1", + "jakub-onderka/php-console-highlighter": "0.3.*", + "nikic/php-parser": "^1.2.1|~2.0", + "php": ">=5.3.9", + "symfony/console": "~2.3.10|^2.4.2|~3.0", + "symfony/var-dumper": "~2.7|~3.0" + }, + "require-dev": { + "fabpot/php-cs-fixer": "~1.5", + "phpunit/phpunit": "~3.7|~4.0|~5.0", + "squizlabs/php_codesniffer": "~2.0", + "symfony/finder": "~2.1|~3.0" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "0.7.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psy/functions.php" + ], + "psr-4": { + "Psy\\": "src/Psy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "time": "2015-11-12 16:18:56" + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.4.1", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/0697e6aa65c83edf97bb0f23d8763f94e3f11421", + "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1,<0.9.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "email", + "mail", + "mailer" + ], + "time": "2015-06-06 14:19:39" + }, + { + "name": "symfony/console", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0", + "reference": "5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2016-02-02 13:44:19" + }, + { + "name": "symfony/debug", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "29606049ced1ec715475f88d1bbe587252a3476e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/29606049ced1ec715475f88d1bbe587252a3476e", + "reference": "29606049ced1ec715475f88d1bbe587252a3476e", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/class-loader": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2016-01-27 05:14:46" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa", + "reference": "4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2016-01-27 05:14:46" + }, + { + "name": "symfony/finder", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "623bda0abd9aa29e529c8e9c08b3b84171914723" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/623bda0abd9aa29e529c8e9c08b3b84171914723", + "reference": "623bda0abd9aa29e529c8e9c08b3b84171914723", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2016-01-27 05:14:46" + }, + { + "name": "symfony/http-foundation", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "9344a87ceedfc50354a39653e54257ee9aa6a77d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9344a87ceedfc50354a39653e54257ee9aa6a77d", + "reference": "9344a87ceedfc50354a39653e54257ee9aa6a77d", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/expression-language": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "https://symfony.com", + "time": "2016-02-02 13:44:19" + }, + { + "name": "symfony/http-kernel", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "cec02604450481ac26710ca4249cc61b57b23942" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/cec02604450481ac26710ca4249cc61b57b23942", + "reference": "cec02604450481ac26710ca4249cc61b57b23942", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0", + "symfony/debug": "~2.8|~3.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0" + }, + "conflict": { + "symfony/config": "<2.8" + }, + "require-dev": { + "symfony/browser-kit": "~2.8|~3.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/css-selector": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/dom-crawler": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "symfony/routing": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0", + "symfony/templating": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", + "symfony/var-dumper": "~2.8|~3.0" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/class-loader": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "", + "symfony/var-dumper": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpKernel Component", + "homepage": "https://symfony.com", + "time": "2016-02-03 12:38:44" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "1289d16209491b584839022f29257ad859b8532d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/1289d16209491b584839022f29257ad859b8532d", + "reference": "1289d16209491b584839022f29257ad859b8532d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/polyfill-php56", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "4d891fff050101a53a4caabb03277284942d1ad9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/4d891fff050101a53a4caabb03277284942d1ad9", + "reference": "4d891fff050101a53a4caabb03277284942d1ad9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/polyfill-util", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "8de62801aa12bc4dfcf85eef5d21981ae7bb3cc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/8de62801aa12bc4dfcf85eef5d21981ae7bb3cc4", + "reference": "8de62801aa12bc4dfcf85eef5d21981ae7bb3cc4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/process", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "dfecef47506179db2501430e732adbf3793099c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/dfecef47506179db2501430e732adbf3793099c8", + "reference": "dfecef47506179db2501430e732adbf3793099c8", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2016-02-02 13:44:19" + }, + { + "name": "symfony/routing", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "4686baa55a835e1c1ede9b86ba02415c8c8d6166" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/4686baa55a835e1c1ede9b86ba02415c8c8d6166", + "reference": "4686baa55a835e1c1ede9b86ba02415c8c8d6166", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "conflict": { + "symfony/config": "<2.8" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/common": "~2.2", + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation loader", + "symfony/config": "For using the all-in-one router or any loader", + "symfony/dependency-injection": "For loading routes from a service", + "symfony/expression-language": "For using expression matching", + "symfony/yaml": "For using the YAML loader" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Routing Component", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "time": "2016-01-27 05:14:46" + }, + { + "name": "symfony/translation", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91", + "reference": "2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/config": "<2.8" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0", + "symfony/intl": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "https://symfony.com", + "time": "2016-02-02 13:44:19" + }, + { + "name": "symfony/var-dumper", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "24bb94807eff00db49374c37ebf56a0304e8aef3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/24bb94807eff00db49374c37ebf56a0304e8aef3", + "reference": "24bb94807eff00db49374c37ebf56a0304e8aef3", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "twig/twig": "~1.20|~2.0" + }, + "suggest": { + "ext-symfony_debug": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "time": "2016-01-07 13:38:51" + }, + { + "name": "vlucas/phpdotenv", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "9caf304153dc2288e4970caec6f1f3b3bc205412" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/9caf304153dc2288e4970caec6f1f3b3bc205412", + "reference": "9caf304153dc2288e4970caec6f1f3b3bc205412", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "^4.8|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "authors": [ + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "http://www.vancelucas.com" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "homepage": "http://github.com/vlucas/phpdotenv", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "time": "2015-12-29 15:10:30" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "fzaninotto/faker", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/fzaninotto/Faker.git", + "reference": "d0190b156bcca848d401fb80f31f504f37141c8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d0190b156bcca848d401fb80f31f504f37141c8d", + "reference": "d0190b156bcca848d401fb80f31f504f37141c8d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "suggest": { + "ext-intl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "time": "2015-05-29 06:29:14" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "1.3.3", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "autoload": { + "classmap": [ + "hamcrest" + ], + "files": [ + "hamcrest/Hamcrest.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "time": "2015-05-11 14:41:42" + }, + { + "name": "mockery/mockery", + "version": "0.9.4", + "source": { + "type": "git", + "url": "https://github.com/padraic/mockery.git", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/padraic/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "~1.1", + "lib-pcre": ">=7.0", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", + "homepage": "http://github.com/padraic/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "time": "2015-04-02 19:54:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2015-02-03 12:10:50" + }, + { + "name": "phpspec/prophecy", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7", + "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "phpdocumentor/reflection-docblock": "~2.0", + "sebastian/comparator": "~1.1" + }, + "require-dev": { + "phpspec/phpspec": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2015-08-13 10:07:40" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "^1.3.2", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2015-10-06 15:47:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2015-06-21 13:08:43" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21 13:50:34" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", + "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2015-06-21 08:01:12" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2015-09-15 10:49:45" + }, + { + "name": "phpunit/phpunit", + "version": "4.8.22", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "dfb11aa5236376b4fc63853cf746af39fe780e72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/dfb11aa5236376b4fc63853cf746af39fe780e72", + "reference": "dfb11aa5236376b4fc63853cf746af39fe780e72", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": ">=1.0.6", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.3", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.1|~3.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.8.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2016-02-02 09:01:21" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2015-10-02 06:51:40" + }, + { + "name": "sebastian/comparator", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2015-07-26 15:48:44" + }, + { + "name": "sebastian/diff", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2015-12-08 07:14:41" + }, + { + "name": "sebastian/environment", + "version": "1.3.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "6e7133793a8e5a5714a551a8324337374be209df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df", + "reference": "6e7133793a8e5a5714a551a8324337374be209df", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2015-12-02 08:37:27" + }, + { + "name": "sebastian/exporter", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "7ae5513327cb536431847bcc0c10edba2701064e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", + "reference": "7ae5513327cb536431847bcc0c10edba2701064e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2015-06-21 07:55:53" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12 03:26:01" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2015-11-11 19:50:13" + }, + { + "name": "sebastian/version", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2015-06-21 13:59:46" + }, + { + "name": "symfony/css-selector", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "6605602690578496091ac20ec7a5cbd160d4dff4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/6605602690578496091ac20ec7a5cbd160d4dff4", + "reference": "6605602690578496091ac20ec7a5cbd160d4dff4", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2016-01-27 05:14:46" + }, + { + "name": "symfony/dom-crawler", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "b693a9650aa004576b593ff2e91ae749dc90123d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b693a9650aa004576b593ff2e91ae749dc90123d", + "reference": "b693a9650aa004576b593ff2e91ae749dc90123d", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "~2.8|~3.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "https://symfony.com", + "time": "2016-01-25 09:56:57" + }, + { + "name": "symfony/yaml", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "3cf0709d7fe936e97bee9e954382e449003f1d9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/3cf0709d7fe936e97bee9e954382e449003f1d9a", + "reference": "3cf0709d7fe936e97bee9e954382e449003f1d9a", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2016-02-02 13:44:19" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.5.9" + }, + "platform-dev": [] +} diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..dc6f1eb --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,16 @@ +var elixir = require('laravel-elixir'); + +/* + |-------------------------------------------------------------------------- + | Elixir Asset Management + |-------------------------------------------------------------------------- + | + | Elixir provides a clean, fluent API for defining some basic Gulp tasks + | for your Laravel application. By default, we are compiling the Sass + | file for our application, as well as publishing vendor resources. + | + */ + +elixir(function(mix) { + mix.sass('app.scss'); +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..460ee90 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "private": true, + "devDependencies": { + "gulp": "^3.8.8" + }, + "dependencies": { + "laravel-elixir": "^4.0.0", + "bootstrap-sass": "^3.0.0" + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..cc0841c --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,27 @@ + + + + + ./tests/ + + + + + app/ + + + + + + + + + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..f67a6cf --- /dev/null +++ b/readme.md @@ -0,0 +1,27 @@ +## Laravel PHP Framework + +[![Build Status](https://travis-ci.org/laravel/framework.svg)](https://travis-ci.org/laravel/framework) +[![Total Downloads](https://poser.pugx.org/laravel/framework/d/total.svg)](https://packagist.org/packages/laravel/framework) +[![Latest Stable Version](https://poser.pugx.org/laravel/framework/v/stable.svg)](https://packagist.org/packages/laravel/framework) +[![Latest Unstable Version](https://poser.pugx.org/laravel/framework/v/unstable.svg)](https://packagist.org/packages/laravel/framework) +[![License](https://poser.pugx.org/laravel/framework/license.svg)](https://packagist.org/packages/laravel/framework) + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as authentication, routing, sessions, queueing, and caching. + +Laravel is accessible, yet powerful, providing powerful tools needed for large, robust applications. A superb inversion of control container, expressive migration system, and tightly integrated unit testing support give you the tools you need to build any application with which you are tasked. + +## Official Documentation + +Documentation for the framework can be found on the [Laravel website](http://laravel.com/docs). + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](http://laravel.com/docs/contributions). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell at taylor@laravel.com. All security vulnerabilities will be promptly addressed. + +### License + +The Laravel framework is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) diff --git a/server.php b/server.php new file mode 100644 index 0000000..f65c7c4 --- /dev/null +++ b/server.php @@ -0,0 +1,21 @@ + + */ + +$uri = urldecode( + parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) +); + +// This file allows us to emulate Apache's "mod_rewrite" functionality from the +// built-in PHP web server. This provides a convenient way to test a Laravel +// application without having installed a "real" web server software here. +if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { + return false; +} + +require_once __DIR__.'/public/index.php'; diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/AbstractContextAwareMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/AbstractContextAwareMatcher.php new file mode 100644 index 0000000..0bf094f --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/AbstractContextAwareMatcher.php @@ -0,0 +1,65 @@ + + */ +abstract class AbstractContextAwareMatcher extends AbstractMatcher implements ContextAware +{ + /** + * Context instance (for ContextAware interface). + * + * @var Context + */ + protected $context; + + /** + * ContextAware interface. + * + * @param Context $context + */ + public function setContext(Context $context) + { + $this->context = $context; + } + + /** + * Get a Context variable by name. + * + * @param $var Variable name + * + * @return mixed + */ + protected function getVariable($var) + { + return $this->context->get($var); + } + + /** + * Get all variables in the current Context. + * + * @return array + */ + protected function getVariables() + { + return $this->context->getAll(); + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/AbstractMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/AbstractMatcher.php new file mode 100644 index 0000000..ca2a129 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/AbstractMatcher.php @@ -0,0 +1,186 @@ + + */ +abstract class AbstractMatcher +{ + /** Syntax types */ + const CONSTANT_SYNTAX = '^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$'; + const VAR_SYNTAX = '^\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$'; + const MISC_OPERATORS = '+-*/^|&'; + /** Token values */ + const T_OPEN_TAG = 'T_OPEN_TAG'; + const T_VARIABLE = 'T_VARIABLE'; + const T_OBJECT_OPERATOR = 'T_OBJECT_OPERATOR'; + const T_DOUBLE_COLON = 'T_DOUBLE_COLON'; + const T_NEW = 'T_NEW'; + const T_CLONE = 'T_CLONE'; + const T_NS_SEPARATOR = 'T_NS_SEPARATOR'; + const T_STRING = 'T_STRING'; + const T_WHITESPACE = 'T_WHITESPACE'; + const T_AND_EQUAL = 'T_AND_EQUAL'; + const T_BOOLEAN_AND = 'T_BOOLEAN_AND'; + const T_BOOLEAN_OR = 'T_BOOLEAN_OR'; + + const T_ENCAPSED_AND_WHITESPACE = 'T_ENCAPSED_AND_WHITESPACE'; + const T_REQUIRE = 'T_REQUIRE'; + const T_REQUIRE_ONCE = 'T_REQUIRE_ONCE'; + const T_INCLUDE = 'T_INCLUDE'; + const T_INCLUDE_ONCE = 'T_INCLUDE_ONCE'; + + /** + * Check whether this matcher can provide completions for $tokens. + * + * @param array $tokens Tokenized readline input. + * + * @return bool + */ + public function hasMatched(array $tokens) + { + return false; + } + + /** + * Get current readline input word. + * + * @param array $tokens Tokenized readline input (see token_get_all) + * + * @return string + */ + protected function getInput(array $tokens) + { + $var = ''; + $firstToken = array_pop($tokens); + if (self::tokenIs($firstToken, self::T_STRING)) { + $var = $firstToken[1]; + } + + return $var; + } + + /** + * Get current namespace and class (if any) from readline input. + * + * @param array $tokens Tokenized readline input (see token_get_all) + * + * @return string + */ + protected function getNamespaceAndClass($tokens) + { + $class = ''; + while (self::hasToken( + array(self::T_NS_SEPARATOR, self::T_STRING), + $token = array_pop($tokens) + )) { + $class = $token[1] . $class; + } + + return $class; + } + + /** + * Provide tab completion matches for readline input. + * + * @param array $tokens information substracted with get_token_all + * @param array $info readline_info object + * + * @return array The matches resulting from the query + */ + abstract public function getMatches(array $tokens, array $info = array()); + + /** + * Check whether $word starts with $prefix. + * + * @param string $prefix + * @param string $word + * + * @return bool + */ + public static function startsWith($prefix, $word) + { + return preg_match(sprintf('#^%s#', $prefix), $word); + } + + /** + * Check whether $token matches a given syntax pattern. + * + * @param mixed $token A PHP token (see token_get_all) + * @param string $syntax A syntax pattern (default: variable pattern) + * + * @return bool + */ + public static function hasSyntax($token, $syntax = self::VAR_SYNTAX) + { + if (!is_array($token)) { + return false; + } + + $regexp = sprintf('#%s#', $syntax); + + return (bool) preg_match($regexp, $token[1]); + } + + /** + * Check whether $token type is $which. + * + * @param string $which A PHP token type + * @param mixed $token A PHP token (see token_get_all) + * + * @return bool + */ + public static function tokenIs($token, $which) + { + if (!is_array($token)) { + return false; + } + + return token_name($token[0]) === $which; + } + + /** + * Check whether $token is an operator. + * + * @param mixed $token A PHP token (see token_get_all) + * + * @return bool + */ + public static function isOperator($token) + { + if (!is_string($token)) { + return false; + } + + return strpos(self::MISC_OPERATORS, $token) !== false; + } + + /** + * Check whether $token type is present in $coll. + * + * @param array $coll A list of token types + * @param mixed $token A PHP token (see token_get_all) + * + * @return bool + */ + public static function hasToken(array $coll, $token) + { + if (!is_array($token)) { + return false; + } + + return in_array(token_name($token[0]), $coll); + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassAttributesMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassAttributesMatcher.php new file mode 100644 index 0000000..4d5e2f4 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassAttributesMatcher.php @@ -0,0 +1,79 @@ + + */ +class ClassAttributesMatcher extends AbstractMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $input = $this->getInput($tokens); + + $firstToken = array_pop($tokens); + if (self::tokenIs($firstToken, self::T_STRING)) { + // second token is the nekudotayim operator + array_pop($tokens); + } + + $class = $this->getNamespaceAndClass($tokens); + + $reflection = new \ReflectionClass($class); + $vars = array_merge( + array_map( + function ($var) { + return '$' . $var; + }, + array_keys($reflection->getStaticProperties()) + ), + array_keys($reflection->getConstants()) + ); + + return array_map( + function ($name) use ($class) { + return $class . '::' . $name; + }, + array_filter( + $vars, + function ($var) use ($input) { + return AbstractMatcher::startsWith($input, $var); + } + ) + ); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + switch (true) { + case self::tokenIs($prevToken, self::T_DOUBLE_COLON) && self::tokenIs($token, self::T_STRING): + case self::tokenIs($token, self::T_DOUBLE_COLON): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassMethodsMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassMethodsMatcher.php new file mode 100644 index 0000000..1791d37 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassMethodsMatcher.php @@ -0,0 +1,71 @@ + + */ +class ClassMethodsMatcher extends AbstractMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $input = $this->getInput($tokens); + + $firstToken = array_pop($tokens); + if (self::tokenIs($firstToken, self::T_STRING)) { + // second token is the nekudotayim operator + array_pop($tokens); + } + + $class = $this->getNamespaceAndClass($tokens); + + $reflection = new \ReflectionClass($class); + $methods = $reflection->getMethods(\ReflectionMethod::IS_STATIC); + $methods = array_map(function (\ReflectionMethod $method) { + return $method->getName(); + }, $methods); + + return array_map( + function ($name) use ($class) { + return $class . '::' . $name; + }, + array_filter($methods, function ($method) use ($input) { + return AbstractMatcher::startsWith($input, $method); + }) + ); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + switch (true) { + case self::tokenIs($prevToken, self::T_DOUBLE_COLON) && self::tokenIs($token, self::T_STRING): + case self::tokenIs($token, self::T_DOUBLE_COLON): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassNamesMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassNamesMatcher.php new file mode 100644 index 0000000..f79d257 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ClassNamesMatcher.php @@ -0,0 +1,77 @@ + + */ +class ClassNamesMatcher extends AbstractMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $class = $this->getNamespaceAndClass($tokens); + if (strlen($class) > 0 && $class[0] === '\\') { + $class = substr($class, 1, strlen($class)); + } + $quotedClass = preg_quote($class); + + return array_map( + function ($className) use ($class) { + // get the number of namespace separators + $nsPos = substr_count($class, '\\'); + $pieces = explode('\\', $className); + //$methods = Mirror::get($class); + return implode('\\', array_slice($pieces, $nsPos, count($pieces))); + }, + array_filter( + get_declared_classes(), + function ($className) use ($quotedClass) { + return AbstractMatcher::startsWith($quotedClass, $className); + } + ) + ); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + $blacklistedTokens = array( + self::T_INCLUDE, self::T_INCLUDE_ONCE, self::T_REQUIRE, self::T_REQUIRE_ONCE, + ); + + switch (true) { + case self::hasToken(array($blacklistedTokens), $token): + case self::hasToken(array($blacklistedTokens), $prevToken): + case is_string($token) && $token === '$': + return false; + case self::hasToken(array(self::T_NEW, self::T_OPEN_TAG, self::T_NS_SEPARATOR), $prevToken): + case self::hasToken(array(self::T_NEW, self::T_OPEN_TAG, self::T_NS_SEPARATOR), $token): + case self::hasToken(array(self::T_OPEN_TAG, self::T_VARIABLE), $token): + case self::isOperator($token): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/CommandsMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/CommandsMatcher.php new file mode 100644 index 0000000..a9a0fea --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/CommandsMatcher.php @@ -0,0 +1,114 @@ + + */ +class CommandsMatcher extends AbstractMatcher +{ + /** @var string[] */ + protected $commands = array(); + + /** + * CommandsMatcher constructor. + * + * @param Command[] $commands + */ + public function __construct(array $commands) + { + $this->setCommands($commands); + } + + /** + * Set Commands for completion. + * + * @param Command[] $commands + */ + public function setCommands(array $commands) + { + $names = array(); + foreach ($commands as $command) { + $names = array_merge(array($command->getName()), $names); + $names = array_merge($command->getAliases(), $names); + } + $this->commands = $names; + } + + /** + * Check whether a command $name is defined. + * + * @param string $name + * + * @return bool + */ + protected function isCommand($name) + { + return in_array($name, $this->commands); + } + + /** + * Check whether input matches a defined command. + * + * @param string $name + * + * @return bool + */ + protected function matchCommand($name) + { + foreach ($this->commands as $cmd) { + if ($this->startsWith($name, $cmd)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $input = $this->getInput($tokens); + + return array_filter($this->commands, function ($command) use ($input) { + return AbstractMatcher::startsWith($input, $command); + }); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + /* $openTag */ array_shift($tokens); + $command = array_shift($tokens); + + switch (true) { + case self::tokenIs($command, self::T_STRING) && + !$this->isCommand($command[1]) && + $this->matchCommand($command[1]) && + empty($tokens): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ConstantsMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ConstantsMatcher.php new file mode 100644 index 0000000..dfce451 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ConstantsMatcher.php @@ -0,0 +1,54 @@ + + */ +class ConstantsMatcher extends AbstractMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $const = $this->getInput($tokens); + + return array_filter(array_keys(get_defined_constants()), function ($constant) use ($const) { + return AbstractMatcher::startsWith($const, $constant); + }); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + switch (true) { + case self::tokenIs($prevToken, self::T_NEW): + case self::tokenIs($prevToken, self::T_NS_SEPARATOR): + return false; + case self::hasToken(array(self::T_OPEN_TAG, self::T_STRING), $token): + case self::isOperator($token): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/FunctionsMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/FunctionsMatcher.php new file mode 100644 index 0000000..0cad85d --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/FunctionsMatcher.php @@ -0,0 +1,56 @@ + + */ +class FunctionsMatcher extends AbstractMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $func = $this->getInput($tokens); + + $functions = get_defined_functions(); + $allFunctions = array_merge($functions['user'], $functions['internal']); + + return array_filter($allFunctions, function ($function) use ($func) { + return AbstractMatcher::startsWith($func, $function); + }); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + switch (true) { + case self::tokenIs($prevToken, self::T_NEW): + return false; + case self::hasToken(array(self::T_OPEN_TAG, self::T_STRING), $token): + case self::isOperator($token): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/KeywordsMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/KeywordsMatcher.php new file mode 100644 index 0000000..510c807 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/KeywordsMatcher.php @@ -0,0 +1,85 @@ + + */ +class KeywordsMatcher extends AbstractMatcher +{ + protected $keywords = array( + 'array', 'clone', 'declare', 'die', 'echo', 'empty', 'eval', 'exit', 'include', + 'include_once', 'isset', 'list', 'print', 'require', 'require_once', 'unset', + ); + + protected $mandatoryStartKeywords = array( + 'die', 'echo', 'print', 'unset', + ); + + /** + * Get all (completable) PHP keywords. + * + * @return array + */ + public function getKeywords() + { + return $this->keywords; + } + + /** + * Check whether $keyword is a (completable) PHP keyword. + * + * @param string $keyword + * + * @return bool + */ + public function isKeyword($keyword) + { + return in_array($keyword, $this->keywords); + } + + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $input = $this->getInput($tokens); + + return array_filter($this->keywords, function ($keyword) use ($input) { + return AbstractMatcher::startsWith($input, $keyword); + }); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + switch (true) { + case self::hasToken(array(self::T_OPEN_TAG, self::T_VARIABLE), $token): +// case is_string($token) && $token === '$': + case self::hasToken(array(self::T_OPEN_TAG, self::T_VARIABLE), $prevToken) && + self::tokenIs($token, self::T_STRING): + case self::isOperator($token): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/MongoClientMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/MongoClientMatcher.php new file mode 100644 index 0000000..4a75d00 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/MongoClientMatcher.php @@ -0,0 +1,71 @@ + + */ +class MongoClientMatcher extends AbstractContextAwareMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $input = $this->getInput($tokens); + + $firstToken = array_pop($tokens); + if (self::tokenIs($firstToken, self::T_STRING)) { + // second token is the object operator + array_pop($tokens); + } + $objectToken = array_pop($tokens); + $objectName = str_replace('$', '', $objectToken[1]); + $object = $this->getVariable($objectName); + + if (!$object instanceof \MongoClient) { + return array(); + } + + $list = $object->listDBs(); + + return array_filter( + array_map(function ($info) { + return $info['name']; + }, $list['databases']), + function ($var) use ($input) { + return AbstractMatcher::startsWith($input, $var); + } + ); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + switch (true) { + case self::tokenIs($token, self::T_OBJECT_OPERATOR): + case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/MongoDatabaseMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/MongoDatabaseMatcher.php new file mode 100644 index 0000000..161c456 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/MongoDatabaseMatcher.php @@ -0,0 +1,67 @@ + + */ +class MongoDatabaseMatcher extends AbstractContextAwareMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $input = $this->getInput($tokens); + + $firstToken = array_pop($tokens); + if (self::tokenIs($firstToken, self::T_STRING)) { + // second token is the object operator + array_pop($tokens); + } + $objectToken = array_pop($tokens); + $objectName = str_replace('$', '', $objectToken[1]); + $object = $this->getVariable($objectName); + + if (!$object instanceof \MongoDB) { + return array(); + } + + return array_filter( + $object->getCollectionNames(), + function ($var) use ($input) { + return AbstractMatcher::startsWith($input, $var); + } + ); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + switch (true) { + case self::tokenIs($token, self::T_OBJECT_OPERATOR): + case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ObjectAttributesMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ObjectAttributesMatcher.php new file mode 100644 index 0000000..c0c7dab --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ObjectAttributesMatcher.php @@ -0,0 +1,71 @@ + + */ +class ObjectAttributesMatcher extends AbstractContextAwareMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $input = $this->getInput($tokens); + + $firstToken = array_pop($tokens); + if (self::tokenIs($firstToken, self::T_STRING)) { + // second token is the object operator + array_pop($tokens); + } + $objectToken = array_pop($tokens); + $objectName = str_replace('$', '', $objectToken[1]); + + try { + $object = $this->getVariable($objectName); + } catch (InvalidArgumentException $e) { + return array(); + } + + return array_filter( + array_keys(get_class_vars(get_class($object))), + function ($var) use ($input) { + return AbstractMatcher::startsWith($input, $var); + } + ); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + switch (true) { + case self::tokenIs($token, self::T_OBJECT_OPERATOR): + case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ObjectMethodsMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ObjectMethodsMatcher.php new file mode 100644 index 0000000..ebb9f56 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/ObjectMethodsMatcher.php @@ -0,0 +1,71 @@ + + */ +class ObjectMethodsMatcher extends AbstractContextAwareMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $input = $this->getInput($tokens); + + $firstToken = array_pop($tokens); + if (self::tokenIs($firstToken, self::T_STRING)) { + // second token is the object operator + array_pop($tokens); + } + $objectToken = array_pop($tokens); + $objectName = str_replace('$', '', $objectToken[1]); + + try { + $object = $this->getVariable($objectName); + } catch (InvalidArgumentException $e) { + return array(); + } + + return array_filter( + get_class_methods($object), + function ($var) use ($input) { + return AbstractMatcher::startsWith($input, $var); + } + ); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + $prevToken = array_pop($tokens); + + switch (true) { + case self::tokenIs($token, self::T_OBJECT_OPERATOR): + case self::tokenIs($prevToken, self::T_OBJECT_OPERATOR): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/VariablesMatcher.php b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/VariablesMatcher.php new file mode 100644 index 0000000..4835ca4 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/TabCompletion/Matcher/VariablesMatcher.php @@ -0,0 +1,51 @@ + + */ +class VariablesMatcher extends AbstractContextAwareMatcher +{ + /** + * {@inheritdoc} + */ + public function getMatches(array $tokens, array $info = array()) + { + $var = str_replace('$', '', $this->getInput($tokens)); + + return array_filter(array_keys($this->getVariables()), function ($variable) use ($var) { + return AbstractMatcher::startsWith($var, $variable); + }); + } + + /** + * {@inheritdoc} + */ + public function hasMatched(array $tokens) + { + $token = array_pop($tokens); + + switch (true) { + case self::hasToken(array(self::T_OPEN_TAG, self::T_VARIABLE), $token): + case is_string($token) && $token === '$': + case self::isOperator($token): + return true; + } + + return false; + } +} diff --git a/vendor/psy/psysh/src/Psy/Util/Docblock.php b/vendor/psy/psysh/src/Psy/Util/Docblock.php new file mode 100644 index 0000000..4e949da --- /dev/null +++ b/vendor/psy/psysh/src/Psy/Util/Docblock.php @@ -0,0 +1,241 @@ + + * @author Justin Hileman + */ +class Docblock +{ + /** + * Tags in the docblock that have a whitespace-delimited number of parameters + * (such as `@param type var desc` and `@return type desc`) and the names of + * those parameters. + * + * @var array + */ + public static $vectors = array( + 'throws' => array('type', 'desc'), + 'param' => array('type', 'var', 'desc'), + 'return' => array('type', 'desc'), + ); + + protected $reflector; + + /** + * The description of the symbol. + * + * @var string + */ + public $desc; + + /** + * The tags defined in the docblock. + * + * The array has keys which are the tag names (excluding the @) and values + * that are arrays, each of which is an entry for the tag. + * + * In the case where the tag name is defined in {@see DocBlock::$vectors} the + * value within the tag-value array is an array in itself with keys as + * described by {@see DocBlock::$vectors}. + * + * @var array + */ + public $tags; + + /** + * The entire DocBlock comment that was parsed. + * + * @var string + */ + public $comment; + + /** + * Docblock constructor. + * + * @param \Reflector $reflector + */ + public function __construct(\Reflector $reflector) + { + $this->reflector = $reflector; + $this->setComment($reflector->getDocComment()); + } + + /** + * Set and parse the docblock comment. + * + * @param string $comment The docblock + */ + protected function setComment($comment) + { + $this->desc = ''; + $this->tags = array(); + $this->comment = $comment; + + $this->parseComment($comment); + } + + /** + * Find the length of the docblock prefix. + * + * @param array $lines + * + * @return int Prefix length + */ + protected static function prefixLength(array $lines) + { + // find only lines with interesting things + $lines = array_filter($lines, function ($line) { + return substr($line, strspn($line, "* \t\n\r\0\x0B")); + }); + + // if we sort the lines, we only have to compare two items + sort($lines); + + $first = reset($lines); + $last = end($lines); + + // find the longest common substring + $count = min(strlen($first), strlen($last)); + for ($i = 0; $i < $count; $i++) { + if ($first[$i] !== $last[$i]) { + return $i; + } + } + + return $count; + } + + /** + * Parse the comment into the component parts and set the state of the object. + * + * @param string $comment The docblock + */ + protected function parseComment($comment) + { + // Strip the opening and closing tags of the docblock + $comment = substr($comment, 3, -2); + + // Split into arrays of lines + $comment = array_filter(preg_split('/\r?\n\r?/', $comment)); + + // Trim asterisks and whitespace from the beginning and whitespace from the end of lines + $prefixLength = self::prefixLength($comment); + $comment = array_map(function ($line) use ($prefixLength) { + return rtrim(substr($line, $prefixLength)); + }, $comment); + + // Group the lines together by @tags + $blocks = array(); + $b = -1; + foreach ($comment as $line) { + if (self::isTagged($line)) { + $b++; + $blocks[] = array(); + } elseif ($b === -1) { + $b = 0; + $blocks[] = array(); + } + $blocks[$b][] = $line; + } + + // Parse the blocks + foreach ($blocks as $block => $body) { + $body = trim(implode("\n", $body)); + + if ($block === 0 && !self::isTagged($body)) { + // This is the description block + $this->desc = $body; + } else { + // This block is tagged + $tag = substr(self::strTag($body), 1); + $body = ltrim(substr($body, strlen($tag) + 2)); + + if (isset(self::$vectors[$tag])) { + // The tagged block is a vector + $count = count(self::$vectors[$tag]); + if ($body) { + $parts = preg_split('/\s+/', $body, $count); + } else { + $parts = array(); + } + + // Default the trailing values + $parts = array_pad($parts, $count, null); + + // Store as a mapped array + $this->tags[$tag][] = array_combine(self::$vectors[$tag], $parts); + } else { + // The tagged block is only text + $this->tags[$tag][] = $body; + } + } + } + } + + /** + * Whether or not a docblock contains a given @tag. + * + * @param string $tag The name of the @tag to check for + * + * @return bool + */ + public function hasTag($tag) + { + return is_array($this->tags) && array_key_exists($tag, $this->tags); + } + + /** + * The value of a tag. + * + * @param string $tag + * + * @return array + */ + public function tag($tag) + { + return $this->hasTag($tag) ? $this->tags[$tag] : null; + } + + /** + * Whether or not a string begins with a @tag. + * + * @param string $str + * + * @return bool + */ + public static function isTagged($str) + { + return isset($str[1]) && $str[0] === '@' && ctype_alpha($str[1]); + } + + /** + * The tag at the beginning of a string. + * + * @param string $str + * + * @return string|null + */ + public static function strTag($str) + { + if (preg_match('/^@[a-z0-9_]+/', $str, $matches)) { + return $matches[0]; + } + } +} diff --git a/vendor/psy/psysh/src/Psy/Util/Json.php b/vendor/psy/psysh/src/Psy/Util/Json.php new file mode 100644 index 0000000..3c79ce7 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/Util/Json.php @@ -0,0 +1,35 @@ +=')) { + $opt |= JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; + } + + return json_encode($val, $opt); + } +} diff --git a/vendor/psy/psysh/src/Psy/Util/Mirror.php b/vendor/psy/psysh/src/Psy/Util/Mirror.php new file mode 100644 index 0000000..15c07b8 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/Util/Mirror.php @@ -0,0 +1,94 @@ +hasConstant($member)) { + return new ReflectionConstant($class, $member); + } elseif ($filter & self::METHOD && $class->hasMethod($member)) { + return $class->getMethod($member); + } elseif ($filter & self::PROPERTY && $class->hasProperty($member)) { + return $class->getProperty($member); + } elseif ($filter & self::STATIC_PROPERTY && $class->hasProperty($member) && $class->getProperty($member)->isStatic()) { + return $class->getProperty($member); + } else { + throw new RuntimeException(sprintf( + 'Unknown member %s on class %s', + $member, + is_object($value) ? get_class($value) : $value + )); + } + } + + /** + * Get a ReflectionClass (or ReflectionObject) if possible. + * + * @throws \InvalidArgumentException if $value is not a class name or instance. + * + * @param mixed $value + * + * @return \ReflectionClass + */ + private static function getClass($value) + { + if (is_object($value)) { + return new \ReflectionObject($value); + } + + if (!is_string($value)) { + throw new \InvalidArgumentException('Mirror expects an object or class'); + } elseif (!class_exists($value) && !interface_exists($value) && !(function_exists('trait_exists') && trait_exists($value))) { + throw new \InvalidArgumentException('Unknown class or function: ' . $value); + } + + return new \ReflectionClass($value); + } +} diff --git a/vendor/psy/psysh/src/Psy/Util/Str.php b/vendor/psy/psysh/src/Psy/Util/Str.php new file mode 100644 index 0000000..90abedc --- /dev/null +++ b/vendor/psy/psysh/src/Psy/Util/Str.php @@ -0,0 +1,114 @@ +filter = $filter; + + return parent::cloneVar($var, $filter); + } + + /** + * {@inheritdoc} + */ + protected function castResource(Stub $stub, $isNested) + { + return Caster::EXCLUDE_VERBOSE & $this->filter ? array() : parent::castResource($stub, $isNested); + } +} diff --git a/vendor/psy/psysh/src/Psy/VarDumper/Dumper.php b/vendor/psy/psysh/src/Psy/VarDumper/Dumper.php new file mode 100644 index 0000000..9db93e0 --- /dev/null +++ b/vendor/psy/psysh/src/Psy/VarDumper/Dumper.php @@ -0,0 +1,103 @@ +formatter = $formatter; + parent::__construct(); + $this->setColors(false); + } + + /** + * {@inheritdoc} + */ + public function enterHash(Cursor $cursor, $type, $class, $hasChild) + { + if (Cursor::HASH_INDEXED === $type || Cursor::HASH_ASSOC === $type) { + $class = 0; + } + parent::enterHash($cursor, $type, $class, $hasChild); + } + + /** + * {@inheritdoc} + */ + protected function dumpKey(Cursor $cursor) + { + if (Cursor::HASH_INDEXED !== $cursor->hashType) { + parent::dumpKey($cursor); + } + } + + protected function style($style, $value, $attr = array()) + { + if ('ref' === $style) { + $value = strtr($value, '@', '#'); + } + $style = $this->styles[$style]; + $value = "<{$style}>" . $this->formatter->escape($value) . ""; + $cchr = $this->styles['cchr']; + $value = preg_replace_callback(self::$controlCharsRx, function ($c) use ($cchr) { + switch ($c[0]) { + case "\t": + $c = '\t'; + break; + case "\n": + $c = '\n'; + break; + case "\v": + $c = '\v'; + break; + case "\f": + $c = '\f'; + break; + case "\r": + $c = '\r'; + break; + case "\033": + $c = '\e'; + break; + default: + $c = sprintf('\x%02X', ord($c[0])); + break; + } + + return "<{$cchr}>{$c}"; + }, $value); + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function dumpLine($depth, $endOfValue = false) + { + if ($endOfValue && 0 < $depth) { + $this->line .= ','; + } + $this->line = $this->formatter->format($this->line); + parent::dumpLine($depth, $endOfValue); + } +} diff --git a/vendor/psy/psysh/src/Psy/VarDumper/Presenter.php b/vendor/psy/psysh/src/Psy/VarDumper/Presenter.php new file mode 100644 index 0000000..8c61a1f --- /dev/null +++ b/vendor/psy/psysh/src/Psy/VarDumper/Presenter.php @@ -0,0 +1,123 @@ + 'number', + 'const' => 'const', + 'str' => 'string', + 'cchr' => 'default', + 'note' => 'class', + 'ref' => 'default', + 'public' => 'public', + 'protected' => 'protected', + 'private' => 'private', + 'meta' => 'comment', + 'key' => 'comment', + 'index' => 'number', + ); + + public function __construct(OutputFormatter $formatter) + { + $this->dumper = new Dumper($formatter); + $this->dumper->setStyles($this->styles); + + $this->cloner = new Cloner(); + $this->cloner->addCasters(array('*' => function ($obj, array $a, Stub $stub, $isNested, $filter = 0) { + if ($filter || $isNested) { + if ($obj instanceof \Exception) { + $a = Caster::filter($a, Caster::EXCLUDE_NOT_IMPORTANT | Caster::EXCLUDE_EMPTY, $this->exceptionsImportants); + } else { + $a = Caster::filter($a, Caster::EXCLUDE_PROTECTED | Caster::EXCLUDE_PRIVATE); + } + } + + return $a; + })); + } + + /** + * Register casters. + * + * @see http://symfony.com/doc/current/components/var_dumper/advanced.html#casters + * + * @param callable[] $casters A map of casters. + */ + public function addCasters(array $casters) + { + $this->cloner->addCasters($casters); + } + + /** + * Present a reference to the value. + * + * @param mixed $value + * + * @return string + */ + public function presentRef($value) + { + return $this->present($value, 0); + } + + /** + * Present a full representation of the value. + * + * If $depth is 0, the value will be presented as a ref instead. + * + * @param mixed $value + * @param int $depth (default: null) + * @param int $options One of Presenter constants + * + * @return string + */ + public function present($value, $depth = null, $options = 0) + { + $data = $this->cloner->cloneVar($value, !($options & self::VERBOSE) ? Caster::EXCLUDE_VERBOSE : 0); + + if (null !== $depth) { + $data = $data->withMaxDepth($depth); + } + + $output = ''; + $this->dumper->dump($data, function ($line, $depth) use (&$output) { + if ($depth >= 0) { + if ('' !== $output) { + $output .= PHP_EOL; + } + $output .= str_repeat(' ', $depth) . $line; + } + }); + + return OutputFormatter::escape($output); + } +} diff --git a/vendor/psy/psysh/src/Psy/VarDumper/PresenterAware.php b/vendor/psy/psysh/src/Psy/VarDumper/PresenterAware.php new file mode 100644 index 0000000..4b4999b --- /dev/null +++ b/vendor/psy/psysh/src/Psy/VarDumper/PresenterAware.php @@ -0,0 +1,26 @@ +assertTrue(spl_autoload_unregister(array('Psy\Autoloader', 'autoload'))); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/AbstractClassPassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/AbstractClassPassTest.php new file mode 100644 index 0000000..571fa6e --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/AbstractClassPassTest.php @@ -0,0 +1,61 @@ +pass = new AbstractClassPass(); + $this->traverser = new NodeTraverser(); + $this->traverser->addVisitor($this->pass); + } + + /** + * @dataProvider invalidStatements + * @expectedException \Psy\Exception\FatalErrorException + */ + public function testProcessStatementFails($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function invalidStatements() + { + return array( + array('class A { abstract function a(); }'), + array('abstract class B { abstract function b() {} }'), + array('abstract class B { abstract function b() { echo "yep"; } }'), + ); + } + + /** + * @dataProvider validStatements + */ + public function testProcessStatementPasses($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function validStatements() + { + return array( + array('abstract class C { function c() {} }'), + array('abstract class D { abstract function d(); }'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/AssignThisVariablePassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/AssignThisVariablePassTest.php new file mode 100644 index 0000000..6cf77fb --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/AssignThisVariablePassTest.php @@ -0,0 +1,62 @@ +pass = new AssignThisVariablePass(); + $this->traverser = new NodeTraverser(); + $this->traverser->addVisitor($this->pass); + } + + /** + * @dataProvider invalidStatements + * @expectedException \Psy\Exception\FatalErrorException + */ + public function testProcessStatementFails($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function invalidStatements() + { + return array( + array('$this = 3'), + array('strtolower($this = "this")'), + ); + } + + /** + * @dataProvider validStatements + */ + public function testProcessStatementPasses($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function validStatements() + { + return array( + array('$this'), + array('$a = $this'), + array('$a = "this"; $$a = 3'), + array('$$this = "b"'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/CallTimePassByReferencePassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/CallTimePassByReferencePassTest.php new file mode 100644 index 0000000..3932f4c --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/CallTimePassByReferencePassTest.php @@ -0,0 +1,73 @@ +pass = new CallTimePassByReferencePass(); + $this->traverser = new NodeTraverser(); + $this->traverser->addVisitor($this->pass); + } + + /** + * @dataProvider invalidStatements + * @expectedException \Psy\Exception\FatalErrorException + */ + public function testProcessStatementFails($code) + { + if (version_compare(PHP_VERSION, '5.4', '<')) { + $this->markTestSkipped(); + } + + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function invalidStatements() + { + return array( + array('f(&$arg)'), + array('$object->method($first, &$arg)'), + array('$closure($first, &$arg, $last)'), + array('A::b(&$arg)'), + ); + } + + /** + * @dataProvider validStatements + */ + public function testProcessStatementPasses($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function validStatements() + { + $data = array( + array('array(&$var)'), + array('$a = &$b'), + array('f(array(&$b))'), + ); + + if (version_compare(PHP_VERSION, '5.4', '<')) { + $data = array_merge($data, $this->invalidStatements()); + } + + return $data; + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/CalledClassPassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/CalledClassPassTest.php new file mode 100644 index 0000000..1cde523 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/CalledClassPassTest.php @@ -0,0 +1,98 @@ +pass = new CalledClassPass(); + $this->traverser = new NodeTraverser(); + $this->traverser->addVisitor($this->pass); + } + + /** + * @dataProvider invalidStatements + * @expectedException \Psy\Exception\ErrorException + */ + public function testProcessStatementFails($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function invalidStatements() + { + return array( + array('get_class()'), + array('get_class(null)'), + array('get_called_class()'), + array('get_called_class(null)'), + array('function foo() { return get_class(); }'), + array('function foo() { return get_class(null); }'), + array('function foo() { return get_called_class(); }'), + array('function foo() { return get_called_class(null); }'), + ); + } + + /** + * @dataProvider validStatements + */ + public function testProcessStatementPasses($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function validStatements() + { + return array( + array('get_class($foo)'), + array('get_class(bar())'), + array('get_called_class($foo)'), + array('get_called_class(bar())'), + array('function foo($bar) { return get_class($bar); }'), + array('function foo($bar) { return get_called_class($bar); }'), + array('class Foo { function bar() { return get_class(); } }'), + array('class Foo { function bar() { return get_class(null); } }'), + array('class Foo { function bar() { return get_called_class(); } }'), + array('class Foo { function bar() { return get_called_class(null); } }'), + array('$foo = function () {}; $foo()'), + ); + } + + /** + * @dataProvider validTraitStatements + */ + public function testProcessTraitStatementPasses($code) + { + if (version_compare(PHP_VERSION, '5.4', '<')) { + $this->markTestSkipped(); + } + + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function validTraitStatements() + { + return array( + array('trait Foo { function bar() { return get_class(); } }'), + array('trait Foo { function bar() { return get_class(null); } }'), + array('trait Foo { function bar() { return get_called_class(); } }'), + array('trait Foo { function bar() { return get_called_class(null); } }'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/CodeCleanerTestCase.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/CodeCleanerTestCase.php new file mode 100644 index 0000000..5a9bb73 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/CodeCleanerTestCase.php @@ -0,0 +1,98 @@ +pass = $pass; + if (!isset($this->traverser)) { + $this->traverser = new NodeTraverser(); + } + $this->traverser->addVisitor($this->pass); + } + + protected function parse($code, $prefix = 'getParser()->parse($code); + } catch (\PhpParser\Error $e) { + if (!$this->parseErrorIsEOF($e)) { + throw ParseErrorException::fromParseError($e); + } + + try { + // Unexpected EOF, try again with an implicit semicolon + return $this->getParser()->parse($code . ';'); + } catch (\PhpParser\Error $e) { + return false; + } + } + } + + protected function traverse(array $stmts) + { + return $this->traverser->traverse($stmts); + } + + protected function prettyPrint(array $stmts) + { + return $this->getPrinter()->prettyPrint($stmts); + } + + protected function assertProcessesAs($from, $to) + { + $stmts = $this->parse($from); + $stmts = $this->traverse($stmts); + $this->assertEquals($to, $this->prettyPrint($stmts)); + } + + private function getParser() + { + if (!isset($this->parser)) { + $parserFactory = new ParserFactory(); + $this->parser = $parserFactory->createParser(); + } + + return $this->parser; + } + + private function getPrinter() + { + if (!isset($this->printer)) { + $this->printer = new Printer(); + } + + return $this->printer; + } + + private function parseErrorIsEOF(\PhpParser\Error $e) + { + $msg = $e->getRawMessage(); + + return ($msg === 'Unexpected token EOF') || (strpos($msg, 'Syntax error, unexpected EOF') !== false); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/Fixtures/ClassWithCallStatic.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/Fixtures/ClassWithCallStatic.php new file mode 100644 index 0000000..8f8624b --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/Fixtures/ClassWithCallStatic.php @@ -0,0 +1,20 @@ +pass = new FunctionReturnInWriteContextPass(); + $this->traverser = new NodeTraverser(); + $this->traverser->addVisitor($this->pass); + } + + /** + * @dataProvider invalidStatements + * @expectedException \Psy\Exception\FatalErrorException + * @expectedExceptionMessage Can't use function return value in write context + */ + public function testProcessStatementFails($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function invalidStatements() + { + return array( + array('f(&g())'), + array('array(& $object->method())'), + array('$a->method(& $closure())'), + array('array(& A::b())'), + array('f() = 5'), + ); + } + + public function testIsset() + { + try { + $this->traverser->traverse($this->parse('isset(strtolower("A"))')); + $this->fail(); + } catch (FatalErrorException $e) { + if (version_compare(PHP_VERSION, '5.5', '>=')) { + $this->assertContains( + 'Cannot use isset() on the result of a function call (you can use "null !== func()" instead)', + $e->getMessage() + ); + } else { + $this->assertContains("Can't use function return value in write context", $e->getMessage()); + } + } + } + + /** + * @expectedException \Psy\Exception\FatalErrorException + * @expectedExceptionMessage Can't use function return value in write context + */ + public function testEmpty() + { + if (version_compare(PHP_VERSION, '5.5', '>=')) { + $this->markTestSkipped(); + } + + $this->traverser->traverse($this->parse('empty(strtolower("A"))')); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ImplicitReturnPassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ImplicitReturnPassTest.php new file mode 100644 index 0000000..ba57fa0 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ImplicitReturnPassTest.php @@ -0,0 +1,38 @@ +setPass(new ImplicitReturnPass()); + } + + /** + * @dataProvider implicitReturns + */ + public function testProcess($from, $to) + { + $this->assertProcessesAs($from, $to); + } + + public function implicitReturns() + { + return array( + array('4', 'return 4;'), + array('foo()', 'return foo();'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/InstanceOfPassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/InstanceOfPassTest.php new file mode 100644 index 0000000..db0d091 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/InstanceOfPassTest.php @@ -0,0 +1,74 @@ +setPass(new InstanceOfPass()); + } + + /** + * @dataProvider invalidStatements + * @expectedException \Psy\Exception\FatalErrorException + */ + public function testProcessInvalidStatement($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function invalidStatements() + { + return array( + array('null instanceof stdClass'), + array('true instanceof stdClass'), + array('9 instanceof stdClass'), + array('1.0 instanceof stdClass'), + array('"foo" instanceof stdClass'), + array('__DIR__ instanceof stdClass'), + array('PHP_SAPI instanceof stdClass'), + array('1+1 instanceof stdClass'), + array('true && false instanceof stdClass'), + array('"a"."b" instanceof stdClass'), + array('!5 instanceof stdClass'), + ); + } + + /** + * @dataProvider validStatements + */ + public function testProcessValidStatement($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function validStatements() + { + $data = array( + array('$a instanceof stdClass'), + array('strtolower("foo") instanceof stdClass'), + array('array(1) instanceof stdClass'), + array('(string) "foo" instanceof stdClass'), + array('(1+1) instanceof stdClass'), + array('"foo ${foo} $bar" instanceof stdClass'), + array('DateTime::ISO8601 instanceof stdClass'), + + ); + + return $data; + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/LeavePsyshAlonePassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/LeavePsyshAlonePassTest.php new file mode 100644 index 0000000..717f381 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/LeavePsyshAlonePassTest.php @@ -0,0 +1,69 @@ +setPass(new LeavePsyshAlonePass()); + } + + public function testPassesInlineHtmlThroughJustFine() + { + $inline = $this->parse('not php at all!', ''); + $this->traverse($inline); + } + + /** + * @dataProvider validStatements + */ + public function testProcessStatementPasses($code) + { + $stmts = $this->parse($code); + $this->traverse($stmts); + } + + public function validStatements() + { + return array( + array('array_merge()'), + array('__psysh__()'), + array('$this'), + array('$psysh'), + array('$__psysh'), + array('$banana'), + ); + } + + /** + * @dataProvider invalidStatements + * @expectedException \Psy\Exception\RuntimeException + */ + public function testProcessStatementFails($code) + { + $stmts = $this->parse($code); + $this->traverse($stmts); + } + + public function invalidStatements() + { + return array( + array('$__psysh__'), + array('var_dump($__psysh__)'), + array('$__psysh__ = "your mom"'), + array('$__psysh__->fakeFunctionCall()'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/LegacyEmptyPassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/LegacyEmptyPassTest.php new file mode 100644 index 0000000..da38e07 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/LegacyEmptyPassTest.php @@ -0,0 +1,77 @@ +setPass(new LegacyEmptyPass()); + } + + /** + * @dataProvider invalidStatements + * @expectedException \Psy\Exception\ParseErrorException + */ + public function testProcessInvalidStatement($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function invalidStatements() + { + if (version_compare(PHP_VERSION, '5.5', '>=')) { + return array( + array('empty()'), + ); + } + + return array( + array('empty()'), + array('empty(null)'), + array('empty(PHP_EOL)'), + array('empty("wat")'), + array('empty(1.1)'), + array('empty(Foo::$bar)'), + ); + } + + /** + * @dataProvider validStatements + */ + public function testProcessValidStatement($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function validStatements() + { + if (version_compare(PHP_VERSION, '5.5', '<')) { + return array( + array('empty($foo)'), + ); + } + + return array( + array('empty($foo)'), + array('empty(null)'), + array('empty(PHP_EOL)'), + array('empty("wat")'), + array('empty(1.1)'), + array('empty(Foo::$bar)'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/MagicConstantsPassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/MagicConstantsPassTest.php new file mode 100644 index 0000000..1cc33cb --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/MagicConstantsPassTest.php @@ -0,0 +1,39 @@ +setPass(new MagicConstantsPass()); + } + + /** + * @dataProvider magicConstants + */ + public function testProcess($from, $to) + { + $this->assertProcessesAs($from, $to); + } + + public function magicConstants() + { + return array( + array('__DIR__;', 'getcwd();'), + array('__FILE__;', "'';"), + array('___FILE___;', '___FILE___;'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/NamespacePassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/NamespacePassTest.php new file mode 100644 index 0000000..bb24c91 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/NamespacePassTest.php @@ -0,0 +1,49 @@ +cleaner = new CodeCleaner(); + $this->setPass(new NamespacePass($this->cleaner)); + } + + public function testProcess() + { + $this->process('array_merge()'); + $this->assertNull($this->cleaner->getNamespace()); + + // A non-block namespace statement should set the current namespace. + $this->process('namespace Alpha'); + $this->assertEquals(array('Alpha'), $this->cleaner->getNamespace()); + + // A new non-block namespace statement should override the current namespace. + $this->process('namespace Beta'); + $this->assertEquals(array('Beta'), $this->cleaner->getNamespace()); + + $this->process('namespace Gamma { array_merge(); }'); + $this->assertNull($this->cleaner->getNamespace()); + } + + private function process($code) + { + $stmts = $this->parse($code); + $this->traverse($stmts); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/StaticConstructorPassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/StaticConstructorPassTest.php new file mode 100644 index 0000000..ab8c567 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/StaticConstructorPassTest.php @@ -0,0 +1,91 @@ +setPass(new StaticConstructorPass()); + } + + /** + * @dataProvider invalidStatements + * @expectedException \Psy\Exception\FatalErrorException + */ + public function testProcessInvalidStatement($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + /** + * @dataProvider invalidParserStatements + * @expectedException \Psy\Exception\ParseErrorException + */ + public function testProcessInvalidStatementCatchedByParser($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function invalidStatements() + { + $statements = array( + array('class A { public static function A() {}}'), + array('class A { private static function A() {}}'), + ); + + if (version_compare(PHP_VERSION, '5.3.3', '<')) { + $statements[] = array('namespace B; class A { private static function A() {}}'); + } + + return $statements; + } + + public function invalidParserStatements() + { + $statements = array( + array('class A { public static function __construct() {}}'), + array('class A { private static function __construct() {}}'), + array('class A { private static function __construct() {} public function A() {}}'), + array('namespace B; class A { private static function __construct() {}}'), + ); + + return $statements; + } + + /** + * @dataProvider validStatements + */ + public function testProcessValidStatement($code) + { + $stmts = $this->parse($code); + $this->traverser->traverse($stmts); + } + + public function validStatements() + { + $statements = array( + array('class A { public static function A() {} public function __construct() {}}'), + array('class A { private function __construct() {} public static function A() {}}'), + ); + + if (version_compare(PHP_VERSION, '5.3.3', '>=')) { + $statements[] = array('namespace B; class A { private static function A() {}}'); + } + + return $statements; + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/UseStatementPassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/UseStatementPassTest.php new file mode 100644 index 0000000..d3a7a75 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/UseStatementPassTest.php @@ -0,0 +1,52 @@ +setPass(new UseStatementPass()); + } + + /** + * @dataProvider useStatements + */ + public function testProcess($from, $to) + { + $this->assertProcessesAs($from, $to); + } + + public function useStatements() + { + return array( + array( + "use StdClass as NotSoStd;\n\$std = new NotSoStd();", + '$std = new \\StdClass();', + ), + array( + "namespace Foo;\n\nuse StdClass as S;\n\$std = new S();", + "namespace Foo;\n\n\$std = new \\StdClass();", + ), + array( + "namespace Foo;\n\nuse \\StdClass as S;\n\$std = new S();", + "namespace Foo;\n\n\$std = new \\StdClass();", + ), + array( + "use Foo\\Bar as fb;\n\$baz = new fb\\Baz();", + '$baz = new \\Foo\\Bar\\Baz();', + ), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidClassNamePassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidClassNamePassTest.php new file mode 100644 index 0000000..e5cb114 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidClassNamePassTest.php @@ -0,0 +1,244 @@ +setPass(new ValidClassNamePass()); + } + + /** + * @dataProvider getInvalid + */ + public function testProcessInvalid($code, $php54 = false) + { + try { + $stmts = $this->parse($code); + $this->traverse($stmts); + $this->fail(); + } catch (Exception $e) { + if ($php54 && version_compare(PHP_VERSION, '5.4', '<')) { + $this->assertInstanceOf('Psy\Exception\ParseErrorException', $e); + } else { + $this->assertInstanceOf('Psy\Exception\FatalErrorException', $e); + } + } + } + + public function getInvalid() + { + // class declarations + return array( + // core class + array('class stdClass {}'), + // capitalization + array('class stdClass {}'), + + // collisions with interfaces and traits + array('interface stdClass {}'), + array('trait stdClass {}', true), + + // collisions inside the same code snippet + array(' + class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + '), + array(' + class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + trait Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + ', true), + array(' + trait Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + ', true), + array(' + trait Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + interface Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + ', true), + array(' + interface Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + trait Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + ', true), + array(' + interface Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + '), + array(' + class Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + interface Psy_Test_CodeCleaner_ValidClassNamePass_Alpha {} + '), + + // namespaced collisions + array(' + namespace Psy\\Test\\CodeCleaner { + class ValidClassNamePassTest {} + } + '), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass { + class Beta {} + } + namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass { + class Beta {} + } + '), + + // extends and implements + array('class ValidClassNamePassTest extends NotAClass {}'), + array('class ValidClassNamePassTest extends ArrayAccess {}'), + array('class ValidClassNamePassTest implements stdClass {}'), + array('class ValidClassNamePassTest implements ArrayAccess, stdClass {}'), + array('interface ValidClassNamePassTest extends stdClass {}'), + array('interface ValidClassNamePassTest extends ArrayAccess, stdClass {}'), + + // class instantiations + array('new Psy_Test_CodeCleaner_ValidClassNamePass_Gamma();'), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass { + new Psy_Test_CodeCleaner_ValidClassNamePass_Delta(); + } + '), + + // class constant fetch + array('Psy\\Test\\CodeCleaner\\ValidClassNamePass\\NotAClass::FOO'), + + // static call + array('Psy\\Test\\CodeCleaner\\ValidClassNamePass\\NotAClass::foo()'), + array('Psy\\Test\\CodeCleaner\\ValidClassNamePass\\NotAClass::$foo()'), + ); + } + + /** + * @dataProvider getValid + */ + public function testProcessValid($code) + { + $stmts = $this->parse($code); + $this->traverse($stmts); + } + + public function getValid() + { + $valid = array( + // class declarations + array('class Psy_Test_CodeCleaner_ValidClassNamePass_Epsilon {}'), + array('namespace Psy\Test\CodeCleaner\ValidClassNamePass; class Zeta {}'), + array(' + namespace { class Psy_Test_CodeCleaner_ValidClassNamePass_Eta {}; } + namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass { + class Psy_Test_CodeCleaner_ValidClassNamePass_Eta {} + } + '), + array('namespace Psy\Test\CodeCleaner\ValidClassNamePass { class stdClass {} }'), + + // class instantiations + array('new stdClass();'), + array('new stdClass();'), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass { + class Theta {} + } + namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass { + new Theta(); + } + '), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass { + class Iota {} + new Iota(); + } + '), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidClassNamePass { + class Kappa {} + } + namespace { + new \\Psy\\Test\\CodeCleaner\\ValidClassNamePass\\Kappa(); + } + '), + + // Class constant fetch (ValidConstantPassTest validates the actual constant) + array('class A {} A::FOO'), + array('$a = new DateTime; $a::ATOM'), + array('interface A { const B = 1; } A::B'), + + // static call + array('DateTime::createFromFormat()'), + array('DateTime::$someMethod()'), + array('Psy\Test\CodeCleaner\Fixtures\ClassWithStatic::doStuff()'), + array('Psy\Test\CodeCleaner\Fixtures\ClassWithCallStatic::doStuff()'), + + // Allow `self` and `static` as class names. + array(' + class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic { + public static function getInstance() { + return new self(); + } + } + '), + array(' + class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic { + public static function getInstance() { + return new SELF(); + } + } + '), + array(' + class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic { + public static function getInstance() { + return new self; + } + } + '), + array(' + class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic { + public static function getInstance() { + return new static(); + } + } + '), + array(' + class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic { + public static function getInstance() { + return new Static(); + } + } + '), + array(' + class Psy_Test_CodeCleaner_ValidClassNamePass_ClassWithStatic { + public static function getInstance() { + return new static; + } + } + '), + + // PHP 7.0 anonymous classes. + array('$obj = new class() {}'), + ); + + if (version_compare(PHP_VERSION, '5.5', '>=')) { + $valid[] = array('interface A {} A::class'); + $valid[] = array('interface A {} A::CLASS'); + $valid[] = array('class A {} A::class'); + $valid[] = array('class A {} A::CLASS'); + $valid[] = array('A::class'); + $valid[] = array('A::CLASS'); + } + + return $valid; + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidConstantPassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidConstantPassTest.php new file mode 100644 index 0000000..65a6d6b --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidConstantPassTest.php @@ -0,0 +1,66 @@ +setPass(new ValidConstantPass()); + } + + /** + * @dataProvider getInvalidReferences + * @expectedException \Psy\Exception\FatalErrorException + */ + public function testProcessInvalidConstantReferences($code) + { + $stmts = $this->parse($code); + $this->traverse($stmts); + } + + public function getInvalidReferences() + { + return array( + array('Foo\BAR'), + + // class constant fetch + array('Psy\Test\CodeCleaner\ValidConstantPassTest::FOO'), + array('DateTime::BACON'), + ); + } + + /** + * @dataProvider getValidReferences + */ + public function testProcessValidConstantReferences($code) + { + $stmts = $this->parse($code); + $this->traverse($stmts); + } + + public function getValidReferences() + { + return array( + array('PHP_EOL'), + + // class constant fetch + array('NotAClass::FOO'), + array('DateTime::ATOM'), + array('$a = new DateTime; $a::ATOM'), + array('DateTime::class'), + array('$a = new DateTime; $a::class'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidFunctionNamePassTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidFunctionNamePassTest.php new file mode 100644 index 0000000..c07e80e --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleaner/ValidFunctionNamePassTest.php @@ -0,0 +1,130 @@ +setPass(new ValidFunctionNamePass()); + } + + /** + * @dataProvider getInvalidFunctions + * @expectedException \Psy\Exception\FatalErrorException + */ + public function testProcessInvalidFunctionCallsAndDeclarations($code) + { + $stmts = $this->parse($code); + $this->traverse($stmts); + } + + public function getInvalidFunctions() + { + return array( + // function declarations + array('function array_merge() {}'), + array('function Array_Merge() {}'), + array(' + function psy_test_codecleaner_validfunctionnamepass_alpha() {} + function psy_test_codecleaner_validfunctionnamepass_alpha() {} + '), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + function beta() {} + } + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + function beta() {} + } + '), + + // function calls + array('psy_test_codecleaner_validfunctionnamepass_gamma()'), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + delta(); + } + '), + + // recursion + array('function a() { a(); } function a() {}'), + ); + } + + /** + * @dataProvider getValidFunctions + */ + public function testProcessValidFunctionCallsAndDeclarations($code) + { + $stmts = $this->parse($code); + $this->traverse($stmts); + } + + public function getValidFunctions() + { + return array( + array('function psy_test_codecleaner_validfunctionnamepass_epsilon() {}'), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + function zeta() {} + } + '), + array(' + namespace { + function psy_test_codecleaner_validfunctionnamepass_eta() {} + } + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + function psy_test_codecleaner_validfunctionnamepass_eta() {} + } + '), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + function psy_test_codecleaner_validfunctionnamepass_eta() {} + } + namespace { + function psy_test_codecleaner_validfunctionnamepass_eta() {} + } + '), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + function array_merge() {} + } + '), + + // function calls + array('array_merge();'), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + function theta() {} + } + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + theta(); + } + '), + // closures + array('$test = function(){};$test()'), + array(' + namespace Psy\\Test\\CodeCleaner\\ValidFunctionNamePass { + function theta() {} + } + namespace { + Psy\\Test\\CodeCleaner\\ValidFunctionNamePass\\theta(); + } + '), + + // recursion + array('function a() { a(); }'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/CodeCleanerTest.php b/vendor/psy/psysh/test/Psy/Test/CodeCleanerTest.php new file mode 100644 index 0000000..37f47ac --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/CodeCleanerTest.php @@ -0,0 +1,95 @@ +assertEquals($expected, $cc->clean($lines, $requireSemicolons)); + } + + public function semicolonCodeProvider() + { + return array( + array(array('true'), false, 'return true;'), + array(array('true;'), false, 'return true;'), + array(array('true;'), true, 'return true;'), + array(array('true'), true, false), + + array(array('echo "foo";', 'true'), false, "echo 'foo';\nreturn true;"), + array(array('echo "foo";', 'true'), true , false), + ); + } + + /** + * @dataProvider unclosedStatementsProvider + */ + public function testUnclosedStatements(array $lines, $isUnclosed) + { + $cc = new CodeCleaner(); + $res = $cc->clean($lines); + + if ($isUnclosed) { + $this->assertFalse($res); + } else { + $this->assertNotFalse($res); + } + } + + public function unclosedStatementsProvider() + { + return array( + array(array('echo "'), true), + array(array('echo \''), true), + array(array('if (1) {'), true), + + array(array('echo ""'), false), + array(array("echo ''"), false), + array(array('if (1) {}'), false), + + array(array("\$content = <<clean(array($code)); + } + + public function invalidStatementsProvider() + { + return array( + array('function "what'), + array("function 'what"), + array('echo }'), + array('echo {'), + array('if (1) }'), + array('echo """'), + array("echo '''"), + array('$foo "bar'), + array('$foo \'bar'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/ConfigurationTest.php b/vendor/psy/psysh/test/Psy/Test/ConfigurationTest.php new file mode 100644 index 0000000..7669725 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/ConfigurationTest.php @@ -0,0 +1,173 @@ +assertEquals(function_exists('readline'), $config->hasReadline()); + $this->assertEquals(function_exists('readline'), $config->useReadline()); + $this->assertEquals(function_exists('pcntl_signal'), $config->hasPcntl()); + $this->assertEquals(function_exists('pcntl_signal'), $config->usePcntl()); + $this->assertFalse($config->requireSemicolons()); + } + + public function testGettersAndSetters() + { + $config = new Configuration(); + + $this->assertNull($config->getDataDir()); + $config->setDataDir('wheee'); + $this->assertEquals('wheee', $config->getDataDir()); + + $this->assertNull($config->getConfigDir()); + $config->setConfigDir('wheee'); + $this->assertEquals('wheee', $config->getConfigDir()); + } + + /** + * @dataProvider directories + */ + public function testFilesAndDirectories($home, $configFile, $historyFile, $manualDbFile) + { + $oldHome = getenv('HOME'); + putenv("HOME=$home"); + + $config = new Configuration(); + $this->assertEquals(realpath($configFile), realpath($config->getConfigFile())); + $this->assertEquals(realpath($historyFile), realpath($config->getHistoryFile())); + $this->assertEquals(realpath($manualDbFile), realpath($config->getManualDbFile())); + + putenv("HOME=$oldHome"); + } + + public function directories() + { + $base = realpath(__DIR__ . '/../../fixtures'); + + return array( + array( + $base . '/default', + $base . '/default/.config/psysh/config.php', + $base . '/default/.config/psysh/psysh_history', + $base . '/default/.local/share/psysh/php_manual.sqlite', + ), + array( + $base . '/legacy', + $base . '/legacy/.psysh/rc.php', + $base . '/legacy/.psysh/history', + $base . '/legacy/.psysh/php_manual.sqlite', + ), + array( + $base . '/mixed', + $base . '/mixed/.psysh/config.php', + $base . '/mixed/.psysh/psysh_history', + null, + ), + ); + } + + public function testLoadConfig() + { + $config = new Configuration(); + $cleaner = new CodeCleaner(); + $pager = new PassthruPager(new ConsoleOutput()); + $loop = new Loop($config); + + $config->loadConfig(array( + 'useReadline' => false, + 'usePcntl' => false, + 'codeCleaner' => $cleaner, + 'pager' => $pager, + 'loop' => $loop, + 'requireSemicolons' => true, + 'errorLoggingLevel' => E_ERROR | E_WARNING, + )); + + $this->assertFalse($config->useReadline()); + $this->assertFalse($config->usePcntl()); + $this->assertSame($cleaner, $config->getCodeCleaner()); + $this->assertSame($pager, $config->getPager()); + $this->assertSame($loop, $config->getLoop()); + $this->assertTrue($config->requireSemicolons()); + $this->assertEquals(E_ERROR | E_WARNING, $config->errorLoggingLevel()); + } + + public function testLoadConfigFile() + { + $config = new Configuration(array('configFile' => __DIR__ . '/../../fixtures/config.php')); + + $runtimeDir = $this->joinPath(realpath(sys_get_temp_dir()), 'psysh_test', 'withconfig', 'temp'); + + $this->assertStringStartsWith($runtimeDir, realpath($config->getTempFile('foo', 123))); + $this->assertStringStartsWith($runtimeDir, realpath(dirname($config->getPipe('pipe', 123)))); + $this->assertStringStartsWith($runtimeDir, realpath($config->getRuntimeDir())); + + $this->assertEquals(function_exists('readline'), $config->useReadline()); + $this->assertFalse($config->usePcntl()); + $this->assertEquals(E_ALL & ~E_NOTICE, $config->errorLoggingLevel()); + } + + public function testLoadLocalConfigFile() + { + $oldPwd = getenv('PWD'); + putenv('PWD=' . realpath(__DIR__ . '/../../fixtures/project/')); + + $config = new Configuration(); + + // When no configuration file is specified local project config is merged + $this->assertFalse($config->useReadline()); + $this->assertTrue($config->usePcntl()); + + $config = new Configuration(array('configFile' => __DIR__ . '/../../fixtures/config.php')); + + // Defining a configuration file skips loading local project config + $this->assertTrue($config->useReadline()); + $this->assertFalse($config->usePcntl()); + + putenv("PWD=$oldPwd"); + } + + /** + * @expectedException Psy\Exception\DeprecatedException + */ + public function testBaseDirConfigIsDeprecated() + { + $config = new Configuration(array('baseDir' => 'fake')); + } + + private function joinPath() + { + return implode(DIRECTORY_SEPARATOR, func_get_args()); + } + + public function testConfigIncludes() + { + $config = new Configuration(array( + 'defaultIncludes' => array('/file.php'), + 'configFile' => __DIR__ . '/../../fixtures/empty.php', + )); + + $includes = $config->getDefaultIncludes(); + $this->assertCount(1, $includes); + $this->assertEquals('/file.php', $includes[0]); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Exception/BreakExceptionTest.php b/vendor/psy/psysh/test/Psy/Test/Exception/BreakExceptionTest.php new file mode 100644 index 0000000..94a90fd --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Exception/BreakExceptionTest.php @@ -0,0 +1,34 @@ +assertTrue($e instanceof Exception); + $this->assertTrue($e instanceof BreakException); + } + + public function testMessage() + { + $e = new BreakException('foo'); + + $this->assertContains('foo', $e->getMessage()); + $this->assertEquals('foo', $e->getRawMessage()); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Exception/ErrorExceptionTest.php b/vendor/psy/psysh/test/Psy/Test/Exception/ErrorExceptionTest.php new file mode 100644 index 0000000..a04f427 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Exception/ErrorExceptionTest.php @@ -0,0 +1,108 @@ +assertTrue($e instanceof Exception); + $this->assertTrue($e instanceof \ErrorException); + $this->assertTrue($e instanceof ErrorException); + } + + public function testMessage() + { + $e = new ErrorException('foo'); + + $this->assertContains('foo', $e->getMessage()); + $this->assertEquals('foo', $e->getRawMessage()); + } + + /** + * @dataProvider getLevels + */ + public function testErrorLevels($level, $type) + { + $e = new ErrorException('foo', 0, $level); + $this->assertContains('PHP ' . $type, $e->getMessage()); + } + + /** + * @dataProvider getLevels + */ + public function testThrowException($level, $type) + { + try { + ErrorException::throwException($level, '{whot}', '{file}', '13'); + } catch (ErrorException $e) { + $this->assertContains('PHP ' . $type, $e->getMessage()); + $this->assertContains('{whot}', $e->getMessage()); + $this->assertContains('in {file}', $e->getMessage()); + $this->assertContains('on line 13', $e->getMessage()); + } + } + + public function getLevels() + { + return array( + array(E_WARNING, 'warning'), + array(E_CORE_WARNING, 'warning'), + array(E_COMPILE_WARNING, 'warning'), + array(E_USER_WARNING, 'warning'), + array(E_STRICT, 'Strict error'), + array(0, 'error'), + ); + } + + /** + * @dataProvider getUserLevels + */ + public function testThrowExceptionAsErrorHandler($level, $type) + { + set_error_handler(array('Psy\Exception\ErrorException', 'throwException')); + try { + trigger_error('{whot}', $level); + } catch (ErrorException $e) { + $this->assertContains('PHP ' . $type, $e->getMessage()); + $this->assertContains('{whot}', $e->getMessage()); + } + restore_error_handler(); + } + + public function getUserLevels() + { + return array( + array(E_USER_ERROR, 'error'), + array(E_USER_WARNING, 'warning'), + array(E_USER_NOTICE, 'error'), + array(E_USER_DEPRECATED, 'error'), + ); + } + + public function testIgnoreExecutionLoopFilename() + { + $e = new ErrorException('{{message}}', 0, 1, '/fake/path/to/Psy/ExecutionLoop/Loop.php'); + $this->assertEmpty($e->getFile()); + + $e = new ErrorException('{{message}}', 0, 1, 'c:\fake\path\to\Psy\ExecutionLoop\Loop.php'); + $this->assertEmpty($e->getFile()); + + $e = new ErrorException('{{message}}', 0, 1, '/fake/path/to/Psy/File.php'); + $this->assertNotEmpty($e->getFile()); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Exception/FatalErrorExceptionTest.php b/vendor/psy/psysh/test/Psy/Test/Exception/FatalErrorExceptionTest.php new file mode 100644 index 0000000..ee7173d --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Exception/FatalErrorExceptionTest.php @@ -0,0 +1,46 @@ +assertTrue($e instanceof Exception); + $this->assertTrue($e instanceof \ErrorException); + $this->assertTrue($e instanceof FatalErrorException); + } + + public function testMessage() + { + $e = new FatalErrorException('{msg}', 0, 0, '{filename}', 13); + + $this->assertEquals('{msg}', $e->getRawMessage()); + $this->assertContains('{msg}', $e->getMessage()); + $this->assertContains('{filename}', $e->getMessage()); + $this->assertContains('line 13', $e->getMessage()); + } + + public function testMessageWithNoFilename() + { + $e = new FatalErrorException('{msg}'); + + $this->assertEquals('{msg}', $e->getRawMessage()); + $this->assertContains('{msg}', $e->getMessage()); + $this->assertContains('eval()\'d code', $e->getMessage()); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Exception/ParseErrorExceptionTest.php b/vendor/psy/psysh/test/Psy/Test/Exception/ParseErrorExceptionTest.php new file mode 100644 index 0000000..59602d1 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Exception/ParseErrorExceptionTest.php @@ -0,0 +1,43 @@ +assertTrue($e instanceof Exception); + $this->assertTrue($e instanceof \PhpParser\Error); + $this->assertTrue($e instanceof ParseErrorException); + } + + public function testMessage() + { + $e = new ParseErrorException('{msg}', 1); + + $this->assertContains('{msg}', $e->getMessage()); + $this->assertContains('PHP Parse error:', $e->getMessage()); + } + + public function testConstructFromParseError() + { + $e = ParseErrorException::fromParseError(new \PhpParser\Error('{msg}')); + + $this->assertContains('{msg}', $e->getRawMessage()); + $this->assertContains('PHP Parse error:', $e->getMessage()); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Exception/RuntimeExceptionTest.php b/vendor/psy/psysh/test/Psy/Test/Exception/RuntimeExceptionTest.php new file mode 100644 index 0000000..0396a26 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Exception/RuntimeExceptionTest.php @@ -0,0 +1,31 @@ +assertTrue($e instanceof Exception); + $this->assertTrue($e instanceof \RuntimeException); + $this->assertTrue($e instanceof RuntimeException); + + $this->assertEquals($msg, $e->getMessage()); + $this->assertEquals($msg, $e->getRawMessage()); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Formatter/CodeFormatterTest.php b/vendor/psy/psysh/test/Psy/Test/Formatter/CodeFormatterTest.php new file mode 100644 index 0000000..b5a09bf --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Formatter/CodeFormatterTest.php @@ -0,0 +1,61 @@ + 18| private function ignoreThisMethod(\$arg) + 19| { + 20| echo 'whot!'; + 21| } +EOS; + + $formatted = CodeFormatter::format(new \ReflectionMethod($this, 'ignoreThisMethod')); + $formattedWithoutColors = preg_replace('#' . chr(27) . '\[\d\d?m#', '', $formatted); + + $this->assertEquals($expected, rtrim($formattedWithoutColors)); + $this->assertNotEquals($expected, rtrim($formatted)); + } + + /** + * @dataProvider filenames + * @expectedException Psy\Exception\RuntimeException + */ + public function testCodeFormatterThrowsException($filename) + { + $reflector = $this->getMockBuilder('ReflectionClass') + ->disableOriginalConstructor() + ->getMock(); + + $reflector + ->expects($this->once()) + ->method('getFileName') + ->will($this->returnValue($filename)); + + CodeFormatter::format($reflector); + } + + public function filenames() + { + return array(array(null), array('not a file')); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Formatter/DocblockFormatterTest.php b/vendor/psy/psysh/test/Psy/Test/Formatter/DocblockFormatterTest.php new file mode 100644 index 0000000..4aad9a8 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Formatter/DocblockFormatterTest.php @@ -0,0 +1,63 @@ + + * + * @throws InvalidArgumentException if $foo is empty. + * + * @param mixed $foo It's a foo thing. + * @param int $bar This is definitely bar. + * + * @return string A string of no consequence. + */ + private function methodWithDocblock($foo, $bar = 1) + { + if (empty($foo)) { + throw new \InvalidArgumentException(); + } + + return 'method called'; + } + + public function testFormat() + { + $expected = <<Description: + This is a docblock! + +Throws: + InvalidArgumentException if \$foo is empty. + +Param: + mixed \$foo It's a foo thing. + int \$bar This is definitely bar. + +Return: + string A string of no consequence. + +Author: Justin Hileman \ +EOS; + + $this->assertEquals( + $expected, + DocblockFormatter::format(new \ReflectionMethod($this, 'methodWithDocblock')) + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Formatter/SignatureFormatterTest.php b/vendor/psy/psysh/test/Psy/Test/Formatter/SignatureFormatterTest.php new file mode 100644 index 0000000..41e9451 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Formatter/SignatureFormatterTest.php @@ -0,0 +1,77 @@ +assertEquals($expected, strip_tags(SignatureFormatter::format($reflector))); + } + + public function signatureReflectors() + { + return array( + array( + new \ReflectionClass($this), + "class Psy\Test\Formatter\SignatureFormatterTest " + . 'extends PHPUnit_Framework_TestCase implements ' + . 'Countable, PHPUnit_Framework_SelfDescribing, ' + . 'PHPUnit_Framework_Test', + ), + array( + new \ReflectionFunction('implode'), + defined('HHVM_VERSION') ? 'function implode($arg1, $arg2 = null)' : 'function implode($glue, $pieces)', + ), + array( + new ReflectionConstant($this, 'FOO'), + 'const FOO = "foo value"', + ), + array( + new \ReflectionMethod($this, 'someFakeMethod'), + 'private function someFakeMethod(array $one, $two = \'TWO\', Reflector $three = null)', + ), + array( + new \ReflectionProperty($this, 'bar'), + 'private static $bar', + ), + array( + new \ReflectionClass('Psy\CodeCleaner\CodeCleanerPass'), + 'abstract class Psy\CodeCleaner\CodeCleanerPass ' + . 'extends PhpParser\NodeVisitorAbstract ' + . 'implements PhpParser\NodeVisitor', + ), + ); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSignatureFormatterThrowsUnknownReflectorExpeption() + { + $refl = $this->getMock('Reflector'); + SignatureFormatter::format($refl); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Readline/GNUReadlineTest.php b/vendor/psy/psysh/test/Psy/Test/Readline/GNUReadlineTest.php new file mode 100644 index 0000000..60756aa --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Readline/GNUReadlineTest.php @@ -0,0 +1,80 @@ +markTestSkipped('GNUReadline not enabled'); + } + + $this->historyFile = tempnam(sys_get_temp_dir(), 'psysh_test_history'); + file_put_contents($this->historyFile, "_HiStOrY_V2_\n"); + } + + public function testHistory() + { + $readline = new GNUReadline($this->historyFile); + $this->assertEmpty($readline->listHistory()); + $readline->addHistory('foo'); + $this->assertEquals(array('foo'), $readline->listHistory()); + $readline->addHistory('bar'); + $this->assertEquals(array('foo', 'bar'), $readline->listHistory()); + $readline->addHistory('baz'); + $this->assertEquals(array('foo', 'bar', 'baz'), $readline->listHistory()); + $readline->clearHistory(); + $this->assertEmpty($readline->listHistory()); + } + + /** + * @depends testHistory + */ + public function testHistorySize() + { + $readline = new GNUReadline($this->historyFile, 2); + $this->assertEmpty($readline->listHistory()); + $readline->addHistory('foo'); + $readline->addHistory('bar'); + $this->assertEquals(array('foo', 'bar'), $readline->listHistory()); + $readline->addHistory('baz'); + $this->assertEquals(array('bar', 'baz'), $readline->listHistory()); + $readline->addHistory('w00t'); + $this->assertEquals(array('baz', 'w00t'), $readline->listHistory()); + $readline->clearHistory(); + $this->assertEmpty($readline->listHistory()); + } + + /** + * @depends testHistory + */ + public function testHistoryEraseDups() + { + $readline = new GNUReadline($this->historyFile, 0, true); + $this->assertEmpty($readline->listHistory()); + $readline->addHistory('foo'); + $readline->addHistory('bar'); + $readline->addHistory('foo'); + $this->assertEquals(array('bar', 'foo'), $readline->listHistory()); + $readline->addHistory('baz'); + $readline->addHistory('w00t'); + $readline->addHistory('baz'); + $this->assertEquals(array('bar', 'foo', 'w00t', 'baz'), $readline->listHistory()); + $readline->clearHistory(); + $this->assertEmpty($readline->listHistory()); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Readline/LibeditTest.php b/vendor/psy/psysh/test/Psy/Test/Readline/LibeditTest.php new file mode 100644 index 0000000..f0d01a0 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Readline/LibeditTest.php @@ -0,0 +1,128 @@ +markTestSkipped('Libedit not enabled'); + } + + $this->historyFile = tempnam(sys_get_temp_dir(), 'psysh_test_history'); + if (false === file_put_contents($this->historyFile, "_HiStOrY_V2_\n")) { + $this->fail('Unable to write history file: ' . $this->historyFile); + } + // Calling readline_read_history before readline_clear_history + // avoids segfault with PHP 5.5.7 & libedit v3.1 + readline_read_history($this->historyFile); + readline_clear_history(); + } + + public function tearDown() + { + if (is_file($this->historyFile)) { + unlink($this->historyFile); + } + } + + public function testHistory() + { + $readline = new Libedit($this->historyFile); + $this->assertEmpty($readline->listHistory()); + $readline->addHistory('foo'); + $this->assertEquals(array('foo'), $readline->listHistory()); + $readline->addHistory('bar'); + $this->assertEquals(array('foo', 'bar'), $readline->listHistory()); + $readline->addHistory('baz'); + $this->assertEquals(array('foo', 'bar', 'baz'), $readline->listHistory()); + $readline->clearHistory(); + $this->assertEmpty($readline->listHistory()); + } + + /** + * @depends testHistory + */ + public function testHistorySize() + { + $readline = new Libedit($this->historyFile, 2); + $this->assertEmpty($readline->listHistory()); + $readline->addHistory('foo'); + $readline->addHistory('bar'); + $this->assertEquals(array('foo', 'bar'), $readline->listHistory()); + $readline->addHistory('baz'); + $this->assertEquals(array('bar', 'baz'), $readline->listHistory()); + $readline->addHistory('w00t'); + $this->assertEquals(array('baz', 'w00t'), $readline->listHistory()); + $readline->clearHistory(); + $this->assertEmpty($readline->listHistory()); + } + + /** + * @depends testHistory + */ + public function testHistoryEraseDups() + { + $readline = new Libedit($this->historyFile, 0, true); + $this->assertEmpty($readline->listHistory()); + $readline->addHistory('foo'); + $readline->addHistory('bar'); + $readline->addHistory('foo'); + $this->assertEquals(array('bar', 'foo'), $readline->listHistory()); + $readline->addHistory('baz'); + $readline->addHistory('w00t'); + $readline->addHistory('baz'); + $this->assertEquals(array('bar', 'foo', 'w00t', 'baz'), $readline->listHistory()); + $readline->clearHistory(); + $this->assertEmpty($readline->listHistory()); + } + + public function testListHistory() + { + $readline = new Libedit($this->historyFile); + file_put_contents( + $this->historyFile, + "This is an entry\n\0This is a comment\nThis is an entry\0With a comment\n", + FILE_APPEND + ); + $this->assertEquals(array( + 'This is an entry', + 'This is an entry', + ), $readline->listHistory()); + $readline->clearHistory(); + } + + /** + * Libedit being a BSD library, + * it doesn't support non-unix line separators. + */ + public function testLinebreaksSupport() + { + $readline = new Libedit($this->historyFile); + file_put_contents( + $this->historyFile, + "foo\rbar\nbaz\r\nw00t", + FILE_APPEND + ); + $this->assertEquals(array( + "foo\rbar", + "baz\r", + 'w00t', + ), $readline->listHistory()); + $readline->clearHistory(); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Readline/TransientTest.php b/vendor/psy/psysh/test/Psy/Test/Readline/TransientTest.php new file mode 100644 index 0000000..6fcd182 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Readline/TransientTest.php @@ -0,0 +1,76 @@ +assertEmpty($readline->listHistory()); + $readline->addHistory('foo'); + $this->assertEquals(array('foo'), $readline->listHistory()); + $readline->addHistory('bar'); + $this->assertEquals(array('foo', 'bar'), $readline->listHistory()); + $readline->addHistory('baz'); + $this->assertEquals(array('foo', 'bar', 'baz'), $readline->listHistory()); + $readline->clearHistory(); + $this->assertEmpty($readline->listHistory()); + } + + /** + * @depends testHistory + */ + public function testHistorySize() + { + $readline = new Transient(null, 2); + $this->assertEmpty($readline->listHistory()); + $readline->addHistory('foo'); + $readline->addHistory('bar'); + $this->assertEquals(array('foo', 'bar'), $readline->listHistory()); + $readline->addHistory('baz'); + $this->assertEquals(array('bar', 'baz'), $readline->listHistory()); + $readline->addHistory('w00t'); + $this->assertEquals(array('baz', 'w00t'), $readline->listHistory()); + $readline->clearHistory(); + $this->assertEmpty($readline->listHistory()); + } + + /** + * @depends testHistory + */ + public function testHistoryEraseDups() + { + $readline = new Transient(null, 0, true); + $this->assertEmpty($readline->listHistory()); + $readline->addHistory('foo'); + $readline->addHistory('bar'); + $readline->addHistory('foo'); + $this->assertEquals(array('bar', 'foo'), $readline->listHistory()); + $readline->addHistory('baz'); + $readline->addHistory('w00t'); + $readline->addHistory('baz'); + $this->assertEquals(array('bar', 'foo', 'w00t', 'baz'), $readline->listHistory()); + $readline->clearHistory(); + $this->assertEmpty($readline->listHistory()); + } + + public function testSomeThingsAreAlwaysTrue() + { + $readline = new Transient(); + $this->assertTrue(Transient::isSupported()); + $this->assertTrue($readline->readHistory()); + $this->assertTrue($readline->writeHistory()); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Reflection/ReflectionConstantTest.php b/vendor/psy/psysh/test/Psy/Test/Reflection/ReflectionConstantTest.php new file mode 100644 index 0000000..0e12fa7 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Reflection/ReflectionConstantTest.php @@ -0,0 +1,60 @@ +getDeclaringClass(); + + $this->assertTrue($class instanceof \ReflectionClass); + $this->assertEquals('Psy\Test\Reflection\ReflectionConstantTest', $class->getName()); + $this->assertEquals('CONSTANT_ONE', $refl->getName()); + $this->assertEquals('CONSTANT_ONE', (string) $refl); + $this->assertEquals('one', $refl->getValue()); + $this->assertEquals(null, $refl->getFileName()); + $this->assertFalse($refl->getDocComment()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testUnknownConstantThrowsException() + { + new ReflectionConstant($this, 'UNKNOWN_CONSTANT'); + } + + /** + * @expectedException \RuntimeException + * @dataProvider notYetImplemented + */ + public function testNotYetImplemented($method) + { + $refl = new ReflectionConstant($this, 'CONSTANT_ONE'); + $refl->$method(); + } + + public function notYetImplemented() + { + return array( + array('getStartLine'), + array('getEndLine'), + array('export'), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/ShellTest.php b/vendor/psy/psysh/test/Psy/Test/ShellTest.php new file mode 100644 index 0000000..db47dc5 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/ShellTest.php @@ -0,0 +1,340 @@ +streams as $stream) { + fclose($stream); + } + } + + public function testScopeVariables() + { + $one = 'banana'; + $two = 123; + $three = new \StdClass(); + $__psysh__ = 'ignore this'; + $_ = 'ignore this'; + $_e = 'ignore this'; + + $shell = new Shell($this->getConfig()); + $shell->setScopeVariables(compact('one', 'two', 'three', '__psysh__', '_', '_e')); + + $this->assertNotContains('__psysh__', $shell->getScopeVariableNames()); + $this->assertEquals(array('one', 'two', 'three', '_'), $shell->getScopeVariableNames()); + $this->assertEquals('banana', $shell->getScopeVariable('one')); + $this->assertEquals(123, $shell->getScopeVariable('two')); + $this->assertSame($three, $shell->getScopeVariable('three')); + $this->assertNull($shell->getScopeVariable('_')); + + $shell->setScopeVariables(array()); + $this->assertEquals(array('_'), $shell->getScopeVariableNames()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testUnknownScopeVariablesThrowExceptions() + { + $shell = new Shell($this->getConfig()); + $shell->setScopeVariables(array('foo' => 'FOO', 'bar' => 1)); + $shell->getScopeVariable('baz'); + } + + public function testIncludes() + { + $config = $this->getConfig(array('configFile' => __DIR__ . '/../../fixtures/empty.php')); + + $shell = new Shell($config); + $this->assertEmpty($shell->getIncludes()); + $shell->setIncludes(array('foo', 'bar', 'baz')); + $this->assertEquals(array('foo', 'bar', 'baz'), $shell->getIncludes()); + } + + public function testIncludesConfig() + { + $config = $this->getConfig(array( + 'defaultIncludes' => array('/file.php'), + 'configFile' => __DIR__ . '/../../fixtures/empty.php', + )); + + $shell = new Shell($config); + + $includes = $shell->getIncludes(); + $this->assertEquals('/file.php', $includes[0]); + } + + public function testAddMatchersViaConfig() + { + $config = $this->getConfig(array( + 'tabCompletionMatchers' => array( + new ClassMethodsMatcher(), + ), + )); + + $matchers = $config->getTabCompletionMatchers(); + + $this->assertTrue(array_pop($matchers) instanceof ClassMethodsMatcher); + } + + public function testRenderingExceptions() + { + $shell = new Shell($this->getConfig()); + $output = $this->getOutput(); + $stream = $output->getStream(); + $e = new ParseErrorException('message', 13); + + $shell->setOutput($output); + $shell->addCode('code'); + $this->assertTrue($shell->hasCode()); + $this->assertNotEmpty($shell->getCodeBuffer()); + + $shell->writeException($e); + + $this->assertSame($e, $shell->getScopeVariable('_e')); + $this->assertFalse($shell->hasCode()); + $this->assertEmpty($shell->getCodeBuffer()); + + rewind($stream); + $streamContents = stream_get_contents($stream); + + $this->assertContains('PHP Parse error', $streamContents); + $this->assertContains('message', $streamContents); + $this->assertContains('line 13', $streamContents); + } + + public function testHandlingErrors() + { + $shell = new Shell($this->getConfig()); + $output = $this->getOutput(); + $stream = $output->getStream(); + $shell->setOutput($output); + + $oldLevel = error_reporting(); + error_reporting($oldLevel & ~E_USER_NOTICE); + + try { + $shell->handleError(E_USER_NOTICE, 'wheee', null, 13); + } catch (ErrorException $e) { + error_reporting($oldLevel); + $this->fail('Unexpected error exception'); + } + error_reporting($oldLevel); + + rewind($stream); + $streamContents = stream_get_contents($stream); + + $this->assertContains('PHP error:', $streamContents); + $this->assertContains('wheee', $streamContents); + $this->assertContains('line 13', $streamContents); + } + + /** + * @expectedException Psy\Exception\ErrorException + */ + public function testNotHandlingErrors() + { + $shell = new Shell($this->getConfig()); + $oldLevel = error_reporting(); + error_reporting($oldLevel | E_USER_NOTICE); + + try { + $shell->handleError(E_USER_NOTICE, 'wheee', null, 13); + } catch (ErrorException $e) { + error_reporting($oldLevel); + throw $e; + } + } + + public function testVersion() + { + $shell = new Shell($this->getConfig()); + + $this->assertInstanceOf('Symfony\Component\Console\Application', $shell); + $this->assertContains(Shell::VERSION, $shell->getVersion()); + $this->assertContains(phpversion(), $shell->getVersion()); + $this->assertContains(php_sapi_name(), $shell->getVersion()); + } + + public function testCodeBuffer() + { + $shell = new Shell($this->getConfig()); + + $shell->addCode('class'); + $this->assertNull($shell->flushCode()); + $this->assertTrue($shell->hasCode()); + + $shell->addCode('a'); + $this->assertNull($shell->flushCode()); + $this->assertTrue($shell->hasCode()); + + $shell->addCode('{}'); + $code = $shell->flushCode(); + $this->assertFalse($shell->hasCode()); + $code = preg_replace('/\s+/', ' ', $code); + $this->assertNotNull($code); + $this->assertEquals('class a { }', $code); + } + + public function testKeepCodeBufferOpen() + { + $shell = new Shell($this->getConfig()); + + $shell->addCode('1 \\'); + $this->assertNull($shell->flushCode()); + $this->assertTrue($shell->hasCode()); + + $shell->addCode('+ 1 \\'); + $this->assertNull($shell->flushCode()); + $this->assertTrue($shell->hasCode()); + + $shell->addCode('+ 1'); + $code = $shell->flushCode(); + $this->assertFalse($shell->hasCode()); + $code = preg_replace('/\s+/', ' ', $code); + $this->assertNotNull($code); + $this->assertEquals('return 1 + 1 + 1;', $code); + } + + /** + * @expectedException \Psy\Exception\ParseErrorException + */ + public function testCodeBufferThrowsParseExceptions() + { + $shell = new Shell($this->getConfig()); + $shell->addCode('this is not valid'); + $shell->flushCode(); + } + + public function testClosuresSupport() + { + $shell = new Shell($this->getConfig()); + $code = '$test = function () {}'; + $shell->addCode($code); + $shell->flushCode(); + $code = '$test()'; + $shell->addCode($code); + $shell->flushCode(); + } + + public function testWriteStdout() + { + $output = $this->getOutput(); + $stream = $output->getStream(); + $shell = new Shell($this->getConfig()); + $shell->setOutput($output); + + $shell->writeStdout("{{stdout}}\n"); + + rewind($stream); + $streamContents = stream_get_contents($stream); + + $this->assertEquals('{{stdout}}' . PHP_EOL, $streamContents); + } + + public function testWriteStdoutWithoutNewline() + { + $output = $this->getOutput(); + $stream = $output->getStream(); + $shell = new Shell($this->getConfig()); + $shell->setOutput($output); + + $shell->writeStdout('{{stdout}}'); + + rewind($stream); + $streamContents = stream_get_contents($stream); + + $this->assertEquals('{{stdout}}' . PHP_EOL, $streamContents); + } + + /** + * @dataProvider getReturnValues + */ + public function testWriteReturnValue($input, $expected) + { + $output = $this->getOutput(); + $stream = $output->getStream(); + $shell = new Shell($this->getConfig()); + $shell->setOutput($output); + + $shell->writeReturnValue($input); + rewind($stream); + $this->assertEquals($expected, stream_get_contents($stream)); + } + + public function getReturnValues() + { + return array( + array('{{return value}}', "=> \"\033[32m{{return value}}\033[39m\"" . PHP_EOL), + array(1, "=> \033[35m1\033[39m" . PHP_EOL), + ); + } + + /** + * @dataProvider getRenderedExceptions + */ + public function testWriteException($exception, $expected) + { + $output = $this->getOutput(); + $stream = $output->getStream(); + $shell = new Shell($this->getConfig()); + $shell->setOutput($output); + + $shell->writeException($exception); + rewind($stream); + $this->assertEquals($expected, stream_get_contents($stream)); + } + + public function getRenderedExceptions() + { + return array( + array(new \Exception('{{message}}'), "Exception with message '{{message}}'" . PHP_EOL), + ); + } + + private function getOutput() + { + $stream = fopen('php://memory', 'w+'); + $this->streams[] = $stream; + + $output = new StreamOutput($stream, StreamOutput::VERBOSITY_NORMAL, false); + + return $output; + } + + private function getConfig(array $config = array()) + { + // Mebbe there's a better way than this? + $dir = tempnam(sys_get_temp_dir(), 'psysh_shell_test_'); + unlink($dir); + + $defaults = array( + 'configDir' => $dir, + 'dataDir' => $dir, + 'runtimeDir' => $dir, + ); + + return new Configuration(array_merge($defaults, $config)); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/TabCompletion/AutoCompleterTest.php b/vendor/psy/psysh/test/Psy/Test/TabCompletion/AutoCompleterTest.php new file mode 100644 index 0000000..8bb4828 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/TabCompletion/AutoCompleterTest.php @@ -0,0 +1,145 @@ +getAutoCompleter(); + foreach ($matchers as $matcher) { + if ($matcher instanceof ContextAware) { + $matcher->setContext($context); + } + $tabCompletion->addMatcher($matcher); + } + + $context->setAll(array('foo' => 12, 'bar' => new \DOMDocument())); + + $code = $tabCompletion->processCallback('', 0, array( + 'line_buffer' => $line, + 'point' => 0, + 'end' => strlen($line), + )); + + foreach ($mustContain as $mc) { + $this->assertContains($mc, $code); + } + + foreach ($mustNotContain as $mnc) { + $this->assertNotContains($mnc, $code); + } + } + + /** + * TODO + * ==== + * draft, open to modifications + * - [ ] if the variable is an array, return the square bracket for completion + * - [ ] if the variable is a constructor or method, reflect to complete as a function call + * - [ ] if the preceding token is a variable, call operators or keywords compatible for completion + * - [X] a command always should be the second token after php_open_tag + * - [X] keywords are never consecutive + * - [X] namespacing completion should work just fine + * - [X] after a new keyword, should always be a class constructor, never a function call or keyword, constant, + * or variable that does not contain a existing class name. + * - [X] on a namespaced constructor the completion must show the classes related, not constants. + * + * @return array + */ + public function classesInput() + { + return array( + // input, must had, must not had + array('T_OPE', array('T_OPEN_TAG'), array()), + array('st', array('stdClass'), array()), + array('stdCla', array('stdClass'), array()), + array('new s', array('stdClass'), array()), + array( + 'new ', + array('stdClass', 'Psy\\Context', 'Psy\\Configuration'), + array('require', 'array_search', 'T_OPEN_TAG', '$foo'), + ), + array('new Psy\\C', array('Context'), array('CASE_LOWER')), + array('\s', array('stdClass'), array()), + array('array_', array('array_search', 'array_map', 'array_merge'), array()), + array('$bar->', array('load'), array()), + array('$b', array('bar'), array()), + array('6 + $b', array('bar'), array()), + array('$f', array('foo'), array()), + array('l', array('ls'), array()), + array('ls ', array(), array('ls')), + array('sho', array('show'), array()), + array('12 + clone $', array('foo'), array()), + // array( + // '$foo ', + // array('+', 'clone'), + // array('$foo', 'DOMDocument', 'array_map') + // ), requires a operator matcher? + array('$', array('foo', 'bar'), array('require', 'array_search', 'T_OPEN_TAG', 'Psy')), + array( + 'Psy\\', + array('Context', 'TabCompletion\\Matcher\\AbstractMatcher'), + array('require', 'array_search'), + ), + array( + 'Psy\Test\TabCompletion\StaticSample::CO', + array('Psy\Test\TabCompletion\StaticSample::CONSTANT_VALUE'), + array(), + ), + array( + 'Psy\Test\TabCompletion\StaticSample::', + array('Psy\Test\TabCompletion\StaticSample::$staticVariable'), + array(), + ), + array( + 'Psy\Test\TabCompletion\StaticSample::', + array('Psy\Test\TabCompletion\StaticSample::staticFunction'), + array(), + ), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/TabCompletion/StaticSample.php b/vendor/psy/psysh/test/Psy/Test/TabCompletion/StaticSample.php new file mode 100644 index 0000000..7fb0ada --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/TabCompletion/StaticSample.php @@ -0,0 +1,27 @@ +getMockBuilder('ReflectionClass') + ->disableOriginalConstructor() + ->getMock(); + + $reflector->expects($this->once()) + ->method('getDocComment') + ->will($this->returnValue($comment)); + + $docblock = new Docblock($reflector); + + $this->assertEquals($body, $docblock->desc); + + foreach ($tags as $tag => $value) { + $this->assertTrue($docblock->hasTag($tag)); + $this->assertEquals($value, $docblock->tag($tag)); + } + } + + public function comments() + { + return array( + array('', '', array()), + array( + '/** + * This is a docblock + * + * @throws \Exception with a description + */', + 'This is a docblock', + array( + 'throws' => array(array('type' => '\Exception', 'desc' => 'with a description')), + ), + ), + array( + '/** + * This is a slightly longer docblock + * + * @param int $foo Is a Foo + * @param string $bar With some sort of description + * @param \ClassName $baz is cool too + * + * @return int At least it isn\'t a string + */', + 'This is a slightly longer docblock', + array( + 'param' => array( + array('type' => 'int', 'desc' => 'Is a Foo', 'var' => '$foo'), + array('type' => 'string', 'desc' => 'With some sort of description', 'var' => '$bar'), + array('type' => '\ClassName', 'desc' => 'is cool too', 'var' => '$baz'), + ), + 'return' => array( + array('type' => 'int', 'desc' => 'At least it isn\'t a string'), + ), + ), + ), + array( + '/** + * This is a docblock! + * + * It spans lines, too! + * + * @tagname plus a description + * + * @return + */', + "This is a docblock!\n\nIt spans lines, too!", + array( + 'tagname' => array('plus a description'), + ), + ), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Util/MirrorTest.php b/vendor/psy/psysh/test/Psy/Test/Util/MirrorTest.php new file mode 100644 index 0000000..82726d3 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Util/MirrorTest.php @@ -0,0 +1,80 @@ +assertTrue($refl instanceof \ReflectionFunction); + + $refl = Mirror::get('Psy\Test\Util\MirrorTest'); + $this->assertTrue($refl instanceof \ReflectionClass); + + $refl = Mirror::get($this); + $this->assertTrue($refl instanceof \ReflectionObject); + + $refl = Mirror::get($this, 'FOO'); + $this->assertTrue($refl instanceof ReflectionConstant); + + $refl = Mirror::get($this, 'bar'); + $this->assertTrue($refl instanceof \ReflectionProperty); + + $refl = Mirror::get($this, 'baz'); + $this->assertTrue($refl instanceof \ReflectionProperty); + + $refl = Mirror::get($this, 'aPublicMethod'); + $this->assertTrue($refl instanceof \ReflectionMethod); + + $refl = Mirror::get($this, 'baz', Mirror::STATIC_PROPERTY); + $this->assertTrue($refl instanceof \ReflectionProperty); + } + + /** + * @expectedException \RuntimeException + */ + public function testMirrorThrowsExceptions() + { + Mirror::get($this, 'notAMethod'); + } + + /** + * @expectedException \InvalidArgumentException + * @dataProvider invalidArguments + */ + public function testMirrorThrowsInvalidArgumentExceptions($value) + { + Mirror::get($value); + } + + public function invalidArguments() + { + return array( + array('not_a_function_or_class'), + array(array()), + array(1), + ); + } +} diff --git a/vendor/psy/psysh/test/Psy/Test/Util/StrTest.php b/vendor/psy/psysh/test/Psy/Test/Util/StrTest.php new file mode 100644 index 0000000..2972e25 --- /dev/null +++ b/vendor/psy/psysh/test/Psy/Test/Util/StrTest.php @@ -0,0 +1,31 @@ +assertEquals($expected, Str::unvis($input)); + } + + public function testUnvisProvider() + { + //return require_once(__DIR__.'/../../../fixtures/unvis_fixtures.php'); + return json_decode(file_get_contents(__DIR__ . '/../../../fixtures/unvis_fixtures.json')); + } +} diff --git a/vendor/psy/psysh/test/fixtures/config.php b/vendor/psy/psysh/test/fixtures/config.php new file mode 100644 index 0000000..0057eca --- /dev/null +++ b/vendor/psy/psysh/test/fixtures/config.php @@ -0,0 +1,18 @@ +setRuntimeDir(sys_get_temp_dir() . '/psysh_test/withconfig/temp'); + +return array( + 'useReadline' => true, + 'usePcntl' => false, + 'errorLoggingLevel' => E_ALL & ~E_NOTICE, +); diff --git a/vendor/psy/psysh/test/fixtures/default/.config/psysh/config.php b/vendor/psy/psysh/test/fixtures/default/.config/psysh/config.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/vendor/psy/psysh/test/fixtures/default/.config/psysh/config.php @@ -0,0 +1 @@ + false, + 'usePcntl' => true, +); diff --git a/vendor/psy/psysh/test/fixtures/unvis_fixtures.json b/vendor/psy/psysh/test/fixtures/unvis_fixtures.json new file mode 100644 index 0000000..960fb02 --- /dev/null +++ b/vendor/psy/psysh/test/fixtures/unvis_fixtures.json @@ -0,0 +1 @@ +[["", ""], ["\\^A", "\u0001"], ["\\^B", "\u0002"], ["\\^C", "\u0003"], ["\\^D", "\u0004"], ["\\^E", "\u0005"], ["\\^F", "\u0006"], ["\\^G", "\u0007"], ["\\^H", "\b"], ["\\^I", "\t"], ["\\^J", "\n"], ["\\^K", "\u000b"], ["\\^L", "\f"], ["\\^M", "\r"], ["\\^N", "\u000e"], ["\\^O", "\u000f"], ["\\^P", "\u0010"], ["\\^Q", "\u0011"], ["\\^R", "\u0012"], ["\\^S", "\u0013"], ["\\^T", "\u0014"], ["\\^U", "\u0015"], ["\\^V", "\u0016"], ["\\^W", "\u0017"], ["\\^X", "\u0018"], ["\\^Y", "\u0019"], ["\\^Z", "\u001a"], ["\\^[", "\u001b"], ["\\^\\", "\u001c"], ["\\^]", "\u001d"], ["\\^^", "\u001e"], ["\\^_", "\u001f"], ["\\040", " "], ["!", "!"], ["\"", "\""], ["#", "#"], ["$", "$"], ["%", "%"], ["&", "&"], ["'", "'"], ["(", "("], [")", ")"], ["*", "*"], ["+", "+"], [",", ","], ["-", "-"], [".", "."], ["/", "/"], ["0", "0"], ["1", "1"], ["2", "2"], ["3", "3"], ["4", "4"], ["5", "5"], ["6", "6"], ["7", "7"], ["8", "8"], ["9", "9"], [":", ":"], [";", ";"], ["<", "<"], ["=", "="], [">", ">"], ["?", "?"], ["@", "@"], ["A", "A"], ["B", "B"], ["C", "C"], ["D", "D"], ["E", "E"], ["F", "F"], ["G", "G"], ["H", "H"], ["I", "I"], ["J", "J"], ["K", "K"], ["L", "L"], ["M", "M"], ["N", "N"], ["O", "O"], ["P", "P"], ["Q", "Q"], ["R", "R"], ["S", "S"], ["T", "T"], ["U", "U"], ["V", "V"], ["W", "W"], ["X", "X"], ["Y", "Y"], ["Z", "Z"], ["[", "["], ["\\\\", "\\"], ["]", "]"], ["^", "^"], ["_", "_"], ["`", "`"], ["a", "a"], ["b", "b"], ["c", "c"], ["d", "d"], ["e", "e"], ["f", "f"], ["g", "g"], ["h", "h"], ["i", "i"], ["j", "j"], ["k", "k"], ["l", "l"], ["m", "m"], ["n", "n"], ["o", "o"], ["p", "p"], ["q", "q"], ["r", "r"], ["s", "s"], ["t", "t"], ["u", "u"], ["v", "v"], ["w", "w"], ["x", "x"], ["y", "y"], ["z", "z"], ["{", "{"], ["|", "|"], ["}", "}"], ["~", "~"], ["\\^?", "\u007f"], ["\\M-B\\M^@", "\u0080"], ["\\M-B\\M^A", "\u0081"], ["\\M-B\\M^B", "\u0082"], ["\\M-B\\M^C", "\u0083"], ["\\M-B\\M^D", "\u0084"], ["\\M-B\\M^E", "\u0085"], ["\\M-B\\M^F", "\u0086"], ["\\M-B\\M^G", "\u0087"], ["\\M-B\\M^H", "\u0088"], ["\\M-B\\M^I", "\u0089"], ["\\M-B\\M^J", "\u008a"], ["\\M-B\\M^K", "\u008b"], ["\\M-B\\M^L", "\u008c"], ["\\M-B\\M^M", "\u008d"], ["\\M-B\\M^N", "\u008e"], ["\\M-B\\M^O", "\u008f"], ["\\M-B\\M^P", "\u0090"], ["\\M-B\\M^Q", "\u0091"], ["\\M-B\\M^R", "\u0092"], ["\\M-B\\M^S", "\u0093"], ["\\M-B\\M^T", "\u0094"], ["\\M-B\\M^U", "\u0095"], ["\\M-B\\M^V", "\u0096"], ["\\M-B\\M^W", "\u0097"], ["\\M-B\\M^X", "\u0098"], ["\\M-B\\M^Y", "\u0099"], ["\\M-B\\M^Z", "\u009a"], ["\\M-B\\M^[", "\u009b"], ["\\M-B\\M^\\", "\u009c"], ["\\M-B\\M^]", "\u009d"], ["\\M-B\\M^^", "\u009e"], ["\\M-B\\M^_", "\u009f"], ["\\M-B\\240", "\u00a0"], ["\\M-B\\M-!", "\u00a1"], ["\\M-B\\M-\"", "\u00a2"], ["\\M-B\\M-#", "\u00a3"], ["\\M-B\\M-$", "\u00a4"], ["\\M-B\\M-%", "\u00a5"], ["\\M-B\\M-&", "\u00a6"], ["\\M-B\\M-'", "\u00a7"], ["\\M-B\\M-(", "\u00a8"], ["\\M-B\\M-)", "\u00a9"], ["\\M-B\\M-*", "\u00aa"], ["\\M-B\\M-+", "\u00ab"], ["\\M-B\\M-,", "\u00ac"], ["\\M-B\\M--", "\u00ad"], ["\\M-B\\M-.", "\u00ae"], ["\\M-B\\M-/", "\u00af"], ["\\M-B\\M-0", "\u00b0"], ["\\M-B\\M-1", "\u00b1"], ["\\M-B\\M-2", "\u00b2"], ["\\M-B\\M-3", "\u00b3"], ["\\M-B\\M-4", "\u00b4"], ["\\M-B\\M-5", "\u00b5"], ["\\M-B\\M-6", "\u00b6"], ["\\M-B\\M-7", "\u00b7"], ["\\M-B\\M-8", "\u00b8"], ["\\M-B\\M-9", "\u00b9"], ["\\M-B\\M-:", "\u00ba"], ["\\M-B\\M-;", "\u00bb"], ["\\M-B\\M-<", "\u00bc"], ["\\M-B\\M-=", "\u00bd"], ["\\M-B\\M->", "\u00be"], ["\\M-B\\M-?", "\u00bf"], ["\\M-C\\M^@", "\u00c0"], ["\\M-C\\M^A", "\u00c1"], ["\\M-C\\M^B", "\u00c2"], ["\\M-C\\M^C", "\u00c3"], ["\\M-C\\M^D", "\u00c4"], ["\\M-C\\M^E", "\u00c5"], ["\\M-C\\M^F", "\u00c6"], ["\\M-C\\M^G", "\u00c7"], ["\\M-C\\M^H", "\u00c8"], ["\\M-C\\M^I", "\u00c9"], ["\\M-C\\M^J", "\u00ca"], ["\\M-C\\M^K", "\u00cb"], ["\\M-C\\M^L", "\u00cc"], ["\\M-C\\M^M", "\u00cd"], ["\\M-C\\M^N", "\u00ce"], ["\\M-C\\M^O", "\u00cf"], ["\\M-C\\M^P", "\u00d0"], ["\\M-C\\M^Q", "\u00d1"], ["\\M-C\\M^R", "\u00d2"], ["\\M-C\\M^S", "\u00d3"], ["\\M-C\\M^T", "\u00d4"], ["\\M-C\\M^U", "\u00d5"], ["\\M-C\\M^V", "\u00d6"], ["\\M-C\\M^W", "\u00d7"], ["\\M-C\\M^X", "\u00d8"], ["\\M-C\\M^Y", "\u00d9"], ["\\M-C\\M^Z", "\u00da"], ["\\M-C\\M^[", "\u00db"], ["\\M-C\\M^\\", "\u00dc"], ["\\M-C\\M^]", "\u00dd"], ["\\M-C\\M^^", "\u00de"], ["\\M-C\\M^_", "\u00df"], ["\\M-C\\240", "\u00e0"], ["\\M-C\\M-!", "\u00e1"], ["\\M-C\\M-\"", "\u00e2"], ["\\M-C\\M-#", "\u00e3"], ["\\M-C\\M-$", "\u00e4"], ["\\M-C\\M-%", "\u00e5"], ["\\M-C\\M-&", "\u00e6"], ["\\M-C\\M-'", "\u00e7"], ["\\M-C\\M-(", "\u00e8"], ["\\M-C\\M-)", "\u00e9"], ["\\M-C\\M-*", "\u00ea"], ["\\M-C\\M-+", "\u00eb"], ["\\M-C\\M-,", "\u00ec"], ["\\M-C\\M--", "\u00ed"], ["\\M-C\\M-.", "\u00ee"], ["\\M-C\\M-/", "\u00ef"], ["\\M-C\\M-0", "\u00f0"], ["\\M-C\\M-1", "\u00f1"], ["\\M-C\\M-2", "\u00f2"], ["\\M-C\\M-3", "\u00f3"], ["\\M-C\\M-4", "\u00f4"], ["\\M-C\\M-5", "\u00f5"], ["\\M-C\\M-6", "\u00f6"], ["\\M-C\\M-7", "\u00f7"], ["\\M-C\\M-8", "\u00f8"], ["\\M-C\\M-9", "\u00f9"], ["\\M-C\\M-:", "\u00fa"], ["\\M-C\\M-;", "\u00fb"], ["\\M-C\\M-<", "\u00fc"], ["\\M-C\\M-=", "\u00fd"], ["\\M-C\\M->", "\u00fe"], ["\\M-C\\M-?", "\u00ff"], ["\\M-D\\M^@", "\u0100"], ["\\M-D\\M^A", "\u0101"], ["\\M-D\\M^B", "\u0102"], ["\\M-D\\M^C", "\u0103"], ["\\M-D\\M^D", "\u0104"], ["\\M-D\\M^E", "\u0105"], ["\\M-D\\M^F", "\u0106"], ["\\M-D\\M^G", "\u0107"], ["\\M-D\\M^H", "\u0108"], ["\\M-D\\M^I", "\u0109"], ["\\M-D\\M^J", "\u010a"], ["\\M-D\\M^K", "\u010b"], ["\\M-D\\M^L", "\u010c"], ["\\M-D\\M^M", "\u010d"], ["\\M-D\\M^N", "\u010e"], ["\\M-D\\M^O", "\u010f"], ["\\M-D\\M^P", "\u0110"], ["\\M-D\\M^Q", "\u0111"], ["\\M-D\\M^R", "\u0112"], ["\\M-D\\M^S", "\u0113"], ["\\M-D\\M^T", "\u0114"], ["\\M-D\\M^U", "\u0115"], ["\\M-D\\M^V", "\u0116"], ["\\M-D\\M^W", "\u0117"], ["\\M-D\\M^X", "\u0118"], ["\\M-D\\M^Y", "\u0119"], ["\\M-D\\M^Z", "\u011a"], ["\\M-D\\M^[", "\u011b"], ["\\M-D\\M^\\", "\u011c"], ["\\M-D\\M^]", "\u011d"], ["\\M-D\\M^^", "\u011e"], ["\\M-D\\M^_", "\u011f"], ["\\M-D\\240", "\u0120"], ["\\M-D\\M-!", "\u0121"], ["\\M-D\\M-\"", "\u0122"], ["\\M-D\\M-#", "\u0123"], ["\\M-D\\M-$", "\u0124"], ["\\M-D\\M-%", "\u0125"], ["\\M-D\\M-&", "\u0126"], ["\\M-D\\M-'", "\u0127"], ["\\M-D\\M-(", "\u0128"], ["\\M-D\\M-)", "\u0129"], ["\\M-D\\M-*", "\u012a"], ["\\M-D\\M-+", "\u012b"], ["\\M-D\\M-,", "\u012c"], ["\\M-D\\M--", "\u012d"], ["\\M-D\\M-.", "\u012e"], ["\\M-D\\M-/", "\u012f"], ["\\M-D\\M-0", "\u0130"], ["\\M-D\\M-1", "\u0131"], ["\\M-D\\M-2", "\u0132"], ["\\M-D\\M-3", "\u0133"], ["\\M-D\\M-4", "\u0134"], ["\\M-D\\M-5", "\u0135"], ["\\M-D\\M-6", "\u0136"], ["\\M-D\\M-7", "\u0137"], ["\\M-D\\M-8", "\u0138"], ["\\M-D\\M-9", "\u0139"], ["\\M-D\\M-:", "\u013a"], ["\\M-D\\M-;", "\u013b"], ["\\M-D\\M-<", "\u013c"], ["\\M-D\\M-=", "\u013d"], ["\\M-D\\M->", "\u013e"], ["\\M-D\\M-?", "\u013f"], ["\\M-E\\M^@", "\u0140"], ["\\M-E\\M^A", "\u0141"], ["\\M-E\\M^B", "\u0142"], ["\\M-E\\M^C", "\u0143"], ["\\M-E\\M^D", "\u0144"], ["\\M-E\\M^E", "\u0145"], ["\\M-E\\M^F", "\u0146"], ["\\M-E\\M^G", "\u0147"], ["\\M-E\\M^H", "\u0148"], ["\\M-E\\M^I", "\u0149"], ["\\M-E\\M^J", "\u014a"], ["\\M-E\\M^K", "\u014b"], ["\\M-E\\M^L", "\u014c"], ["\\M-E\\M^M", "\u014d"], ["\\M-E\\M^N", "\u014e"], ["\\M-E\\M^O", "\u014f"], ["\\M-E\\M^P", "\u0150"], ["\\M-E\\M^Q", "\u0151"], ["\\M-E\\M^R", "\u0152"], ["\\M-E\\M^S", "\u0153"], ["\\M-E\\M^T", "\u0154"], ["\\M-E\\M^U", "\u0155"], ["\\M-E\\M^V", "\u0156"], ["\\M-E\\M^W", "\u0157"], ["\\M-E\\M^X", "\u0158"], ["\\M-E\\M^Y", "\u0159"], ["\\M-E\\M^Z", "\u015a"], ["\\M-E\\M^[", "\u015b"], ["\\M-E\\M^\\", "\u015c"], ["\\M-E\\M^]", "\u015d"], ["\\M-E\\M^^", "\u015e"], ["\\M-E\\M^_", "\u015f"], ["\\M-E\\240", "\u0160"], ["\\M-E\\M-!", "\u0161"], ["\\M-E\\M-\"", "\u0162"], ["\\M-E\\M-#", "\u0163"], ["\\M-E\\M-$", "\u0164"], ["\\M-E\\M-%", "\u0165"], ["\\M-E\\M-&", "\u0166"], ["\\M-E\\M-'", "\u0167"], ["\\M-E\\M-(", "\u0168"], ["\\M-E\\M-)", "\u0169"], ["\\M-E\\M-*", "\u016a"], ["\\M-E\\M-+", "\u016b"], ["\\M-E\\M-,", "\u016c"], ["\\M-E\\M--", "\u016d"], ["\\M-E\\M-.", "\u016e"], ["\\M-E\\M-/", "\u016f"], ["\\M-E\\M-0", "\u0170"], ["\\M-E\\M-1", "\u0171"], ["\\M-E\\M-2", "\u0172"], ["\\M-E\\M-3", "\u0173"], ["\\M-E\\M-4", "\u0174"], ["\\M-E\\M-5", "\u0175"], ["\\M-E\\M-6", "\u0176"], ["\\M-E\\M-7", "\u0177"], ["\\M-E\\M-8", "\u0178"], ["\\M-E\\M-9", "\u0179"], ["\\M-E\\M-:", "\u017a"], ["\\M-E\\M-;", "\u017b"], ["\\M-E\\M-<", "\u017c"], ["\\M-E\\M-=", "\u017d"], ["\\M-E\\M->", "\u017e"], ["\\M-E\\M-?", "\u017f"], ["\\M-F\\M^@", "\u0180"], ["\\M-F\\M^A", "\u0181"], ["\\M-F\\M^B", "\u0182"], ["\\M-F\\M^C", "\u0183"], ["\\M-F\\M^D", "\u0184"], ["\\M-F\\M^E", "\u0185"], ["\\M-F\\M^F", "\u0186"], ["\\M-F\\M^G", "\u0187"], ["\\M-F\\M^H", "\u0188"], ["\\M-F\\M^I", "\u0189"], ["\\M-F\\M^J", "\u018a"], ["\\M-F\\M^K", "\u018b"], ["\\M-F\\M^L", "\u018c"], ["\\M-F\\M^M", "\u018d"], ["\\M-F\\M^N", "\u018e"], ["\\M-F\\M^O", "\u018f"], ["\\M-F\\M^P", "\u0190"], ["\\M-F\\M^Q", "\u0191"], ["\\M-F\\M^R", "\u0192"], ["\\M-F\\M^S", "\u0193"], ["\\M-F\\M^T", "\u0194"], ["\\M-F\\M^U", "\u0195"], ["\\M-F\\M^V", "\u0196"], ["\\M-F\\M^W", "\u0197"], ["\\M-F\\M^X", "\u0198"], ["\\M-F\\M^Y", "\u0199"], ["\\M-F\\M^Z", "\u019a"], ["\\M-F\\M^[", "\u019b"], ["\\M-F\\M^\\", "\u019c"], ["\\M-F\\M^]", "\u019d"], ["\\M-F\\M^^", "\u019e"], ["\\M-F\\M^_", "\u019f"], ["\\M-F\\240", "\u01a0"], ["\\M-F\\M-!", "\u01a1"], ["\\M-F\\M-\"", "\u01a2"], ["\\M-F\\M-#", "\u01a3"], ["\\M-F\\M-$", "\u01a4"], ["\\M-F\\M-%", "\u01a5"], ["\\M-F\\M-&", "\u01a6"], ["\\M-F\\M-'", "\u01a7"], ["\\M-F\\M-(", "\u01a8"], ["\\M-F\\M-)", "\u01a9"], ["\\M-F\\M-*", "\u01aa"], ["\\M-F\\M-+", "\u01ab"], ["\\M-F\\M-,", "\u01ac"], ["\\M-F\\M--", "\u01ad"], ["\\M-F\\M-.", "\u01ae"], ["\\M-F\\M-/", "\u01af"], ["\\M-F\\M-0", "\u01b0"], ["\\M-F\\M-1", "\u01b1"], ["\\M-F\\M-2", "\u01b2"], ["\\M-F\\M-3", "\u01b3"], ["\\M-F\\M-4", "\u01b4"], ["\\M-F\\M-5", "\u01b5"], ["\\M-F\\M-6", "\u01b6"], ["\\M-F\\M-7", "\u01b7"], ["\\M-F\\M-8", "\u01b8"], ["\\M-F\\M-9", "\u01b9"], ["\\M-F\\M-:", "\u01ba"], ["\\M-F\\M-;", "\u01bb"], ["\\M-F\\M-<", "\u01bc"], ["\\M-F\\M-=", "\u01bd"], ["\\M-F\\M->", "\u01be"], ["\\M-F\\M-?", "\u01bf"], ["\\M-G\\M^@", "\u01c0"], ["\\M-G\\M^A", "\u01c1"], ["\\M-G\\M^B", "\u01c2"], ["\\M-G\\M^C", "\u01c3"], ["\\M-G\\M^D", "\u01c4"], ["\\M-G\\M^E", "\u01c5"], ["\\M-G\\M^F", "\u01c6"], ["\\M-G\\M^G", "\u01c7"], ["\\M-G\\M^H", "\u01c8"], ["\\M-G\\M^I", "\u01c9"], ["\\M-G\\M^J", "\u01ca"], ["\\M-G\\M^K", "\u01cb"], ["\\M-G\\M^L", "\u01cc"], ["\\M-G\\M^M", "\u01cd"], ["\\M-G\\M^N", "\u01ce"], ["\\M-G\\M^O", "\u01cf"], ["\\M-G\\M^P", "\u01d0"], ["\\M-G\\M^Q", "\u01d1"], ["\\M-G\\M^R", "\u01d2"], ["\\M-G\\M^S", "\u01d3"], ["\\M-G\\M^T", "\u01d4"], ["\\M-G\\M^U", "\u01d5"], ["\\M-G\\M^V", "\u01d6"], ["\\M-G\\M^W", "\u01d7"], ["\\M-G\\M^X", "\u01d8"], ["\\M-G\\M^Y", "\u01d9"], ["\\M-G\\M^Z", "\u01da"], ["\\M-G\\M^[", "\u01db"], ["\\M-G\\M^\\", "\u01dc"], ["\\M-G\\M^]", "\u01dd"], ["\\M-G\\M^^", "\u01de"], ["\\M-G\\M^_", "\u01df"], ["\\M-G\\240", "\u01e0"], ["\\M-G\\M-!", "\u01e1"], ["\\M-G\\M-\"", "\u01e2"], ["\\M-G\\M-#", "\u01e3"], ["\\M-G\\M-$", "\u01e4"], ["\\M-G\\M-%", "\u01e5"], ["\\M-G\\M-&", "\u01e6"], ["\\M-G\\M-'", "\u01e7"], ["\\M-G\\M-(", "\u01e8"], ["\\M-G\\M-)", "\u01e9"], ["\\M-G\\M-*", "\u01ea"], ["\\M-G\\M-+", "\u01eb"], ["\\M-G\\M-,", "\u01ec"], ["\\M-G\\M--", "\u01ed"], ["\\M-G\\M-.", "\u01ee"], ["\\M-G\\M-/", "\u01ef"], ["\\M-G\\M-0", "\u01f0"], ["\\M-G\\M-1", "\u01f1"], ["\\M-G\\M-2", "\u01f2"], ["\\M-G\\M-3", "\u01f3"], ["\\M-G\\M-4", "\u01f4"], ["\\M-G\\M-5", "\u01f5"], ["\\M-G\\M-6", "\u01f6"], ["\\M-G\\M-7", "\u01f7"], ["\\M-G\\M-8", "\u01f8"], ["\\M-G\\M-9", "\u01f9"], ["\\M-G\\M-:", "\u01fa"], ["\\M-G\\M-;", "\u01fb"], ["\\M-G\\M-<", "\u01fc"], ["\\M-G\\M-=", "\u01fd"], ["\\M-G\\M->", "\u01fe"], ["\\M-G\\M-?", "\u01ff"], ["\\M-H\\M^@", "\u0200"], ["\\M-H\\M^A", "\u0201"], ["\\M-H\\M^B", "\u0202"], ["\\M-H\\M^C", "\u0203"], ["\\M-H\\M^D", "\u0204"], ["\\M-H\\M^E", "\u0205"], ["\\M-H\\M^F", "\u0206"], ["\\M-H\\M^G", "\u0207"], ["\\M-H\\M^H", "\u0208"], ["\\M-H\\M^I", "\u0209"], ["\\M-H\\M^J", "\u020a"], ["\\M-H\\M^K", "\u020b"], ["\\M-H\\M^L", "\u020c"], ["\\M-H\\M^M", "\u020d"], ["\\M-H\\M^N", "\u020e"], ["\\M-H\\M^O", "\u020f"], ["\\M-H\\M^P", "\u0210"], ["\\M-H\\M^Q", "\u0211"], ["\\M-H\\M^R", "\u0212"], ["\\M-H\\M^S", "\u0213"], ["\\M-H\\M^T", "\u0214"], ["\\M-H\\M^U", "\u0215"], ["\\M-H\\M^V", "\u0216"], ["\\M-H\\M^W", "\u0217"], ["\\M-H\\M^X", "\u0218"], ["\\M-H\\M^Y", "\u0219"], ["\\M-H\\M^Z", "\u021a"], ["\\M-H\\M^[", "\u021b"], ["\\M-H\\M^\\", "\u021c"], ["\\M-H\\M^]", "\u021d"], ["\\M-H\\M^^", "\u021e"], ["\\M-H\\M^_", "\u021f"], ["\\M-H\\240", "\u0220"], ["\\M-H\\M-!", "\u0221"], ["\\M-H\\M-\"", "\u0222"], ["\\M-H\\M-#", "\u0223"], ["\\M-H\\M-$", "\u0224"], ["\\M-H\\M-%", "\u0225"], ["\\M-H\\M-&", "\u0226"], ["\\M-H\\M-'", "\u0227"], ["\\M-H\\M-(", "\u0228"], ["\\M-H\\M-)", "\u0229"], ["\\M-H\\M-*", "\u022a"], ["\\M-H\\M-+", "\u022b"], ["\\M-H\\M-,", "\u022c"], ["\\M-H\\M--", "\u022d"], ["\\M-H\\M-.", "\u022e"], ["\\M-H\\M-/", "\u022f"], ["\\M-H\\M-0", "\u0230"], ["\\M-H\\M-1", "\u0231"], ["\\M-H\\M-2", "\u0232"], ["\\M-H\\M-3", "\u0233"], ["\\M-H\\M-4", "\u0234"], ["\\M-H\\M-5", "\u0235"], ["\\M-H\\M-6", "\u0236"], ["\\M-H\\M-7", "\u0237"], ["\\M-H\\M-8", "\u0238"], ["\\M-H\\M-9", "\u0239"], ["\\M-H\\M-:", "\u023a"], ["\\M-H\\M-;", "\u023b"], ["\\M-H\\M-<", "\u023c"], ["\\M-H\\M-=", "\u023d"], ["\\M-H\\M->", "\u023e"], ["\\M-H\\M-?", "\u023f"], ["\\M-I\\M^@", "\u0240"], ["\\M-I\\M^A", "\u0241"], ["\\M-I\\M^B", "\u0242"], ["\\M-I\\M^C", "\u0243"], ["\\M-I\\M^D", "\u0244"], ["\\M-I\\M^E", "\u0245"], ["\\M-I\\M^F", "\u0246"], ["\\M-I\\M^G", "\u0247"], ["\\M-I\\M^H", "\u0248"], ["\\M-I\\M^I", "\u0249"], ["\\M-I\\M^J", "\u024a"], ["\\M-I\\M^K", "\u024b"], ["\\M-I\\M^L", "\u024c"], ["\\M-I\\M^M", "\u024d"], ["\\M-I\\M^N", "\u024e"], ["\\M-I\\M^O", "\u024f"], ["\\M-M\\M-0", "\u0370"], ["\\M-M\\M-1", "\u0371"], ["\\M-M\\M-2", "\u0372"], ["\\M-M\\M-3", "\u0373"], ["\\M-M\\M-4", "\u0374"], ["\\M-M\\M-5", "\u0375"], ["\\M-M\\M-6", "\u0376"], ["\\M-M\\M-7", "\u0377"], ["\\M-M\\M-8", "\u0378"], ["\\M-M\\M-9", "\u0379"], ["\\M-M\\M-:", "\u037a"], ["\\M-M\\M-;", "\u037b"], ["\\M-M\\M-<", "\u037c"], ["\\M-M\\M-=", "\u037d"], ["\\M-M\\M->", "\u037e"], ["\\M-M\\M-?", "\u037f"], ["\\M-N\\M^@", "\u0380"], ["\\M-N\\M^A", "\u0381"], ["\\M-N\\M^B", "\u0382"], ["\\M-N\\M^C", "\u0383"], ["\\M-N\\M^D", "\u0384"], ["\\M-N\\M^E", "\u0385"], ["\\M-N\\M^F", "\u0386"], ["\\M-N\\M^G", "\u0387"], ["\\M-N\\M^H", "\u0388"], ["\\M-N\\M^I", "\u0389"], ["\\M-N\\M^J", "\u038a"], ["\\M-N\\M^K", "\u038b"], ["\\M-N\\M^L", "\u038c"], ["\\M-N\\M^M", "\u038d"], ["\\M-N\\M^N", "\u038e"], ["\\M-N\\M^O", "\u038f"], ["\\M-N\\M^P", "\u0390"], ["\\M-N\\M^Q", "\u0391"], ["\\M-N\\M^R", "\u0392"], ["\\M-N\\M^S", "\u0393"], ["\\M-N\\M^T", "\u0394"], ["\\M-N\\M^U", "\u0395"], ["\\M-N\\M^V", "\u0396"], ["\\M-N\\M^W", "\u0397"], ["\\M-N\\M^X", "\u0398"], ["\\M-N\\M^Y", "\u0399"], ["\\M-N\\M^Z", "\u039a"], ["\\M-N\\M^[", "\u039b"], ["\\M-N\\M^\\", "\u039c"], ["\\M-N\\M^]", "\u039d"], ["\\M-N\\M^^", "\u039e"], ["\\M-N\\M^_", "\u039f"], ["\\M-N\\240", "\u03a0"], ["\\M-N\\M-!", "\u03a1"], ["\\M-N\\M-\"", "\u03a2"], ["\\M-N\\M-#", "\u03a3"], ["\\M-N\\M-$", "\u03a4"], ["\\M-N\\M-%", "\u03a5"], ["\\M-N\\M-&", "\u03a6"], ["\\M-N\\M-'", "\u03a7"], ["\\M-N\\M-(", "\u03a8"], ["\\M-N\\M-)", "\u03a9"], ["\\M-N\\M-*", "\u03aa"], ["\\M-N\\M-+", "\u03ab"], ["\\M-N\\M-,", "\u03ac"], ["\\M-N\\M--", "\u03ad"], ["\\M-N\\M-.", "\u03ae"], ["\\M-N\\M-/", "\u03af"], ["\\M-N\\M-0", "\u03b0"], ["\\M-N\\M-1", "\u03b1"], ["\\M-N\\M-2", "\u03b2"], ["\\M-N\\M-3", "\u03b3"], ["\\M-N\\M-4", "\u03b4"], ["\\M-N\\M-5", "\u03b5"], ["\\M-N\\M-6", "\u03b6"], ["\\M-N\\M-7", "\u03b7"], ["\\M-N\\M-8", "\u03b8"], ["\\M-N\\M-9", "\u03b9"], ["\\M-N\\M-:", "\u03ba"], ["\\M-N\\M-;", "\u03bb"], ["\\M-N\\M-<", "\u03bc"], ["\\M-N\\M-=", "\u03bd"], ["\\M-N\\M->", "\u03be"], ["\\M-N\\M-?", "\u03bf"], ["\\M-O\\M^@", "\u03c0"], ["\\M-O\\M^A", "\u03c1"], ["\\M-O\\M^B", "\u03c2"], ["\\M-O\\M^C", "\u03c3"], ["\\M-O\\M^D", "\u03c4"], ["\\M-O\\M^E", "\u03c5"], ["\\M-O\\M^F", "\u03c6"], ["\\M-O\\M^G", "\u03c7"], ["\\M-O\\M^H", "\u03c8"], ["\\M-O\\M^I", "\u03c9"], ["\\M-O\\M^J", "\u03ca"], ["\\M-O\\M^K", "\u03cb"], ["\\M-O\\M^L", "\u03cc"], ["\\M-O\\M^M", "\u03cd"], ["\\M-O\\M^N", "\u03ce"], ["\\M-O\\M^O", "\u03cf"], ["\\M-O\\M^P", "\u03d0"], ["\\M-O\\M^Q", "\u03d1"], ["\\M-O\\M^R", "\u03d2"], ["\\M-O\\M^S", "\u03d3"], ["\\M-O\\M^T", "\u03d4"], ["\\M-O\\M^U", "\u03d5"], ["\\M-O\\M^V", "\u03d6"], ["\\M-O\\M^W", "\u03d7"], ["\\M-O\\M^X", "\u03d8"], ["\\M-O\\M^Y", "\u03d9"], ["\\M-O\\M^Z", "\u03da"], ["\\M-O\\M^[", "\u03db"], ["\\M-O\\M^\\", "\u03dc"], ["\\M-O\\M^]", "\u03dd"], ["\\M-O\\M^^", "\u03de"], ["\\M-O\\M^_", "\u03df"], ["\\M-O\\240", "\u03e0"], ["\\M-O\\M-!", "\u03e1"], ["\\M-O\\M-\"", "\u03e2"], ["\\M-O\\M-#", "\u03e3"], ["\\M-O\\M-$", "\u03e4"], ["\\M-O\\M-%", "\u03e5"], ["\\M-O\\M-&", "\u03e6"], ["\\M-O\\M-'", "\u03e7"], ["\\M-O\\M-(", "\u03e8"], ["\\M-O\\M-)", "\u03e9"], ["\\M-O\\M-*", "\u03ea"], ["\\M-O\\M-+", "\u03eb"], ["\\M-O\\M-,", "\u03ec"], ["\\M-O\\M--", "\u03ed"], ["\\M-O\\M-.", "\u03ee"], ["\\M-O\\M-/", "\u03ef"], ["\\M-O\\M-0", "\u03f0"], ["\\M-O\\M-1", "\u03f1"], ["\\M-O\\M-2", "\u03f2"], ["\\M-O\\M-3", "\u03f3"], ["\\M-O\\M-4", "\u03f4"], ["\\M-O\\M-5", "\u03f5"], ["\\M-O\\M-6", "\u03f6"], ["\\M-O\\M-7", "\u03f7"], ["\\M-O\\M-8", "\u03f8"], ["\\M-O\\M-9", "\u03f9"], ["\\M-O\\M-:", "\u03fa"], ["\\M-O\\M-;", "\u03fb"], ["\\M-O\\M-<", "\u03fc"], ["\\M-O\\M-=", "\u03fd"], ["\\M-O\\M->", "\u03fe"], ["\\M-O\\M-?", "\u03ff"], ["\\M-P\\M^@", "\u0400"], ["\\M-P\\M^A", "\u0401"], ["\\M-P\\M^B", "\u0402"], ["\\M-P\\M^C", "\u0403"], ["\\M-P\\M^D", "\u0404"], ["\\M-P\\M^E", "\u0405"], ["\\M-P\\M^F", "\u0406"], ["\\M-P\\M^G", "\u0407"], ["\\M-P\\M^H", "\u0408"], ["\\M-P\\M^I", "\u0409"], ["\\M-P\\M^J", "\u040a"], ["\\M-P\\M^K", "\u040b"], ["\\M-P\\M^L", "\u040c"], ["\\M-P\\M^M", "\u040d"], ["\\M-P\\M^N", "\u040e"], ["\\M-P\\M^O", "\u040f"], ["\\M-P\\M^P", "\u0410"], ["\\M-P\\M^Q", "\u0411"], ["\\M-P\\M^R", "\u0412"], ["\\M-P\\M^S", "\u0413"], ["\\M-P\\M^T", "\u0414"], ["\\M-P\\M^U", "\u0415"], ["\\M-P\\M^V", "\u0416"], ["\\M-P\\M^W", "\u0417"], ["\\M-P\\M^X", "\u0418"], ["\\M-P\\M^Y", "\u0419"], ["\\M-P\\M^Z", "\u041a"], ["\\M-P\\M^[", "\u041b"], ["\\M-P\\M^\\", "\u041c"], ["\\M-P\\M^]", "\u041d"], ["\\M-P\\M^^", "\u041e"], ["\\M-P\\M^_", "\u041f"], ["\\M-P\\240", "\u0420"], ["\\M-P\\M-!", "\u0421"], ["\\M-P\\M-\"", "\u0422"], ["\\M-P\\M-#", "\u0423"], ["\\M-P\\M-$", "\u0424"], ["\\M-P\\M-%", "\u0425"], ["\\M-P\\M-&", "\u0426"], ["\\M-P\\M-'", "\u0427"], ["\\M-P\\M-(", "\u0428"], ["\\M-P\\M-)", "\u0429"], ["\\M-P\\M-*", "\u042a"], ["\\M-P\\M-+", "\u042b"], ["\\M-P\\M-,", "\u042c"], ["\\M-P\\M--", "\u042d"], ["\\M-P\\M-.", "\u042e"], ["\\M-P\\M-/", "\u042f"], ["\\M-P\\M-0", "\u0430"], ["\\M-P\\M-1", "\u0431"], ["\\M-P\\M-2", "\u0432"], ["\\M-P\\M-3", "\u0433"], ["\\M-P\\M-4", "\u0434"], ["\\M-P\\M-5", "\u0435"], ["\\M-P\\M-6", "\u0436"], ["\\M-P\\M-7", "\u0437"], ["\\M-P\\M-8", "\u0438"], ["\\M-P\\M-9", "\u0439"], ["\\M-P\\M-:", "\u043a"], ["\\M-P\\M-;", "\u043b"], ["\\M-P\\M-<", "\u043c"], ["\\M-P\\M-=", "\u043d"], ["\\M-P\\M->", "\u043e"], ["\\M-P\\M-?", "\u043f"], ["\\M-Q\\M^@", "\u0440"], ["\\M-Q\\M^A", "\u0441"], ["\\M-Q\\M^B", "\u0442"], ["\\M-Q\\M^C", "\u0443"], ["\\M-Q\\M^D", "\u0444"], ["\\M-Q\\M^E", "\u0445"], ["\\M-Q\\M^F", "\u0446"], ["\\M-Q\\M^G", "\u0447"], ["\\M-Q\\M^H", "\u0448"], ["\\M-Q\\M^I", "\u0449"], ["\\M-Q\\M^J", "\u044a"], ["\\M-Q\\M^K", "\u044b"], ["\\M-Q\\M^L", "\u044c"], ["\\M-Q\\M^M", "\u044d"], ["\\M-Q\\M^N", "\u044e"], ["\\M-Q\\M^O", "\u044f"], ["\\M-Q\\M^P", "\u0450"], ["\\M-Q\\M^Q", "\u0451"], ["\\M-Q\\M^R", "\u0452"], ["\\M-Q\\M^S", "\u0453"], ["\\M-Q\\M^T", "\u0454"], ["\\M-Q\\M^U", "\u0455"], ["\\M-Q\\M^V", "\u0456"], ["\\M-Q\\M^W", "\u0457"], ["\\M-Q\\M^X", "\u0458"], ["\\M-Q\\M^Y", "\u0459"], ["\\M-Q\\M^Z", "\u045a"], ["\\M-Q\\M^[", "\u045b"], ["\\M-Q\\M^\\", "\u045c"], ["\\M-Q\\M^]", "\u045d"], ["\\M-Q\\M^^", "\u045e"], ["\\M-Q\\M^_", "\u045f"], ["\\M-Q\\240", "\u0460"], ["\\M-Q\\M-!", "\u0461"], ["\\M-Q\\M-\"", "\u0462"], ["\\M-Q\\M-#", "\u0463"], ["\\M-Q\\M-$", "\u0464"], ["\\M-Q\\M-%", "\u0465"], ["\\M-Q\\M-&", "\u0466"], ["\\M-Q\\M-'", "\u0467"], ["\\M-Q\\M-(", "\u0468"], ["\\M-Q\\M-)", "\u0469"], ["\\M-Q\\M-*", "\u046a"], ["\\M-Q\\M-+", "\u046b"], ["\\M-Q\\M-,", "\u046c"], ["\\M-Q\\M--", "\u046d"], ["\\M-Q\\M-.", "\u046e"], ["\\M-Q\\M-/", "\u046f"], ["\\M-Q\\M-0", "\u0470"], ["\\M-Q\\M-1", "\u0471"], ["\\M-Q\\M-2", "\u0472"], ["\\M-Q\\M-3", "\u0473"], ["\\M-Q\\M-4", "\u0474"], ["\\M-Q\\M-5", "\u0475"], ["\\M-Q\\M-6", "\u0476"], ["\\M-Q\\M-7", "\u0477"], ["\\M-Q\\M-8", "\u0478"], ["\\M-Q\\M-9", "\u0479"], ["\\M-Q\\M-:", "\u047a"], ["\\M-Q\\M-;", "\u047b"], ["\\M-Q\\M-<", "\u047c"], ["\\M-Q\\M-=", "\u047d"], ["\\M-Q\\M->", "\u047e"], ["\\M-Q\\M-?", "\u047f"], ["\\M-R\\M^@", "\u0480"], ["\\M-R\\M^A", "\u0481"], ["\\M-R\\M^B", "\u0482"], ["\\M-R\\M^C", "\u0483"], ["\\M-R\\M^D", "\u0484"], ["\\M-R\\M^E", "\u0485"], ["\\M-R\\M^F", "\u0486"], ["\\M-R\\M^G", "\u0487"], ["\\M-R\\M^H", "\u0488"], ["\\M-R\\M^I", "\u0489"], ["\\M-R\\M^J", "\u048a"], ["\\M-R\\M^K", "\u048b"], ["\\M-R\\M^L", "\u048c"], ["\\M-R\\M^M", "\u048d"], ["\\M-R\\M^N", "\u048e"], ["\\M-R\\M^O", "\u048f"], ["\\M-R\\M^P", "\u0490"], ["\\M-R\\M^Q", "\u0491"], ["\\M-R\\M^R", "\u0492"], ["\\M-R\\M^S", "\u0493"], ["\\M-R\\M^T", "\u0494"], ["\\M-R\\M^U", "\u0495"], ["\\M-R\\M^V", "\u0496"], ["\\M-R\\M^W", "\u0497"], ["\\M-R\\M^X", "\u0498"], ["\\M-R\\M^Y", "\u0499"], ["\\M-R\\M^Z", "\u049a"], ["\\M-R\\M^[", "\u049b"], ["\\M-R\\M^\\", "\u049c"], ["\\M-R\\M^]", "\u049d"], ["\\M-R\\M^^", "\u049e"], ["\\M-R\\M^_", "\u049f"], ["\\M-R\\240", "\u04a0"], ["\\M-R\\M-!", "\u04a1"], ["\\M-R\\M-\"", "\u04a2"], ["\\M-R\\M-#", "\u04a3"], ["\\M-R\\M-$", "\u04a4"], ["\\M-R\\M-%", "\u04a5"], ["\\M-R\\M-&", "\u04a6"], ["\\M-R\\M-'", "\u04a7"], ["\\M-R\\M-(", "\u04a8"], ["\\M-R\\M-)", "\u04a9"], ["\\M-R\\M-*", "\u04aa"], ["\\M-R\\M-+", "\u04ab"], ["\\M-R\\M-,", "\u04ac"], ["\\M-R\\M--", "\u04ad"], ["\\M-R\\M-.", "\u04ae"], ["\\M-R\\M-/", "\u04af"], ["\\M-R\\M-0", "\u04b0"], ["\\M-R\\M-1", "\u04b1"], ["\\M-R\\M-2", "\u04b2"], ["\\M-R\\M-3", "\u04b3"], ["\\M-R\\M-4", "\u04b4"], ["\\M-R\\M-5", "\u04b5"], ["\\M-R\\M-6", "\u04b6"], ["\\M-R\\M-7", "\u04b7"], ["\\M-R\\M-8", "\u04b8"], ["\\M-R\\M-9", "\u04b9"], ["\\M-R\\M-:", "\u04ba"], ["\\M-R\\M-;", "\u04bb"], ["\\M-R\\M-<", "\u04bc"], ["\\M-R\\M-=", "\u04bd"], ["\\M-R\\M->", "\u04be"], ["\\M-R\\M-?", "\u04bf"], ["\\M-S\\M^@", "\u04c0"], ["\\M-S\\M^A", "\u04c1"], ["\\M-S\\M^B", "\u04c2"], ["\\M-S\\M^C", "\u04c3"], ["\\M-S\\M^D", "\u04c4"], ["\\M-S\\M^E", "\u04c5"], ["\\M-S\\M^F", "\u04c6"], ["\\M-S\\M^G", "\u04c7"], ["\\M-S\\M^H", "\u04c8"], ["\\M-S\\M^I", "\u04c9"], ["\\M-S\\M^J", "\u04ca"], ["\\M-S\\M^K", "\u04cb"], ["\\M-S\\M^L", "\u04cc"], ["\\M-S\\M^M", "\u04cd"], ["\\M-S\\M^N", "\u04ce"], ["\\M-S\\M^O", "\u04cf"], ["\\M-S\\M^P", "\u04d0"], ["\\M-S\\M^Q", "\u04d1"], ["\\M-S\\M^R", "\u04d2"], ["\\M-S\\M^S", "\u04d3"], ["\\M-S\\M^T", "\u04d4"], ["\\M-S\\M^U", "\u04d5"], ["\\M-S\\M^V", "\u04d6"], ["\\M-S\\M^W", "\u04d7"], ["\\M-S\\M^X", "\u04d8"], ["\\M-S\\M^Y", "\u04d9"], ["\\M-S\\M^Z", "\u04da"], ["\\M-S\\M^[", "\u04db"], ["\\M-S\\M^\\", "\u04dc"], ["\\M-S\\M^]", "\u04dd"], ["\\M-S\\M^^", "\u04de"], ["\\M-S\\M^_", "\u04df"], ["\\M-S\\240", "\u04e0"], ["\\M-S\\M-!", "\u04e1"], ["\\M-S\\M-\"", "\u04e2"], ["\\M-S\\M-#", "\u04e3"], ["\\M-S\\M-$", "\u04e4"], ["\\M-S\\M-%", "\u04e5"], ["\\M-S\\M-&", "\u04e6"], ["\\M-S\\M-'", "\u04e7"], ["\\M-S\\M-(", "\u04e8"], ["\\M-S\\M-)", "\u04e9"], ["\\M-S\\M-*", "\u04ea"], ["\\M-S\\M-+", "\u04eb"], ["\\M-S\\M-,", "\u04ec"], ["\\M-S\\M--", "\u04ed"], ["\\M-S\\M-.", "\u04ee"], ["\\M-S\\M-/", "\u04ef"], ["\\M-S\\M-0", "\u04f0"], ["\\M-S\\M-1", "\u04f1"], ["\\M-S\\M-2", "\u04f2"], ["\\M-S\\M-3", "\u04f3"], ["\\M-S\\M-4", "\u04f4"], ["\\M-S\\M-5", "\u04f5"], ["\\M-S\\M-6", "\u04f6"], ["\\M-S\\M-7", "\u04f7"], ["\\M-S\\M-8", "\u04f8"], ["\\M-S\\M-9", "\u04f9"], ["\\M-S\\M-:", "\u04fa"], ["\\M-S\\M-;", "\u04fb"], ["\\M-S\\M-<", "\u04fc"], ["\\M-S\\M-=", "\u04fd"], ["\\M-S\\M->", "\u04fe"], ["\\M-S\\M-?", "\u04ff"], ["\\M-T\\M^@", "\u0500"], ["\\M-T\\M^A", "\u0501"], ["\\M-T\\M^B", "\u0502"], ["\\M-T\\M^C", "\u0503"], ["\\M-T\\M^D", "\u0504"], ["\\M-T\\M^E", "\u0505"], ["\\M-T\\M^F", "\u0506"], ["\\M-T\\M^G", "\u0507"], ["\\M-T\\M^H", "\u0508"], ["\\M-T\\M^I", "\u0509"], ["\\M-T\\M^J", "\u050a"], ["\\M-T\\M^K", "\u050b"], ["\\M-T\\M^L", "\u050c"], ["\\M-T\\M^M", "\u050d"], ["\\M-T\\M^N", "\u050e"], ["\\M-T\\M^O", "\u050f"], ["\\M-T\\M^P", "\u0510"], ["\\M-T\\M^Q", "\u0511"], ["\\M-T\\M^R", "\u0512"], ["\\M-T\\M^S", "\u0513"], ["\\M-T\\M^T", "\u0514"], ["\\M-T\\M^U", "\u0515"], ["\\M-T\\M^V", "\u0516"], ["\\M-T\\M^W", "\u0517"], ["\\M-T\\M^X", "\u0518"], ["\\M-T\\M^Y", "\u0519"], ["\\M-T\\M^Z", "\u051a"], ["\\M-T\\M^[", "\u051b"], ["\\M-T\\M^\\", "\u051c"], ["\\M-T\\M^]", "\u051d"], ["\\M-T\\M^^", "\u051e"], ["\\M-T\\M^_", "\u051f"], ["\\M-T\\240", "\u0520"], ["\\M-T\\M-!", "\u0521"], ["\\M-T\\M-\"", "\u0522"], ["\\M-T\\M-#", "\u0523"], ["\\M-T\\M-$", "\u0524"], ["\\M-T\\M-%", "\u0525"], ["\\M-T\\M-&", "\u0526"], ["\\M-T\\M-'", "\u0527"], ["\\M-T\\M-(", "\u0528"], ["\\M-T\\M-)", "\u0529"], ["\\M-T\\M-*", "\u052a"], ["\\M-T\\M-+", "\u052b"], ["\\M-T\\M-,", "\u052c"], ["\\M-T\\M--", "\u052d"], ["\\M-T\\M-.", "\u052e"], ["\\M-T\\M-/", "\u052f"], ["\\M-V\\M^P", "\u0590"], ["\\M-V\\M^Q", "\u0591"], ["\\M-V\\M^R", "\u0592"], ["\\M-V\\M^S", "\u0593"], ["\\M-V\\M^T", "\u0594"], ["\\M-V\\M^U", "\u0595"], ["\\M-V\\M^V", "\u0596"], ["\\M-V\\M^W", "\u0597"], ["\\M-V\\M^X", "\u0598"], ["\\M-V\\M^Y", "\u0599"], ["\\M-V\\M^Z", "\u059a"], ["\\M-V\\M^[", "\u059b"], ["\\M-V\\M^\\", "\u059c"], ["\\M-V\\M^]", "\u059d"], ["\\M-V\\M^^", "\u059e"], ["\\M-V\\M^_", "\u059f"], ["\\M-V\\240", "\u05a0"], ["\\M-V\\M-!", "\u05a1"], ["\\M-V\\M-\"", "\u05a2"], ["\\M-V\\M-#", "\u05a3"], ["\\M-V\\M-$", "\u05a4"], ["\\M-V\\M-%", "\u05a5"], ["\\M-V\\M-&", "\u05a6"], ["\\M-V\\M-'", "\u05a7"], ["\\M-V\\M-(", "\u05a8"], ["\\M-V\\M-)", "\u05a9"], ["\\M-V\\M-*", "\u05aa"], ["\\M-V\\M-+", "\u05ab"], ["\\M-V\\M-,", "\u05ac"], ["\\M-V\\M--", "\u05ad"], ["\\M-V\\M-.", "\u05ae"], ["\\M-V\\M-/", "\u05af"], ["\\M-V\\M-0", "\u05b0"], ["\\M-V\\M-1", "\u05b1"], ["\\M-V\\M-2", "\u05b2"], ["\\M-V\\M-3", "\u05b3"], ["\\M-V\\M-4", "\u05b4"], ["\\M-V\\M-5", "\u05b5"], ["\\M-V\\M-6", "\u05b6"], ["\\M-V\\M-7", "\u05b7"], ["\\M-V\\M-8", "\u05b8"], ["\\M-V\\M-9", "\u05b9"], ["\\M-V\\M-:", "\u05ba"], ["\\M-V\\M-;", "\u05bb"], ["\\M-V\\M-<", "\u05bc"], ["\\M-V\\M-=", "\u05bd"], ["\\M-V\\M->", "\u05be"], ["\\M-V\\M-?", "\u05bf"], ["\\M-W\\M^@", "\u05c0"], ["\\M-W\\M^A", "\u05c1"], ["\\M-W\\M^B", "\u05c2"], ["\\M-W\\M^C", "\u05c3"], ["\\M-W\\M^D", "\u05c4"], ["\\M-W\\M^E", "\u05c5"], ["\\M-W\\M^F", "\u05c6"], ["\\M-W\\M^G", "\u05c7"], ["\\M-W\\M^H", "\u05c8"], ["\\M-W\\M^I", "\u05c9"], ["\\M-W\\M^J", "\u05ca"], ["\\M-W\\M^K", "\u05cb"], ["\\M-W\\M^L", "\u05cc"], ["\\M-W\\M^M", "\u05cd"], ["\\M-W\\M^N", "\u05ce"], ["\\M-W\\M^O", "\u05cf"], ["\\M-W\\M^P", "\u05d0"], ["\\M-W\\M^Q", "\u05d1"], ["\\M-W\\M^R", "\u05d2"], ["\\M-W\\M^S", "\u05d3"], ["\\M-W\\M^T", "\u05d4"], ["\\M-W\\M^U", "\u05d5"], ["\\M-W\\M^V", "\u05d6"], ["\\M-W\\M^W", "\u05d7"], ["\\M-W\\M^X", "\u05d8"], ["\\M-W\\M^Y", "\u05d9"], ["\\M-W\\M^Z", "\u05da"], ["\\M-W\\M^[", "\u05db"], ["\\M-W\\M^\\", "\u05dc"], ["\\M-W\\M^]", "\u05dd"], ["\\M-W\\M^^", "\u05de"], ["\\M-W\\M^_", "\u05df"], ["\\M-W\\240", "\u05e0"], ["\\M-W\\M-!", "\u05e1"], ["\\M-W\\M-\"", "\u05e2"], ["\\M-W\\M-#", "\u05e3"], ["\\M-W\\M-$", "\u05e4"], ["\\M-W\\M-%", "\u05e5"], ["\\M-W\\M-&", "\u05e6"], ["\\M-W\\M-'", "\u05e7"], ["\\M-W\\M-(", "\u05e8"], ["\\M-W\\M-)", "\u05e9"], ["\\M-W\\M-*", "\u05ea"], ["\\M-W\\M-+", "\u05eb"], ["\\M-W\\M-,", "\u05ec"], ["\\M-W\\M--", "\u05ed"], ["\\M-W\\M-.", "\u05ee"], ["\\M-W\\M-/", "\u05ef"], ["\\M-W\\M-0", "\u05f0"], ["\\M-W\\M-1", "\u05f1"], ["\\M-W\\M-2", "\u05f2"], ["\\M-W\\M-3", "\u05f3"], ["\\M-W\\M-4", "\u05f4"], ["\\M-W\\M-5", "\u05f5"], ["\\M-W\\M-6", "\u05f6"], ["\\M-W\\M-7", "\u05f7"], ["\\M-W\\M-8", "\u05f8"], ["\\M-W\\M-9", "\u05f9"], ["\\M-W\\M-:", "\u05fa"], ["\\M-W\\M-;", "\u05fb"], ["\\M-W\\M-<", "\u05fc"], ["\\M-W\\M-=", "\u05fd"], ["\\M-W\\M->", "\u05fe"], ["\\M-W\\M-?", "\u05ff"], ["\\M-X\\M^@", "\u0600"], ["\\M-X\\M^A", "\u0601"], ["\\M-X\\M^B", "\u0602"], ["\\M-X\\M^C", "\u0603"], ["\\M-X\\M^D", "\u0604"], ["\\M-X\\M^E", "\u0605"], ["\\M-X\\M^F", "\u0606"], ["\\M-X\\M^G", "\u0607"], ["\\M-X\\M^H", "\u0608"], ["\\M-X\\M^I", "\u0609"], ["\\M-X\\M^J", "\u060a"], ["\\M-X\\M^K", "\u060b"], ["\\M-X\\M^L", "\u060c"], ["\\M-X\\M^M", "\u060d"], ["\\M-X\\M^N", "\u060e"], ["\\M-X\\M^O", "\u060f"], ["\\M-X\\M^P", "\u0610"], ["\\M-X\\M^Q", "\u0611"], ["\\M-X\\M^R", "\u0612"], ["\\M-X\\M^S", "\u0613"], ["\\M-X\\M^T", "\u0614"], ["\\M-X\\M^U", "\u0615"], ["\\M-X\\M^V", "\u0616"], ["\\M-X\\M^W", "\u0617"], ["\\M-X\\M^X", "\u0618"], ["\\M-X\\M^Y", "\u0619"], ["\\M-X\\M^Z", "\u061a"], ["\\M-X\\M^[", "\u061b"], ["\\M-X\\M^\\", "\u061c"], ["\\M-X\\M^]", "\u061d"], ["\\M-X\\M^^", "\u061e"], ["\\M-X\\M^_", "\u061f"], ["\\M-X\\240", "\u0620"], ["\\M-X\\M-!", "\u0621"], ["\\M-X\\M-\"", "\u0622"], ["\\M-X\\M-#", "\u0623"], ["\\M-X\\M-$", "\u0624"], ["\\M-X\\M-%", "\u0625"], ["\\M-X\\M-&", "\u0626"], ["\\M-X\\M-'", "\u0627"], ["\\M-X\\M-(", "\u0628"], ["\\M-X\\M-)", "\u0629"], ["\\M-X\\M-*", "\u062a"], ["\\M-X\\M-+", "\u062b"], ["\\M-X\\M-,", "\u062c"], ["\\M-X\\M--", "\u062d"], ["\\M-X\\M-.", "\u062e"], ["\\M-X\\M-/", "\u062f"], ["\\M-X\\M-0", "\u0630"], ["\\M-X\\M-1", "\u0631"], ["\\M-X\\M-2", "\u0632"], ["\\M-X\\M-3", "\u0633"], ["\\M-X\\M-4", "\u0634"], ["\\M-X\\M-5", "\u0635"], ["\\M-X\\M-6", "\u0636"], ["\\M-X\\M-7", "\u0637"], ["\\M-X\\M-8", "\u0638"], ["\\M-X\\M-9", "\u0639"], ["\\M-X\\M-:", "\u063a"], ["\\M-X\\M-;", "\u063b"], ["\\M-X\\M-<", "\u063c"], ["\\M-X\\M-=", "\u063d"], ["\\M-X\\M->", "\u063e"], ["\\M-X\\M-?", "\u063f"], ["\\M-Y\\M^@", "\u0640"], ["\\M-Y\\M^A", "\u0641"], ["\\M-Y\\M^B", "\u0642"], ["\\M-Y\\M^C", "\u0643"], ["\\M-Y\\M^D", "\u0644"], ["\\M-Y\\M^E", "\u0645"], ["\\M-Y\\M^F", "\u0646"], ["\\M-Y\\M^G", "\u0647"], ["\\M-Y\\M^H", "\u0648"], ["\\M-Y\\M^I", "\u0649"], ["\\M-Y\\M^J", "\u064a"], ["\\M-Y\\M^K", "\u064b"], ["\\M-Y\\M^L", "\u064c"], ["\\M-Y\\M^M", "\u064d"], ["\\M-Y\\M^N", "\u064e"], ["\\M-Y\\M^O", "\u064f"], ["\\M-Y\\M^P", "\u0650"], ["\\M-Y\\M^Q", "\u0651"], ["\\M-Y\\M^R", "\u0652"], ["\\M-Y\\M^S", "\u0653"], ["\\M-Y\\M^T", "\u0654"], ["\\M-Y\\M^U", "\u0655"], ["\\M-Y\\M^V", "\u0656"], ["\\M-Y\\M^W", "\u0657"], ["\\M-Y\\M^X", "\u0658"], ["\\M-Y\\M^Y", "\u0659"], ["\\M-Y\\M^Z", "\u065a"], ["\\M-Y\\M^[", "\u065b"], ["\\M-Y\\M^\\", "\u065c"], ["\\M-Y\\M^]", "\u065d"], ["\\M-Y\\M^^", "\u065e"], ["\\M-Y\\M^_", "\u065f"], ["\\M-Y\\240", "\u0660"], ["\\M-Y\\M-!", "\u0661"], ["\\M-Y\\M-\"", "\u0662"], ["\\M-Y\\M-#", "\u0663"], ["\\M-Y\\M-$", "\u0664"], ["\\M-Y\\M-%", "\u0665"], ["\\M-Y\\M-&", "\u0666"], ["\\M-Y\\M-'", "\u0667"], ["\\M-Y\\M-(", "\u0668"], ["\\M-Y\\M-)", "\u0669"], ["\\M-Y\\M-*", "\u066a"], ["\\M-Y\\M-+", "\u066b"], ["\\M-Y\\M-,", "\u066c"], ["\\M-Y\\M--", "\u066d"], ["\\M-Y\\M-.", "\u066e"], ["\\M-Y\\M-/", "\u066f"], ["\\M-Y\\M-0", "\u0670"], ["\\M-Y\\M-1", "\u0671"], ["\\M-Y\\M-2", "\u0672"], ["\\M-Y\\M-3", "\u0673"], ["\\M-Y\\M-4", "\u0674"], ["\\M-Y\\M-5", "\u0675"], ["\\M-Y\\M-6", "\u0676"], ["\\M-Y\\M-7", "\u0677"], ["\\M-Y\\M-8", "\u0678"], ["\\M-Y\\M-9", "\u0679"], ["\\M-Y\\M-:", "\u067a"], ["\\M-Y\\M-;", "\u067b"], ["\\M-Y\\M-<", "\u067c"], ["\\M-Y\\M-=", "\u067d"], ["\\M-Y\\M->", "\u067e"], ["\\M-Y\\M-?", "\u067f"], ["\\M-Z\\M^@", "\u0680"], ["\\M-Z\\M^A", "\u0681"], ["\\M-Z\\M^B", "\u0682"], ["\\M-Z\\M^C", "\u0683"], ["\\M-Z\\M^D", "\u0684"], ["\\M-Z\\M^E", "\u0685"], ["\\M-Z\\M^F", "\u0686"], ["\\M-Z\\M^G", "\u0687"], ["\\M-Z\\M^H", "\u0688"], ["\\M-Z\\M^I", "\u0689"], ["\\M-Z\\M^J", "\u068a"], ["\\M-Z\\M^K", "\u068b"], ["\\M-Z\\M^L", "\u068c"], ["\\M-Z\\M^M", "\u068d"], ["\\M-Z\\M^N", "\u068e"], ["\\M-Z\\M^O", "\u068f"], ["\\M-Z\\M^P", "\u0690"], ["\\M-Z\\M^Q", "\u0691"], ["\\M-Z\\M^R", "\u0692"], ["\\M-Z\\M^S", "\u0693"], ["\\M-Z\\M^T", "\u0694"], ["\\M-Z\\M^U", "\u0695"], ["\\M-Z\\M^V", "\u0696"], ["\\M-Z\\M^W", "\u0697"], ["\\M-Z\\M^X", "\u0698"], ["\\M-Z\\M^Y", "\u0699"], ["\\M-Z\\M^Z", "\u069a"], ["\\M-Z\\M^[", "\u069b"], ["\\M-Z\\M^\\", "\u069c"], ["\\M-Z\\M^]", "\u069d"], ["\\M-Z\\M^^", "\u069e"], ["\\M-Z\\M^_", "\u069f"], ["\\M-Z\\240", "\u06a0"], ["\\M-Z\\M-!", "\u06a1"], ["\\M-Z\\M-\"", "\u06a2"], ["\\M-Z\\M-#", "\u06a3"], ["\\M-Z\\M-$", "\u06a4"], ["\\M-Z\\M-%", "\u06a5"], ["\\M-Z\\M-&", "\u06a6"], ["\\M-Z\\M-'", "\u06a7"], ["\\M-Z\\M-(", "\u06a8"], ["\\M-Z\\M-)", "\u06a9"], ["\\M-Z\\M-*", "\u06aa"], ["\\M-Z\\M-+", "\u06ab"], ["\\M-Z\\M-,", "\u06ac"], ["\\M-Z\\M--", "\u06ad"], ["\\M-Z\\M-.", "\u06ae"], ["\\M-Z\\M-/", "\u06af"], ["\\M-Z\\M-0", "\u06b0"], ["\\M-Z\\M-1", "\u06b1"], ["\\M-Z\\M-2", "\u06b2"], ["\\M-Z\\M-3", "\u06b3"], ["\\M-Z\\M-4", "\u06b4"], ["\\M-Z\\M-5", "\u06b5"], ["\\M-Z\\M-6", "\u06b6"], ["\\M-Z\\M-7", "\u06b7"], ["\\M-Z\\M-8", "\u06b8"], ["\\M-Z\\M-9", "\u06b9"], ["\\M-Z\\M-:", "\u06ba"], ["\\M-Z\\M-;", "\u06bb"], ["\\M-Z\\M-<", "\u06bc"], ["\\M-Z\\M-=", "\u06bd"], ["\\M-Z\\M->", "\u06be"], ["\\M-Z\\M-?", "\u06bf"], ["\\M-[\\M^@", "\u06c0"], ["\\M-[\\M^A", "\u06c1"], ["\\M-[\\M^B", "\u06c2"], ["\\M-[\\M^C", "\u06c3"], ["\\M-[\\M^D", "\u06c4"], ["\\M-[\\M^E", "\u06c5"], ["\\M-[\\M^F", "\u06c6"], ["\\M-[\\M^G", "\u06c7"], ["\\M-[\\M^H", "\u06c8"], ["\\M-[\\M^I", "\u06c9"], ["\\M-[\\M^J", "\u06ca"], ["\\M-[\\M^K", "\u06cb"], ["\\M-[\\M^L", "\u06cc"], ["\\M-[\\M^M", "\u06cd"], ["\\M-[\\M^N", "\u06ce"], ["\\M-[\\M^O", "\u06cf"], ["\\M-[\\M^P", "\u06d0"], ["\\M-[\\M^Q", "\u06d1"], ["\\M-[\\M^R", "\u06d2"], ["\\M-[\\M^S", "\u06d3"], ["\\M-[\\M^T", "\u06d4"], ["\\M-[\\M^U", "\u06d5"], ["\\M-[\\M^V", "\u06d6"], ["\\M-[\\M^W", "\u06d7"], ["\\M-[\\M^X", "\u06d8"], ["\\M-[\\M^Y", "\u06d9"], ["\\M-[\\M^Z", "\u06da"], ["\\M-[\\M^[", "\u06db"], ["\\M-[\\M^\\", "\u06dc"], ["\\M-[\\M^]", "\u06dd"], ["\\M-[\\M^^", "\u06de"], ["\\M-[\\M^_", "\u06df"], ["\\M-[\\240", "\u06e0"], ["\\M-[\\M-!", "\u06e1"], ["\\M-[\\M-\"", "\u06e2"], ["\\M-[\\M-#", "\u06e3"], ["\\M-[\\M-$", "\u06e4"], ["\\M-[\\M-%", "\u06e5"], ["\\M-[\\M-&", "\u06e6"], ["\\M-[\\M-'", "\u06e7"], ["\\M-[\\M-(", "\u06e8"], ["\\M-[\\M-)", "\u06e9"], ["\\M-[\\M-*", "\u06ea"], ["\\M-[\\M-+", "\u06eb"], ["\\M-[\\M-,", "\u06ec"], ["\\M-[\\M--", "\u06ed"], ["\\M-[\\M-.", "\u06ee"], ["\\M-[\\M-/", "\u06ef"], ["\\M-[\\M-0", "\u06f0"], ["\\M-[\\M-1", "\u06f1"], ["\\M-[\\M-2", "\u06f2"], ["\\M-[\\M-3", "\u06f3"], ["\\M-[\\M-4", "\u06f4"], ["\\M-[\\M-5", "\u06f5"], ["\\M-[\\M-6", "\u06f6"], ["\\M-[\\M-7", "\u06f7"], ["\\M-[\\M-8", "\u06f8"], ["\\M-[\\M-9", "\u06f9"], ["\\M-[\\M-:", "\u06fa"], ["\\M-[\\M-;", "\u06fb"], ["\\M-[\\M-<", "\u06fc"], ["\\M-[\\M-=", "\u06fd"], ["\\M-[\\M->", "\u06fe"], ["\\M-[\\M-?", "\u06ff"], ["\\M-b\\M-:\\M^@", "\u2e80"], ["\\M-b\\M-:\\M^A", "\u2e81"], ["\\M-b\\M-:\\M^B", "\u2e82"], ["\\M-b\\M-:\\M^C", "\u2e83"], ["\\M-b\\M-:\\M^D", "\u2e84"], ["\\M-b\\M-:\\M^E", "\u2e85"], ["\\M-b\\M-:\\M^F", "\u2e86"], ["\\M-b\\M-:\\M^G", "\u2e87"], ["\\M-b\\M-:\\M^H", "\u2e88"], ["\\M-b\\M-:\\M^I", "\u2e89"], ["\\M-b\\M-:\\M^J", "\u2e8a"], ["\\M-b\\M-:\\M^K", "\u2e8b"], ["\\M-b\\M-:\\M^L", "\u2e8c"], ["\\M-b\\M-:\\M^M", "\u2e8d"], ["\\M-b\\M-:\\M^N", "\u2e8e"], ["\\M-b\\M-:\\M^O", "\u2e8f"], ["\\M-b\\M-:\\M^P", "\u2e90"], ["\\M-b\\M-:\\M^Q", "\u2e91"], ["\\M-b\\M-:\\M^R", "\u2e92"], ["\\M-b\\M-:\\M^S", "\u2e93"], ["\\M-b\\M-:\\M^T", "\u2e94"], ["\\M-b\\M-:\\M^U", "\u2e95"], ["\\M-b\\M-:\\M^V", "\u2e96"], ["\\M-b\\M-:\\M^W", "\u2e97"], ["\\M-b\\M-:\\M^X", "\u2e98"], ["\\M-b\\M-:\\M^Y", "\u2e99"], ["\\M-b\\M-:\\M^Z", "\u2e9a"], ["\\M-b\\M-:\\M^[", "\u2e9b"], ["\\M-b\\M-:\\M^\\", "\u2e9c"], ["\\M-b\\M-:\\M^]", "\u2e9d"], ["\\M-b\\M-:\\M^^", "\u2e9e"], ["\\M-b\\M-:\\M^_", "\u2e9f"], ["\\M-b\\M-:\\240", "\u2ea0"], ["\\M-b\\M-:\\M-!", "\u2ea1"], ["\\M-b\\M-:\\M-\"", "\u2ea2"], ["\\M-b\\M-:\\M-#", "\u2ea3"], ["\\M-b\\M-:\\M-$", "\u2ea4"], ["\\M-b\\M-:\\M-%", "\u2ea5"], ["\\M-b\\M-:\\M-&", "\u2ea6"], ["\\M-b\\M-:\\M-'", "\u2ea7"], ["\\M-b\\M-:\\M-(", "\u2ea8"], ["\\M-b\\M-:\\M-)", "\u2ea9"], ["\\M-b\\M-:\\M-*", "\u2eaa"], ["\\M-b\\M-:\\M-+", "\u2eab"], ["\\M-b\\M-:\\M-,", "\u2eac"], ["\\M-b\\M-:\\M--", "\u2ead"], ["\\M-b\\M-:\\M-.", "\u2eae"], ["\\M-b\\M-:\\M-/", "\u2eaf"], ["\\M-b\\M-:\\M-0", "\u2eb0"], ["\\M-b\\M-:\\M-1", "\u2eb1"], ["\\M-b\\M-:\\M-2", "\u2eb2"], ["\\M-b\\M-:\\M-3", "\u2eb3"], ["\\M-b\\M-:\\M-4", "\u2eb4"], ["\\M-b\\M-:\\M-5", "\u2eb5"], ["\\M-b\\M-:\\M-6", "\u2eb6"], ["\\M-b\\M-:\\M-7", "\u2eb7"], ["\\M-b\\M-:\\M-8", "\u2eb8"], ["\\M-b\\M-:\\M-9", "\u2eb9"], ["\\M-b\\M-:\\M-:", "\u2eba"], ["\\M-b\\M-:\\M-;", "\u2ebb"], ["\\M-b\\M-:\\M-<", "\u2ebc"], ["\\M-b\\M-:\\M-=", "\u2ebd"], ["\\M-b\\M-:\\M->", "\u2ebe"], ["\\M-b\\M-:\\M-?", "\u2ebf"], ["\\M-b\\M-;\\M^@", "\u2ec0"], ["\\M-b\\M-;\\M^A", "\u2ec1"], ["\\M-b\\M-;\\M^B", "\u2ec2"], ["\\M-b\\M-;\\M^C", "\u2ec3"], ["\\M-b\\M-;\\M^D", "\u2ec4"], ["\\M-b\\M-;\\M^E", "\u2ec5"], ["\\M-b\\M-;\\M^F", "\u2ec6"], ["\\M-b\\M-;\\M^G", "\u2ec7"], ["\\M-b\\M-;\\M^H", "\u2ec8"], ["\\M-b\\M-;\\M^I", "\u2ec9"], ["\\M-b\\M-;\\M^J", "\u2eca"], ["\\M-b\\M-;\\M^K", "\u2ecb"], ["\\M-b\\M-;\\M^L", "\u2ecc"], ["\\M-b\\M-;\\M^M", "\u2ecd"], ["\\M-b\\M-;\\M^N", "\u2ece"], ["\\M-b\\M-;\\M^O", "\u2ecf"], ["\\M-b\\M-;\\M^P", "\u2ed0"], ["\\M-b\\M-;\\M^Q", "\u2ed1"], ["\\M-b\\M-;\\M^R", "\u2ed2"], ["\\M-b\\M-;\\M^S", "\u2ed3"], ["\\M-b\\M-;\\M^T", "\u2ed4"], ["\\M-b\\M-;\\M^U", "\u2ed5"], ["\\M-b\\M-;\\M^V", "\u2ed6"], ["\\M-b\\M-;\\M^W", "\u2ed7"], ["\\M-b\\M-;\\M^X", "\u2ed8"], ["\\M-b\\M-;\\M^Y", "\u2ed9"], ["\\M-b\\M-;\\M^Z", "\u2eda"], ["\\M-b\\M-;\\M^[", "\u2edb"], ["\\M-b\\M-;\\M^\\", "\u2edc"], ["\\M-b\\M-;\\M^]", "\u2edd"], ["\\M-b\\M-;\\M^^", "\u2ede"], ["\\M-b\\M-;\\M^_", "\u2edf"], ["\\M-b\\M-;\\240", "\u2ee0"], ["\\M-b\\M-;\\M-!", "\u2ee1"], ["\\M-b\\M-;\\M-\"", "\u2ee2"], ["\\M-b\\M-;\\M-#", "\u2ee3"], ["\\M-b\\M-;\\M-$", "\u2ee4"], ["\\M-b\\M-;\\M-%", "\u2ee5"], ["\\M-b\\M-;\\M-&", "\u2ee6"], ["\\M-b\\M-;\\M-'", "\u2ee7"], ["\\M-b\\M-;\\M-(", "\u2ee8"], ["\\M-b\\M-;\\M-)", "\u2ee9"], ["\\M-b\\M-;\\M-*", "\u2eea"], ["\\M-b\\M-;\\M-+", "\u2eeb"], ["\\M-b\\M-;\\M-,", "\u2eec"], ["\\M-b\\M-;\\M--", "\u2eed"], ["\\M-b\\M-;\\M-.", "\u2eee"], ["\\M-b\\M-;\\M-/", "\u2eef"], ["\\M-b\\M-;\\M-0", "\u2ef0"], ["\\M-b\\M-;\\M-1", "\u2ef1"], ["\\M-b\\M-;\\M-2", "\u2ef2"], ["\\M-b\\M-;\\M-3", "\u2ef3"], ["\\M-b\\M-;\\M-4", "\u2ef4"], ["\\M-b\\M-;\\M-5", "\u2ef5"], ["\\M-b\\M-;\\M-6", "\u2ef6"], ["\\M-b\\M-;\\M-7", "\u2ef7"], ["\\M-b\\M-;\\M-8", "\u2ef8"], ["\\M-b\\M-;\\M-9", "\u2ef9"], ["\\M-b\\M-;\\M-:", "\u2efa"], ["\\M-b\\M-;\\M-;", "\u2efb"], ["\\M-b\\M-;\\M-<", "\u2efc"], ["\\M-b\\M-;\\M-=", "\u2efd"], ["\\M-b\\M-;\\M->", "\u2efe"], ["\\M-b\\M-;\\M-?", "\u2eff"], ["\\M-c\\M^A\\M^@", "\u3040"], ["\\M-c\\M^A\\M^A", "\u3041"], ["\\M-c\\M^A\\M^B", "\u3042"], ["\\M-c\\M^A\\M^C", "\u3043"], ["\\M-c\\M^A\\M^D", "\u3044"], ["\\M-c\\M^A\\M^E", "\u3045"], ["\\M-c\\M^A\\M^F", "\u3046"], ["\\M-c\\M^A\\M^G", "\u3047"], ["\\M-c\\M^A\\M^H", "\u3048"], ["\\M-c\\M^A\\M^I", "\u3049"], ["\\M-c\\M^A\\M^J", "\u304a"], ["\\M-c\\M^A\\M^K", "\u304b"], ["\\M-c\\M^A\\M^L", "\u304c"], ["\\M-c\\M^A\\M^M", "\u304d"], ["\\M-c\\M^A\\M^N", "\u304e"], ["\\M-c\\M^A\\M^O", "\u304f"], ["\\M-c\\M^A\\M^P", "\u3050"], ["\\M-c\\M^A\\M^Q", "\u3051"], ["\\M-c\\M^A\\M^R", "\u3052"], ["\\M-c\\M^A\\M^S", "\u3053"], ["\\M-c\\M^A\\M^T", "\u3054"], ["\\M-c\\M^A\\M^U", "\u3055"], ["\\M-c\\M^A\\M^V", "\u3056"], ["\\M-c\\M^A\\M^W", "\u3057"], ["\\M-c\\M^A\\M^X", "\u3058"], ["\\M-c\\M^A\\M^Y", "\u3059"], ["\\M-c\\M^A\\M^Z", "\u305a"], ["\\M-c\\M^A\\M^[", "\u305b"], ["\\M-c\\M^A\\M^\\", "\u305c"], ["\\M-c\\M^A\\M^]", "\u305d"], ["\\M-c\\M^A\\M^^", "\u305e"], ["\\M-c\\M^A\\M^_", "\u305f"], ["\\M-c\\M^A\\240", "\u3060"], ["\\M-c\\M^A\\M-!", "\u3061"], ["\\M-c\\M^A\\M-\"", "\u3062"], ["\\M-c\\M^A\\M-#", "\u3063"], ["\\M-c\\M^A\\M-$", "\u3064"], ["\\M-c\\M^A\\M-%", "\u3065"], ["\\M-c\\M^A\\M-&", "\u3066"], ["\\M-c\\M^A\\M-'", "\u3067"], ["\\M-c\\M^A\\M-(", "\u3068"], ["\\M-c\\M^A\\M-)", "\u3069"], ["\\M-c\\M^A\\M-*", "\u306a"], ["\\M-c\\M^A\\M-+", "\u306b"], ["\\M-c\\M^A\\M-,", "\u306c"], ["\\M-c\\M^A\\M--", "\u306d"], ["\\M-c\\M^A\\M-.", "\u306e"], ["\\M-c\\M^A\\M-/", "\u306f"], ["\\M-c\\M^A\\M-0", "\u3070"], ["\\M-c\\M^A\\M-1", "\u3071"], ["\\M-c\\M^A\\M-2", "\u3072"], ["\\M-c\\M^A\\M-3", "\u3073"], ["\\M-c\\M^A\\M-4", "\u3074"], ["\\M-c\\M^A\\M-5", "\u3075"], ["\\M-c\\M^A\\M-6", "\u3076"], ["\\M-c\\M^A\\M-7", "\u3077"], ["\\M-c\\M^A\\M-8", "\u3078"], ["\\M-c\\M^A\\M-9", "\u3079"], ["\\M-c\\M^A\\M-:", "\u307a"], ["\\M-c\\M^A\\M-;", "\u307b"], ["\\M-c\\M^A\\M-<", "\u307c"], ["\\M-c\\M^A\\M-=", "\u307d"], ["\\M-c\\M^A\\M->", "\u307e"], ["\\M-c\\M^A\\M-?", "\u307f"], ["\\M-c\\M^B\\M^@", "\u3080"], ["\\M-c\\M^B\\M^A", "\u3081"], ["\\M-c\\M^B\\M^B", "\u3082"], ["\\M-c\\M^B\\M^C", "\u3083"], ["\\M-c\\M^B\\M^D", "\u3084"], ["\\M-c\\M^B\\M^E", "\u3085"], ["\\M-c\\M^B\\M^F", "\u3086"], ["\\M-c\\M^B\\M^G", "\u3087"], ["\\M-c\\M^B\\M^H", "\u3088"], ["\\M-c\\M^B\\M^I", "\u3089"], ["\\M-c\\M^B\\M^J", "\u308a"], ["\\M-c\\M^B\\M^K", "\u308b"], ["\\M-c\\M^B\\M^L", "\u308c"], ["\\M-c\\M^B\\M^M", "\u308d"], ["\\M-c\\M^B\\M^N", "\u308e"], ["\\M-c\\M^B\\M^O", "\u308f"], ["\\M-c\\M^B\\M^P", "\u3090"], ["\\M-c\\M^B\\M^Q", "\u3091"], ["\\M-c\\M^B\\M^R", "\u3092"], ["\\M-c\\M^B\\M^S", "\u3093"], ["\\M-c\\M^B\\M^T", "\u3094"], ["\\M-c\\M^B\\M^U", "\u3095"], ["\\M-c\\M^B\\M^V", "\u3096"], ["\\M-c\\M^B\\M^W", "\u3097"], ["\\M-c\\M^B\\M^X", "\u3098"], ["\\M-c\\M^B\\M^Y", "\u3099"], ["\\M-c\\M^B\\M^Z", "\u309a"], ["\\M-c\\M^B\\M^[", "\u309b"], ["\\M-c\\M^B\\M^\\", "\u309c"], ["\\M-c\\M^B\\M^]", "\u309d"], ["\\M-c\\M^B\\M^^", "\u309e"], ["\\M-c\\M^B\\M^_", "\u309f"], ["\\M-c\\M^B\\240", "\u30a0"], ["\\M-c\\M^B\\M-!", "\u30a1"], ["\\M-c\\M^B\\M-\"", "\u30a2"], ["\\M-c\\M^B\\M-#", "\u30a3"], ["\\M-c\\M^B\\M-$", "\u30a4"], ["\\M-c\\M^B\\M-%", "\u30a5"], ["\\M-c\\M^B\\M-&", "\u30a6"], ["\\M-c\\M^B\\M-'", "\u30a7"], ["\\M-c\\M^B\\M-(", "\u30a8"], ["\\M-c\\M^B\\M-)", "\u30a9"], ["\\M-c\\M^B\\M-*", "\u30aa"], ["\\M-c\\M^B\\M-+", "\u30ab"], ["\\M-c\\M^B\\M-,", "\u30ac"], ["\\M-c\\M^B\\M--", "\u30ad"], ["\\M-c\\M^B\\M-.", "\u30ae"], ["\\M-c\\M^B\\M-/", "\u30af"], ["\\M-c\\M^B\\M-0", "\u30b0"], ["\\M-c\\M^B\\M-1", "\u30b1"], ["\\M-c\\M^B\\M-2", "\u30b2"], ["\\M-c\\M^B\\M-3", "\u30b3"], ["\\M-c\\M^B\\M-4", "\u30b4"], ["\\M-c\\M^B\\M-5", "\u30b5"], ["\\M-c\\M^B\\M-6", "\u30b6"], ["\\M-c\\M^B\\M-7", "\u30b7"], ["\\M-c\\M^B\\M-8", "\u30b8"], ["\\M-c\\M^B\\M-9", "\u30b9"], ["\\M-c\\M^B\\M-:", "\u30ba"], ["\\M-c\\M^B\\M-;", "\u30bb"], ["\\M-c\\M^B\\M-<", "\u30bc"], ["\\M-c\\M^B\\M-=", "\u30bd"], ["\\M-c\\M^B\\M->", "\u30be"], ["\\M-c\\M^B\\M-?", "\u30bf"], ["\\M-c\\M^C\\M^@", "\u30c0"], ["\\M-c\\M^C\\M^A", "\u30c1"], ["\\M-c\\M^C\\M^B", "\u30c2"], ["\\M-c\\M^C\\M^C", "\u30c3"], ["\\M-c\\M^C\\M^D", "\u30c4"], ["\\M-c\\M^C\\M^E", "\u30c5"], ["\\M-c\\M^C\\M^F", "\u30c6"], ["\\M-c\\M^C\\M^G", "\u30c7"], ["\\M-c\\M^C\\M^H", "\u30c8"], ["\\M-c\\M^C\\M^I", "\u30c9"], ["\\M-c\\M^C\\M^J", "\u30ca"], ["\\M-c\\M^C\\M^K", "\u30cb"], ["\\M-c\\M^C\\M^L", "\u30cc"], ["\\M-c\\M^C\\M^M", "\u30cd"], ["\\M-c\\M^C\\M^N", "\u30ce"], ["\\M-c\\M^C\\M^O", "\u30cf"], ["\\M-c\\M^C\\M^P", "\u30d0"], ["\\M-c\\M^C\\M^Q", "\u30d1"], ["\\M-c\\M^C\\M^R", "\u30d2"], ["\\M-c\\M^C\\M^S", "\u30d3"], ["\\M-c\\M^C\\M^T", "\u30d4"], ["\\M-c\\M^C\\M^U", "\u30d5"], ["\\M-c\\M^C\\M^V", "\u30d6"], ["\\M-c\\M^C\\M^W", "\u30d7"], ["\\M-c\\M^C\\M^X", "\u30d8"], ["\\M-c\\M^C\\M^Y", "\u30d9"], ["\\M-c\\M^C\\M^Z", "\u30da"], ["\\M-c\\M^C\\M^[", "\u30db"], ["\\M-c\\M^C\\M^\\", "\u30dc"], ["\\M-c\\M^C\\M^]", "\u30dd"], ["\\M-c\\M^C\\M^^", "\u30de"], ["\\M-c\\M^C\\M^_", "\u30df"], ["\\M-c\\M^C\\240", "\u30e0"], ["\\M-c\\M^C\\M-!", "\u30e1"], ["\\M-c\\M^C\\M-\"", "\u30e2"], ["\\M-c\\M^C\\M-#", "\u30e3"], ["\\M-c\\M^C\\M-$", "\u30e4"], ["\\M-c\\M^C\\M-%", "\u30e5"], ["\\M-c\\M^C\\M-&", "\u30e6"], ["\\M-c\\M^C\\M-'", "\u30e7"], ["\\M-c\\M^C\\M-(", "\u30e8"], ["\\M-c\\M^C\\M-)", "\u30e9"], ["\\M-c\\M^C\\M-*", "\u30ea"], ["\\M-c\\M^C\\M-+", "\u30eb"], ["\\M-c\\M^C\\M-,", "\u30ec"], ["\\M-c\\M^C\\M--", "\u30ed"], ["\\M-c\\M^C\\M-.", "\u30ee"], ["\\M-c\\M^C\\M-/", "\u30ef"], ["\\M-c\\M^C\\M-0", "\u30f0"], ["\\M-c\\M^C\\M-1", "\u30f1"], ["\\M-c\\M^C\\M-2", "\u30f2"], ["\\M-c\\M^C\\M-3", "\u30f3"], ["\\M-c\\M^C\\M-4", "\u30f4"], ["\\M-c\\M^C\\M-5", "\u30f5"], ["\\M-c\\M^C\\M-6", "\u30f6"], ["\\M-c\\M^C\\M-7", "\u30f7"], ["\\M-c\\M^C\\M-8", "\u30f8"], ["\\M-c\\M^C\\M-9", "\u30f9"], ["\\M-c\\M^C\\M-:", "\u30fa"], ["\\M-c\\M^C\\M-;", "\u30fb"], ["\\M-c\\M^C\\M-<", "\u30fc"], ["\\M-c\\M^C\\M-=", "\u30fd"], ["\\M-c\\M^C\\M->", "\u30fe"], ["\\M-c\\M^C\\M-?", "\u30ff"], ["foo\\040bar", "foo bar"], ["foo\\^Jbar", "foo\nbar"], ["$bar\\040=\\040'baz';", "$bar = 'baz';"], ["$foo\\040=\\040\"\\\\x20\\\\\\\\x20\\\\\\\\\\\\x20\\\\\\\\\\\\\\\\x20\"", "$foo = \"\\x20\\\\x20\\\\\\x20\\\\\\\\x20\""], ["$foo\\040=\\040function($bar)\\040use($baz)\\040{\\^J\\^Ireturn\\040$baz->getFoo()\\^J};", "$foo = function($bar) use($baz) {\n\treturn $baz->getFoo()\n};"], ["", ""]] \ No newline at end of file diff --git a/vendor/psy/psysh/test/tools/gen_unvis_fixtures.py b/vendor/psy/psysh/test/tools/gen_unvis_fixtures.py new file mode 100644 index 0000000..e02a741 --- /dev/null +++ b/vendor/psy/psysh/test/tools/gen_unvis_fixtures.py @@ -0,0 +1,94 @@ +#! /usr/bin/env python3 +import sys +from os.path import abspath, expanduser, dirname, join +from itertools import chain +import json +import argparse + +from vis import vis, unvis, VIS_WHITE + + +__dir__ = dirname(abspath(__file__)) + +OUTPUT_FILE = join(__dir__, '..', 'fixtures', 'unvis_fixtures.json') + +# Add custom fixtures here +CUSTOM_FIXTURES = [ + # test long multibyte string + ''.join(chr(cp) for cp in range(1024)), + 'foo bar', + 'foo\nbar', + "$bar = 'baz';", + r'$foo = "\x20\\x20\\\x20\\\\x20"', + '$foo = function($bar) use($baz) {\n\treturn $baz->getFoo()\n};' +] + +RANGES = { + # All valid codepoints in the BMP + 'bmp': chain(range(0x0000, 0xD800), range(0xE000, 0xFFFF)), + # Smaller set of pertinent? codepoints inside BMP + # see: http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + 'small': chain( + # latin blocks + range(0x0000, 0x0250), + # Greek, Cyrillic + range(0x0370, 0x0530), + # Hebrew, Arabic + range(0x590, 0x0700), + # CJK radicals + range(0x2E80, 0x2F00), + # Hiragana, Katakana + range(0x3040, 0x3100) + ) +} + + +if __name__ == '__main__': + + argp = argparse.ArgumentParser( + description='Generates test data for Psy\\Test\\Util\\StrTest') + argp.add_argument('-f', '--format-output', action='store_true', + help='Indent JSON output to ease debugging') + argp.add_argument('-a', '--all', action='store_true', + help="""Generates test data for all codepoints of the BMP. + (same as --range=bmp). WARNING: You will need quite + a lot of RAM to run the testsuite ! + """) + argp.add_argument('-r', '--range', + help="""Choose the range of codepoints used to generate + test data.""", + choices=list(RANGES.keys()), + default='small') + argp.add_argument('-o', '--output-file', + help="""Write test data to OUTPUT_FILE + (defaults to PSYSH_DIR/test/fixtures)""") + args = argp.parse_args() + + cp_range = RANGES['bmp'] if args.all else RANGES[args.range] + indent = 2 if args.format_output else None + if args.output_file: + OUTPUT_FILE = abspath(expanduser(args.output_file)) + + fixtures = [] + + # use SMALL_RANGE by default, it should be enough. + # use BMP_RANGE for a more complete smoke test + for codepoint in cp_range: + char = chr(codepoint) + encoded = vis(char, VIS_WHITE) + decoded = unvis(encoded) + fixtures.append((encoded, decoded)) + + # Add our own custom fixtures at the end, + # since they would fail anyway if one of the previous did. + for fixture in CUSTOM_FIXTURES: + encoded = vis(fixture, VIS_WHITE) + decoded = unvis(encoded) + fixtures.append((encoded, decoded)) + + with open(OUTPUT_FILE, 'w') as fp: + # dump as json to avoid backslashin and quotin nightmare + # between php and python + json.dump(fixtures, fp, indent=indent) + + sys.exit(0) diff --git a/vendor/psy/psysh/test/tools/vis.py b/vendor/psy/psysh/test/tools/vis.py new file mode 100644 index 0000000..4e45c4c --- /dev/null +++ b/vendor/psy/psysh/test/tools/vis.py @@ -0,0 +1,126 @@ +""" +vis.py +====== + +Ctypes based module to access libbsd's strvis & strunvis functions. + +The `vis` function is the equivalent of strvis. +The `unvis` function is the equivalent of strunvis. +All functions accept unicode string as input and return a unicode string. + +Constants: +---------- + +* to select alternate encoding format + `VIS_OCTAL`: use octal \ddd format + `VIS_CSTYLE`: use \[nrft0..] where appropiate + +* to alter set of characters encoded + (default is to encode all non-graphic except space, tab, and newline). + `VIS_SP`: also encode space + `VIS_TAB`: also encode tab + `VIS_NL`: also encode newline + `VIS_WHITE`: same as (VIS_SP | VIS_TAB | VIS_NL) + `VIS_SAFE`: only encode "unsafe" characters + +* other + `VIS_NOSLASH`: inhibit printing '\' + `VIS_HTTP1808`: http-style escape % hex hex + `VIS_HTTPSTYLE`: http-style escape % hex hex + `VIS_MIMESTYLE`: mime-style escape = HEX HEX + `VIS_HTTP1866`: http-style &#num; or &string; + `VIS_NOESCAPE`: don't decode `\' + `VIS_GLOB`: encode glob(3) magic characters + +:Authors: + - ju1ius (http://github.com/ju1ius) +:Version: 1 +:Date: 2014-01-05 +""" +from ctypes import CDLL, c_char_p, c_int +from ctypes.util import find_library + + +__all__ = [ + 'vis', 'unvis', + 'VIS_OCTAL', 'VIS_CSTYLE', + 'VIS_SP', 'VIS_TAB', 'VIS_NL', 'VIS_WHITE', 'VIS_SAFE', + 'VIS_NOSLASH', 'VIS_HTTP1808', 'VIS_HTTPSTYLE', 'VIS_MIMESTYLE', + 'VIS_HTTP1866', 'VIS_NOESCAPE', 'VIS_GLOB' +] + + +############################################################# +# Constants from bsd/vis.h +############################################################# + +#to select alternate encoding format +VIS_OCTAL = 0x0001 +VIS_CSTYLE = 0x0002 +# to alter set of characters encoded +# (default is to encode all non-graphic except space, tab, and newline). +VIS_SP = 0x0004 +VIS_TAB = 0x0008 +VIS_NL = 0x0010 +VIS_WHITE = VIS_SP | VIS_TAB | VIS_NL +VIS_SAFE = 0x0020 +# other +VIS_NOSLASH = 0x0040 +VIS_HTTP1808 = 0x0080 +VIS_HTTPSTYLE = 0x0080 +VIS_MIMESTYLE = 0x0100 +VIS_HTTP1866 = 0x0200 +VIS_NOESCAPE = 0x0400 +VIS_GLOB = 0x1000 + +############################################################# +# Import libbsd/vis functions +############################################################# + +_libbsd = CDLL(find_library('bsd')) + +_strvis = _libbsd.strvis +_strvis.argtypes = [c_char_p, c_char_p, c_int] +_strvis.restype = c_int + +_strunvis = _libbsd.strunvis +_strvis.argtypes = [c_char_p, c_char_p] +_strvis.restype = c_int + + +def vis(src, flags=VIS_WHITE): + """ + Encodes the string `src` into libbsd's vis encoding. + `flags` must be one of the VIS_* constants + + C definition: + int strvis(char *dst, char *src, int flags); + """ + src = bytes(src, 'utf-8') + dst_p = c_char_p(bytes(len(src) * 4)) + src_p = c_char_p(src) + flags = c_int(flags) + + bytes_written = _strvis(dst_p, src_p, flags) + if -1 == bytes_written: + raise RuntimeError('vis failed to encode string "{}"'.format(src)) + + return dst_p.value.decode('utf-8') + + +def unvis(src): + """ + Decodes a string encoded by vis. + + C definition: + int strunvis(char *dst, char *src); + """ + src = bytes(src, 'utf-8') + dst_p = c_char_p(bytes(len(src))) + src_p = c_char_p(src) + + bytes_written = _strunvis(dst_p, src_p) + if -1 == bytes_written: + raise RuntimeError('unvis failed to decode string "{}"'.format(src)) + + return dst_p.value.decode('utf-8') diff --git a/vendor/sebastian/comparator/.gitignore b/vendor/sebastian/comparator/.gitignore new file mode 100644 index 0000000..c2990fc --- /dev/null +++ b/vendor/sebastian/comparator/.gitignore @@ -0,0 +1,6 @@ +/build/coverage +/composer.lock +/composer.phar +/phpunit.xml +/.idea +/vendor diff --git a/vendor/sebastian/comparator/.travis.yml b/vendor/sebastian/comparator/.travis.yml new file mode 100644 index 0000000..082370f --- /dev/null +++ b/vendor/sebastian/comparator/.travis.yml @@ -0,0 +1,25 @@ +language: php + +sudo: false + +install: + - travis_retry composer install --no-interaction --prefer-source + +script: ./vendor/bin/phpunit --configuration ./build/travis-ci.xml + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +notifications: + email: false + webhooks: + urls: + - https://webhooks.gitter.im/e/6668f52f3dd4e3f81960 + on_success: always + on_failure: always + on_start: false diff --git a/vendor/sebastian/comparator/LICENSE b/vendor/sebastian/comparator/LICENSE new file mode 100644 index 0000000..334b637 --- /dev/null +++ b/vendor/sebastian/comparator/LICENSE @@ -0,0 +1,33 @@ +Comparator + +Copyright (c) 2002-2015, Sebastian Bergmann . +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 Sebastian Bergmann nor the names of his + 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. diff --git a/vendor/sebastian/comparator/README.md b/vendor/sebastian/comparator/README.md new file mode 100644 index 0000000..faea412 --- /dev/null +++ b/vendor/sebastian/comparator/README.md @@ -0,0 +1,41 @@ +[![Build Status](https://travis-ci.org/sebastianbergmann/comparator.svg?branch=master)](https://travis-ci.org/sebastianbergmann/comparator) + +# Comparator + +This component provides the functionality to compare PHP values for equality. + +## Installation + +To add Comparator as a local, per-project dependency to your project, simply add a dependency on `sebastian/comparator` to your project's `composer.json` file. Here is a minimal example of a `composer.json` file that just defines a dependency on Comparator 1.2: + +```JSON +{ + "require": { + "sebastian/comparator": "~1.2" + } +} +``` + +## Usage + +```php +getComparatorFor($date1, $date2); + +try { + $comparator->assertEquals($date1, $date2); + print "Dates match"; +} + +catch (ComparisonFailure $failure) { + print "Dates don't match"; +} +``` + diff --git a/vendor/sebastian/comparator/build.xml b/vendor/sebastian/comparator/build.xml new file mode 100644 index 0000000..552ebce --- /dev/null +++ b/vendor/sebastian/comparator/build.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sebastian/comparator/build/travis-ci.xml b/vendor/sebastian/comparator/build/travis-ci.xml new file mode 100644 index 0000000..751a3bc --- /dev/null +++ b/vendor/sebastian/comparator/build/travis-ci.xml @@ -0,0 +1,11 @@ + + + + ../tests + + + diff --git a/vendor/sebastian/comparator/composer.json b/vendor/sebastian/comparator/composer.json new file mode 100644 index 0000000..79f0803 --- /dev/null +++ b/vendor/sebastian/comparator/composer.json @@ -0,0 +1,44 @@ +{ + "name": "sebastian/comparator", + "description": "Provides the functionality to compare PHP values for equality", + "keywords": ["comparator","compare","equality"], + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + } +} + diff --git a/vendor/sebastian/comparator/phpunit.xml.dist b/vendor/sebastian/comparator/phpunit.xml.dist new file mode 100644 index 0000000..e1eaf2c --- /dev/null +++ b/vendor/sebastian/comparator/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + tests + + + + + + + + + src + + + + diff --git a/vendor/sebastian/comparator/src/ArrayComparator.php b/vendor/sebastian/comparator/src/ArrayComparator.php new file mode 100644 index 0000000..80f1cdc --- /dev/null +++ b/vendor/sebastian/comparator/src/ArrayComparator.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares arrays for equality. + */ +class ArrayComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return is_array($expected) && is_array($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @param array $processed + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) + { + if ($canonicalize) { + sort($expected); + sort($actual); + } + + $remaining = $actual; + $expString = $actString = "Array (\n"; + $equal = true; + + foreach ($expected as $key => $value) { + unset($remaining[$key]); + + if (!array_key_exists($key, $actual)) { + $expString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + + $equal = false; + + continue; + } + + try { + $comparator = $this->factory->getComparatorFor($value, $actual[$key]); + $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); + + $expString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + $actString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($actual[$key]) + ); + } catch (ComparisonFailure $e) { + $expString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $e->getExpectedAsString() + ? $this->indent($e->getExpectedAsString()) + : $this->exporter->shortenedExport($e->getExpected()) + ); + + $actString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $e->getActualAsString() + ? $this->indent($e->getActualAsString()) + : $this->exporter->shortenedExport($e->getActual()) + ); + + $equal = false; + } + } + + foreach ($remaining as $key => $value) { + $actString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + + $equal = false; + } + + $expString .= ')'; + $actString .= ')'; + + if (!$equal) { + throw new ComparisonFailure( + $expected, + $actual, + $expString, + $actString, + false, + 'Failed asserting that two arrays are equal.' + ); + } + } + + protected function indent($lines) + { + return trim(str_replace("\n", "\n ", $lines)); + } +} diff --git a/vendor/sebastian/comparator/src/Comparator.php b/vendor/sebastian/comparator/src/Comparator.php new file mode 100644 index 0000000..ef126a4 --- /dev/null +++ b/vendor/sebastian/comparator/src/Comparator.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use SebastianBergmann\Exporter\Exporter; + +/** + * Abstract base class for comparators which compare values for equality. + */ +abstract class Comparator +{ + /** + * @var Factory + */ + protected $factory; + + /** + * @var Exporter + */ + protected $exporter; + + public function __construct() + { + $this->exporter = new Exporter; + } + + /** + * @param Factory $factory + */ + public function setFactory(Factory $factory) + { + $this->factory = $factory; + } + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + abstract public function accepts($expected, $actual); + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false); +} diff --git a/vendor/sebastian/comparator/src/ComparisonFailure.php b/vendor/sebastian/comparator/src/ComparisonFailure.php new file mode 100644 index 0000000..2c25453 --- /dev/null +++ b/vendor/sebastian/comparator/src/ComparisonFailure.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use SebastianBergmann\Diff\Differ; + +/** + * Thrown when an assertion for string equality failed. + */ +class ComparisonFailure extends \RuntimeException +{ + /** + * Expected value of the retrieval which does not match $actual. + * @var mixed + */ + protected $expected; + + /** + * Actually retrieved value which does not match $expected. + * @var mixed + */ + protected $actual; + + /** + * The string representation of the expected value + * @var string + */ + protected $expectedAsString; + + /** + * The string representation of the actual value + * @var string + */ + protected $actualAsString; + + /** + * @var bool + */ + protected $identical; + + /** + * Optional message which is placed in front of the first line + * returned by toString(). + * @var string + */ + protected $message; + + /** + * Initialises with the expected value and the actual value. + * + * @param mixed $expected Expected value retrieved. + * @param mixed $actual Actual value retrieved. + * @param string $expectedAsString + * @param string $actualAsString + * @param bool $identical + * @param string $message A string which is prefixed on all returned lines + * in the difference output. + */ + public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '') + { + $this->expected = $expected; + $this->actual = $actual; + $this->expectedAsString = $expectedAsString; + $this->actualAsString = $actualAsString; + $this->message = $message; + } + + /** + * @return mixed + */ + public function getActual() + { + return $this->actual; + } + + /** + * @return mixed + */ + public function getExpected() + { + return $this->expected; + } + + /** + * @return string + */ + public function getActualAsString() + { + return $this->actualAsString; + } + + /** + * @return string + */ + public function getExpectedAsString() + { + return $this->expectedAsString; + } + + /** + * @return string + */ + public function getDiff() + { + if (!$this->actualAsString && !$this->expectedAsString) { + return ''; + } + + $differ = new Differ("\n--- Expected\n+++ Actual\n"); + + return $differ->diff($this->expectedAsString, $this->actualAsString); + } + + /** + * @return string + */ + public function toString() + { + return $this->message . $this->getDiff(); + } +} diff --git a/vendor/sebastian/comparator/src/DOMNodeComparator.php b/vendor/sebastian/comparator/src/DOMNodeComparator.php new file mode 100644 index 0000000..4c8a39d --- /dev/null +++ b/vendor/sebastian/comparator/src/DOMNodeComparator.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use DOMDocument; +use DOMNode; + +/** + * Compares DOMNode instances for equality. + */ +class DOMNodeComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof DOMNode && $actual instanceof DOMNode; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $expectedAsString = $this->nodeToText($expected, true, $ignoreCase); + $actualAsString = $this->nodeToText($actual, true, $ignoreCase); + + if ($expectedAsString !== $actualAsString) { + if ($expected instanceof DOMDocument) { + $type = 'documents'; + } else { + $type = 'nodes'; + } + + throw new ComparisonFailure( + $expected, + $actual, + $expectedAsString, + $actualAsString, + false, + sprintf("Failed asserting that two DOM %s are equal.\n", $type) + ); + } + } + + /** + * Returns the normalized, whitespace-cleaned, and indented textual + * representation of a DOMNode. + * + * @param DOMNode $node + * @param bool $canonicalize + * @param bool $ignoreCase + * @return string + */ + private function nodeToText(DOMNode $node, $canonicalize, $ignoreCase) + { + if ($canonicalize) { + $document = new DOMDocument; + $document->loadXML($node->C14N()); + + $node = $document; + } + + if ($node instanceof DOMDocument) { + $document = $node; + } else { + $document = $node->ownerDocument; + } + + $document->formatOutput = true; + $document->normalizeDocument(); + + if ($node instanceof DOMDocument) { + $text = $node->saveXML(); + } else { + $text = $document->saveXML($node); + } + + if ($ignoreCase) { + $text = strtolower($text); + } + + return $text; + } +} diff --git a/vendor/sebastian/comparator/src/DateTimeComparator.php b/vendor/sebastian/comparator/src/DateTimeComparator.php new file mode 100644 index 0000000..f7bcd23 --- /dev/null +++ b/vendor/sebastian/comparator/src/DateTimeComparator.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares DateTimeInterface instances for equality. + */ +class DateTimeComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return ($expected instanceof \DateTime || $expected instanceof \DateTimeInterface) && + ($actual instanceof \DateTime || $actual instanceof \DateTimeInterface); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $delta = new \DateInterval(sprintf('PT%sS', abs($delta))); + + $expectedLower = clone $expected; + $expectedUpper = clone $expected; + + if ($actual < $expectedLower->sub($delta) || + $actual > $expectedUpper->add($delta)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->dateTimeToString($expected), + $this->dateTimeToString($actual), + false, + 'Failed asserting that two DateTime objects are equal.' + ); + } + } + + /** + * Returns an ISO 8601 formatted string representation of a datetime or + * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly + * initialized. + * + * @param \DateTimeInterface $datetime + * @return string + */ + protected function dateTimeToString($datetime) + { + $string = $datetime->format(\DateTime::ISO8601); + + return $string ? $string : 'Invalid DateTimeInterface object'; + } +} diff --git a/vendor/sebastian/comparator/src/DoubleComparator.php b/vendor/sebastian/comparator/src/DoubleComparator.php new file mode 100644 index 0000000..051f668 --- /dev/null +++ b/vendor/sebastian/comparator/src/DoubleComparator.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares doubles for equality. + */ +class DoubleComparator extends NumericComparator +{ + /** + * Smallest value available in PHP. + * + * @var float + */ + const EPSILON = 0.0000000001; + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return (is_double($expected) || is_double($actual)) && is_numeric($expected) && is_numeric($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if ($delta == 0) { + $delta = self::EPSILON; + } + + parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); + } +} diff --git a/vendor/sebastian/comparator/src/ExceptionComparator.php b/vendor/sebastian/comparator/src/ExceptionComparator.php new file mode 100644 index 0000000..5233646 --- /dev/null +++ b/vendor/sebastian/comparator/src/ExceptionComparator.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares Exception instances for equality. + */ +class ExceptionComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \Exception && $actual instanceof \Exception; + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset( + $array['file'], + $array['line'], + $array['trace'], + $array['string'], + $array['xdebug_message'] + ); + + return $array; + } +} diff --git a/vendor/sebastian/comparator/src/Factory.php b/vendor/sebastian/comparator/src/Factory.php new file mode 100644 index 0000000..ed3d5e1 --- /dev/null +++ b/vendor/sebastian/comparator/src/Factory.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Factory for comparators which compare values for equality. + */ +class Factory +{ + /** + * @var Comparator[] + */ + private $comparators = array(); + + /** + * @var Factory + */ + private static $instance; + + /** + * Constructs a new factory. + */ + public function __construct() + { + $this->register(new TypeComparator); + $this->register(new ScalarComparator); + $this->register(new NumericComparator); + $this->register(new DoubleComparator); + $this->register(new ArrayComparator); + $this->register(new ResourceComparator); + $this->register(new ObjectComparator); + $this->register(new ExceptionComparator); + $this->register(new SplObjectStorageComparator); + $this->register(new DOMNodeComparator); + $this->register(new MockObjectComparator); + $this->register(new DateTimeComparator); + } + + /** + * @return Factory + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new self; + } + + return self::$instance; + } + + /** + * Returns the correct comparator for comparing two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return Comparator + */ + public function getComparatorFor($expected, $actual) + { + foreach ($this->comparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + } + + /** + * Registers a new comparator. + * + * This comparator will be returned by getInstance() if its accept() method + * returns TRUE for the compared values. It has higher priority than the + * existing comparators, meaning that its accept() method will be tested + * before those of the other comparators. + * + * @param Comparator $comparator The registered comparator + */ + public function register(Comparator $comparator) + { + array_unshift($this->comparators, $comparator); + + $comparator->setFactory($this); + } + + /** + * Unregisters a comparator. + * + * This comparator will no longer be returned by getInstance(). + * + * @param Comparator $comparator The unregistered comparator + */ + public function unregister(Comparator $comparator) + { + foreach ($this->comparators as $key => $_comparator) { + if ($comparator === $_comparator) { + unset($this->comparators[$key]); + } + } + } +} diff --git a/vendor/sebastian/comparator/src/MockObjectComparator.php b/vendor/sebastian/comparator/src/MockObjectComparator.php new file mode 100644 index 0000000..a16903c --- /dev/null +++ b/vendor/sebastian/comparator/src/MockObjectComparator.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares PHPUnit_Framework_MockObject_MockObject instances for equality. + */ +class MockObjectComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \PHPUnit_Framework_MockObject_MockObject && $actual instanceof \PHPUnit_Framework_MockObject_MockObject; + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset($array['__phpunit_invocationMocker']); + + return $array; + } +} \ No newline at end of file diff --git a/vendor/sebastian/comparator/src/NumericComparator.php b/vendor/sebastian/comparator/src/NumericComparator.php new file mode 100644 index 0000000..90890c0 --- /dev/null +++ b/vendor/sebastian/comparator/src/NumericComparator.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares numerical values for equality. + */ +class NumericComparator extends ScalarComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + // all numerical values, but not if one of them is a double + // or both of them are strings + return is_numeric($expected) && is_numeric($actual) && + !(is_double($expected) || is_double($actual)) && + !(is_string($expected) && is_string($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if (is_infinite($actual) && is_infinite($expected)) { + return; + } + + if ((is_infinite($actual) xor is_infinite($expected)) || + (is_nan($actual) or is_nan($expected)) || + abs($actual - $expected) > $delta) { + throw new ComparisonFailure( + $expected, + $actual, + '', + '', + false, + sprintf( + 'Failed asserting that %s matches expected %s.', + $this->exporter->export($actual), + $this->exporter->export($expected) + ) + ); + } + } +} diff --git a/vendor/sebastian/comparator/src/ObjectComparator.php b/vendor/sebastian/comparator/src/ObjectComparator.php new file mode 100644 index 0000000..08ffb67 --- /dev/null +++ b/vendor/sebastian/comparator/src/ObjectComparator.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares objects for equality. + */ +class ObjectComparator extends ArrayComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return is_object($expected) && is_object($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @param array $processed + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) + { + if (get_class($actual) !== get_class($expected)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + sprintf( + '%s is not instance of expected class "%s".', + $this->exporter->export($actual), + get_class($expected) + ) + ); + } + + // don't compare twice to allow for cyclic dependencies + if (in_array(array($actual, $expected), $processed, true) || + in_array(array($expected, $actual), $processed, true)) { + return; + } + + $processed[] = array($actual, $expected); + + // don't compare objects if they are identical + // this helps to avoid the error "maximum function nesting level reached" + // CAUTION: this conditional clause is not tested + if ($actual !== $expected) { + try { + parent::assertEquals( + $this->toArray($expected), + $this->toArray($actual), + $delta, + $canonicalize, + $ignoreCase, + $processed + ); + } catch (ComparisonFailure $e) { + throw new ComparisonFailure( + $expected, + $actual, + // replace "Array" with "MyClass object" + substr_replace($e->getExpectedAsString(), get_class($expected) . ' Object', 0, 5), + substr_replace($e->getActualAsString(), get_class($actual) . ' Object', 0, 5), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + return $this->exporter->toArray($object); + } +} diff --git a/vendor/sebastian/comparator/src/ResourceComparator.php b/vendor/sebastian/comparator/src/ResourceComparator.php new file mode 100644 index 0000000..877cc7d --- /dev/null +++ b/vendor/sebastian/comparator/src/ResourceComparator.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares resources for equality. + */ +class ResourceComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return is_resource($expected) && is_resource($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if ($actual != $expected) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual) + ); + } + } +} diff --git a/vendor/sebastian/comparator/src/ScalarComparator.php b/vendor/sebastian/comparator/src/ScalarComparator.php new file mode 100644 index 0000000..df5a746 --- /dev/null +++ b/vendor/sebastian/comparator/src/ScalarComparator.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares scalar or NULL values for equality. + */ +class ScalarComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + * @since Method available since Release 3.6.0 + */ + public function accepts($expected, $actual) + { + return ((is_scalar($expected) xor null === $expected) && + (is_scalar($actual) xor null === $actual)) + // allow comparison between strings and objects featuring __toString() + || (is_string($expected) && is_object($actual) && method_exists($actual, '__toString')) + || (is_object($expected) && method_exists($expected, '__toString') && is_string($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $expectedToCompare = $expected; + $actualToCompare = $actual; + + // always compare as strings to avoid strange behaviour + // otherwise 0 == 'Foobar' + if (is_string($expected) || is_string($actual)) { + $expectedToCompare = (string) $expectedToCompare; + $actualToCompare = (string) $actualToCompare; + + if ($ignoreCase) { + $expectedToCompare = strtolower($expectedToCompare); + $actualToCompare = strtolower($actualToCompare); + } + } + + if ($expectedToCompare != $actualToCompare) { + if (is_string($expected) && is_string($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two strings are equal.' + ); + } + + throw new ComparisonFailure( + $expected, + $actual, + // no diff is required + '', + '', + false, + sprintf( + 'Failed asserting that %s matches expected %s.', + $this->exporter->export($actual), + $this->exporter->export($expected) + ) + ); + } + } +} diff --git a/vendor/sebastian/comparator/src/SplObjectStorageComparator.php b/vendor/sebastian/comparator/src/SplObjectStorageComparator.php new file mode 100644 index 0000000..a1ec119 --- /dev/null +++ b/vendor/sebastian/comparator/src/SplObjectStorageComparator.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares \SplObjectStorage instances for equality. + */ +class SplObjectStorageComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \SplObjectStorage && $actual instanceof \SplObjectStorage; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + foreach ($actual as $object) { + if (!$expected->contains($object)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + + foreach ($expected as $object) { + if (!$actual->contains($object)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + } +} diff --git a/vendor/sebastian/comparator/src/TypeComparator.php b/vendor/sebastian/comparator/src/TypeComparator.php new file mode 100644 index 0000000..6fe2077 --- /dev/null +++ b/vendor/sebastian/comparator/src/TypeComparator.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares values for type equality. + */ +class TypeComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return true; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if (gettype($expected) != gettype($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + false, + sprintf( + '%s does not match expected type "%s".', + $this->exporter->shortenedExport($actual), + gettype($expected) + ) + ); + } + } +} diff --git a/vendor/sebastian/comparator/tests/ArrayComparatorTest.php b/vendor/sebastian/comparator/tests/ArrayComparatorTest.php new file mode 100644 index 0000000..ee44615 --- /dev/null +++ b/vendor/sebastian/comparator/tests/ArrayComparatorTest.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\ArrayComparator + * + */ +class ArrayComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new ArrayComparator; + $this->comparator->setFactory(new Factory); + } + + public function acceptsFailsProvider() + { + return array( + array(array(), null), + array(null, array()), + array(null, null) + ); + } + + public function assertEqualsSucceedsProvider() + { + return array( + array( + array('a' => 1, 'b' => 2), + array('b' => 2, 'a' => 1) + ), + array( + array(1), + array('1') + ), + array( + array(3, 2, 1), + array(2, 3, 1), + 0, + true + ), + array( + array(2.3), + array(2.5), + 0.5 + ), + array( + array(array(2.3)), + array(array(2.5)), + 0.5 + ), + array( + array(new Struct(2.3)), + array(new Struct(2.5)), + 0.5 + ), + ); + } + + public function assertEqualsFailsProvider() + { + return array( + array( + array(), + array(0 => 1) + ), + array( + array(0 => 1), + array() + ), + array( + array(0 => null), + array() + ), + array( + array(0 => 1, 1 => 2), + array(0 => 1, 1 => 3) + ), + array( + array('a', 'b' => array(1, 2)), + array('a', 'b' => array(2, 1)) + ), + array( + array(2.3), + array(4.2), + 0.5 + ), + array( + array(array(2.3)), + array(array(4.2)), + 0.5 + ), + array( + array(new Struct(2.3)), + array(new Struct(4.2)), + 0.5 + ) + ); + } + + /** + * @covers ::accepts + */ + public function testAcceptsSucceeds() + { + $this->assertTrue( + $this->comparator->accepts(array(), array()) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual, $delta = 0.0, $canonicalize = false) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual, $delta, $canonicalize); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual,$delta = 0.0, $canonicalize = false) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', + 'Failed asserting that two arrays are equal' + ); + $this->comparator->assertEquals($expected, $actual, $delta, $canonicalize); + } +} diff --git a/vendor/sebastian/comparator/tests/DOMNodeComparatorTest.php b/vendor/sebastian/comparator/tests/DOMNodeComparatorTest.php new file mode 100644 index 0000000..a769b84 --- /dev/null +++ b/vendor/sebastian/comparator/tests/DOMNodeComparatorTest.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use DOMNode; +use DOMDocument; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\DOMNodeComparator + * + */ +class DOMNodeComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new DOMNodeComparator; + } + + public function acceptsSucceedsProvider() + { + $document = new DOMDocument; + $node = new DOMNode; + + return array( + array($document, $document), + array($node, $node), + array($document, $node), + array($node, $document) + ); + } + + public function acceptsFailsProvider() + { + $document = new DOMDocument; + + return array( + array($document, null), + array(null, $document), + array(null, null) + ); + } + + public function assertEqualsSucceedsProvider() + { + return array( + array( + $this->createDOMDocument(''), + $this->createDOMDocument('') + ), + array( + $this->createDOMDocument(''), + $this->createDOMDocument('') + ), + array( + $this->createDOMDocument(''), + $this->createDOMDocument('') + ), + array( + $this->createDOMDocument("\n \n"), + $this->createDOMDocument('') + ), + ); + } + + public function assertEqualsFailsProvider() + { + return array( + array( + $this->createDOMDocument(''), + $this->createDOMDocument('') + ), + array( + $this->createDOMDocument(''), + $this->createDOMDocument('') + ), + array( + $this->createDOMDocument(' bar '), + $this->createDOMDocument('') + ), + array( + $this->createDOMDocument(''), + $this->createDOMDocument('') + ), + array( + $this->createDOMDocument(' bar '), + $this->createDOMDocument(' bir ') + ) + ); + } + + private function createDOMDocument($content) + { + $document = new DOMDocument; + $document->preserveWhiteSpace = false; + $document->loadXML($content); + + return $document; + } + + /** + * @covers ::accepts + * @dataProvider acceptsSucceedsProvider + */ + public function testAcceptsSucceeds($expected, $actual) + { + $this->assertTrue( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', + 'Failed asserting that two DOM' + ); + $this->comparator->assertEquals($expected, $actual); + } +} diff --git a/vendor/sebastian/comparator/tests/DateTimeComparatorTest.php b/vendor/sebastian/comparator/tests/DateTimeComparatorTest.php new file mode 100644 index 0000000..9abcff7 --- /dev/null +++ b/vendor/sebastian/comparator/tests/DateTimeComparatorTest.php @@ -0,0 +1,216 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use DateTime; +use DateTimeImmutable; +use DateTimeZone; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\DateTimeComparator + * + */ +class DateTimeComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new DateTimeComparator; + } + + public function acceptsFailsProvider() + { + $datetime = new DateTime; + + return array( + array($datetime, null), + array(null, $datetime), + array(null, null) + ); + } + + public function assertEqualsSucceedsProvider() + { + return array( + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')) + ), + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 04:13:25', new DateTimeZone('America/New_York')), + 10 + ), + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 04:14:40', new DateTimeZone('America/New_York')), + 65 + ), + array( + new DateTime('2013-03-29', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29', new DateTimeZone('America/New_York')) + ), + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 03:13:35', new DateTimeZone('America/Chicago')) + ), + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 03:13:49', new DateTimeZone('America/Chicago')), + 15 + ), + array( + new DateTime('2013-03-30', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 23:00:00', new DateTimeZone('America/Chicago')) + ), + array( + new DateTime('2013-03-30', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 23:01:30', new DateTimeZone('America/Chicago')), + 100 + ), + array( + new DateTime('@1364616000'), + new DateTime('2013-03-29 23:00:00', new DateTimeZone('America/Chicago')) + ), + array( + new DateTime('2013-03-29T05:13:35-0500'), + new DateTime('2013-03-29T04:13:35-0600') + ) + ); + } + + public function assertEqualsFailsProvider() + { + return array( + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 03:13:35', new DateTimeZone('America/New_York')) + ), + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 03:13:35', new DateTimeZone('America/New_York')), + 3500 + ), + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 05:13:35', new DateTimeZone('America/New_York')), + 3500 + ), + array( + new DateTime('2013-03-29', new DateTimeZone('America/New_York')), + new DateTime('2013-03-30', new DateTimeZone('America/New_York')) + ), + array( + new DateTime('2013-03-29', new DateTimeZone('America/New_York')), + new DateTime('2013-03-30', new DateTimeZone('America/New_York')), + 43200 + ), + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/Chicago')), + ), + array( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/Chicago')), + 3500 + ), + array( + new DateTime('2013-03-30', new DateTimeZone('America/New_York')), + new DateTime('2013-03-30', new DateTimeZone('America/Chicago')) + ), + array( + new DateTime('2013-03-29T05:13:35-0600'), + new DateTime('2013-03-29T04:13:35-0600') + ), + array( + new DateTime('2013-03-29T05:13:35-0600'), + new DateTime('2013-03-29T05:13:35-0500') + ), + ); + } + + /** + * @covers ::accepts + */ + public function testAcceptsSucceeds() + { + $this->assertTrue( + $this->comparator->accepts( + new DateTime, + new DateTime + ) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual, $delta = 0.0) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual, $delta); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual, $delta = 0.0) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', + 'Failed asserting that two DateTime objects are equal.' + ); + $this->comparator->assertEquals($expected, $actual, $delta); + } + + /** + * @requires PHP 5.5 + * @covers ::accepts + */ + public function testAcceptsDateTimeInterface() + { + $this->assertTrue($this->comparator->accepts(new DateTime, new DateTimeImmutable)); + } + + /** + * @requires PHP 5.5 + * @covers ::assertEquals + */ + public function testSupportsDateTimeInterface() + { + $this->comparator->assertEquals( + new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York')), + new DateTimeImmutable('2013-03-29 04:13:35', new DateTimeZone('America/New_York')) + ); + } +} diff --git a/vendor/sebastian/comparator/tests/DoubleComparatorTest.php b/vendor/sebastian/comparator/tests/DoubleComparatorTest.php new file mode 100644 index 0000000..6b898ba --- /dev/null +++ b/vendor/sebastian/comparator/tests/DoubleComparatorTest.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\DoubleComparator + * + */ +class DoubleComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new DoubleComparator; + } + + public function acceptsSucceedsProvider() + { + return array( + array(0, 5.0), + array(5.0, 0), + array('5', 4.5), + array(1.2e3, 7E-10), + array(3, acos(8)), + array(acos(8), 3), + array(acos(8), acos(8)) + ); + } + + public function acceptsFailsProvider() + { + return array( + array(5, 5), + array('4.5', 5), + array(0x539, 02471), + array(5.0, false), + array(null, 5.0) + ); + } + + public function assertEqualsSucceedsProvider() + { + return array( + array(2.3, 2.3), + array('2.3', 2.3), + array(5.0, 5), + array(5, 5.0), + array(5.0, '5'), + array(1.2e3, 1200), + array(2.3, 2.5, 0.5), + array(3, 3.05, 0.05), + array(1.2e3, 1201, 1), + array((string)(1/3), 1 - 2/3), + array(1/3, (string)(1 - 2/3)) + ); + } + + public function assertEqualsFailsProvider() + { + return array( + array(2.3, 4.2), + array('2.3', 4.2), + array(5.0, '4'), + array(5.0, 6), + array(1.2e3, 1201), + array(2.3, 2.5, 0.2), + array(3, 3.05, 0.04), + array(3, acos(8)), + array(acos(8), 3), + array(acos(8), acos(8)) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsSucceedsProvider + */ + public function testAcceptsSucceeds($expected, $actual) + { + $this->assertTrue( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual, $delta = 0.0) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual, $delta); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual, $delta = 0.0) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', 'matches expected' + ); + $this->comparator->assertEquals($expected, $actual, $delta); + } +} diff --git a/vendor/sebastian/comparator/tests/ExceptionComparatorTest.php b/vendor/sebastian/comparator/tests/ExceptionComparatorTest.php new file mode 100644 index 0000000..fa8cc0a --- /dev/null +++ b/vendor/sebastian/comparator/tests/ExceptionComparatorTest.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use \Exception; +use \RuntimeException; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\ExceptionComparator + * + */ +class ExceptionComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new ExceptionComparator; + $this->comparator->setFactory(new Factory); + } + + public function acceptsSucceedsProvider() + { + return array( + array(new Exception, new Exception), + array(new RuntimeException, new RuntimeException), + array(new Exception, new RuntimeException) + ); + } + + public function acceptsFailsProvider() + { + return array( + array(new Exception, null), + array(null, new Exception), + array(null, null) + ); + } + + public function assertEqualsSucceedsProvider() + { + $exception1 = new Exception; + $exception2 = new Exception; + + $exception3 = new RunTimeException('Error', 100); + $exception4 = new RunTimeException('Error', 100); + + return array( + array($exception1, $exception1), + array($exception1, $exception2), + array($exception3, $exception3), + array($exception3, $exception4) + ); + } + + public function assertEqualsFailsProvider() + { + $typeMessage = 'not instance of expected class'; + $equalMessage = 'Failed asserting that two objects are equal.'; + + $exception1 = new Exception('Error', 100); + $exception2 = new Exception('Error', 101); + $exception3 = new Exception('Errors', 101); + + $exception4 = new RunTimeException('Error', 100); + $exception5 = new RunTimeException('Error', 101); + + return array( + array($exception1, $exception2, $equalMessage), + array($exception1, $exception3, $equalMessage), + array($exception1, $exception4, $typeMessage), + array($exception2, $exception3, $equalMessage), + array($exception4, $exception5, $equalMessage) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsSucceedsProvider + */ + public function testAcceptsSucceeds($expected, $actual) + { + $this->assertTrue( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual, $message) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', $message + ); + $this->comparator->assertEquals($expected, $actual); + } +} diff --git a/vendor/sebastian/comparator/tests/FactoryTest.php b/vendor/sebastian/comparator/tests/FactoryTest.php new file mode 100644 index 0000000..cb58c20 --- /dev/null +++ b/vendor/sebastian/comparator/tests/FactoryTest.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\Factory + * + */ +class FactoryTest extends \PHPUnit_Framework_TestCase +{ + public function instanceProvider() + { + $tmpfile = tmpfile(); + + return array( + array(NULL, NULL, 'SebastianBergmann\\Comparator\\ScalarComparator'), + array(NULL, TRUE, 'SebastianBergmann\\Comparator\\ScalarComparator'), + array(TRUE, NULL, 'SebastianBergmann\\Comparator\\ScalarComparator'), + array(TRUE, TRUE, 'SebastianBergmann\\Comparator\\ScalarComparator'), + array(FALSE, FALSE, 'SebastianBergmann\\Comparator\\ScalarComparator'), + array(TRUE, FALSE, 'SebastianBergmann\\Comparator\\ScalarComparator'), + array(FALSE, TRUE, 'SebastianBergmann\\Comparator\\ScalarComparator'), + array('', '', 'SebastianBergmann\\Comparator\\ScalarComparator'), + array('0', '0', 'SebastianBergmann\\Comparator\\ScalarComparator'), + array('0', 0, 'SebastianBergmann\\Comparator\\NumericComparator'), + array(0, '0', 'SebastianBergmann\\Comparator\\NumericComparator'), + array(0, 0, 'SebastianBergmann\\Comparator\\NumericComparator'), + array(1.0, 0, 'SebastianBergmann\\Comparator\\DoubleComparator'), + array(0, 1.0, 'SebastianBergmann\\Comparator\\DoubleComparator'), + array(1.0, 1.0, 'SebastianBergmann\\Comparator\\DoubleComparator'), + array(array(1), array(1), 'SebastianBergmann\\Comparator\\ArrayComparator'), + array($tmpfile, $tmpfile, 'SebastianBergmann\\Comparator\\ResourceComparator'), + array(new \stdClass, new \stdClass, 'SebastianBergmann\\Comparator\\ObjectComparator'), + array(new \DateTime, new \DateTime, 'SebastianBergmann\\Comparator\\DateTimeComparator'), + array(new \SplObjectStorage, new \SplObjectStorage, 'SebastianBergmann\\Comparator\\SplObjectStorageComparator'), + array(new \Exception, new \Exception, 'SebastianBergmann\\Comparator\\ExceptionComparator'), + array(new \DOMDocument, new \DOMDocument, 'SebastianBergmann\\Comparator\\DOMNodeComparator'), + // mixed types + array($tmpfile, array(1), 'SebastianBergmann\\Comparator\\TypeComparator'), + array(array(1), $tmpfile, 'SebastianBergmann\\Comparator\\TypeComparator'), + array($tmpfile, '1', 'SebastianBergmann\\Comparator\\TypeComparator'), + array('1', $tmpfile, 'SebastianBergmann\\Comparator\\TypeComparator'), + array($tmpfile, new \stdClass, 'SebastianBergmann\\Comparator\\TypeComparator'), + array(new \stdClass, $tmpfile, 'SebastianBergmann\\Comparator\\TypeComparator'), + array(new \stdClass, array(1), 'SebastianBergmann\\Comparator\\TypeComparator'), + array(array(1), new \stdClass, 'SebastianBergmann\\Comparator\\TypeComparator'), + array(new \stdClass, '1', 'SebastianBergmann\\Comparator\\TypeComparator'), + array('1', new \stdClass, 'SebastianBergmann\\Comparator\\TypeComparator'), + array(new ClassWithToString, '1', 'SebastianBergmann\\Comparator\\ScalarComparator'), + array('1', new ClassWithToString, 'SebastianBergmann\\Comparator\\ScalarComparator'), + array(1.0, new \stdClass, 'SebastianBergmann\\Comparator\\TypeComparator'), + array(new \stdClass, 1.0, 'SebastianBergmann\\Comparator\\TypeComparator'), + array(1.0, array(1), 'SebastianBergmann\\Comparator\\TypeComparator'), + array(array(1), 1.0, 'SebastianBergmann\\Comparator\\TypeComparator'), + ); + } + + /** + * @dataProvider instanceProvider + * @covers ::getComparatorFor + * @covers ::__construct + */ + public function testGetComparatorFor($a, $b, $expected) + { + $factory = new Factory; + $actual = $factory->getComparatorFor($a, $b); + $this->assertInstanceOf($expected, $actual); + } + + /** + * @covers ::register + */ + public function testRegister() + { + $comparator = new TestClassComparator; + + $factory = new Factory; + $factory->register($comparator); + + $a = new TestClass; + $b = new TestClass; + $expected = 'SebastianBergmann\\Comparator\\TestClassComparator'; + $actual = $factory->getComparatorFor($a, $b); + + $factory->unregister($comparator); + $this->assertInstanceOf($expected, $actual); + } + + /** + * @covers ::unregister + */ + public function testUnregister() + { + $comparator = new TestClassComparator; + + $factory = new Factory; + $factory->register($comparator); + $factory->unregister($comparator); + + $a = new TestClass; + $b = new TestClass; + $expected = 'SebastianBergmann\\Comparator\\ObjectComparator'; + $actual = $factory->getComparatorFor($a, $b); + + $this->assertInstanceOf($expected, $actual); + } +} diff --git a/vendor/sebastian/comparator/tests/MockObjectComparatorTest.php b/vendor/sebastian/comparator/tests/MockObjectComparatorTest.php new file mode 100644 index 0000000..7910ad7 --- /dev/null +++ b/vendor/sebastian/comparator/tests/MockObjectComparatorTest.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\MockObjectComparator + * + */ +class MockObjectComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new MockObjectComparator; + $this->comparator->setFactory(new Factory); + } + + public function acceptsSucceedsProvider() + { + $testmock = $this->getMock('SebastianBergmann\\Comparator\\TestClass'); + $stdmock = $this->getMock('stdClass'); + + return array( + array($testmock, $testmock), + array($stdmock, $stdmock), + array($stdmock, $testmock) + ); + } + + public function acceptsFailsProvider() + { + $stdmock = $this->getMock('stdClass'); + + return array( + array($stdmock, null), + array(null, $stdmock), + array(null, null) + ); + } + + public function assertEqualsSucceedsProvider() + { + // cyclic dependencies + $book1 = $this->getMock('SebastianBergmann\\Comparator\\Book', null); + $book1->author = $this->getMock('SebastianBergmann\\Comparator\\Author', null, array('Terry Pratchett')); + $book1->author->books[] = $book1; + $book2 = $this->getMock('SebastianBergmann\\Comparator\\Book', null); + $book2->author = $this->getMock('SebastianBergmann\\Comparator\\Author', null, array('Terry Pratchett')); + $book2->author->books[] = $book2; + + $object1 = $this->getMock('SebastianBergmann\\Comparator\\SampleClass', null, array(4, 8, 15)); + $object2 = $this->getMock('SebastianBergmann\\Comparator\\SampleClass', null, array(4, 8, 15)); + + return array( + array($object1, $object1), + array($object1, $object2), + array($book1, $book1), + array($book1, $book2), + array( + $this->getMock('SebastianBergmann\\Comparator\\Struct', null, array(2.3)), + $this->getMock('SebastianBergmann\\Comparator\\Struct', null, array(2.5)), + 0.5 + ) + ); + } + + public function assertEqualsFailsProvider() + { + $typeMessage = 'is not instance of expected class'; + $equalMessage = 'Failed asserting that two objects are equal.'; + + // cyclic dependencies + $book1 = $this->getMock('SebastianBergmann\\Comparator\\Book', null); + $book1->author = $this->getMock('SebastianBergmann\\Comparator\\Author', null, array('Terry Pratchett')); + $book1->author->books[] = $book1; + $book2 = $this->getMock('SebastianBergmann\\Comparator\\Book', null); + $book2->author = $this->getMock('SebastianBergmann\\Comparator\\Author', null, array('Terry Pratch')); + $book2->author->books[] = $book2; + + $book3 = $this->getMock('SebastianBergmann\\Comparator\\Book', null); + $book3->author = 'Terry Pratchett'; + $book4 = $this->getMock('stdClass'); + $book4->author = 'Terry Pratchett'; + + $object1 = $this->getMock('SebastianBergmann\\Comparator\\SampleClass', null, array(4, 8, 15)); + $object2 = $this->getMock('SebastianBergmann\\Comparator\\SampleClass', null, array(16, 23, 42)); + + return array( + array( + $this->getMock('SebastianBergmann\\Comparator\\SampleClass', null, array(4, 8, 15)), + $this->getMock('SebastianBergmann\\Comparator\\SampleClass', null, array(16, 23, 42)), + $equalMessage + ), + array($object1, $object2, $equalMessage), + array($book1, $book2, $equalMessage), + array($book3, $book4, $typeMessage), + array( + $this->getMock('SebastianBergmann\\Comparator\\Struct', null, array(2.3)), + $this->getMock('SebastianBergmann\\Comparator\\Struct', null, array(4.2)), + $equalMessage, + 0.5 + ) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsSucceedsProvider + */ + public function testAcceptsSucceeds($expected, $actual) + { + $this->assertTrue( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual, $delta = 0.0) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual, $delta); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual, $message, $delta = 0.0) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', $message + ); + $this->comparator->assertEquals($expected, $actual, $delta); + } +} diff --git a/vendor/sebastian/comparator/tests/NumericComparatorTest.php b/vendor/sebastian/comparator/tests/NumericComparatorTest.php new file mode 100644 index 0000000..fec7d00 --- /dev/null +++ b/vendor/sebastian/comparator/tests/NumericComparatorTest.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\NumericComparator + * + */ +class NumericComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new NumericComparator; + } + + public function acceptsSucceedsProvider() + { + return array( + array(5, 10), + array(8, '0'), + array('10', 0), + array(0x74c3b00c, 42), + array(0755, 0777) + ); + } + + public function acceptsFailsProvider() + { + return array( + array('5', '10'), + array(8, 5.0), + array(5.0, 8), + array(10, null), + array(false, 12) + ); + } + + public function assertEqualsSucceedsProvider() + { + return array( + array(1337, 1337), + array('1337', 1337), + array(0x539, 1337), + array(02471, 1337), + array(1337, 1338, 1), + array('1337', 1340, 5), + ); + } + + public function assertEqualsFailsProvider() + { + return array( + array(1337, 1338), + array('1338', 1337), + array(0x539, 1338), + array(1337, 1339, 1), + array('1337', 1340, 2), + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsSucceedsProvider + */ + public function testAcceptsSucceeds($expected, $actual) + { + $this->assertTrue( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual, $delta = 0.0) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual, $delta); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual, $delta = 0.0) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', 'matches expected' + ); + $this->comparator->assertEquals($expected, $actual, $delta); + } +} diff --git a/vendor/sebastian/comparator/tests/ObjectComparatorTest.php b/vendor/sebastian/comparator/tests/ObjectComparatorTest.php new file mode 100644 index 0000000..a958232 --- /dev/null +++ b/vendor/sebastian/comparator/tests/ObjectComparatorTest.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use stdClass; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\ObjectComparator + * + */ +class ObjectComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new ObjectComparator; + $this->comparator->setFactory(new Factory); + } + + public function acceptsSucceedsProvider() + { + return array( + array(new TestClass, new TestClass), + array(new stdClass, new stdClass), + array(new stdClass, new TestClass) + ); + } + + public function acceptsFailsProvider() + { + return array( + array(new stdClass, null), + array(null, new stdClass), + array(null, null) + ); + } + + public function assertEqualsSucceedsProvider() + { + // cyclic dependencies + $book1 = new Book; + $book1->author = new Author('Terry Pratchett'); + $book1->author->books[] = $book1; + $book2 = new Book; + $book2->author = new Author('Terry Pratchett'); + $book2->author->books[] = $book2; + + $object1 = new SampleClass(4, 8, 15); + $object2 = new SampleClass(4, 8, 15); + + return array( + array($object1, $object1), + array($object1, $object2), + array($book1, $book1), + array($book1, $book2), + array(new Struct(2.3), new Struct(2.5), 0.5) + ); + } + + public function assertEqualsFailsProvider() + { + $typeMessage = 'is not instance of expected class'; + $equalMessage = 'Failed asserting that two objects are equal.'; + + // cyclic dependencies + $book1 = new Book; + $book1->author = new Author('Terry Pratchett'); + $book1->author->books[] = $book1; + $book2 = new Book; + $book2->author = new Author('Terry Pratch'); + $book2->author->books[] = $book2; + + $book3 = new Book; + $book3->author = 'Terry Pratchett'; + $book4 = new stdClass; + $book4->author = 'Terry Pratchett'; + + $object1 = new SampleClass( 4, 8, 15); + $object2 = new SampleClass(16, 23, 42); + + return array( + array(new SampleClass(4, 8, 15), new SampleClass(16, 23, 42), $equalMessage), + array($object1, $object2, $equalMessage), + array($book1, $book2, $equalMessage), + array($book3, $book4, $typeMessage), + array(new Struct(2.3), new Struct(4.2), $equalMessage, 0.5) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsSucceedsProvider + */ + public function testAcceptsSucceeds($expected, $actual) + { + $this->assertTrue( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual, $delta = 0.0) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual, $delta); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual, $message, $delta = 0.0) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', $message + ); + $this->comparator->assertEquals($expected, $actual, $delta); + } +} diff --git a/vendor/sebastian/comparator/tests/ResourceComparatorTest.php b/vendor/sebastian/comparator/tests/ResourceComparatorTest.php new file mode 100644 index 0000000..a9e9e95 --- /dev/null +++ b/vendor/sebastian/comparator/tests/ResourceComparatorTest.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\ResourceComparator + * + */ +class ResourceComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new ResourceComparator; + } + + public function acceptsSucceedsProvider() + { + $tmpfile1 = tmpfile(); + $tmpfile2 = tmpfile(); + + return array( + array($tmpfile1, $tmpfile1), + array($tmpfile2, $tmpfile2), + array($tmpfile1, $tmpfile2) + ); + } + + public function acceptsFailsProvider() + { + $tmpfile1 = tmpfile(); + + return array( + array($tmpfile1, null), + array(null, $tmpfile1), + array(null, null) + ); + } + + public function assertEqualsSucceedsProvider() + { + $tmpfile1 = tmpfile(); + $tmpfile2 = tmpfile(); + + return array( + array($tmpfile1, $tmpfile1), + array($tmpfile2, $tmpfile2) + ); + } + + public function assertEqualsFailsProvider() + { + $tmpfile1 = tmpfile(); + $tmpfile2 = tmpfile(); + + return array( + array($tmpfile1, $tmpfile2), + array($tmpfile2, $tmpfile1) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsSucceedsProvider + */ + public function testAcceptsSucceeds($expected, $actual) + { + $this->assertTrue( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual) + { + $this->setExpectedException('SebastianBergmann\\Comparator\\ComparisonFailure'); + $this->comparator->assertEquals($expected, $actual); + } +} diff --git a/vendor/sebastian/comparator/tests/ScalarComparatorTest.php b/vendor/sebastian/comparator/tests/ScalarComparatorTest.php new file mode 100644 index 0000000..e5156aa --- /dev/null +++ b/vendor/sebastian/comparator/tests/ScalarComparatorTest.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\ScalarComparator + * + */ +class ScalarComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new ScalarComparator; + } + + public function acceptsSucceedsProvider() + { + return array( + array("string", "string"), + array(new ClassWithToString, "string"), + array("string", new ClassWithToString), + array("string", null), + array(false, "string"), + array(false, true), + array(null, false), + array(null, null), + array("10", 10), + array("", false), + array("1", true), + array(1, true), + array(0, false), + array(0.1, "0.1") + ); + } + + public function acceptsFailsProvider() + { + return array( + array(array(), array()), + array("string", array()), + array(new ClassWithToString, new ClassWithToString), + array(false, new ClassWithToString), + array(tmpfile(), tmpfile()) + ); + } + + public function assertEqualsSucceedsProvider() + { + return array( + array("string", "string"), + array(new ClassWithToString, new ClassWithToString), + array("string representation", new ClassWithToString), + array(new ClassWithToString, "string representation"), + array("string", "STRING", true), + array("STRING", "string", true), + array("String Representation", new ClassWithToString, true), + array(new ClassWithToString, "String Representation", true), + array("10", 10), + array("", false), + array("1", true), + array(1, true), + array(0, false), + array(0.1, "0.1"), + array(false, null), + array(false, false), + array(true, true), + array(null, null) + ); + } + + public function assertEqualsFailsProvider() + { + $stringException = 'Failed asserting that two strings are equal.'; + $otherException = 'matches expected'; + + return array( + array("string", "other string", $stringException), + array("string", "STRING", $stringException), + array("STRING", "string", $stringException), + array("string", "other string", $stringException), + // https://github.com/sebastianbergmann/phpunit/issues/1023 + array('9E6666666','9E7777777', $stringException), + array(new ClassWithToString, "does not match", $otherException), + array("does not match", new ClassWithToString, $otherException), + array(0, 'Foobar', $otherException), + array('Foobar', 0, $otherException), + array("10", 25, $otherException), + array("1", false, $otherException), + array("", true, $otherException), + array(false, true, $otherException), + array(true, false, $otherException), + array(null, true, $otherException), + array(0, true, $otherException) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsSucceedsProvider + */ + public function testAcceptsSucceeds($expected, $actual) + { + $this->assertTrue( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual, $ignoreCase = false) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual, 0.0, false, $ignoreCase); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual, $message) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', $message + ); + $this->comparator->assertEquals($expected, $actual); + } +} diff --git a/vendor/sebastian/comparator/tests/SplObjectStorageComparatorTest.php b/vendor/sebastian/comparator/tests/SplObjectStorageComparatorTest.php new file mode 100644 index 0000000..766dd2d --- /dev/null +++ b/vendor/sebastian/comparator/tests/SplObjectStorageComparatorTest.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use SplObjectStorage; +use stdClass; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\SplObjectStorageComparator + * + */ +class SplObjectStorageComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new SplObjectStorageComparator; + } + + public function acceptsFailsProvider() + { + return array( + array(new SplObjectStorage, new stdClass), + array(new stdClass, new SplObjectStorage), + array(new stdClass, new stdClass) + ); + } + + public function assertEqualsSucceedsProvider() + { + $object1 = new stdClass(); + $object2 = new stdClass(); + + $storage1 = new SplObjectStorage(); + $storage2 = new SplObjectStorage(); + + $storage3 = new SplObjectStorage(); + $storage3->attach($object1); + $storage3->attach($object2); + + $storage4 = new SplObjectStorage(); + $storage4->attach($object2); + $storage4->attach($object1); + + return array( + array($storage1, $storage1), + array($storage1, $storage2), + array($storage3, $storage3), + array($storage3, $storage4) + ); + } + + public function assertEqualsFailsProvider() + { + $object1 = new stdClass; + $object2 = new stdClass; + + $storage1 = new SplObjectStorage; + + $storage2 = new SplObjectStorage; + $storage2->attach($object1); + + $storage3 = new SplObjectStorage; + $storage3->attach($object2); + $storage3->attach($object1); + + return array( + array($storage1, $storage2), + array($storage1, $storage3), + array($storage2, $storage3), + ); + } + + /** + * @covers ::accepts + */ + public function testAcceptsSucceeds() + { + $this->assertTrue( + $this->comparator->accepts( + new SplObjectStorage, + new SplObjectStorage + ) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsFailsProvider + */ + public function testAcceptsFails($expected, $actual) + { + $this->assertFalse( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual) + { + $this->setExpectedException( + 'SebastianBergmann\\Comparator\\ComparisonFailure', + 'Failed asserting that two objects are equal.' + ); + $this->comparator->assertEquals($expected, $actual); + } +} diff --git a/vendor/sebastian/comparator/tests/TypeComparatorTest.php b/vendor/sebastian/comparator/tests/TypeComparatorTest.php new file mode 100644 index 0000000..bb51f8a --- /dev/null +++ b/vendor/sebastian/comparator/tests/TypeComparatorTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use stdClass; + +/** + * @coversDefaultClass SebastianBergmann\Comparator\TypeComparator + * + */ +class TypeComparatorTest extends \PHPUnit_Framework_TestCase +{ + private $comparator; + + protected function setUp() + { + $this->comparator = new TypeComparator; + } + + public function acceptsSucceedsProvider() + { + return array( + array(true, 1), + array(false, array(1)), + array(null, new stdClass), + array(1.0, 5), + array("", "") + ); + } + + public function assertEqualsSucceedsProvider() + { + return array( + array(true, true), + array(true, false), + array(false, false), + array(null, null), + array(new stdClass, new stdClass), + array(0, 0), + array(1.0, 2.0), + array("hello", "world"), + array("", ""), + array(array(), array(1,2,3)) + ); + } + + public function assertEqualsFailsProvider() + { + return array( + array(true, null), + array(null, false), + array(1.0, 0), + array(new stdClass, array()), + array("1", 1) + ); + } + + /** + * @covers ::accepts + * @dataProvider acceptsSucceedsProvider + */ + public function testAcceptsSucceeds($expected, $actual) + { + $this->assertTrue( + $this->comparator->accepts($expected, $actual) + ); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsSucceedsProvider + */ + public function testAssertEqualsSucceeds($expected, $actual) + { + $exception = null; + + try { + $this->comparator->assertEquals($expected, $actual); + } + + catch (ComparisonFailure $exception) { + } + + $this->assertNull($exception, 'Unexpected ComparisonFailure'); + } + + /** + * @covers ::assertEquals + * @dataProvider assertEqualsFailsProvider + */ + public function testAssertEqualsFails($expected, $actual) + { + $this->setExpectedException('SebastianBergmann\\Comparator\\ComparisonFailure', 'does not match expected type'); + $this->comparator->assertEquals($expected, $actual); + } +} diff --git a/vendor/sebastian/comparator/tests/_files/Author.php b/vendor/sebastian/comparator/tests/_files/Author.php new file mode 100644 index 0000000..ae698c3 --- /dev/null +++ b/vendor/sebastian/comparator/tests/_files/Author.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * An author. + * + */ +class Author +{ + // the order of properties is important for testing the cycle! + public $books = array(); + + private $name = ''; + + public function __construct($name) + { + $this->name = $name; + } +} diff --git a/vendor/sebastian/comparator/tests/_files/Book.php b/vendor/sebastian/comparator/tests/_files/Book.php new file mode 100644 index 0000000..6171bc3 --- /dev/null +++ b/vendor/sebastian/comparator/tests/_files/Book.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * A book. + * + */ +class Book +{ + // the order of properties is important for testing the cycle! + public $author = null; +} diff --git a/vendor/sebastian/comparator/tests/_files/ClassWithToString.php b/vendor/sebastian/comparator/tests/_files/ClassWithToString.php new file mode 100644 index 0000000..e8e03bc --- /dev/null +++ b/vendor/sebastian/comparator/tests/_files/ClassWithToString.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +class ClassWithToString +{ + public function __toString() + { + return 'string representation'; + } +} diff --git a/vendor/sebastian/comparator/tests/_files/SampleClass.php b/vendor/sebastian/comparator/tests/_files/SampleClass.php new file mode 100644 index 0000000..6079dd3 --- /dev/null +++ b/vendor/sebastian/comparator/tests/_files/SampleClass.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * A sample class. + * + */ +class SampleClass +{ + public $a; + protected $b; + protected $c; + + public function __construct($a, $b, $c) + { + $this->a = $a; + $this->b = $b; + $this->c = $c; + } +} diff --git a/vendor/sebastian/comparator/tests/_files/Struct.php b/vendor/sebastian/comparator/tests/_files/Struct.php new file mode 100644 index 0000000..79ec559 --- /dev/null +++ b/vendor/sebastian/comparator/tests/_files/Struct.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * A struct. + * + */ +class Struct +{ + public $var; + + public function __construct($var) + { + $this->var = $var; + } +} diff --git a/vendor/sebastian/comparator/tests/_files/TestClass.php b/vendor/sebastian/comparator/tests/_files/TestClass.php new file mode 100644 index 0000000..e4c9b78 --- /dev/null +++ b/vendor/sebastian/comparator/tests/_files/TestClass.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +class TestClass { +} diff --git a/vendor/sebastian/comparator/tests/_files/TestClassComparator.php b/vendor/sebastian/comparator/tests/_files/TestClassComparator.php new file mode 100644 index 0000000..52aac3f --- /dev/null +++ b/vendor/sebastian/comparator/tests/_files/TestClassComparator.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +class TestClassComparator extends ObjectComparator { +} diff --git a/vendor/sebastian/comparator/tests/autoload.php b/vendor/sebastian/comparator/tests/autoload.php new file mode 100644 index 0000000..f4d9bbc --- /dev/null +++ b/vendor/sebastian/comparator/tests/autoload.php @@ -0,0 +1,38 @@ + '/ArrayComparatorTest.php', + 'sebastianbergmann\\comparator\\author' => '/_files/Author.php', + 'sebastianbergmann\\comparator\\book' => '/_files/Book.php', + 'sebastianbergmann\\comparator\\classwithtostring' => '/_files/ClassWithToString.php', + 'sebastianbergmann\\comparator\\datetimecomparatortest' => '/DateTimeComparatorTest.php', + 'sebastianbergmann\\comparator\\domnodecomparatortest' => '/DOMNodeComparatorTest.php', + 'sebastianbergmann\\comparator\\doublecomparatortest' => '/DoubleComparatorTest.php', + 'sebastianbergmann\\comparator\\exceptioncomparatortest' => '/ExceptionComparatorTest.php', + 'sebastianbergmann\\comparator\\factorytest' => '/FactoryTest.php', + 'sebastianbergmann\\comparator\\mockobjectcomparatortest' => '/MockObjectComparatorTest.php', + 'sebastianbergmann\\comparator\\numericcomparatortest' => '/NumericComparatorTest.php', + 'sebastianbergmann\\comparator\\objectcomparatortest' => '/ObjectComparatorTest.php', + 'sebastianbergmann\\comparator\\resourcecomparatortest' => '/ResourceComparatorTest.php', + 'sebastianbergmann\\comparator\\sampleclass' => '/_files/SampleClass.php', + 'sebastianbergmann\\comparator\\scalarcomparatortest' => '/ScalarComparatorTest.php', + 'sebastianbergmann\\comparator\\splobjectstoragecomparatortest' => '/SplObjectStorageComparatorTest.php', + 'sebastianbergmann\\comparator\\struct' => '/_files/Struct.php', + 'sebastianbergmann\\comparator\\testclass' => '/_files/TestClass.php', + 'sebastianbergmann\\comparator\\testclasscomparator' => '/_files/TestClassComparator.php', + 'sebastianbergmann\\comparator\\typecomparatortest' => '/TypeComparatorTest.php' + ); + } + $cn = strtolower($class); + if (isset($classes[$cn])) { + require __DIR__ . $classes[$cn]; + } + } +); +// @codeCoverageIgnoreEnd diff --git a/vendor/sebastian/comparator/tests/bootstrap.php b/vendor/sebastian/comparator/tests/bootstrap.php new file mode 100644 index 0000000..8f1c57c --- /dev/null +++ b/vendor/sebastian/comparator/tests/bootstrap.php @@ -0,0 +1,7 @@ +files() + ->in('src') + ->in('tests') + ->name('*.php'); + +return Symfony\CS\Config\Config::create() + ->level(\Symfony\CS\FixerInterface::NONE_LEVEL) + ->fixers( + array( + 'align_double_arrow', + 'align_equals', + 'braces', + 'concat_with_spaces', + 'duplicate_semicolon', + 'elseif', + 'empty_return', + 'encoding', + 'eof_ending', + 'extra_empty_lines', + 'function_call_space', + 'function_declaration', + 'indentation', + 'join_function', + 'line_after_namespace', + 'linefeed', + 'list_commas', + 'lowercase_constants', + 'lowercase_keywords', + 'method_argument_space', + 'multiple_use', + 'namespace_no_leading_whitespace', + 'no_blank_lines_after_class_opening', + 'no_empty_lines_after_phpdocs', + 'parenthesis', + 'php_closing_tag', + 'phpdoc_indent', + 'phpdoc_no_access', + 'phpdoc_no_empty_return', + 'phpdoc_no_package', + 'phpdoc_params', + 'phpdoc_scalar', + 'phpdoc_separation', + 'phpdoc_to_comment', + 'phpdoc_trim', + 'phpdoc_types', + 'phpdoc_var_without_name', + 'remove_lines_between_uses', + 'return', + 'self_accessor', + 'short_tag', + 'single_line_after_imports', + 'single_quote', + 'spaces_before_semicolon', + 'spaces_cast', + 'ternary_spaces', + 'trailing_spaces', + 'trim_array_spaces', + 'unused_use', + 'visibility', + 'whitespacy_lines' + ) + ) + ->finder($finder); + diff --git a/vendor/sebastian/diff/.travis.yml b/vendor/sebastian/diff/.travis.yml new file mode 100644 index 0000000..c5ea677 --- /dev/null +++ b/vendor/sebastian/diff/.travis.yml @@ -0,0 +1,16 @@ +language: php + +install: + - travis_retry composer install --no-interaction --prefer-source + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +notifications: + email: false + irc: "irc.freenode.org#phpunit" diff --git a/vendor/sebastian/diff/LICENSE b/vendor/sebastian/diff/LICENSE new file mode 100644 index 0000000..0941c06 --- /dev/null +++ b/vendor/sebastian/diff/LICENSE @@ -0,0 +1,33 @@ +Diff + +Copyright (c) 2002-2015, Sebastian Bergmann . +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 Sebastian Bergmann nor the names of his + 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. diff --git a/vendor/sebastian/diff/README.md b/vendor/sebastian/diff/README.md new file mode 100644 index 0000000..921957b --- /dev/null +++ b/vendor/sebastian/diff/README.md @@ -0,0 +1,126 @@ +# Diff + +Diff implementation for PHP, factored out of PHPUnit into a stand-alone component. + +## Installation + +To add this package as a local, per-project dependency to your project, simply add a dependency on `sebastian/diff` to your project's `composer.json` file. Here is a minimal example of a `composer.json` file that just defines a dependency on Diff: + + { + "require": { + "sebastian/diff": "*" + } + } + +### Usage + +The `Differ` class can be used to generate a textual representation of the difference between two strings: + +```php +use SebastianBergmann\Diff\Differ; + +$differ = new Differ; +print $differ->diff('foo', 'bar'); +``` + +The code above yields the output below: + + --- Original + +++ New + @@ @@ + -foo + +bar + +The `Parser` class can be used to parse a unified diff into an object graph: + +```php +use SebastianBergmann\Diff\Parser; +use SebastianBergmann\Git; + +$git = new Git('/usr/local/src/money'); + +$diff = $git->getDiff( + '948a1a07768d8edd10dcefa8315c1cbeffb31833', + 'c07a373d2399f3e686234c4f7f088d635eb9641b' +); + +$parser = new Parser; + +print_r($parser->parse($diff)); +``` + +The code above yields the output below: + + Array + ( + [0] => SebastianBergmann\Diff\Diff Object + ( + [from:SebastianBergmann\Diff\Diff:private] => a/tests/MoneyTest.php + [to:SebastianBergmann\Diff\Diff:private] => b/tests/MoneyTest.php + [chunks:SebastianBergmann\Diff\Diff:private] => Array + ( + [0] => SebastianBergmann\Diff\Chunk Object + ( + [start:SebastianBergmann\Diff\Chunk:private] => 87 + [startRange:SebastianBergmann\Diff\Chunk:private] => 7 + [end:SebastianBergmann\Diff\Chunk:private] => 87 + [endRange:SebastianBergmann\Diff\Chunk:private] => 7 + [lines:SebastianBergmann\Diff\Chunk:private] => Array + ( + [0] => SebastianBergmann\Diff\Line Object + ( + [type:SebastianBergmann\Diff\Line:private] => 3 + [content:SebastianBergmann\Diff\Line:private] => * @covers SebastianBergmann\Money\Money::add + ) + + [1] => SebastianBergmann\Diff\Line Object + ( + [type:SebastianBergmann\Diff\Line:private] => 3 + [content:SebastianBergmann\Diff\Line:private] => * @covers SebastianBergmann\Money\Money::newMoney + ) + + [2] => SebastianBergmann\Diff\Line Object + ( + [type:SebastianBergmann\Diff\Line:private] => 3 + [content:SebastianBergmann\Diff\Line:private] => */ + ) + + [3] => SebastianBergmann\Diff\Line Object + ( + [type:SebastianBergmann\Diff\Line:private] => 2 + [content:SebastianBergmann\Diff\Line:private] => public function testAnotherMoneyWithSameCurrencyObjectCanBeAdded() + ) + + [4] => SebastianBergmann\Diff\Line Object + ( + [type:SebastianBergmann\Diff\Line:private] => 1 + [content:SebastianBergmann\Diff\Line:private] => public function testAnotherMoneyObjectWithSameCurrencyCanBeAdded() + ) + + [5] => SebastianBergmann\Diff\Line Object + ( + [type:SebastianBergmann\Diff\Line:private] => 3 + [content:SebastianBergmann\Diff\Line:private] => { + ) + + [6] => SebastianBergmann\Diff\Line Object + ( + [type:SebastianBergmann\Diff\Line:private] => 3 + [content:SebastianBergmann\Diff\Line:private] => $a = new Money(1, new Currency('EUR')); + ) + + [7] => SebastianBergmann\Diff\Line Object + ( + [type:SebastianBergmann\Diff\Line:private] => 3 + [content:SebastianBergmann\Diff\Line:private] => $b = new Money(2, new Currency('EUR')); + ) + + ) + + ) + + ) + + ) + + ) diff --git a/vendor/sebastian/diff/build.xml b/vendor/sebastian/diff/build.xml new file mode 100644 index 0000000..7366087 --- /dev/null +++ b/vendor/sebastian/diff/build.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sebastian/diff/composer.json b/vendor/sebastian/diff/composer.json new file mode 100644 index 0000000..4b4b8d1 --- /dev/null +++ b/vendor/sebastian/diff/composer.json @@ -0,0 +1,33 @@ +{ + "name": "sebastian/diff", + "description": "Diff implementation", + "keywords": ["diff"], + "homepage": "https://github.com/sebastianbergmann/diff", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + } +} diff --git a/vendor/sebastian/diff/phpunit.xml.dist b/vendor/sebastian/diff/phpunit.xml.dist new file mode 100644 index 0000000..387a9b3 --- /dev/null +++ b/vendor/sebastian/diff/phpunit.xml.dist @@ -0,0 +1,17 @@ + + + + tests + + + + + src + + + + diff --git a/vendor/sebastian/diff/src/Chunk.php b/vendor/sebastian/diff/src/Chunk.php new file mode 100644 index 0000000..eb08103 --- /dev/null +++ b/vendor/sebastian/diff/src/Chunk.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + */ +class Chunk +{ + /** + * @var int + */ + private $start; + + /** + * @var int + */ + private $startRange; + + /** + * @var int + */ + private $end; + /** + * @var int + */ + private $endRange; + + /** + * @var array + */ + private $lines; + + /** + * @param int $start + * @param int $startRange + * @param int $end + * @param int $endRange + * @param array $lines + */ + public function __construct($start = 0, $startRange = 1, $end = 0, $endRange = 1, array $lines = array()) + { + $this->start = (int) $start; + $this->startRange = (int) $startRange; + $this->end = (int) $end; + $this->endRange = (int) $endRange; + $this->lines = $lines; + } + + /** + * @return int + */ + public function getStart() + { + return $this->start; + } + + /** + * @return int + */ + public function getStartRange() + { + return $this->startRange; + } + + /** + * @return int + */ + public function getEnd() + { + return $this->end; + } + + /** + * @return int + */ + public function getEndRange() + { + return $this->endRange; + } + + /** + * @return array + */ + public function getLines() + { + return $this->lines; + } + + /** + * @param array $lines + */ + public function setLines(array $lines) + { + $this->lines = $lines; + } +} diff --git a/vendor/sebastian/diff/src/Diff.php b/vendor/sebastian/diff/src/Diff.php new file mode 100644 index 0000000..c4a0892 --- /dev/null +++ b/vendor/sebastian/diff/src/Diff.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + */ +class Diff +{ + /** + * @var string + */ + private $from; + + /** + * @var string + */ + private $to; + + /** + * @var Chunk[] + */ + private $chunks; + + /** + * @param string $from + * @param string $to + * @param Chunk[] $chunks + */ + public function __construct($from, $to, array $chunks = array()) + { + $this->from = $from; + $this->to = $to; + $this->chunks = $chunks; + } + + /** + * @return string + */ + public function getFrom() + { + return $this->from; + } + + /** + * @return string + */ + public function getTo() + { + return $this->to; + } + + /** + * @return Chunk[] + */ + public function getChunks() + { + return $this->chunks; + } + + /** + * @param Chunk[] $chunks + */ + public function setChunks(array $chunks) + { + $this->chunks = $chunks; + } +} diff --git a/vendor/sebastian/diff/src/Differ.php b/vendor/sebastian/diff/src/Differ.php new file mode 100644 index 0000000..4960111 --- /dev/null +++ b/vendor/sebastian/diff/src/Differ.php @@ -0,0 +1,261 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +use SebastianBergmann\Diff\LCS\LongestCommonSubsequence; +use SebastianBergmann\Diff\LCS\TimeEfficientImplementation; +use SebastianBergmann\Diff\LCS\MemoryEfficientImplementation; + +/** + * Diff implementation. + */ +class Differ +{ + /** + * @var string + */ + private $header; + + /** + * @var bool + */ + private $showNonDiffLines; + + /** + * @param string $header + */ + public function __construct($header = "--- Original\n+++ New\n", $showNonDiffLines = true) + { + $this->header = $header; + $this->showNonDiffLines = $showNonDiffLines; + } + + /** + * Returns the diff between two arrays or strings as string. + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequence $lcs + * + * @return string + */ + public function diff($from, $to, LongestCommonSubsequence $lcs = null) + { + if (!is_array($from) && !is_string($from)) { + $from = (string) $from; + } + + if (!is_array($to) && !is_string($to)) { + $to = (string) $to; + } + + $buffer = $this->header; + $diff = $this->diffToArray($from, $to, $lcs); + + $inOld = false; + $i = 0; + $old = array(); + + foreach ($diff as $line) { + if ($line[1] === 0 /* OLD */) { + if ($inOld === false) { + $inOld = $i; + } + } elseif ($inOld !== false) { + if (($i - $inOld) > 5) { + $old[$inOld] = $i - 1; + } + + $inOld = false; + } + + ++$i; + } + + $start = isset($old[0]) ? $old[0] : 0; + $end = count($diff); + + if ($tmp = array_search($end, $old)) { + $end = $tmp; + } + + $newChunk = true; + + for ($i = $start; $i < $end; $i++) { + if (isset($old[$i])) { + $buffer .= "\n"; + $newChunk = true; + $i = $old[$i]; + } + + if ($newChunk) { + if ($this->showNonDiffLines === true) { + $buffer .= "@@ @@\n"; + } + $newChunk = false; + } + + if ($diff[$i][1] === 1 /* ADDED */) { + $buffer .= '+' . $diff[$i][0] . "\n"; + } elseif ($diff[$i][1] === 2 /* REMOVED */) { + $buffer .= '-' . $diff[$i][0] . "\n"; + } elseif ($this->showNonDiffLines === true) { + $buffer .= ' ' . $diff[$i][0] . "\n"; + } + } + + return $buffer; + } + + /** + * Returns the diff between two arrays or strings as array. + * + * Each array element contains two elements: + * - [0] => string $token + * - [1] => 2|1|0 + * + * - 2: REMOVED: $token was removed from $from + * - 1: ADDED: $token was added to $from + * - 0: OLD: $token is not changed in $to + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequence $lcs + * + * @return array + */ + public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null) + { + preg_match_all('(\r\n|\r|\n)', $from, $fromMatches); + preg_match_all('(\r\n|\r|\n)', $to, $toMatches); + + if (is_string($from)) { + $from = preg_split('(\r\n|\r|\n)', $from); + } + + if (is_string($to)) { + $to = preg_split('(\r\n|\r|\n)', $to); + } + + $start = array(); + $end = array(); + $fromLength = count($from); + $toLength = count($to); + $length = min($fromLength, $toLength); + + for ($i = 0; $i < $length; ++$i) { + if ($from[$i] === $to[$i]) { + $start[] = $from[$i]; + unset($from[$i], $to[$i]); + } else { + break; + } + } + + $length -= $i; + + for ($i = 1; $i < $length; ++$i) { + if ($from[$fromLength - $i] === $to[$toLength - $i]) { + array_unshift($end, $from[$fromLength - $i]); + unset($from[$fromLength - $i], $to[$toLength - $i]); + } else { + break; + } + } + + if ($lcs === null) { + $lcs = $this->selectLcsImplementation($from, $to); + } + + $common = $lcs->calculate(array_values($from), array_values($to)); + $diff = array(); + + if (isset($fromMatches[0]) && $toMatches[0] && + count($fromMatches[0]) === count($toMatches[0]) && + $fromMatches[0] !== $toMatches[0]) { + $diff[] = array( + '#Warning: Strings contain different line endings!', 0 + ); + } + + foreach ($start as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + reset($from); + reset($to); + + foreach ($common as $token) { + while ((($fromToken = reset($from)) !== $token)) { + $diff[] = array(array_shift($from), 2 /* REMOVED */); + } + + while ((($toToken = reset($to)) !== $token)) { + $diff[] = array(array_shift($to), 1 /* ADDED */); + } + + $diff[] = array($token, 0 /* OLD */); + + array_shift($from); + array_shift($to); + } + + while (($token = array_shift($from)) !== null) { + $diff[] = array($token, 2 /* REMOVED */); + } + + while (($token = array_shift($to)) !== null) { + $diff[] = array($token, 1 /* ADDED */); + } + + foreach ($end as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + return $diff; + } + + /** + * @param array $from + * @param array $to + * + * @return LongestCommonSubsequence + */ + private function selectLcsImplementation(array $from, array $to) + { + // We do not want to use the time-efficient implementation if its memory + // footprint will probably exceed this value. Note that the footprint + // calculation is only an estimation for the matrix and the LCS method + // will typically allocate a bit more memory than this. + $memoryLimit = 100 * 1024 * 1024; + + if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { + return new MemoryEfficientImplementation; + } + + return new TimeEfficientImplementation; + } + + /** + * Calculates the estimated memory footprint for the DP-based method. + * + * @param array $from + * @param array $to + * + * @return int + */ + private function calculateEstimatedFootprint(array $from, array $to) + { + $itemSize = PHP_INT_SIZE == 4 ? 76 : 144; + + return $itemSize * pow(min(count($from), count($to)), 2); + } +} diff --git a/vendor/sebastian/diff/src/LCS/LongestCommonSubsequence.php b/vendor/sebastian/diff/src/LCS/LongestCommonSubsequence.php new file mode 100644 index 0000000..5ea9cf9 --- /dev/null +++ b/vendor/sebastian/diff/src/LCS/LongestCommonSubsequence.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +/** + * Interface for implementations of longest common subsequence calculation. + */ +interface LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to); +} diff --git a/vendor/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php b/vendor/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php new file mode 100644 index 0000000..b990dc0 --- /dev/null +++ b/vendor/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +/** + * Memory-efficient implementation of longest common subsequence calculation. + */ +class MemoryEfficientImplementation implements LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to) + { + $cFrom = count($from); + $cTo = count($to); + + if ($cFrom == 0) { + return array(); + } elseif ($cFrom == 1) { + if (in_array($from[0], $to)) { + return array($from[0]); + } else { + return array(); + } + } else { + $i = intval($cFrom / 2); + $fromStart = array_slice($from, 0, $i); + $fromEnd = array_slice($from, $i); + $llB = $this->length($fromStart, $to); + $llE = $this->length(array_reverse($fromEnd), array_reverse($to)); + $jMax = 0; + $max = 0; + + for ($j = 0; $j <= $cTo; $j++) { + $m = $llB[$j] + $llE[$cTo - $j]; + + if ($m >= $max) { + $max = $m; + $jMax = $j; + } + } + + $toStart = array_slice($to, 0, $jMax); + $toEnd = array_slice($to, $jMax); + + return array_merge( + $this->calculate($fromStart, $toStart), + $this->calculate($fromEnd, $toEnd) + ); + } + } + + /** + * @param array $from + * @param array $to + * + * @return array + */ + private function length(array $from, array $to) + { + $current = array_fill(0, count($to) + 1, 0); + $cFrom = count($from); + $cTo = count($to); + + for ($i = 0; $i < $cFrom; $i++) { + $prev = $current; + + for ($j = 0; $j < $cTo; $j++) { + if ($from[$i] == $to[$j]) { + $current[$j + 1] = $prev[$j] + 1; + } else { + $current[$j + 1] = max($current[$j], $prev[$j + 1]); + } + } + } + + return $current; + } +} diff --git a/vendor/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php b/vendor/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php new file mode 100644 index 0000000..4fc9d3e --- /dev/null +++ b/vendor/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +/** + * Time-efficient implementation of longest common subsequence calculation. + */ +class TimeEfficientImplementation implements LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to) + { + $common = array(); + $fromLength = count($from); + $toLength = count($to); + $width = $fromLength + 1; + $matrix = new \SplFixedArray($width * ($toLength + 1)); + + for ($i = 0; $i <= $fromLength; ++$i) { + $matrix[$i] = 0; + } + + for ($j = 0; $j <= $toLength; ++$j) { + $matrix[$j * $width] = 0; + } + + for ($i = 1; $i <= $fromLength; ++$i) { + for ($j = 1; $j <= $toLength; ++$j) { + $o = ($j * $width) + $i; + $matrix[$o] = max( + $matrix[$o - 1], + $matrix[$o - $width], + $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 + ); + } + } + + $i = $fromLength; + $j = $toLength; + + while ($i > 0 && $j > 0) { + if ($from[$i-1] === $to[$j-1]) { + $common[] = $from[$i-1]; + --$i; + --$j; + } else { + $o = ($j * $width) + $i; + if ($matrix[$o - $width] > $matrix[$o - 1]) { + --$j; + } else { + --$i; + } + } + } + + return array_reverse($common); + } +} diff --git a/vendor/sebastian/diff/src/Line.php b/vendor/sebastian/diff/src/Line.php new file mode 100644 index 0000000..e0a96b9 --- /dev/null +++ b/vendor/sebastian/diff/src/Line.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + */ +class Line +{ + const ADDED = 1; + const REMOVED = 2; + const UNCHANGED = 3; + + /** + * @var int + */ + private $type; + + /** + * @var string + */ + private $content; + + /** + * @param int $type + * @param string $content + */ + public function __construct($type = self::UNCHANGED, $content = '') + { + $this->type = $type; + $this->content = $content; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @return int + */ + public function getType() + { + return $this->type; + } +} diff --git a/vendor/sebastian/diff/src/Parser.php b/vendor/sebastian/diff/src/Parser.php new file mode 100644 index 0000000..b01511b --- /dev/null +++ b/vendor/sebastian/diff/src/Parser.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + * Unified diff parser. + */ +class Parser +{ + /** + * @param string $string + * + * @return Diff[] + */ + public function parse($string) + { + $lines = preg_split('(\r\n|\r|\n)', $string); + $lineCount = count($lines); + $diffs = array(); + $diff = null; + $collected = array(); + + for ($i = 0; $i < $lineCount; ++$i) { + if (preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && + preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { + if ($diff !== null) { + $this->parseFileDiff($diff, $collected); + $diffs[] = $diff; + $collected = array(); + } + + $diff = new Diff($fromMatch['file'], $toMatch['file']); + ++$i; + } else { + if (preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { + continue; + } + $collected[] = $lines[$i]; + } + } + + if (count($collected) && ($diff !== null)) { + $this->parseFileDiff($diff, $collected); + $diffs[] = $diff; + } + + return $diffs; + } + + /** + * @param Diff $diff + * @param array $lines + */ + private function parseFileDiff(Diff $diff, array $lines) + { + $chunks = array(); + + foreach ($lines as $line) { + if (preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { + $chunk = new Chunk( + $match['start'], + isset($match['startrange']) ? max(1, $match['startrange']) : 1, + $match['end'], + isset($match['endrange']) ? max(1, $match['endrange']) : 1 + ); + + $chunks[] = $chunk; + $diffLines = array(); + continue; + } + + if (preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { + $type = Line::UNCHANGED; + + if ($match['type'] == '+') { + $type = Line::ADDED; + } elseif ($match['type'] == '-') { + $type = Line::REMOVED; + } + + $diffLines[] = new Line($type, $match['line']); + + if (isset($chunk)) { + $chunk->setLines($diffLines); + } + } + } + + $diff->setChunks($chunks); + } +} diff --git a/vendor/sebastian/diff/tests/DifferTest.php b/vendor/sebastian/diff/tests/DifferTest.php new file mode 100644 index 0000000000000000000000000000000000000000..12d0cfeff376f7ecc3a3c4ff9b702f16f3d048aa GIT binary patch literal 11492 zcmeHNZExC05YE@*SFBK^kN^qhEvd=%NC{Odr6o$peL|`hdkI#-=IkYDuF8MEnOz%f z@C&BUT6gDs0M;|J&%Er;<8?0on2l$-vy&V-A^ox8kdZNA1U_ff=ENEiK4#?F7>!^+ z`$`|#DM+GlK|3b*Y)Bn$P?Nl4_T!YACb@LHu{Ic& z#&FJ=PM!@u25E4?vF5hMU?N?hKFj4yI%N(x!j{lIr{164Yw|m(wtT}^*ViyeU|8`F_ik_yhbs^k z{C0K%naAsdw4bQ$B5{Y##OKU5o+xMJP={zT+f|&|7H1kJXEZl8%q?*kf0z}Hg_6XG zh7U1s<+oB9W;P-5m>0B!_g~k6K%LSlp|(w5K%ae^iHTfLq%Ch_b(oLjaAaH4h+EuR z+|Vhfx3=|U=**Uuy0>#?6ohNbjr)8ED`**B z4&@xje{di&foX0uCs?)x8Qn#2OEQq~!L5i4LZLF7 zcmJBxiBs?^`V%Y@?S`fxk|S)EMBW3Kf`E>X6UxEM_wLJq!95>Oa04#6}~r7&hMzR_0l+g`c$}tN__K{ce}u zpg$)@tuuGHHT}RSG)>Vz9~`Ey^B3&(KpOnarEt`FxbOEqkq_Og>+anV^e#CYFb9|K z1iIzoCQ^%hK)2l{?e@Sd(}7tW4$OQhAnS52*;NZi^r%FRhDWkNqpe`moP{uk{>zLx zJz)HHe@somAFfROUXUl@^erL{aa(rv7&pr0GU)-^H%vMK@Wmqe%$^6P=a2$p)pdu~ z$wEOXk!qAnetsI{eV7(E#U3S$>=Xg^hl*RZ`&()Q15#DM;V^gVXsRZq3nGKiRz2FX zMy`Z4ue_v^WI~wVLm+zd35Vi4lyc>_;!~PUD!av`FuybuUoRqlUTD@9g3b8cHYIs3 z1gcc%1&0uP#PIT#sAq>wMyl#Q#~cUCA4Tz!mZ0)W@Q{Jx;`wG-Gg1nc!@4@o5de#_ zU@0)Rq89-?Vi*m20C9n)aY$Rpr|@VP?vl12pXf5`8zo6w%BS!NX9=W4qzP;V26vSw zI$Q>RBd4goR7m9&s0BO_ZlSfn=#(s?H)3(Xvu42&;3Mtmdmp9uVTAIv8(rEuyet1ql1 zx7rkwKnw*6ZH=%A7`yB^NkAyD;G(uph27N{7Le`QL^2SFMiKVv@R*488>lXcaJ#)d z84O|>Mq&vI3nB~v+^o$cK|v7FP;ie5OzP%sFHl54084aSBGBw7G5`nmA{ib0TzQ!D zB9UUhT81)y!}34VtZi6_@YG_^iSaknzW`B%hl$I}%Wm%`M^#1rZm5-3PpzJ-`Iu+w zyjoMgJ8G?_<_}`T4)6>y-*Qy2W=-@{{*B|Wnv$T#EmsL%4lI-!2};^{Zhm$0EsEG& zFD@dz=%937Z3s7FObfXKhr_F~e9KT3fM2gfBk_sfQS*_ZP7Pg-iImiVvwClW8o-VU z>q2;8zK-S-J(w6K6Tzu3f)kzM8xYQ`)ua@o*a3$Fg$3|tG;kpdFR9>lmM8+4y$YA2 zDEZba1~#_nwm~G~o(>V9!Al^jW)4v;V~Ae8yzP9MHMUr0N+r!YYoc(fb#{?%ohh5_ zrN5x8|6gdM(I8|qk}F>>{R}KLD9xgxRN+rTs%LEN8*1a%BZ456{dsF<57K!yAYrS8 zOEk8NUk}M`{vM(?nL`B4Bnx~*Bu%6Zd#zw3JMXp1`4-I<8}#O!Hhr@d8k>{a^F5K; zEhywNWmzG$eni9x>l;bBB>klE<9{vc(b*RE{Y8ECce7!LhRTsb(t@V#?S z{c2L2ak(cHzf~(r9cV?lQYV!QwV|k$>S?p7Tr@Avt8YlLQma(pOb6a?yQdQo3Jp}K zRXhcF4_;PEHBwYcb@7V7hzSPvGBAnDVZy(mVxy#hp=RkqXvWLK43akn%y~Qs2K`^% zB7@P`Zvy;zsR6c|rKSWCt3ZnGf(o}BauPO&e+_nzr3x!2RLf&}JThDUTAYEs=KcXP CM%6h0 literal 0 HcmV?d00001 diff --git a/vendor/sebastian/diff/tests/LCS/TimeEfficientImplementationTest.php b/vendor/sebastian/diff/tests/LCS/TimeEfficientImplementationTest.php new file mode 100644 index 0000000..0f12d89 --- /dev/null +++ b/vendor/sebastian/diff/tests/LCS/TimeEfficientImplementationTest.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +use PHPUnit_Framework_TestCase; + +/** + * Some of these tests are volontary stressfull, in order to give some approximative benchmark hints. + */ +class TimeEfficientImplementationTest extends PHPUnit_Framework_TestCase +{ + private $implementation; + private $memory_limit; + private $stress_sizes = array(1, 2, 3, 100, 500, 1000, 2000); + + protected function setUp() + { + $this->memory_limit = ini_get('memory_limit'); + ini_set('memory_limit', '256M'); + + $this->implementation = new TimeEfficientImplementation; + } + + protected function tearDown() + { + ini_set('memory_limit', $this->memory_limit); + } + + public function testBothEmpty() + { + $from = array(); + $to = array(); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals(array(), $common); + } + + public function testIsStrictComparison() + { + $from = array( + false, 0, 0.0, '', null, array(), + true, 1, 1.0, 'foo', array('foo', 'bar'), array('foo' => 'bar') + ); + $to = $from; + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($from, $common); + + $to = array( + false, false, false, false, false, false, + true, true, true, true, true, true + ); + $expected = array( + false, + true, + ); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($expected, $common); + } + + public function testEqualSequences() + { + foreach ($this->stress_sizes as $size) { + $range = range(1, $size); + $from = $range; + $to = $range; + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($range, $common); + } + } + + public function testDistinctSequences() + { + $from = array('A'); + $to = array('B'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals(array(), $common); + + $from = array('A', 'B', 'C'); + $to = array('D', 'E', 'F'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals(array(), $common); + + foreach ($this->stress_sizes as $size) { + $from = range(1, $size); + $to = range($size + 1, $size * 2); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals(array(), $common); + } + } + + public function testCommonSubsequence() + { + $from = array('A', 'C', 'E', 'F', 'G'); + $to = array('A', 'B', 'D', 'E', 'H'); + $expected = array('A', 'E'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals($expected, $common); + + $from = array('A', 'C', 'E', 'F', 'G'); + $to = array('B', 'C', 'D', 'E', 'F', 'H'); + $expected = array('C', 'E', 'F'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals($expected, $common); + + foreach ($this->stress_sizes as $size) { + $from = $size < 2 ? array(1) : range(1, $size + 1, 2); + $to = $size < 3 ? array(1) : range(1, $size + 1, 3); + $expected = $size < 6 ? array(1) : range(1, $size + 1, 6); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($expected, $common); + } + } + + public function testSingleElementSubsequenceAtStart() + { + foreach ($this->stress_sizes as $size) { + $from = range(1, $size); + $to = array_slice($from, 0, 1); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($to, $common); + } + } + + public function testSingleElementSubsequenceAtMiddle() + { + foreach ($this->stress_sizes as $size) { + $from = range(1, $size); + $to = array_slice($from, (int) $size / 2, 1); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($to, $common); + } + } + + public function testSingleElementSubsequenceAtEnd() + { + foreach ($this->stress_sizes as $size) { + $from = range(1, $size); + $to = array_slice($from, $size - 1, 1); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals($to, $common); + } + } + + public function testReversedSequences() + { + $from = array('A', 'B'); + $to = array('B', 'A'); + $expected = array('A'); + $common = $this->implementation->calculate($from, $to); + $this->assertEquals($expected, $common); + + foreach ($this->stress_sizes as $size) { + $from = range(1, $size); + $to = array_reverse($from); + $common = $this->implementation->calculate($from, $to); + + $this->assertEquals(array(1), $common); + } + } +} diff --git a/vendor/sebastian/diff/tests/ParserTest.php b/vendor/sebastian/diff/tests/ParserTest.php new file mode 100644 index 0000000..3a7b972 --- /dev/null +++ b/vendor/sebastian/diff/tests/ParserTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +use PHPUnit_Framework_TestCase; + +class ParserTest extends PHPUnit_Framework_TestCase +{ + /** + * @var Parser + */ + private $parser; + + protected function setUp() + { + $this->parser = new Parser; + } + + public function testParse() + { + $content = file_get_contents(__DIR__ . '/fixtures/patch.txt'); + + $diffs = $this->parser->parse($content); + + $this->assertCount(1, $diffs); + + $chunks = $diffs[0]->getChunks(); + $this->assertCount(1, $chunks); + + $this->assertEquals(20, $chunks[0]->getStart()); + + $this->assertCount(5, $chunks[0]->getLines()); + } + + public function testParseWithMultipleChunks() + { + $content = file_get_contents(__DIR__ . '/fixtures/patch2.txt'); + + $diffs = $this->parser->parse($content); + + $this->assertCount(1, $diffs); + + $chunks = $diffs[0]->getChunks(); + $this->assertCount(3, $chunks); + + $this->assertEquals(20, $chunks[0]->getStart()); + $this->assertEquals(320, $chunks[1]->getStart()); + $this->assertEquals(600, $chunks[2]->getStart()); + + $this->assertCount(5, $chunks[0]->getLines()); + $this->assertCount(5, $chunks[1]->getLines()); + $this->assertCount(5, $chunks[2]->getLines()); + } +} diff --git a/vendor/sebastian/diff/tests/fixtures/patch.txt b/vendor/sebastian/diff/tests/fixtures/patch.txt new file mode 100644 index 0000000..144b61d --- /dev/null +++ b/vendor/sebastian/diff/tests/fixtures/patch.txt @@ -0,0 +1,9 @@ +diff --git a/Foo.php b/Foo.php +index abcdefg..abcdefh 100644 +--- a/Foo.php ++++ b/Foo.php +@@ -20,4 +20,5 @@ class Foo + const ONE = 1; + const TWO = 2; ++ const THREE = 3; + const FOUR = 4; diff --git a/vendor/sebastian/diff/tests/fixtures/patch2.txt b/vendor/sebastian/diff/tests/fixtures/patch2.txt new file mode 100644 index 0000000..41fbc95 --- /dev/null +++ b/vendor/sebastian/diff/tests/fixtures/patch2.txt @@ -0,0 +1,21 @@ +diff --git a/Foo.php b/Foo.php +index abcdefg..abcdefh 100644 +--- a/Foo.php ++++ b/Foo.php +@@ -20,4 +20,5 @@ class Foo + const ONE = 1; + const TWO = 2; ++ const THREE = 3; + const FOUR = 4; + +@@ -320,4 +320,5 @@ class Foo + const A = 'A'; + const B = 'B'; ++ const C = 'C'; + const D = 'D'; + +@@ -600,4 +600,5 @@ class Foo + public function doSomething() { + ++ return 'foo'; + } diff --git a/vendor/sebastian/environment/.gitignore b/vendor/sebastian/environment/.gitignore new file mode 100644 index 0000000..5794854 --- /dev/null +++ b/vendor/sebastian/environment/.gitignore @@ -0,0 +1,5 @@ +/.idea +/vendor +/composer.lock +/composer.phar +/phpunit.xml diff --git a/vendor/sebastian/environment/.travis.yml b/vendor/sebastian/environment/.travis.yml new file mode 100644 index 0000000..76a9bde --- /dev/null +++ b/vendor/sebastian/environment/.travis.yml @@ -0,0 +1,16 @@ +language: php + +before_script: + - composer self-update + - composer install --no-interaction --prefer-source --dev + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +notifications: + email: false diff --git a/vendor/sebastian/environment/LICENSE b/vendor/sebastian/environment/LICENSE new file mode 100644 index 0000000..08539af --- /dev/null +++ b/vendor/sebastian/environment/LICENSE @@ -0,0 +1,33 @@ +Environment + +Copyright (c) 2014-2015, Sebastian Bergmann . +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 Sebastian Bergmann nor the names of his + 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. diff --git a/vendor/sebastian/environment/README.md b/vendor/sebastian/environment/README.md new file mode 100644 index 0000000..5f3cb3b --- /dev/null +++ b/vendor/sebastian/environment/README.md @@ -0,0 +1,72 @@ +# Environment + +This component provides functionality that helps writing PHP code that has runtime-specific (PHP / HHVM) execution paths. + +[![Latest Stable Version](https://poser.pugx.org/sebastian/environment/v/stable.png)](https://packagist.org/packages/sebastian/environment) +[![Build Status](https://travis-ci.org/sebastianbergmann/environment.png?branch=master)](https://travis-ci.org/sebastianbergmann/environment) + +## Installation + +To add Environment as a local, per-project dependency to your project, simply add a dependency on `sebastian/environment` to your project's `composer.json` file. Here is a minimal example of a `composer.json` file that just defines a dependency on Environment 1.0: + + { + "require": { + "sebastian/environment": "1.0.*" + } + } + +## Usage + +```php +getNameWithVersion()); +var_dump($runtime->getName()); +var_dump($runtime->getVersion()); +var_dump($runtime->getBinary()); +var_dump($runtime->isHHVM()); +var_dump($runtime->isPHP()); +var_dump($runtime->hasXdebug()); +var_dump($runtime->canCollectCodeCoverage()); +``` + +### Output on PHP + + $ php --version + PHP 5.5.8 (cli) (built: Jan 9 2014 08:33:30) + Copyright (c) 1997-2013 The PHP Group + Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies + with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans + + + $ php example.php + string(9) "PHP 5.5.8" + string(3) "PHP" + string(5) "5.5.8" + string(14) "'/usr/bin/php'" + bool(false) + bool(true) + bool(true) + bool(true) + +### Output on HHVM + + $ hhvm --version + HipHop VM 2.4.0-dev (rel) + Compiler: heads/master-0-ga98e57cabee7e7f0d14493ab17d5c7ab0157eb98 + Repo schema: 8d6e69287c41c1f09bb4d327421720d1922cfc67 + + + $ hhvm example.php + string(14) "HHVM 2.4.0-dev" + string(4) "HHVM" + string(9) "2.4.0-dev" + string(42) "'/usr/local/src/hhvm/hphp/hhvm/hhvm' --php" + bool(true) + bool(false) + bool(false) + bool(true) + diff --git a/vendor/sebastian/environment/build.xml b/vendor/sebastian/environment/build.xml new file mode 100644 index 0000000..92fb1c5 --- /dev/null +++ b/vendor/sebastian/environment/build.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sebastian/environment/composer.json b/vendor/sebastian/environment/composer.json new file mode 100644 index 0000000..fd3ec7d --- /dev/null +++ b/vendor/sebastian/environment/composer.json @@ -0,0 +1,29 @@ +{ + "name": "sebastian/environment", + "description": "Provides functionality to handle HHVM/PHP environments", + "keywords": ["environment","hhvm","xdebug"], + "homepage": "http://www.github.com/sebastianbergmann/environment", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + } +} diff --git a/vendor/sebastian/environment/phpunit.xml.dist b/vendor/sebastian/environment/phpunit.xml.dist new file mode 100644 index 0000000..75148fd --- /dev/null +++ b/vendor/sebastian/environment/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + tests + + + + + src + + + diff --git a/vendor/sebastian/environment/src/Console.php b/vendor/sebastian/environment/src/Console.php new file mode 100644 index 0000000..f5d5f92 --- /dev/null +++ b/vendor/sebastian/environment/src/Console.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Environment; + +/** + */ +class Console +{ + const STDIN = 0; + const STDOUT = 1; + const STDERR = 2; + + /** + * Returns true if STDOUT supports colorization. + * + * This code has been copied and adapted from + * Symfony\Component\Console\Output\OutputStream. + * + * @return bool + */ + public function hasColorSupport() + { + if (DIRECTORY_SEPARATOR == '\\') { + return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + } + + if (!defined('STDOUT')) { + return false; + } + + return $this->isInteractive(STDOUT); + } + + /** + * Returns the number of columns of the terminal. + * + * @return int + */ + public function getNumberOfColumns() + { + // Windows terminals have a fixed size of 80 + // but one column is used for the cursor. + if (DIRECTORY_SEPARATOR == '\\') { + return 79; + } + + if (!$this->isInteractive(self::STDIN)) { + return 80; + } + + if (preg_match('#\d+ (\d+)#', shell_exec('stty size'), $match) === 1) { + return (int) $match[1]; + } + + if (preg_match('#columns = (\d+);#', shell_exec('stty'), $match) === 1) { + return (int) $match[1]; + } + + return 80; + } + + /** + * Returns if the file descriptor is an interactive terminal or not. + * + * @param int|resource $fileDescriptor + * + * @return bool + */ + public function isInteractive($fileDescriptor = self::STDOUT) + { + return function_exists('posix_isatty') && @posix_isatty($fileDescriptor); + } +} diff --git a/vendor/sebastian/environment/src/Runtime.php b/vendor/sebastian/environment/src/Runtime.php new file mode 100644 index 0000000..f12071a --- /dev/null +++ b/vendor/sebastian/environment/src/Runtime.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Environment; + +/** + * Utility class for HHVM/PHP environment handling. + */ +class Runtime +{ + /** + * @var string + */ + private static $binary; + + /** + * Returns true when Xdebug is supported or + * the runtime used is PHPDBG (PHP >= 7.0). + * + * @return bool + */ + public function canCollectCodeCoverage() + { + return $this->hasXdebug() || $this->hasPHPDBGCodeCoverage(); + } + + /** + * Returns the path to the binary of the current runtime. + * Appends ' --php' to the path when the runtime is HHVM. + * + * @return string + */ + public function getBinary() + { + // HHVM + if (self::$binary === null && $this->isHHVM()) { + if ((self::$binary = getenv('PHP_BINARY')) === false) { + self::$binary = PHP_BINARY; + } + + self::$binary = escapeshellarg(self::$binary) . ' --php'; + } + + // PHP >= 5.4.0 + if (self::$binary === null && defined('PHP_BINARY')) { + self::$binary = escapeshellarg(PHP_BINARY); + } + + // PHP < 5.4.0 + if (self::$binary === null) { + if (PHP_SAPI == 'cli' && isset($_SERVER['_'])) { + if (strpos($_SERVER['_'], 'phpunit') !== false) { + $file = file($_SERVER['_']); + + if (strpos($file[0], ' ') !== false) { + $tmp = explode(' ', $file[0]); + self::$binary = escapeshellarg(trim($tmp[1])); + } else { + self::$binary = escapeshellarg(ltrim(trim($file[0]), '#!')); + } + } elseif (strpos(basename($_SERVER['_']), 'php') !== false) { + self::$binary = escapeshellarg($_SERVER['_']); + } + } + } + + if (self::$binary === null) { + $possibleBinaryLocations = array( + PHP_BINDIR . '/php', + PHP_BINDIR . '/php-cli.exe', + PHP_BINDIR . '/php.exe' + ); + + foreach ($possibleBinaryLocations as $binary) { + if (is_readable($binary)) { + self::$binary = escapeshellarg($binary); + break; + } + } + } + + if (self::$binary === null) { + self::$binary = 'php'; + } + + return self::$binary; + } + + /** + * @return string + */ + public function getNameWithVersion() + { + return $this->getName() . ' ' . $this->getVersion(); + } + + /** + * @return string + */ + public function getName() + { + if ($this->isHHVM()) { + return 'HHVM'; + } elseif ($this->isPHPDBG()) { + return 'PHPDBG'; + } else { + return 'PHP'; + } + } + + /** + * @return string + */ + public function getVendorUrl() + { + if ($this->isHHVM()) { + return 'http://hhvm.com/'; + } else { + return 'http://php.net/'; + } + } + + /** + * @return string + */ + public function getVersion() + { + if ($this->isHHVM()) { + return HHVM_VERSION; + } else { + return PHP_VERSION; + } + } + + /** + * Returns true when the runtime used is PHP and Xdebug is loaded. + * + * @return bool + */ + public function hasXdebug() + { + return ($this->isPHP() || $this->isHHVM()) && extension_loaded('xdebug'); + } + + /** + * Returns true when the runtime used is HHVM. + * + * @return bool + */ + public function isHHVM() + { + return defined('HHVM_VERSION'); + } + + /** + * Returns true when the runtime used is PHP without the PHPDBG SAPI. + * + * @return bool + */ + public function isPHP() + { + return !$this->isHHVM() && !$this->isPHPDBG(); + } + + /** + * Returns true when the runtime used is PHP with the PHPDBG SAPI. + * + * @return bool + */ + public function isPHPDBG() + { + return PHP_SAPI === 'phpdbg' && !$this->isHHVM(); + } + + /** + * Returns true when the runtime used is PHP with the PHPDBG SAPI + * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). + * + * @return bool + */ + public function hasPHPDBGCodeCoverage() + { + return $this->isPHPDBG() && function_exists('phpdbg_start_oplog'); + } +} diff --git a/vendor/sebastian/environment/tests/ConsoleTest.php b/vendor/sebastian/environment/tests/ConsoleTest.php new file mode 100644 index 0000000..6b40172 --- /dev/null +++ b/vendor/sebastian/environment/tests/ConsoleTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Environment; + +use PHPUnit_Framework_TestCase; + +class ConsoleTest extends PHPUnit_Framework_TestCase +{ + /** + * @var \SebastianBergmann\Environment\Console + */ + private $console; + + protected function setUp() + { + $this->console = new Console; + } + + /** + * @covers \SebastianBergmann\Environment\Console::isInteractive + */ + public function testCanDetectIfStdoutIsInteractiveByDefault() + { + $this->assertInternalType('boolean', $this->console->isInteractive()); + } + + /** + * @covers \SebastianBergmann\Environment\Console::isInteractive + */ + public function testCanDetectIfFileDescriptorIsInteractive() + { + $this->assertInternalType('boolean', $this->console->isInteractive(STDOUT)); + } + + /** + * @covers \SebastianBergmann\Environment\Console::hasColorSupport + * @uses \SebastianBergmann\Environment\Console::isInteractive + */ + public function testCanDetectColorSupport() + { + $this->assertInternalType('boolean', $this->console->hasColorSupport()); + } + + /** + * @covers \SebastianBergmann\Environment\Console::getNumberOfColumns + * @uses \SebastianBergmann\Environment\Console::isInteractive + */ + public function testCanDetectNumberOfColumns() + { + $this->assertInternalType('integer', $this->console->getNumberOfColumns()); + } +} diff --git a/vendor/sebastian/environment/tests/RuntimeTest.php b/vendor/sebastian/environment/tests/RuntimeTest.php new file mode 100644 index 0000000..8ea8373 --- /dev/null +++ b/vendor/sebastian/environment/tests/RuntimeTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Environment; + +use PHPUnit_Framework_TestCase; + +class RuntimeTest extends PHPUnit_Framework_TestCase +{ + /** + * @var \SebastianBergmann\Environment\Runtime + */ + private $env; + + protected function setUp() + { + $this->env = new Runtime; + } + + /** + * @covers \SebastianBergmann\Environment\Runtime::canCollectCodeCoverage + * @uses \SebastianBergmann\Environment\Runtime::hasXdebug + * @uses \SebastianBergmann\Environment\Runtime::isHHVM + * @uses \SebastianBergmann\Environment\Runtime::isPHP + */ + public function testAbilityToCollectCodeCoverageCanBeAssessed() + { + $this->assertInternalType('boolean', $this->env->canCollectCodeCoverage()); + } + + /** + * @covers \SebastianBergmann\Environment\Runtime::getBinary + * @uses \SebastianBergmann\Environment\Runtime::isHHVM + */ + public function testBinaryCanBeRetrieved() + { + $this->assertInternalType('string', $this->env->getBinary()); + } + + /** + * @covers \SebastianBergmann\Environment\Runtime::isHHVM + */ + public function testCanBeDetected() + { + $this->assertInternalType('boolean', $this->env->isHHVM()); + } + + /** + * @covers \SebastianBergmann\Environment\Runtime::isPHP + * @uses \SebastianBergmann\Environment\Runtime::isHHVM + */ + public function testCanBeDetected2() + { + $this->assertInternalType('boolean', $this->env->isPHP()); + } + + /** + * @covers \SebastianBergmann\Environment\Runtime::hasXdebug + * @uses \SebastianBergmann\Environment\Runtime::isHHVM + * @uses \SebastianBergmann\Environment\Runtime::isPHP + */ + public function testXdebugCanBeDetected() + { + $this->assertInternalType('boolean', $this->env->hasXdebug()); + } + + /** + * @covers \SebastianBergmann\Environment\Runtime::getNameWithVersion + * @uses \SebastianBergmann\Environment\Runtime::getName + * @uses \SebastianBergmann\Environment\Runtime::getVersion + * @uses \SebastianBergmann\Environment\Runtime::isHHVM + * @uses \SebastianBergmann\Environment\Runtime::isPHP + */ + public function testNameAndVersionCanBeRetrieved() + { + $this->assertInternalType('string', $this->env->getNameWithVersion()); + } + + /** + * @covers \SebastianBergmann\Environment\Runtime::getName + * @uses \SebastianBergmann\Environment\Runtime::isHHVM + */ + public function testNameCanBeRetrieved() + { + $this->assertInternalType('string', $this->env->getName()); + } + + /** + * @covers \SebastianBergmann\Environment\Runtime::getVersion + * @uses \SebastianBergmann\Environment\Runtime::isHHVM + */ + public function testVersionCanBeRetrieved() + { + $this->assertInternalType('string', $this->env->getVersion()); + } + + /** + * @covers \SebastianBergmann\Environment\Runtime::getVendorUrl + * @uses \SebastianBergmann\Environment\Runtime::isHHVM + */ + public function testVendorUrlCanBeRetrieved() + { + $this->assertInternalType('string', $this->env->getVendorUrl()); + } +} diff --git a/vendor/sebastian/exporter/.gitignore b/vendor/sebastian/exporter/.gitignore new file mode 100644 index 0000000..3beb10f --- /dev/null +++ b/vendor/sebastian/exporter/.gitignore @@ -0,0 +1,9 @@ +.idea +phpunit.xml +composer.lock +composer.phar +vendor/ +cache.properties +build/LICENSE +build/README.md +build/*.tgz diff --git a/vendor/sebastian/exporter/.travis.yml b/vendor/sebastian/exporter/.travis.yml new file mode 100644 index 0000000..657519b --- /dev/null +++ b/vendor/sebastian/exporter/.travis.yml @@ -0,0 +1,23 @@ +language: php + +before_script: + - composer self-update + - composer install --no-interaction --prefer-source --dev + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +notifications: + email: false + webhooks: + urls: + - https://webhooks.gitter.im/e/6668f52f3dd4e3f81960 + on_success: always + on_failure: always + on_start: false + diff --git a/vendor/sebastian/exporter/LICENSE b/vendor/sebastian/exporter/LICENSE new file mode 100644 index 0000000..55a13d4 --- /dev/null +++ b/vendor/sebastian/exporter/LICENSE @@ -0,0 +1,33 @@ +Exporter + +Copyright (c) 2002-2015, Sebastian Bergmann . +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 Sebastian Bergmann nor the names of his + 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. diff --git a/vendor/sebastian/exporter/README.md b/vendor/sebastian/exporter/README.md new file mode 100644 index 0000000..9a40a77 --- /dev/null +++ b/vendor/sebastian/exporter/README.md @@ -0,0 +1,171 @@ +Exporter +======== + +[![Build Status](https://secure.travis-ci.org/sebastianbergmann/exporter.png?branch=master)](https://travis-ci.org/sebastianbergmann/exporter) + +This component provides the functionality to export PHP variables for visualization. + +## Usage + +Exporting: + +```php + '' + 'string' => '' + 'code' => 0 + 'file' => '/home/sebastianbergmann/test.php' + 'line' => 34 + 'trace' => Array &0 () + 'previous' => null +) +*/ + +print $exporter->export(new Exception); +``` + +## Data Types + +Exporting simple types: + +```php +export(46); + +// 4.0 +print $exporter->export(4.0); + +// 'hello, world!' +print $exporter->export('hello, world!'); + +// false +print $exporter->export(false); + +// NAN +print $exporter->export(acos(8)); + +// -INF +print $exporter->export(log(0)); + +// null +print $exporter->export(null); + +// resource(13) of type (stream) +print $exporter->export(fopen('php://stderr', 'w')); + +// Binary String: 0x000102030405 +print $exporter->export(chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5)); +``` + +Exporting complex types: + +```php + Array &1 ( + 0 => 1 + 1 => 2 + 2 => 3 + ) + 1 => Array &2 ( + 0 => '' + 1 => 0 + 2 => false + ) +) +*/ + +print $exporter->export(array(array(1,2,3), array("",0,FALSE))); + +/* +Array &0 ( + 'self' => Array &1 ( + 'self' => Array &1 + ) +) +*/ + +$array = array(); +$array['self'] = &$array; +print $exporter->export($array); + +/* +stdClass Object &0000000003a66dcc0000000025e723e2 ( + 'self' => stdClass Object &0000000003a66dcc0000000025e723e2 +) +*/ + +$obj = new stdClass(); +$obj->self = $obj; +print $exporter->export($obj); +``` + +Compact exports: + +```php +shortenedExport(array()); + +// Array (...) +print $exporter->shortenedExport(array(1,2,3,4,5)); + +// stdClass Object () +print $exporter->shortenedExport(new stdClass); + +// Exception Object (...) +print $exporter->shortenedExport(new Exception); + +// this\nis\na\nsuper\nlong\nstring\nt...\nspace +print $exporter->shortenedExport( +<< + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sebastian/exporter/composer.json b/vendor/sebastian/exporter/composer.json new file mode 100644 index 0000000..723596e --- /dev/null +++ b/vendor/sebastian/exporter/composer.json @@ -0,0 +1,47 @@ +{ + "name": "sebastian/exporter", + "description": "Provides the functionality to export PHP variables for visualization", + "keywords": ["exporter","export"], + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + } +} + diff --git a/vendor/sebastian/exporter/phpunit.xml.dist b/vendor/sebastian/exporter/phpunit.xml.dist new file mode 100644 index 0000000..8d85af6 --- /dev/null +++ b/vendor/sebastian/exporter/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + tests + + + + + src + + + + diff --git a/vendor/sebastian/exporter/src/Exporter.php b/vendor/sebastian/exporter/src/Exporter.php new file mode 100644 index 0000000..a9f5157 --- /dev/null +++ b/vendor/sebastian/exporter/src/Exporter.php @@ -0,0 +1,296 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Exporter; + +use SebastianBergmann\RecursionContext\Context; + +/** + * A nifty utility for visualizing PHP variables. + * + * + * export(new Exception); + * + */ +class Exporter +{ + /** + * Exports a value as a string + * + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - TRUE is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param mixed $value + * @param int $indentation The indentation level of the 2nd+ line + * @return string + */ + public function export($value, $indentation = 0) + { + return $this->recursiveExport($value, $indentation); + } + + /** + * @param mixed $data + * @param Context $context + * @return string + */ + public function shortenedRecursiveExport(&$data, Context $context = null) + { + $result = array(); + $exporter = new self(); + + if (!$context) { + $context = new Context; + } + + $context->add($data); + + foreach ($data as $key => $value) { + if (is_array($value)) { + if ($context->contains($data[$key]) !== false) { + $result[] = '*RECURSION*'; + } + + else { + $result[] = sprintf( + 'array(%s)', + $this->shortenedRecursiveExport($data[$key], $context) + ); + } + } + + else { + $result[] = $exporter->shortenedExport($value); + } + } + + return implode(', ', $result); + } + + /** + * Exports a value into a single-line string + * + * The output of this method is similar to the output of + * SebastianBergmann\Exporter\Exporter::export. This method guarantees + * thought that the result contains now newlines. + * + * Newlines are replaced by the visible string '\n'. Contents of arrays + * and objects (if any) are replaced by '...'. + * + * @param mixed $value + * @return string + * @see SebastianBergmann\Exporter\Exporter::export + */ + public function shortenedExport($value) + { + if (is_string($value)) { + $string = $this->export($value); + + if (strlen($string) > 40) { + $string = substr($string, 0, 30) . '...' . substr($string, -7); + } + + return str_replace("\n", '\n', $string); + } + + if (is_object($value)) { + return sprintf( + '%s Object (%s)', + get_class($value), + count($this->toArray($value)) > 0 ? '...' : '' + ); + } + + if (is_array($value)) { + return sprintf( + 'Array (%s)', + count($value) > 0 ? '...' : '' + ); + } + + return $this->export($value); + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param mixed $value + * @return array + */ + public function toArray($value) + { + if (!is_object($value)) { + return (array) $value; + } + + $array = array(); + + foreach ((array) $value as $key => $val) { + // properties are transformed to keys in the following way: + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { + $key = $matches[1]; + } + + // See https://github.com/php/php-src/commit/5721132 + if ($key === "\0gcdata") { + continue; + } + + $array[$key] = $val; + } + + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection in Zend. + // Format the output similarly to print_r() in this case + if ($value instanceof \SplObjectStorage) { + // However, the fast method does work in HHVM, and exposes the + // internal implementation. Hide it again. + if (property_exists('\SplObjectStorage', '__storage')) { + unset($array['__storage']); + } elseif (property_exists('\SplObjectStorage', 'storage')) { + unset($array['storage']); + } + + if (property_exists('\SplObjectStorage', '__key')) { + unset($array['__key']); + } + + foreach ($value as $key => $val) { + $array[spl_object_hash($val)] = array( + 'obj' => $val, + 'inf' => $value->getInfo(), + ); + } + } + + return $array; + } + + /** + * Recursive implementation of export + * + * @param mixed $value The value to export + * @param int $indentation The indentation level of the 2nd+ line + * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects + * @return string + * @see SebastianBergmann\Exporter\Exporter::export + */ + protected function recursiveExport(&$value, $indentation, $processed = null) + { + if ($value === null) { + return 'null'; + } + + if ($value === true) { + return 'true'; + } + + if ($value === false) { + return 'false'; + } + + if (is_float($value) && floatval(intval($value)) === $value) { + return "$value.0"; + } + + if (is_resource($value)) { + return sprintf( + 'resource(%d) of type (%s)', + $value, + get_resource_type($value) + ); + } + + if (is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + + return "'" . + str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . + "'"; + } + + $whitespace = str_repeat(' ', 4 * $indentation); + + if (!$processed) { + $processed = new Context; + } + + if (is_array($value)) { + if (($key = $processed->contains($value)) !== false) { + return 'Array &' . $key; + } + + $key = $processed->add($value); + $values = ''; + + if (count($value) > 0) { + foreach ($value as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + $this->recursiveExport($k, $indentation), + $this->recursiveExport($value[$k], $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('Array &%s (%s)', $key, $values); + } + + if (is_object($value)) { + $class = get_class($value); + + if ($hash = $processed->contains($value)) { + return sprintf('%s Object &%s', $class, $hash); + } + + $hash = $processed->add($value); + $values = ''; + $array = $this->toArray($value); + + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + $this->recursiveExport($k, $indentation), + $this->recursiveExport($v, $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('%s Object &%s (%s)', $class, $hash, $values); + } + + return var_export($value, true); + } +} diff --git a/vendor/sebastian/exporter/tests/ExporterTest.php b/vendor/sebastian/exporter/tests/ExporterTest.php new file mode 100644 index 0000000..6b590bf --- /dev/null +++ b/vendor/sebastian/exporter/tests/ExporterTest.php @@ -0,0 +1,333 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Exporter; + +/** + * @covers SebastianBergmann\Exporter\Exporter + */ +class ExporterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Exporter + */ + private $exporter; + + protected function setUp() + { + $this->exporter = new Exporter; + } + + public function exportProvider() + { + $obj2 = new \stdClass; + $obj2->foo = 'bar'; + + $obj3 = (object)array(1,2,"Test\r\n",4,5,6,7,8); + + $obj = new \stdClass; + //@codingStandardsIgnoreStart + $obj->null = null; + //@codingStandardsIgnoreEnd + $obj->boolean = true; + $obj->integer = 1; + $obj->double = 1.2; + $obj->string = '1'; + $obj->text = "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext"; + $obj->object = $obj2; + $obj->objectagain = $obj2; + $obj->array = array('foo' => 'bar'); + $obj->self = $obj; + + $storage = new \SplObjectStorage; + $storage->attach($obj2); + $storage->foo = $obj2; + + return array( + array(null, 'null'), + array(true, 'true'), + array(false, 'false'), + array(1, '1'), + array(1.0, '1.0'), + array(1.2, '1.2'), + array(fopen('php://memory', 'r'), 'resource(%d) of type (stream)'), + array('1', "'1'"), + array(array(array(1,2,3), array(3,4,5)), + << Array &1 ( + 0 => 1 + 1 => 2 + 2 => 3 + ) + 1 => Array &2 ( + 0 => 3 + 1 => 4 + 2 => 5 + ) +) +EOF + ), + // \n\r and \r is converted to \n + array("this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", + << null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object &%x ( + 'foo' => 'bar' + ) + 'objectagain' => stdClass Object &%x + 'array' => Array &%d ( + 'foo' => 'bar' + ) + 'self' => stdClass Object &%x +) +EOF + ), + array(array(), 'Array &%d ()'), + array($storage, + << stdClass Object &%x ( + 'foo' => 'bar' + ) + '%x' => Array &0 ( + 'obj' => stdClass Object &%x + 'inf' => null + ) +) +EOF + ), + array($obj3, + << 1 + 1 => 2 + 2 => 'Test\n' + 3 => 4 + 4 => 5 + 5 => 6 + 6 => 7 + 7 => 8 +) +EOF + ), + array( + chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5), + 'Binary String: 0x000102030405' + ), + array( + implode('', array_map('chr', range(0x0e, 0x1f))), + 'Binary String: 0x0e0f101112131415161718191a1b1c1d1e1f' + ), + array( + chr(0x00) . chr(0x09), + 'Binary String: 0x0009' + ), + array( + '', + "''" + ), + ); + } + + /** + * @dataProvider exportProvider + */ + public function testExport($value, $expected) + { + $this->assertStringMatchesFormat( + $expected, + $this->trimNewline($this->exporter->export($value)) + ); + } + + public function testExport2() + { + if (PHP_VERSION === '5.3.3') { + $this->markTestSkipped('Skipped due to "Nesting level too deep - recursive dependency?" fatal error'); + } + + $obj = new \stdClass; + $obj->foo = 'bar'; + + $array = array( + 0 => 0, + 'null' => null, + 'boolean' => true, + 'integer' => 1, + 'double' => 1.2, + 'string' => '1', + 'text' => "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", + 'object' => $obj, + 'objectagain' => $obj, + 'array' => array('foo' => 'bar'), + ); + + $array['self'] = &$array; + + $expected = << 0 + 'null' => null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object &%x ( + 'foo' => 'bar' + ) + 'objectagain' => stdClass Object &%x + 'array' => Array &%d ( + 'foo' => 'bar' + ) + 'self' => Array &%d ( + 0 => 0 + 'null' => null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object &%x + 'objectagain' => stdClass Object &%x + 'array' => Array &%d ( + 'foo' => 'bar' + ) + 'self' => Array &%d + ) +) +EOF; + + $this->assertStringMatchesFormat( + $expected, + $this->trimNewline($this->exporter->export($array)) + ); + } + + public function shortenedExportProvider() + { + $obj = new \stdClass; + $obj->foo = 'bar'; + + $array = array( + 'foo' => 'bar', + ); + + return array( + array(null, 'null'), + array(true, 'true'), + array(1, '1'), + array(1.0, '1.0'), + array(1.2, '1.2'), + array('1', "'1'"), + // \n\r and \r is converted to \n + array("this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", "'this\\nis\\na\\nvery\\nvery\\nvery\\nvery...g\\ntext'"), + array(new \stdClass, 'stdClass Object ()'), + array($obj, 'stdClass Object (...)'), + array(array(), 'Array ()'), + array($array, 'Array (...)'), + ); + } + + /** + * @dataProvider shortenedExportProvider + */ + public function testShortenedExport($value, $expected) + { + $this->assertSame( + $expected, + $this->trimNewline($this->exporter->shortenedExport($value)) + ); + } + + public function provideNonBinaryMultibyteStrings() + { + return array( + array(implode('', array_map('chr', range(0x09, 0x0d))), 5), + array(implode('', array_map('chr', range(0x20, 0x7f))), 96), + array(implode('', array_map('chr', range(0x80, 0xff))), 128), + ); + } + + + /** + * @dataProvider provideNonBinaryMultibyteStrings + */ + public function testNonBinaryStringExport($value, $expectedLength) + { + $this->assertRegExp( + "~'.{{$expectedLength}}'\$~s", + $this->exporter->export($value) + ); + } + + public function testNonObjectCanBeReturnedAsArray() + { + $this->assertEquals(array(true), $this->exporter->toArray(true)); + } + + private function trimNewline($string) + { + return preg_replace('/[ ]*\n/', "\n", $string); + } +} diff --git a/vendor/sebastian/global-state/.gitignore b/vendor/sebastian/global-state/.gitignore new file mode 100644 index 0000000..464180e --- /dev/null +++ b/vendor/sebastian/global-state/.gitignore @@ -0,0 +1,6 @@ +.idea +composer.lock +composer.phar +vendor/ +cache.properties +phpunit.xml diff --git a/vendor/sebastian/global-state/.travis.yml b/vendor/sebastian/global-state/.travis.yml new file mode 100644 index 0000000..0be6154 --- /dev/null +++ b/vendor/sebastian/global-state/.travis.yml @@ -0,0 +1,20 @@ +language: php + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +sudo: false + +before_script: + - composer self-update + - composer install --no-interaction --prefer-source --dev + +script: ./vendor/bin/phpunit + +notifications: + email: false diff --git a/vendor/sebastian/global-state/LICENSE b/vendor/sebastian/global-state/LICENSE new file mode 100644 index 0000000..a555bcd --- /dev/null +++ b/vendor/sebastian/global-state/LICENSE @@ -0,0 +1,33 @@ +GlobalState + +Copyright (c) 2001-2015, Sebastian Bergmann . +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 Sebastian Bergmann nor the names of his + 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. diff --git a/vendor/sebastian/global-state/README.md b/vendor/sebastian/global-state/README.md new file mode 100644 index 0000000..c5e262b --- /dev/null +++ b/vendor/sebastian/global-state/README.md @@ -0,0 +1,15 @@ +# GlobalState + +Snapshotting of global state, factored out of PHPUnit into a stand-alone component. + +[![Build Status](https://travis-ci.org/sebastianbergmann/global-state.svg?branch=master)](https://travis-ci.org/sebastianbergmann/global-state) + +## Installation + +To add this package as a local, per-project dependency to your project, simply add a dependency on `sebastian/global-state` to your project's `composer.json` file. Here is a minimal example of a `composer.json` file that just defines a dependency on GlobalState: + + { + "require": { + "sebastian/global-state": "1.0.*" + } + } diff --git a/vendor/sebastian/global-state/build.xml b/vendor/sebastian/global-state/build.xml new file mode 100644 index 0000000..63c5445 --- /dev/null +++ b/vendor/sebastian/global-state/build.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sebastian/global-state/composer.json b/vendor/sebastian/global-state/composer.json new file mode 100644 index 0000000..7e8849b --- /dev/null +++ b/vendor/sebastian/global-state/composer.json @@ -0,0 +1,37 @@ +{ + "name": "sebastian/global-state", + "description": "Snapshotting of global state", + "keywords": ["global state"], + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "autoload-dev": { + "classmap": [ + "tests/_fixture/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/vendor/sebastian/global-state/phpunit.xml.dist b/vendor/sebastian/global-state/phpunit.xml.dist new file mode 100644 index 0000000..d607918 --- /dev/null +++ b/vendor/sebastian/global-state/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + tests + + + + + src + + + + + + + + diff --git a/vendor/sebastian/global-state/src/Blacklist.php b/vendor/sebastian/global-state/src/Blacklist.php new file mode 100644 index 0000000..5d54091 --- /dev/null +++ b/vendor/sebastian/global-state/src/Blacklist.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +use ReflectionClass; + +/** + * A blacklist for global state elements that should not be snapshotted. + */ +class Blacklist +{ + /** + * @var array + */ + private $globalVariables = array(); + + /** + * @var array + */ + private $classes = array(); + + /** + * @var array + */ + private $classNamePrefixes = array(); + + /** + * @var array + */ + private $parentClasses = array(); + + /** + * @var array + */ + private $interfaces = array(); + + /** + * @var array + */ + private $staticAttributes = array(); + + /** + * @param string $variableName + */ + public function addGlobalVariable($variableName) + { + $this->globalVariables[$variableName] = true; + } + + /** + * @param string $className + */ + public function addClass($className) + { + $this->classes[] = $className; + } + + /** + * @param string $className + */ + public function addSubclassesOf($className) + { + $this->parentClasses[] = $className; + } + + /** + * @param string $interfaceName + */ + public function addImplementorsOf($interfaceName) + { + $this->interfaces[] = $interfaceName; + } + + /** + * @param string $classNamePrefix + */ + public function addClassNamePrefix($classNamePrefix) + { + $this->classNamePrefixes[] = $classNamePrefix; + } + + /** + * @param string $className + * @param string $attributeName + */ + public function addStaticAttribute($className, $attributeName) + { + if (!isset($this->staticAttributes[$className])) { + $this->staticAttributes[$className] = array(); + } + + $this->staticAttributes[$className][$attributeName] = true; + } + + /** + * @param string $variableName + * @return bool + */ + public function isGlobalVariableBlacklisted($variableName) + { + return isset($this->globalVariables[$variableName]); + } + + /** + * @param string $className + * @param string $attributeName + * @return bool + */ + public function isStaticAttributeBlacklisted($className, $attributeName) + { + if (in_array($className, $this->classes)) { + return true; + } + + foreach ($this->classNamePrefixes as $prefix) { + if (strpos($className, $prefix) === 0) { + return true; + } + } + + $class = new ReflectionClass($className); + + foreach ($this->parentClasses as $type) { + if ($class->isSubclassOf($type)) { + return true; + } + } + + foreach ($this->interfaces as $type) { + if ($class->implementsInterface($type)) { + return true; + } + } + + if (isset($this->staticAttributes[$className][$attributeName])) { + return true; + } + + return false; + } +} diff --git a/vendor/sebastian/global-state/src/CodeExporter.php b/vendor/sebastian/global-state/src/CodeExporter.php new file mode 100644 index 0000000..d595c22 --- /dev/null +++ b/vendor/sebastian/global-state/src/CodeExporter.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +/** + * Exports parts of a Snapshot as PHP code. + */ +class CodeExporter +{ + /** + * @param Snapshot $snapshot + * @return string + */ + public function constants(Snapshot $snapshot) + { + $result = ''; + + foreach ($snapshot->constants() as $name => $value) { + $result .= sprintf( + 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", + $name, + $name, + $this->exportVariable($value) + ); + } + + return $result; + } + + /** + * @param Snapshot $snapshot + * @return string + */ + public function iniSettings(Snapshot $snapshot) + { + $result = ''; + + foreach ($snapshot->iniSettings() as $key => $value) { + $result .= sprintf( + '@ini_set(%s, %s);' . "\n", + $this->exportVariable($key), + $this->exportVariable($value) + ); + } + + return $result; + } + + /** + * @param mixed $variable + * @return string + */ + private function exportVariable($variable) + { + if (is_scalar($variable) || is_null($variable) || + (is_array($variable) && $this->arrayOnlyContainsScalars($variable))) { + return var_export($variable, true); + } + + return 'unserialize(' . var_export(serialize($variable), true) . ')'; + } + + /** + * @param array $array + * @return bool + */ + private function arrayOnlyContainsScalars(array $array) + { + $result = true; + + foreach ($array as $element) { + if (is_array($element)) { + $result = self::arrayOnlyContainsScalars($element); + } elseif (!is_scalar($element) && !is_null($element)) { + $result = false; + } + + if ($result === false) { + break; + } + } + + return $result; + } +} diff --git a/vendor/sebastian/global-state/src/Exception.php b/vendor/sebastian/global-state/src/Exception.php new file mode 100644 index 0000000..1e00212 --- /dev/null +++ b/vendor/sebastian/global-state/src/Exception.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +/** + */ +interface Exception +{ +} diff --git a/vendor/sebastian/global-state/src/Restorer.php b/vendor/sebastian/global-state/src/Restorer.php new file mode 100644 index 0000000..78e0ba8 --- /dev/null +++ b/vendor/sebastian/global-state/src/Restorer.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +use ReflectionProperty; + +/** + * Restorer of snapshots of global state. + */ +class Restorer +{ + /** + * Deletes function definitions that are not defined in a snapshot. + * + * @param Snapshot $snapshot + * @throws RuntimeException when the uopz_delete() function is not available + * @see https://github.com/krakjoe/uopz + */ + public function restoreFunctions(Snapshot $snapshot) + { + if (!function_exists('uopz_delete')) { + throw new RuntimeException('The uopz_delete() function is required for this operation'); + } + + $functions = get_defined_functions(); + + foreach (array_diff($functions['user'], $snapshot->functions()) as $function) { + uopz_delete($function); + } + } + + /** + * Restores all global and super-global variables from a snapshot. + * + * @param Snapshot $snapshot + */ + public function restoreGlobalVariables(Snapshot $snapshot) + { + $superGlobalArrays = $snapshot->superGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); + } + + $globalVariables = $snapshot->globalVariables(); + + foreach (array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !in_array($key, $superGlobalArrays) && + !$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) { + if (isset($globalVariables[$key])) { + $GLOBALS[$key] = $globalVariables[$key]; + } else { + unset($GLOBALS[$key]); + } + } + } + } + + /** + * Restores all static attributes in user-defined classes from this snapshot. + * + * @param Snapshot $snapshot + */ + public function restoreStaticAttributes(Snapshot $snapshot) + { + $current = new Snapshot($snapshot->blacklist(), false, false, false, false, true, false, false, false, false); + $newClasses = array_diff($current->classes(), $snapshot->classes()); + unset($current); + + foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { + foreach ($staticAttributes as $name => $value) { + $reflector = new ReflectionProperty($className, $name); + $reflector->setAccessible(true); + $reflector->setValue($value); + } + } + + foreach ($newClasses as $className) { + $class = new \ReflectionClass($className); + $defaults = $class->getDefaultProperties(); + + foreach ($class->getProperties() as $attribute) { + if (!$attribute->isStatic()) { + continue; + } + + $name = $attribute->getName(); + + if ($snapshot->blacklist()->isStaticAttributeBlacklisted($className, $name)) { + continue; + } + + if (!isset($defaults[$name])) { + continue; + } + + $attribute->setAccessible(true); + $attribute->setValue($defaults[$name]); + } + } + } + + /** + * Restores a super-global variable array from this snapshot. + * + * @param Snapshot $snapshot + * @param $superGlobalArray + */ + private function restoreSuperGlobalArray(Snapshot $snapshot, $superGlobalArray) + { + $superGlobalVariables = $snapshot->superGlobalVariables(); + + if (isset($GLOBALS[$superGlobalArray]) && + is_array($GLOBALS[$superGlobalArray]) && + isset($superGlobalVariables[$superGlobalArray])) { + $keys = array_keys( + array_merge( + $GLOBALS[$superGlobalArray], + $superGlobalVariables[$superGlobalArray] + ) + ); + + foreach ($keys as $key) { + if (isset($superGlobalVariables[$superGlobalArray][$key])) { + $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; + } else { + unset($GLOBALS[$superGlobalArray][$key]); + } + } + } + } +} diff --git a/vendor/sebastian/global-state/src/RuntimeException.php b/vendor/sebastian/global-state/src/RuntimeException.php new file mode 100644 index 0000000..c0bf7bd --- /dev/null +++ b/vendor/sebastian/global-state/src/RuntimeException.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +/** + */ +class RuntimeException extends \RuntimeException implements Exception +{ +} diff --git a/vendor/sebastian/global-state/src/Snapshot.php b/vendor/sebastian/global-state/src/Snapshot.php new file mode 100644 index 0000000..3c8aaa9 --- /dev/null +++ b/vendor/sebastian/global-state/src/Snapshot.php @@ -0,0 +1,423 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +use ReflectionClass; +use Serializable; + +/** + * A snapshot of global state. + */ +class Snapshot +{ + /** + * @var Blacklist + */ + private $blacklist; + + /** + * @var array + */ + private $globalVariables = array(); + + /** + * @var array + */ + private $superGlobalArrays = array(); + + /** + * @var array + */ + private $superGlobalVariables = array(); + + /** + * @var array + */ + private $staticAttributes = array(); + + /** + * @var array + */ + private $iniSettings = array(); + + /** + * @var array + */ + private $includedFiles = array(); + + /** + * @var array + */ + private $constants = array(); + + /** + * @var array + */ + private $functions = array(); + + /** + * @var array + */ + private $interfaces = array(); + + /** + * @var array + */ + private $classes = array(); + + /** + * @var array + */ + private $traits = array(); + + /** + * Creates a snapshot of the current global state. + * + * @param Blacklist $blacklist + * @param bool $includeGlobalVariables + * @param bool $includeStaticAttributes + * @param bool $includeConstants + * @param bool $includeFunctions + * @param bool $includeClasses + * @param bool $includeInterfaces + * @param bool $includeTraits + * @param bool $includeIniSettings + * @param bool $includeIncludedFiles + */ + public function __construct(Blacklist $blacklist = null, $includeGlobalVariables = true, $includeStaticAttributes = true, $includeConstants = true, $includeFunctions = true, $includeClasses = true, $includeInterfaces = true, $includeTraits = true, $includeIniSettings = true, $includeIncludedFiles = true) + { + if ($blacklist === null) { + $blacklist = new Blacklist; + } + + $this->blacklist = $blacklist; + + if ($includeConstants) { + $this->snapshotConstants(); + } + + if ($includeFunctions) { + $this->snapshotFunctions(); + } + + if ($includeClasses || $includeStaticAttributes) { + $this->snapshotClasses(); + } + + if ($includeInterfaces) { + $this->snapshotInterfaces(); + } + + if ($includeGlobalVariables) { + $this->setupSuperGlobalArrays(); + $this->snapshotGlobals(); + } + + if ($includeStaticAttributes) { + $this->snapshotStaticAttributes(); + } + + if ($includeIniSettings) { + $this->iniSettings = ini_get_all(null, false); + } + + if ($includeIncludedFiles) { + $this->includedFiles = get_included_files(); + } + + if (function_exists('get_declared_traits')) { + $this->traits = get_declared_traits(); + } + } + + /** + * @return Blacklist + */ + public function blacklist() + { + return $this->blacklist; + } + + /** + * @return array + */ + public function globalVariables() + { + return $this->globalVariables; + } + + /** + * @return array + */ + public function superGlobalVariables() + { + return $this->superGlobalVariables; + } + + /** + * Returns a list of all super-global variable arrays. + * + * @return array + */ + public function superGlobalArrays() + { + return $this->superGlobalArrays; + } + + /** + * @return array + */ + public function staticAttributes() + { + return $this->staticAttributes; + } + + /** + * @return array + */ + public function iniSettings() + { + return $this->iniSettings; + } + + /** + * @return array + */ + public function includedFiles() + { + return $this->includedFiles; + } + + /** + * @return array + */ + public function constants() + { + return $this->constants; + } + + /** + * @return array + */ + public function functions() + { + return $this->functions; + } + + /** + * @return array + */ + public function interfaces() + { + return $this->interfaces; + } + + /** + * @return array + */ + public function classes() + { + return $this->classes; + } + + /** + * @return array + */ + public function traits() + { + return $this->traits; + } + + /** + * Creates a snapshot user-defined constants. + */ + private function snapshotConstants() + { + $constants = get_defined_constants(true); + + if (isset($constants['user'])) { + $this->constants = $constants['user']; + } + } + + /** + * Creates a snapshot user-defined functions. + */ + private function snapshotFunctions() + { + $functions = get_defined_functions(); + + $this->functions = $functions['user']; + } + + /** + * Creates a snapshot user-defined classes. + */ + private function snapshotClasses() + { + foreach (array_reverse(get_declared_classes()) as $className) { + $class = new ReflectionClass($className); + + if (!$class->isUserDefined()) { + break; + } + + $this->classes[] = $className; + } + + $this->classes = array_reverse($this->classes); + } + + /** + * Creates a snapshot user-defined interfaces. + */ + private function snapshotInterfaces() + { + foreach (array_reverse(get_declared_interfaces()) as $interfaceName) { + $class = new ReflectionClass($interfaceName); + + if (!$class->isUserDefined()) { + break; + } + + $this->interfaces[] = $interfaceName; + } + + $this->interfaces = array_reverse($this->interfaces); + } + + /** + * Creates a snapshot of all global and super-global variables. + */ + private function snapshotGlobals() + { + $superGlobalArrays = $this->superGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + $this->snapshotSuperGlobalArray($superGlobalArray); + } + + foreach (array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !in_array($key, $superGlobalArrays) && + $this->canBeSerialized($GLOBALS[$key]) && + !$this->blacklist->isGlobalVariableBlacklisted($key)) { + $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key])); + } + } + } + + /** + * Creates a snapshot a super-global variable array. + * + * @param $superGlobalArray + */ + private function snapshotSuperGlobalArray($superGlobalArray) + { + $this->superGlobalVariables[$superGlobalArray] = array(); + + if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { + foreach ($GLOBALS[$superGlobalArray] as $key => $value) { + $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value)); + } + } + } + + /** + * Creates a snapshot of all static attributes in user-defined classes. + */ + private function snapshotStaticAttributes() + { + foreach ($this->classes as $className) { + $class = new ReflectionClass($className); + $snapshot = array(); + + foreach ($class->getProperties() as $attribute) { + if ($attribute->isStatic()) { + $name = $attribute->getName(); + + if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) { + continue; + } + + $attribute->setAccessible(true); + $value = $attribute->getValue(); + + if ($this->canBeSerialized($value)) { + $snapshot[$name] = unserialize(serialize($value)); + } + } + } + + if (!empty($snapshot)) { + $this->staticAttributes[$className] = $snapshot; + } + } + } + + /** + * Returns a list of all super-global variable arrays. + * + * @return array + */ + private function setupSuperGlobalArrays() + { + $this->superGlobalArrays = array( + '_ENV', + '_POST', + '_GET', + '_COOKIE', + '_SERVER', + '_FILES', + '_REQUEST' + ); + + if (ini_get('register_long_arrays') == '1') { + $this->superGlobalArrays = array_merge( + $this->superGlobalArrays, + array( + 'HTTP_ENV_VARS', + 'HTTP_POST_VARS', + 'HTTP_GET_VARS', + 'HTTP_COOKIE_VARS', + 'HTTP_SERVER_VARS', + 'HTTP_POST_FILES' + ) + ); + } + } + + /** + * @param mixed $variable + * @return bool + * @todo Implement this properly + */ + private function canBeSerialized($variable) + { + if (!is_object($variable)) { + return !is_resource($variable); + } + + if ($variable instanceof \stdClass) { + return true; + } + + $class = new ReflectionClass($variable); + + do { + if ($class->isInternal()) { + return $variable instanceof Serializable; + } + } while ($class = $class->getParentClass()); + + return true; + } +} diff --git a/vendor/sebastian/global-state/tests/BlacklistTest.php b/vendor/sebastian/global-state/tests/BlacklistTest.php new file mode 100644 index 0000000..5d59796 --- /dev/null +++ b/vendor/sebastian/global-state/tests/BlacklistTest.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +use PHPUnit_Framework_TestCase; + +/** + */ +class BlacklistTest extends PHPUnit_Framework_TestCase +{ + /** + * @var \SebastianBergmann\GlobalState\Blacklist + */ + private $blacklist; + + protected function setUp() + { + $this->blacklist = new Blacklist; + } + + public function testGlobalVariableThatIsNotBlacklistedIsNotTreatedAsBlacklisted() + { + $this->assertFalse($this->blacklist->isGlobalVariableBlacklisted('variable')); + } + + public function testGlobalVariableCanBeBlacklisted() + { + $this->blacklist->addGlobalVariable('variable'); + + $this->assertTrue($this->blacklist->isGlobalVariableBlacklisted('variable')); + } + + public function testStaticAttributeThatIsNotBlacklistedIsNotTreatedAsBlacklisted() + { + $this->assertFalse( + $this->blacklist->isStaticAttributeBlacklisted( + 'SebastianBergmann\GlobalState\TestFixture\BlacklistedClass', + 'attribute' + ) + ); + } + + public function testClassCanBeBlacklisted() + { + $this->blacklist->addClass('SebastianBergmann\GlobalState\TestFixture\BlacklistedClass'); + + $this->assertTrue( + $this->blacklist->isStaticAttributeBlacklisted( + 'SebastianBergmann\GlobalState\TestFixture\BlacklistedClass', + 'attribute' + ) + ); + } + + public function testSubclassesCanBeBlacklisted() + { + $this->blacklist->addSubclassesOf('SebastianBergmann\GlobalState\TestFixture\BlacklistedClass'); + + $this->assertTrue( + $this->blacklist->isStaticAttributeBlacklisted( + 'SebastianBergmann\GlobalState\TestFixture\BlacklistedChildClass', + 'attribute' + ) + ); + } + + public function testImplementorsCanBeBlacklisted() + { + $this->blacklist->addImplementorsOf('SebastianBergmann\GlobalState\TestFixture\BlacklistedInterface'); + + $this->assertTrue( + $this->blacklist->isStaticAttributeBlacklisted( + 'SebastianBergmann\GlobalState\TestFixture\BlacklistedImplementor', + 'attribute' + ) + ); + } + + public function testClassNamePrefixesCanBeBlacklisted() + { + $this->blacklist->addClassNamePrefix('SebastianBergmann\GlobalState'); + + $this->assertTrue( + $this->blacklist->isStaticAttributeBlacklisted( + 'SebastianBergmann\GlobalState\TestFixture\BlacklistedClass', + 'attribute' + ) + ); + } + + public function testStaticAttributeCanBeBlacklisted() + { + $this->blacklist->addStaticAttribute( + 'SebastianBergmann\GlobalState\TestFixture\BlacklistedClass', + 'attribute' + ); + + $this->assertTrue( + $this->blacklist->isStaticAttributeBlacklisted( + 'SebastianBergmann\GlobalState\TestFixture\BlacklistedClass', + 'attribute' + ) + ); + } +} diff --git a/vendor/sebastian/global-state/tests/SnapshotTest.php b/vendor/sebastian/global-state/tests/SnapshotTest.php new file mode 100644 index 0000000..1e97488 --- /dev/null +++ b/vendor/sebastian/global-state/tests/SnapshotTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +use ArrayObject; +use PHPUnit_Framework_TestCase; +use SebastianBergmann\GlobalState\TestFixture\SnapshotClass; + +/** + */ +class SnapshotTest extends PHPUnit_Framework_TestCase +{ + public function testStaticAttributes() + { + $blacklist = $this->getBlacklist(); + $blacklist->method('isStaticAttributeBlacklisted')->willReturnCallback(function ($class) { + return $class !== 'SebastianBergmann\GlobalState\TestFixture\SnapshotClass'; + }); + + SnapshotClass::init(); + + $snapshot = new Snapshot($blacklist, false, true, false, false, false, false, false, false, false); + $expected = array('SebastianBergmann\GlobalState\TestFixture\SnapshotClass' => array( + 'string' => 'snapshot', + 'arrayObject' => new ArrayObject(array(1, 2, 3)), + 'stdClass' => new \stdClass(), + )); + + $this->assertEquals($expected, $snapshot->staticAttributes()); + } + + public function testConstants() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, true, false, false, false, false, false, false); + $this->assertArrayHasKey('GLOBALSTATE_TESTSUITE', $snapshot->constants()); + } + + public function testFunctions() + { + require_once __DIR__.'/_fixture/SnapshotFunctions.php'; + + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, true, false, false, false, false, false); + $functions = $snapshot->functions(); + + $this->assertThat( + $functions, + $this->logicalOr( + // Zend + $this->contains('sebastianbergmann\globalstate\testfixture\snapshotfunction'), + // HHVM + $this->contains('SebastianBergmann\GlobalState\TestFixture\snapshotFunction') + ) + ); + + $this->assertNotContains('assert', $functions); + } + + public function testClasses() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, true, false, false, false, false); + $classes = $snapshot->classes(); + + $this->assertContains('PHPUnit_Framework_TestCase', $classes); + $this->assertNotContains('Exception', $classes); + } + + public function testInterfaces() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, false, true, false, false, false); + $interfaces = $snapshot->interfaces(); + + $this->assertContains('PHPUnit_Framework_Test', $interfaces); + $this->assertNotContains('Countable', $interfaces); + } + + /** + * @requires PHP 5.4 + */ + public function testTraits() + { + spl_autoload_call('SebastianBergmann\GlobalState\TestFixture\SnapshotTrait'); + + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, false, false, true, false, false); + $this->assertContains('SebastianBergmann\GlobalState\TestFixture\SnapshotTrait', $snapshot->traits()); + } + + public function testIniSettings() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, false, false, false, true, false); + $iniSettings = $snapshot->iniSettings(); + + $this->assertArrayHasKey('date.timezone', $iniSettings); + $this->assertEquals('Etc/UTC', $iniSettings['date.timezone']); + } + + public function testIncludedFiles() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, false, false, false, false, true); + $this->assertContains(__FILE__, $snapshot->includedFiles()); + } + + /** + * @return \SebastianBergmann\GlobalState\Blacklist + */ + private function getBlacklist() + { + return $this->getMockBuilder('SebastianBergmann\GlobalState\Blacklist') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/vendor/sebastian/global-state/tests/_fixture/BlacklistedChildClass.php b/vendor/sebastian/global-state/tests/_fixture/BlacklistedChildClass.php new file mode 100644 index 0000000..e852b49 --- /dev/null +++ b/vendor/sebastian/global-state/tests/_fixture/BlacklistedChildClass.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +/** + */ +class BlacklistedChildClass extends BlacklistedClass +{ +} diff --git a/vendor/sebastian/global-state/tests/_fixture/BlacklistedClass.php b/vendor/sebastian/global-state/tests/_fixture/BlacklistedClass.php new file mode 100644 index 0000000..284876d --- /dev/null +++ b/vendor/sebastian/global-state/tests/_fixture/BlacklistedClass.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +/** + */ +class BlacklistedClass +{ + private static $attribute; +} diff --git a/vendor/sebastian/global-state/tests/_fixture/BlacklistedImplementor.php b/vendor/sebastian/global-state/tests/_fixture/BlacklistedImplementor.php new file mode 100644 index 0000000..d7b46a9 --- /dev/null +++ b/vendor/sebastian/global-state/tests/_fixture/BlacklistedImplementor.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +/** + */ +class BlacklistedImplementor implements BlacklistedInterface +{ + private static $attribute; +} diff --git a/vendor/sebastian/global-state/tests/_fixture/BlacklistedInterface.php b/vendor/sebastian/global-state/tests/_fixture/BlacklistedInterface.php new file mode 100644 index 0000000..8c3ed67 --- /dev/null +++ b/vendor/sebastian/global-state/tests/_fixture/BlacklistedInterface.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +/** + */ +interface BlacklistedInterface +{ +} diff --git a/vendor/sebastian/global-state/tests/_fixture/SnapshotClass.php b/vendor/sebastian/global-state/tests/_fixture/SnapshotClass.php new file mode 100644 index 0000000..9735818 --- /dev/null +++ b/vendor/sebastian/global-state/tests/_fixture/SnapshotClass.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +use DomDocument; +use ArrayObject; + +/** + */ +class SnapshotClass +{ + private static $string = 'snapshot'; + private static $dom; + private static $closure; + private static $arrayObject; + private static $snapshotDomDocument; + private static $resource; + private static $stdClass; + + public static function init() + { + self::$dom = new DomDocument(); + self::$closure = function () {}; + self::$arrayObject = new ArrayObject(array(1, 2, 3)); + self::$snapshotDomDocument = new SnapshotDomDocument(); + self::$resource = fopen('php://memory', 'r'); + self::$stdClass = new \stdClass(); + } +} diff --git a/vendor/sebastian/global-state/tests/_fixture/SnapshotDomDocument.php b/vendor/sebastian/global-state/tests/_fixture/SnapshotDomDocument.php new file mode 100644 index 0000000..bcf554d --- /dev/null +++ b/vendor/sebastian/global-state/tests/_fixture/SnapshotDomDocument.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +use DomDocument; + +/** + */ +class SnapshotDomDocument extends DomDocument +{ +} diff --git a/vendor/sebastian/global-state/tests/_fixture/SnapshotFunctions.php b/vendor/sebastian/global-state/tests/_fixture/SnapshotFunctions.php new file mode 100644 index 0000000..51d1c74 --- /dev/null +++ b/vendor/sebastian/global-state/tests/_fixture/SnapshotFunctions.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +function snapshotFunction() +{ +} diff --git a/vendor/sebastian/global-state/tests/_fixture/SnapshotTrait.php b/vendor/sebastian/global-state/tests/_fixture/SnapshotTrait.php new file mode 100644 index 0000000..14e81e9 --- /dev/null +++ b/vendor/sebastian/global-state/tests/_fixture/SnapshotTrait.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +/** + */ +trait SnapshotTrait +{ +} diff --git a/vendor/sebastian/recursion-context/.gitignore b/vendor/sebastian/recursion-context/.gitignore new file mode 100644 index 0000000..3beb10f --- /dev/null +++ b/vendor/sebastian/recursion-context/.gitignore @@ -0,0 +1,9 @@ +.idea +phpunit.xml +composer.lock +composer.phar +vendor/ +cache.properties +build/LICENSE +build/README.md +build/*.tgz diff --git a/vendor/sebastian/recursion-context/.travis.yml b/vendor/sebastian/recursion-context/.travis.yml new file mode 100644 index 0000000..c5952a1 --- /dev/null +++ b/vendor/sebastian/recursion-context/.travis.yml @@ -0,0 +1,21 @@ +language: php + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +sudo: false + +before_script: + - composer self-update + - composer install --no-interaction --prefer-source --dev + +script: ./vendor/bin/phpunit + +notifications: + email: false + irc: "irc.freenode.org#phpunit" diff --git a/vendor/sebastian/recursion-context/LICENSE b/vendor/sebastian/recursion-context/LICENSE new file mode 100644 index 0000000..f8c30c2 --- /dev/null +++ b/vendor/sebastian/recursion-context/LICENSE @@ -0,0 +1,33 @@ +Recursion Context + +Copyright (c) 2002-2015, Sebastian Bergmann . +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 Sebastian Bergmann nor the names of his + 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. diff --git a/vendor/sebastian/recursion-context/README.md b/vendor/sebastian/recursion-context/README.md new file mode 100644 index 0000000..bd43985 --- /dev/null +++ b/vendor/sebastian/recursion-context/README.md @@ -0,0 +1,13 @@ +# Recursion Context + +... + +## Installation + +To add Recursion Context as a local, per-project dependency to your project, simply add a dependency on `sebastian/recursion-context` to your project's `composer.json` file. Here is a minimal example of a `composer.json` file that just defines a dependency on Recursion Context 1.0: + + { + "require": { + "sebastian/recursion-context": "~1.0" + } + } diff --git a/vendor/sebastian/recursion-context/build.xml b/vendor/sebastian/recursion-context/build.xml new file mode 100644 index 0000000..b4fd193 --- /dev/null +++ b/vendor/sebastian/recursion-context/build.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sebastian/recursion-context/composer.json b/vendor/sebastian/recursion-context/composer.json new file mode 100644 index 0000000..fed033d --- /dev/null +++ b/vendor/sebastian/recursion-context/composer.json @@ -0,0 +1,36 @@ +{ + "name": "sebastian/recursion-context", + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/sebastian/recursion-context/phpunit.xml.dist b/vendor/sebastian/recursion-context/phpunit.xml.dist new file mode 100644 index 0000000..757e3e6 --- /dev/null +++ b/vendor/sebastian/recursion-context/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + tests + + + + + src + + + diff --git a/vendor/sebastian/recursion-context/src/Context.php b/vendor/sebastian/recursion-context/src/Context.php new file mode 100644 index 0000000..b853d10 --- /dev/null +++ b/vendor/sebastian/recursion-context/src/Context.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + * A context containing previously processed arrays and objects + * when recursively processing a value. + */ +final class Context +{ + /** + * @var array[] + */ + private $arrays; + + /** + * @var \SplObjectStorage + */ + private $objects; + + /** + * Initialises the context + */ + public function __construct() + { + $this->arrays = array(); + $this->objects = new \SplObjectStorage; + } + + /** + * Adds a value to the context. + * + * @param array|object $value The value to add. + * @return int|string The ID of the stored value, either as + * a string or integer. + * @throws InvalidArgumentException Thrown if $value is not an array or + * object + */ + public function add(&$value) + { + if (is_array($value)) { + return $this->addArray($value); + } + + else if (is_object($value)) { + return $this->addObject($value); + } + + throw new InvalidArgumentException( + 'Only arrays and objects are supported' + ); + } + + /** + * Checks if the given value exists within the context. + * + * @param array|object $value The value to check. + * @return int|string|false The string or integer ID of the stored + * value if it has already been seen, or + * false if the value is not stored. + * @throws InvalidArgumentException Thrown if $value is not an array or + * object + */ + public function contains(&$value) + { + if (is_array($value)) { + return $this->containsArray($value); + } + + else if (is_object($value)) { + return $this->containsObject($value); + } + + throw new InvalidArgumentException( + 'Only arrays and objects are supported' + ); + } + + /** + * @param array $array + * @return bool|int + */ + private function addArray(array &$array) + { + $key = $this->containsArray($array); + + if ($key !== false) { + return $key; + } + + $this->arrays[] = &$array; + + return count($this->arrays) - 1; + } + + /** + * @param object $object + * @return string + */ + private function addObject($object) + { + if (!$this->objects->contains($object)) { + $this->objects->attach($object); + } + + return spl_object_hash($object); + } + + /** + * @param array $array + * @return int|false + */ + private function containsArray(array &$array) + { + $keys = array_keys($this->arrays, $array, true); + $hash = '_Key_' . microtime(true); + + foreach ($keys as $key) { + $this->arrays[$key][$hash] = $hash; + + if (isset($array[$hash]) && $array[$hash] === $hash) { + unset($this->arrays[$key][$hash]); + + return $key; + } + + unset($this->arrays[$key][$hash]); + } + + return false; + } + + /** + * @param object $value + * @return string|false + */ + private function containsObject($value) + { + if ($this->objects->contains($value)) { + return spl_object_hash($value); + } + + return false; + } +} diff --git a/vendor/sebastian/recursion-context/src/Exception.php b/vendor/sebastian/recursion-context/src/Exception.php new file mode 100644 index 0000000..4a1557b --- /dev/null +++ b/vendor/sebastian/recursion-context/src/Exception.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + */ +interface Exception +{ +} diff --git a/vendor/sebastian/recursion-context/src/InvalidArgumentException.php b/vendor/sebastian/recursion-context/src/InvalidArgumentException.php new file mode 100644 index 0000000..032c504 --- /dev/null +++ b/vendor/sebastian/recursion-context/src/InvalidArgumentException.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + */ +final class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} diff --git a/vendor/sebastian/recursion-context/tests/ContextTest.php b/vendor/sebastian/recursion-context/tests/ContextTest.php new file mode 100644 index 0000000..8e8cc56 --- /dev/null +++ b/vendor/sebastian/recursion-context/tests/ContextTest.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +use PHPUnit_Framework_TestCase; + +/** + * @covers SebastianBergmann\RecursionContext\Context + */ +class ContextTest extends PHPUnit_Framework_TestCase +{ + /** + * @var \SebastianBergmann\RecursionContext\Context + */ + private $context; + + protected function setUp() + { + $this->context = new Context(); + } + + public function failsProvider() + { + return array( + array(true), + array(false), + array(null), + array('string'), + array(1), + array(1.5), + array(fopen('php://memory', 'r')) + ); + } + + public function valuesProvider() + { + $obj2 = new \stdClass(); + $obj2->foo = 'bar'; + + $obj3 = (object) array(1,2,"Test\r\n",4,5,6,7,8); + + $obj = new \stdClass(); + //@codingStandardsIgnoreStart + $obj->null = null; + //@codingStandardsIgnoreEnd + $obj->boolean = true; + $obj->integer = 1; + $obj->double = 1.2; + $obj->string = '1'; + $obj->text = "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext"; + $obj->object = $obj2; + $obj->objectagain = $obj2; + $obj->array = array('foo' => 'bar'); + $obj->array2 = array(1,2,3,4,5,6); + $obj->array3 = array($obj, $obj2, $obj3); + $obj->self = $obj; + + $storage = new \SplObjectStorage(); + $storage->attach($obj2); + $storage->foo = $obj2; + + return array( + array($obj, spl_object_hash($obj)), + array($obj2, spl_object_hash($obj2)), + array($obj3, spl_object_hash($obj3)), + array($storage, spl_object_hash($storage)), + array($obj->array, 0), + array($obj->array2, 0), + array($obj->array3, 0) + ); + } + + /** + * @covers SebastianBergmann\RecursionContext\Context::add + * @uses SebastianBergmann\RecursionContext\InvalidArgumentException + * @dataProvider failsProvider + */ + public function testAddFails($value) + { + $this->setExpectedException( + 'SebastianBergmann\\RecursionContext\\Exception', + 'Only arrays and objects are supported' + ); + $this->context->add($value); + } + + /** + * @covers SebastianBergmann\RecursionContext\Context::contains + * @uses SebastianBergmann\RecursionContext\InvalidArgumentException + * @dataProvider failsProvider + */ + public function testContainsFails($value) + { + $this->setExpectedException( + 'SebastianBergmann\\RecursionContext\\Exception', + 'Only arrays and objects are supported' + ); + $this->context->contains($value); + } + + /** + * @covers SebastianBergmann\RecursionContext\Context::add + * @dataProvider valuesProvider + */ + public function testAdd($value, $key) + { + $this->assertEquals($key, $this->context->add($value)); + + // Test we get the same key on subsequent adds + $this->assertEquals($key, $this->context->add($value)); + } + + /** + * @covers SebastianBergmann\RecursionContext\Context::contains + * @uses SebastianBergmann\RecursionContext\Context::add + * @depends testAdd + * @dataProvider valuesProvider + */ + public function testContainsFound($value, $key) + { + $this->context->add($value); + $this->assertEquals($key, $this->context->contains($value)); + + // Test we get the same key on subsequent calls + $this->assertEquals($key, $this->context->contains($value)); + } + + /** + * @covers SebastianBergmann\RecursionContext\Context::contains + * @dataProvider valuesProvider + */ + public function testContainsNotFound($value) + { + $this->assertFalse($this->context->contains($value)); + } +} diff --git a/vendor/sebastian/version/.gitattributes b/vendor/sebastian/version/.gitattributes new file mode 100644 index 0000000..461090b --- /dev/null +++ b/vendor/sebastian/version/.gitattributes @@ -0,0 +1 @@ +*.php diff=php diff --git a/vendor/sebastian/version/.gitignore b/vendor/sebastian/version/.gitignore new file mode 100644 index 0000000..a09c56d --- /dev/null +++ b/vendor/sebastian/version/.gitignore @@ -0,0 +1 @@ +/.idea diff --git a/vendor/sebastian/version/LICENSE b/vendor/sebastian/version/LICENSE new file mode 100644 index 0000000..5b79c41 --- /dev/null +++ b/vendor/sebastian/version/LICENSE @@ -0,0 +1,33 @@ +Version + +Copyright (c) 2013-2015, Sebastian Bergmann . +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 Sebastian Bergmann nor the names of his + 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. diff --git a/vendor/sebastian/version/README.md b/vendor/sebastian/version/README.md new file mode 100644 index 0000000..be6b687 --- /dev/null +++ b/vendor/sebastian/version/README.md @@ -0,0 +1,37 @@ +# Version + +**Version** is a library that helps with managing the version number of Git-hosted PHP projects. + +## Installation + +Simply add a dependency on `sebastian/version` to your project's `composer.json` file if you use [Composer](http://getcomposer.org/) to manage the dependencies of your project. + +## Usage + +The constructor of the `SebastianBergmann\Version` class expects two parameters: + +* `$release` is the version number of the latest release (`X.Y.Z`, for instance) or the name of the release series (`X.Y`) when no release has been made from that branch / for that release series yet. +* `$path` is the path to the directory (or a subdirectory thereof) where the sourcecode of the project can be found. Simply passing `__DIR__` here usually suffices. + +Apart from the constructor, the `SebastianBergmann\Version` class has a single public method: `getVersion()`. + +Here is a contrived example that shows the basic usage: + + getVersion()); + ?> + + string(18) "3.7.10-17-g00f3408" + +When a new release is prepared, the string that is passed to the constructor as the first argument needs to be updated. + +### How SebastianBergmann\Version::getVersion() works + +* If `$path` is not (part of) a Git repository and `$release` is in `X.Y.Z` format then `$release` is returned as-is. +* If `$path` is not (part of) a Git repository and `$release` is in `X.Y` format then `$release` is returned suffixed with `-dev`. +* If `$path` is (part of) a Git repository and `$release` is in `X.Y.Z` format then the output of `git describe --tags` is returned as-is. +* If `$path` is (part of) a Git repository and `$release` is in `X.Y` format then a string is returned that begins with `X.Y` and ends with information from `git describe --tags`. diff --git a/vendor/sebastian/version/composer.json b/vendor/sebastian/version/composer.json new file mode 100644 index 0000000..39d6569 --- /dev/null +++ b/vendor/sebastian/version/composer.json @@ -0,0 +1,21 @@ +{ + "name": "sebastian/version", + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues" + }, + "autoload": { + "classmap": [ + "src/" + ] + } +} diff --git a/vendor/sebastian/version/src/Version.php b/vendor/sebastian/version/src/Version.php new file mode 100644 index 0000000..3cb3e09 --- /dev/null +++ b/vendor/sebastian/version/src/Version.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann; + +/** + * @since Class available since Release 1.0.0 + */ +class Version +{ + private $path; + private $release; + private $version; + + /** + * @param string $release + * @param string $path + */ + public function __construct($release, $path) + { + $this->release = $release; + $this->path = $path; + } + + /** + * @return string + */ + public function getVersion() + { + if ($this->version === null) { + if (count(explode('.', $this->release)) == 3) { + $this->version = $this->release; + } else { + $this->version = $this->release . '-dev'; + } + + $git = $this->getGitInformation($this->path); + + if ($git) { + if (count(explode('.', $this->release)) == 3) { + $this->version = $git; + } else { + $git = explode('-', $git); + + $this->version = $this->release . '-' . end($git); + } + } + } + + return $this->version; + } + + /** + * @param string $path + * @return bool|string + */ + private function getGitInformation($path) + { + if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) { + return false; + } + + $dir = getcwd(); + chdir($path); + $returnCode = 1; + $result = @exec('git describe --tags 2>&1', $output, $returnCode); + chdir($dir); + + if ($returnCode !== 0) { + return false; + } + + return $result; + } +} diff --git a/vendor/swiftmailer/swiftmailer/.gitattributes b/vendor/swiftmailer/swiftmailer/.gitattributes new file mode 100644 index 0000000..33b8efd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/.gitattributes @@ -0,0 +1,9 @@ +*.crt -crlf +*.key -crlf +*.srl -crlf +*.pub -crlf +*.priv -crlf +*.txt -crlf + +# ignore /notes in the git-generated distributed .zip archive +/notes export-ignore diff --git a/vendor/swiftmailer/swiftmailer/.gitignore b/vendor/swiftmailer/swiftmailer/.gitignore new file mode 100644 index 0000000..9a23ffe --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/.gitignore @@ -0,0 +1,4 @@ +/tests/acceptance.conf.php +/tests/smoke.conf.php +/build/* +/vendor/ diff --git a/vendor/swiftmailer/swiftmailer/.travis.yml b/vendor/swiftmailer/swiftmailer/.travis.yml new file mode 100644 index 0000000..cadee01 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/.travis.yml @@ -0,0 +1,25 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm-nightly + +before_script: + - cp tests/acceptance.conf.php.default tests/acceptance.conf.php + - cp tests/smoke.conf.php.default tests/smoke.conf.php + - composer self-update + - composer update --no-interaction --prefer-source + - gem install mailcatcher + - mailcatcher --smtp-port 4456 + +script: + - phpunit --verbose + +matrix: + allow_failures: + - php: 5.6 + - php: hhvm-nightly + fast_finish: true diff --git a/vendor/swiftmailer/swiftmailer/CHANGES b/vendor/swiftmailer/swiftmailer/CHANGES new file mode 100644 index 0000000..7330acd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/CHANGES @@ -0,0 +1,206 @@ +Changelog +========= + +5.4.1 (2015-06-06) +------------------ + + * made Swiftmailer exceptions confirm to PHP base exception constructor signature + * fixed MAIL FROM & RCPT TO headers to be RFC compliant + +5.4.0 (2015-03-14) +------------------ + + * added the possibility to add extra certs to PKCS#7 signature + * fix base64 encoding with streams + * added a new RESULT_SPOOLED status for SpoolTransport + * fixed getBody() on attachments when called more than once + * removed dots from generated filenames in filespool + +5.3.1 (2014-12-05) +------------------ + + * fixed cloning of messages with attachments + +5.3.0 (2014-10-04) +------------------ + + * fixed cloning when using signers + * reverted removal of Swift_Encoding + * drop support for PHP 5.2.x + +5.2.2 (2014-09-20) +------------------ + + * fixed Japanese support + * fixed the memory spool when the message changes when in the pool + * added support for cloning messages + * fixed PHP warning in the redirect plugin + * changed the way to and cc-ed email are sent to only use one transaction + +5.2.1 (2014-06-13) +------------------ + + * SECURITY FIX: fixed CLI escaping when using sendmail as a transport + + Prior to 5.2.1, the sendmail transport (Swift_Transport_SendmailTransport) + was vulnerable to an arbitrary shell execution if the "From" header came + from a non-trusted source and no "Return-Path" is configured. + + * fixed parameter in DKIMSigner + * fixed compatibility with PHP < 5.4 + +5.2.0 (2014-05-08) +------------------ + + * fixed Swift_ByteStream_FileByteStream::read() to match to the specification + * fixed from-charset and to-charset arguments in mbstring_convert_encoding() usages + * fixed infinite loop in StreamBuffer + * fixed NullTransport to return the number of ignored emails instead of 0 + * Use phpunit and mockery for unit testing (realityking) + +5.1.0 (2014-03-18) +------------------ + + * fixed data writing to stream when sending large messages + * added support for libopendkim (https://github.com/xdecock/php-opendkim) + * merged SignedMessage and Message + * added Gmail XOAuth2 authentication + * updated the list of known mime types + * added NTLM authentication + +5.0.3 (2013-12-03) +------------------ + + * fixed double-dot bug + * fixed DKIM signer + +5.0.2 (2013-08-30) +------------------ + + * handled correct exception type while reading IoBuffer output + +5.0.1 (2013-06-17) +------------------ + + * changed the spool to only start the transport when a mail has to be sent + * fixed compatibility with PHP 5.2 + * fixed LICENSE file + +5.0.0 (2013-04-30) +------------------ + + * changed the license from LGPL to MIT + +4.3.1 (2013-04-11) +------------------ + + * removed usage of the native QP encoder when the charset is not UTF-8 + * fixed usage of uniqid to avoid collisions + * made a performance improvement when tokenizing large headers + * fixed usage of the PHP native QP encoder on PHP 5.4.7+ + +4.3.0 (2013-01-08) +------------------ + + * made the temporary directory configurable via the TMPDIR env variable + * added S/MIME signer and encryption support + +4.2.2 (2012-10-25) +------------------ + + * added the possibility to throttle messages per second in ThrottlerPlugin (mostly for Amazon SES) + * switched mime.qpcontentencoder to automatically use the PHP native encoder on PHP 5.4.7+ + * allowed specifying a whitelist with regular expressions in RedirectingPlugin + +4.2.1 (2012-07-13) +------------------ + + * changed the coding standards to PSR-1/2 + * fixed issue with autoloading + * added NativeQpContentEncoder to enhance performance (for PHP 5.3+) + +4.2.0 (2012-06-29) +------------------ + + * added documentation about how to use the Japanese support introduced in 4.1.8 + * added a way to override the default configuration in a lazy way + * changed the PEAR init script to lazy-load the initialization + * fixed a bug when calling Swift_Preferences before anything else (regression introduced in 4.1.8) + +4.1.8 (2012-06-17) +------------------ + + * added Japanese iso-2022-jp support + * changed the init script to lazy-load the initialization + * fixed docblocks (@id) which caused some problems with libraries parsing the dobclocks + * fixed Swift_Mime_Headers_IdentificationHeader::setId() when passed an array of ids + * fixed encoding of email addresses in headers + * added replacements setter to the Decorator plugin + +4.1.7 (2012-04-26) +------------------ + + * fixed QpEncoder safeMapShareId property + +4.1.6 (2012-03-23) +------------------ + + * reduced the size of serialized Messages + +4.1.5 (2012-01-04) +------------------ + + * enforced Swift_Spool::queueMessage() to return a Boolean + * made an optimization to the memory spool: start the transport only when required + * prevented stream_socket_client() from generating an error and throw a Swift_TransportException instead + * fixed a PHP warning when calling to mail() when safe_mode is off + * many doc tweaks + +4.1.4 (2011-12-16) +------------------ + + * added a memory spool (Swift_MemorySpool) + * fixed too many opened files when sending emails with attachments + +4.1.3 (2011-10-27) +------------------ + + * added STARTTLS support + * added missing @return tags on fluent methods + * added a MessageLogger plugin that logs all sent messages + * added composer.json + +4.1.2 (2011-09-13) +------------------ + + * fixed wrong detection of magic_quotes_runtime + * fixed fatal errors when no To or Subject header has been set + * fixed charset on parameter header continuations + * added documentation about how to install Swiftmailer from the PEAR channel + * fixed various typos and markup problem in the documentation + * fixed warning when cache directory does not exist + * fixed "slashes are escaped" bug + * changed require_once() to require() in autoload + +4.1.1 (2011-07-04) +------------------ + + * added missing file in PEAR package + +4.1.0 (2011-06-30) +------------------ + + * documentation has been converted to ReST + +4.1.0 RC1 (2011-06-17) +---------------------- + +New features: + + * changed the Decorator Plugin to allow replacements in all headers + * added Swift_Mime_Grammar and Swift_Validate to validate an email address + * modified the autoloader to lazy-initialize Swiftmailer + * removed Swift_Mailer::batchSend() + * added NullTransport + * added new plugins: RedirectingPlugin and ImpersonatePlugin + * added a way to send messages asynchronously (Spool) diff --git a/vendor/swiftmailer/swiftmailer/LICENSE b/vendor/swiftmailer/swiftmailer/LICENSE new file mode 100644 index 0000000..674bb2a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/swiftmailer/swiftmailer/README b/vendor/swiftmailer/swiftmailer/README new file mode 100644 index 0000000..eb5f8bc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/README @@ -0,0 +1,16 @@ +Swift Mailer +------------ + +Swift Mailer is a component based mailing solution for PHP 5. +It is released under the MIT license. + +Homepage: http://swiftmailer.org +Documentation: http://swiftmailer.org/docs +Mailing List: http://groups.google.com/group/swiftmailer +Bugs: https://github.com/swiftmailer/swiftmailer/issues +Repository: https://github.com/swiftmailer/swiftmailer + +Swift Mailer is highly object-oriented by design and lends itself +to use in complex web application with a great deal of flexibility. + +For full details on usage, see the documentation. diff --git a/vendor/swiftmailer/swiftmailer/VERSION b/vendor/swiftmailer/swiftmailer/VERSION new file mode 100644 index 0000000..e61a6df --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/VERSION @@ -0,0 +1 @@ +Swift-5.4.1 diff --git a/vendor/swiftmailer/swiftmailer/composer.json b/vendor/swiftmailer/swiftmailer/composer.json new file mode 100644 index 0000000..a7b5e44 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/composer.json @@ -0,0 +1,31 @@ +{ + "name": "swiftmailer/swiftmailer", + "type": "library", + "description": "Swiftmailer, free feature-rich PHP mailer", + "keywords": ["mail","mailer","email"], + "homepage": "http://swiftmailer.org", + "license": "MIT", + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1,<0.9.4" + }, + "autoload": { + "files": ["lib/swift_required.php"] + }, + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/doc/headers.rst b/vendor/swiftmailer/swiftmailer/doc/headers.rst new file mode 100644 index 0000000..6aec23f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/headers.rst @@ -0,0 +1,742 @@ +Message Headers +=============== + +Sometimes you'll want to add your own headers to a message or modify/remove +headers that are already present. You work with the message's HeaderSet to do +this. + +Header Basics +------------- + +All MIME entities in Swift Mailer -- including the message itself -- +store their headers in a single object called a HeaderSet. This HeaderSet is +retrieved with the ``getHeaders()`` method. + +As mentioned in the previous chapter, everything that forms a part of a message +in Swift Mailer is a MIME entity that is represented by an instance of +``Swift_Mime_MimeEntity``. This includes -- most notably -- the message object +itself, attachments, MIME parts and embedded images. Each of these MIME entities +consists of a body and a set of headers that describe the body. + +For all of the "standard" headers in these MIME entities, such as the +``Content-Type``, there are named methods for working with them, such as +``setContentType()`` and ``getContentType()``. This is because headers are a +moderately complex area of the library. Each header has a slightly different +required structure that it must meet in order to comply with the standards that +govern email (and that are checked by spam blockers etc). + +You fetch the HeaderSet from a MIME entity like so: + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + // Fetch the HeaderSet from a Message object + $headers = $message->getHeaders(); + + $attachment = Swift_Attachment::fromPath('document.pdf'); + + // Fetch the HeaderSet from an attachment object + $headers = $attachment->getHeaders(); + +The job of the HeaderSet is to contain and manage instances of Header objects. +Depending upon the MIME entity the HeaderSet came from, the contents of the +HeaderSet will be different, since an attachment for example has a different +set of headers to those in a message. + +You can find out what the HeaderSet contains with a quick loop, dumping out +the names of the headers: + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + printf("%s
\n", $header->getFieldName()); + } + + /* + Content-Transfer-Encoding + Content-Type + MIME-Version + Date + Message-ID + From + Subject + To + */ + +You can also dump out the rendered HeaderSet by calling its ``toString()`` +method: + +.. code-block:: php + + echo $headers->toString(); + + /* + Message-ID: <1234869991.499a9ee7f1d5e@swift.generated> + Date: Tue, 17 Feb 2009 22:26:31 +1100 + Subject: Awesome subject! + From: sender@example.org + To: recipient@example.org + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + */ + +Where the complexity comes in is when you want to modify an existing header. +This complexity comes from the fact that each header can be of a slightly +different type (such as a Date header, or a header that contains email +addresses, or a header that has key-value parameters on it!). Each header in the +HeaderSet is an instance of ``Swift_Mime_Header``. They all have common +functionality, but knowing exactly what type of header you're working with will +allow you a little more control. + +You can determine the type of header by comparing the return value of its +``getFieldType()`` method with the constants ``TYPE_TEXT``, +``TYPE_PARAMETERIZED``, ``TYPE_DATE``, ``TYPE_MAILBOX``, ``TYPE_ID`` and +``TYPE_PATH`` which are defined in ``Swift_Mime_Header``. + + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + switch ($header->getFieldType()) { + case Swift_Mime_Header::TYPE_TEXT: $type = 'text'; + break; + case Swift_Mime_Header::TYPE_PARAMETERIZED: $type = 'parameterized'; + break; + case Swift_Mime_Header::TYPE_MAILBOX: $type = 'mailbox'; + break; + case Swift_Mime_Header::TYPE_DATE: $type = 'date'; + break; + case Swift_Mime_Header::TYPE_ID: $type = 'ID'; + break; + case Swift_Mime_Header::TYPE_PATH: $type = 'path'; + break; + } + printf("%s: is a %s header
\n", $header->getFieldName(), $type); + } + + /* + Content-Transfer-Encoding: is a text header + Content-Type: is a parameterized header + MIME-Version: is a text header + Date: is a date header + Message-ID: is a ID header + From: is a mailbox header + Subject: is a text header + To: is a mailbox header + */ + +Headers can be removed from the set, modified within the set, or added to the +set. + +The following sections show you how to work with the HeaderSet and explain the +details of each implementation of ``Swift_Mime_Header`` that may +exist within the HeaderSet. + +Header Types +------------ + +Because all headers are modeled on different data (dates, addresses, text!) +there are different types of Header in Swift Mailer. Swift Mailer attempts to +categorize all possible MIME headers into more general groups, defined by a +small number of classes. + +Text Headers +~~~~~~~~~~~~ + +Text headers are the simplest type of Header. They contain textual information +with no special information included within it -- for example the Subject +header in a message. + +There's nothing particularly interesting about a text header, though it is +probably the one you'd opt to use if you need to add a custom header to a +message. It represents text just like you'd think it does. If the text +contains characters that are not permitted in a message header (such as new +lines, or non-ascii characters) then the header takes care of encoding the +text so that it can be used. + +No header -- including text headers -- in Swift Mailer is vulnerable to +header-injection attacks. Swift Mailer breaks any attempt at header injection by +encoding the dangerous data into a non-dangerous form. + +It's easy to add a new text header to a HeaderSet. You do this by calling the +HeaderSet's ``addTextHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addTextHeader('Your-Header-Name', 'the header value'); + +Changing the value of an existing text header is done by calling it's +``setValue()`` method. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('new subject'); + +When output via ``toString()``, a text header produces something like the +following: + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('amazing subject line'); + + echo $subject->toString(); + + /* + + Subject: amazing subject line + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded. This is nothing to be concerned about since +mail clients will decode them back. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('contains – dash'); + + echo $subject->toString(); + + /* + + Subject: contains =?utf-8?Q?=E2=80=93?= dash + + */ + +Parameterized Headers +~~~~~~~~~~~~~~~~~~~~~ + +Parameterized headers are text headers that contain key-value parameters +following the textual content. The Content-Type header of a message is a +parameterized header since it contains charset information after the content +type. + +The parameterized header type is a special type of text header. It extends the +text header by allowing additional information to follow it. All of the methods +from text headers are available in addition to the methods described here. + +Adding a parameterized header to a HeaderSet is done by using the +``addParameterizedHeader()`` method which takes a text value like +``addTextHeader()`` but it also accepts an associative array of +key-value parameters. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addParameterizedHeader( + 'Header-Name', 'header value', + array('foo' => 'bar') + ); + +To change the text value of the header, call it's ``setValue()`` method just as +you do with text headers. + +To change the parameters in the header, call the header's ``setParameters()`` +method or the ``setParameter()`` method (note the pluralization). + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + // setParameters() takes an associative array + $type->setParameters(array( + 'name' => 'file.txt', + 'charset' => 'iso-8859-1' + )); + + // setParameter() takes two args for $key and $value + $type->setParameter('charset', 'iso-8859-1'); + +When output via ``toString()``, a parameterized header produces something like +the following: + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + $type->setValue('text/html'); + $type->setParameter('charset', 'utf-8'); + + echo $type->toString(); + + /* + + Content-Type: text/html; charset=utf-8 + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded, just like they are for text headers. This is +nothing to be concerned about since mail clients will decode them back. +Likewise, if the parameters contain any non-ascii characters they will be +encoded so that they can be transmitted safely. + +.. code-block:: php + + $attachment = Swift_Attachment::newInstance(); + + $disp = $attachment->getHeaders()->get('Content-Disposition'); + + $disp->setValue('attachment'); + $disp->setParameter('filename', 'report–may.pdf'); + + echo $disp->toString(); + + /* + + Content-Disposition: attachment; filename*=utf-8''report%E2%80%93may.pdf + + */ + +Date Headers +~~~~~~~~~~~~ + +Date headers contains an RFC 2822 formatted date (i.e. what PHP's ``date('r')`` +returns). They are used anywhere a date or time is needed to be presented as a +message header. + +The data on which a date header is modeled is simply a UNIX timestamp such as +that returned by ``time()`` or ``strtotime()``. The timestamp is used to create +a correctly structured RFC 2822 formatted date such as +``Tue, 17 Feb 2009 22:26:31 +1100``. + +The obvious place this header type is used is in the ``Date:`` header of the +message itself. + +It's easy to add a new date header to a HeaderSet. You do this by calling +the HeaderSet's ``addDateHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addDateHeader('Your-Header-Name', strtotime('3 days ago')); + +Changing the value of an existing date header is done by calling it's +``setTimestamp()`` method. + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + $date->setTimestamp(time()); + +When output via ``toString()``, a date header produces something like the +following: + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + echo $date->toString(); + + /* + + Date: Wed, 18 Feb 2009 13:35:02 +1100 + + */ + +Mailbox (e-mail address) Headers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Mailbox headers contain one or more email addresses, possibly with +personalized names attached to them. The data on which they are modeled is +represented by an associative array of email addresses and names. + +Mailbox headers are probably the most complex header type to understand in +Swift Mailer because they accept their input as an array which can take various +forms, as described in the previous chapter. + +All of the headers that contain e-mail addresses in a message -- with the +exception of ``Return-Path:`` which has a stricter syntax -- use this header +type. That is, ``To:``, ``From:`` etc. + +You add a new mailbox header to a HeaderSet by calling the HeaderSet's +``addMailboxHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addMailboxHeader('Your-Header-Name', array( + 'person1@example.org' => 'Person Name One', + 'person2@example.org', + 'person3@example.org', + 'person4@example.org' => 'Another named person' + )); + +Changing the value of an existing mailbox header is done by calling it's +``setNameAddresses()`` method. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'joe@example.org' => 'Joe Bloggs', + 'john@example.org' => 'John Doe', + 'no-name@example.org' + )); + +If you don't wish to concern yourself with the complicated accepted input +formats accepted by ``setNameAddresses()`` as described in the previous chapter +and you only want to set one or more addresses (not names) then you can just +use the ``setAddresses()`` method instead. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses(array( + 'joe@example.org', + 'john@example.org', + 'no-name@example.org' + )); + +.. note:: + + Both methods will accept the above input format in practice. + +If all you want to do is set a single address in the header, you can use a +string as the input parameter to ``setAddresses()`` and/or +``setNameAddresses()``. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses('joe-bloggs@example.org'); + +When output via ``toString()``, a mailbox header produces something like the +following: + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'person1@example.org' => 'Name of Person', + 'person2@example.org', + 'person3@example.org' => 'Another Person' + )); + + echo $to->toString(); + + /* + + To: Name of Person , person2@example.org, Another Person + + + */ + +ID Headers +~~~~~~~~~~ + +ID headers contain identifiers for the entity (or the message). The most +notable ID header is the Message-ID header on the message itself. + +An ID that exists inside an ID header looks more-or-less less like an email +address. For example, ``<1234955437.499becad62ec2@example.org>``. +The part to the left of the @ sign is usually unique, based on the current time +and some random factor. The part on the right is usually a domain name. + +Any ID passed to the header's ``setId()`` method absolutely MUST conform to +this structure, otherwise you'll get an Exception thrown at you by Swift Mailer +(a ``Swift_RfcComplianceException``). This is to ensure that the generated +email complies with relevant RFC documents and therefore is less likely to be +blocked as spam. + +It's easy to add a new ID header to a HeaderSet. You do this by calling +the HeaderSet's ``addIdHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addIdHeader('Your-Header-Name', '123456.unqiue@example.org'); + +Changing the value of an existing date header is done by calling its +``setId()`` method. + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + $msgId->setId(time() . '.' . uniqid('thing') . '@example.org'); + +When output via ``toString()``, an ID header produces something like the +following: + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + echo $msgId->toString(); + + /* + + Message-ID: <1234955437.499becad62ec2@example.org> + + */ + +Path Headers +~~~~~~~~~~~~ + +Path headers are like very-restricted mailbox headers. They contain a single +email address with no associated name. The Return-Path header of a message is +a path header. + +You add a new path header to a HeaderSet by calling the HeaderSet's +``addPathHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addPathHeader('Your-Header-Name', 'person@example.org'); + + +Changing the value of an existing path header is done by calling its +``setAddress()`` method. + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('my-address@example.org'); + +When output via ``toString()``, a path header produces something like the +following: + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('person@example.org'); + + echo $return->toString(); + + /* + + Return-Path: + + */ + +Header Operations +----------------- + +Working with the headers in a message involves knowing how to use the methods +on the HeaderSet and on the individual Headers within the HeaderSet. + +Adding new Headers +~~~~~~~~~~~~~~~~~~ + +New headers can be added to the HeaderSet by using one of the provided +``add..Header()`` methods. + +To add a header to a MIME entity (such as the message): + +Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Add the header to the HeaderSet by calling one of the ``add..Header()`` + methods. + +The added header will appear in the message when it is sent. + +.. code-block:: php + + // Adding a custom header to a message + $message = Swift_Message::newInstance(); + $headers = $message->getHeaders(); + $headers->addTextHeader('X-Mine', 'something here'); + + // Adding a custom header to an attachment + $attachment = Swift_Attachment::fromPath('/path/to/doc.pdf'); + $attachment->getHeaders()->addDateHeader('X-Created-Time', time()); + +Retrieving Headers +~~~~~~~~~~~~~~~~~~ + +Headers are retrieved through the HeaderSet's ``get()`` and ``getAll()`` +methods. + +To get a header, or several headers from a MIME entity: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the header(s) from the HeaderSet by calling either ``get()`` or + ``getAll()``. + +When using ``get()`` a single header is returned that matches the name (case +insensitive) that is passed to it. When using ``getAll()`` with a header name, +an array of headers with that name are returned. Calling ``getAll()`` with no +arguments returns an array of all headers present in the entity. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``getAll()`` exists to fetch all + headers with a specified name. In addition, ``get()`` accepts an optional + numerical index, starting from zero to specify which header you want more + specifically. + +.. note:: + + If you want to modify the contents of the header and you don't know for + sure what type of header it is then you may need to check the type by + calling its ``getFieldType()`` method. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Get the To: header + $toHeader = $headers->get('To'); + + // Get all headers named "X-Foo" + $fooHeaders = $headers->getAll('X-Foo'); + + // Get the second header named "X-Foo" + $foo = $headers->get('X-Foo', 1); + + // Get all headers that are present + $all = $headers->getAll(); + +Check if a Header Exists +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can check if a named header is present in a HeaderSet by calling its +``has()`` method. + +To check if a header exists: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``has()`` method specifying the header you're looking + for. + +If the header exists, ``true`` will be returned or ``false`` if not. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``has()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Check if the To: header exists + if ($headers->has('To')) { + echo 'To: exists'; + } + + // Check if an X-Foo header exists twice (i.e. check for the 2nd one) + if ($headers->has('X-Foo', 1)) { + echo 'Second X-Foo header exists'; + } + +Removing Headers +~~~~~~~~~~~~~~~~ + +Removing a Header from the HeaderSet is done by calling the HeaderSet's +``remove()`` or ``removeAll()`` methods. + +To remove an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``remove()`` or ``removeAll()`` methods specifying the + header you want to remove. + +When calling ``remove()`` a single header will be removed. When calling +``removeAll()`` all headers with the given name will be removed. If no headers +exist with the given name, no errors will occur. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``remove()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. For the same reason, ``removeAll()`` exists to + remove all headers that have the given name. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Remove the Subject: header + $headers->remove('Subject'); + + // Remove all X-Foo headers + $headers->removeAll('X-Foo'); + + // Remove only the second X-Foo header + $headers->remove('X-Foo', 1); + +Modifying a Header's Content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To change a Header's content you should know what type of header it is and then +call it's appropriate setter method. All headers also have a +``setFieldBodyModel()`` method that accepts a mixed parameter and delegates to +the correct setter. + +To modify an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the Header by using the HeaderSet's ``get()``. + +* Call the Header's appropriate setter method or call the header's + ``setFieldBodyModel()`` method. + +The header will be updated inside the HeaderSet and the changes will be seen +when the message is sent. + +.. code-block:: php + + $headers = $message->getHeaders(); + + // Change the Subject: header + $subj = $headers->get('Subject'); + $subj->setValue('new subject here'); + + // Change the To: header + $to = $headers->get('To'); + $to->setNameAddresses(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); + + // Using the setFieldBodyModel() just delegates to the correct method + // So here to calls setNameAddresses() + $to->setFieldBodyModel(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); diff --git a/vendor/swiftmailer/swiftmailer/doc/help-resources.rst b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst new file mode 100644 index 0000000..4208935 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst @@ -0,0 +1,44 @@ +Getting Help +============ + +There are a number of ways you can get help when using Swift Mailer, depending +upon the nature of your problem. For bug reports and feature requests create a +new ticket in GitHub. For general advice ask on the Google Group +(swiftmailer). + +Submitting Bugs & Feature Requests +---------------------------------- + +Bugs and feature requests should be posted on GitHub. + +If you post a bug or request a feature in the forum, or on the Google Group +you will most likely be asked to create a ticket in `GitHub`_ since it is +simply not feasible to manage such requests from a number of a different +sources. + +When you go to GitHub you will be asked to create a username and password +before you can create a ticket. This is free and takes very little time. + +When you create your ticket, do not assign it to any milestones. A developer +will assess your ticket and re-assign it as needed. + +If your ticket is reporting a bug present in the current version, which was +not present in the previous version please include the tag "regression" in +your ticket. + +GitHub will update you when work is performed on your ticket. + +Ask on the Google Group +----------------------- + +You can seek advice at Google Groups, within the "swiftmailer" `group`_. + +You can post messages to this group if you want help, or there's something you +wish to discuss with the developers and with other users. + +This is probably the fastest way to get help since it is primarily email-based +for most users, though bug reports should not be posted here since they may +not be resolved. + +.. _`GitHub`: https://github.com/swiftmailer/swiftmailer/issues +.. _`group`: http://groups.google.com/group/swiftmailer diff --git a/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst new file mode 100644 index 0000000..978dca2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst @@ -0,0 +1,46 @@ +Including Swift Mailer (Autoloading) +==================================== + +If you are using Composer, Swift Mailer will be automatically autoloaded. + +If not, you can use the built-in autoloader by requiring the +``swift_required.php`` file:: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + /* rest of code goes here */ + +If you want to override the default Swift Mailer configuration, call the +``init()`` method on the ``Swift`` class and pass it a valid PHP callable (a +PHP function name, a PHP 5.3 anonymous function, ...):: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + function swiftmailer_configurator() { + // configure Swift Mailer + + Swift_DependencyContainer::getInstance()->... + Swift_Preferences::getInstance()->... + } + + Swift::init('swiftmailer_configurator'); + + /* rest of code goes here */ + +The advantage of using the ``init()`` method is that your code will be +executed only if you use Swift Mailer in your script. + +.. note:: + + While Swift Mailer's autoloader is designed to play nicely with other + autoloaders, sometimes you may have a need to avoid using Swift Mailer's + autoloader and use your own instead. Include the ``swift_init.php`` + instead of the ``swift_required.php`` if you need to do this. The very + minimum include is the ``swift_init.php`` file since Swift Mailer will not + work without the dependency injection this file sets up: + + .. code-block:: php + + require_once '/path/to/swift-mailer/lib/swift_init.php'; + + /* rest of code goes here */ diff --git a/vendor/swiftmailer/swiftmailer/doc/index.rst b/vendor/swiftmailer/swiftmailer/doc/index.rst new file mode 100644 index 0000000..a1a0a92 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/index.rst @@ -0,0 +1,16 @@ +Swiftmailer +=========== + +.. toctree:: + :maxdepth: 2 + + introduction + overview + installing + help-resources + including-the-files + messages + headers + sending + plugins + japanese diff --git a/vendor/swiftmailer/swiftmailer/doc/installing.rst b/vendor/swiftmailer/swiftmailer/doc/installing.rst new file mode 100644 index 0000000..557211d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/installing.rst @@ -0,0 +1,89 @@ +Installing the Library +====================== + +Installing with Composer +------------------------ + +The recommended way to install Swiftmailer is via Composer: + +.. code-block:: bash + + $ php composer.phar require swiftmailer/swiftmailer @stable + +Installing from Git +------------------- + +It's possible to download and install Swift Mailer directly from github.com if +you want to keep up-to-date with ease. + +Swift Mailer's source code is kept in a git repository at github.com so you +can get the source directly from the repository. + +.. note:: + + You do not need to have git installed to use Swift Mailer from GitHub. If + you don't have git installed, go to `GitHub`_ and click the "Download" + button. + +Cloning the Repository +~~~~~~~~~~~~~~~~~~~~~~ + +The repository can be cloned from git://github.com/swiftmailer/swiftmailer.git +using the ``git clone`` command. + +You will need to have ``git`` installed before you can use the +``git clone`` command. + +To clone the repository: + +* Open your favorite terminal environment (command line). + +* Move to the directory you want to clone to. + +* Run the command ``git clone git://github.com/swiftmailer/swiftmailer.git + swiftmailer``. + +The source code will be downloaded into a directory called "swiftmailer". + +The example shows the process on a UNIX-like system such as Linux, BSD or Mac +OS X. + +.. code-block:: bash + + $ cd source_code/ + $ git clone git://github.com/swiftmailer/swiftmailer.git swiftmailer + Initialized empty Git repository in /Users/chris/source_code/swiftmailer/.git/ + remote: Counting objects: 6815, done. + remote: Compressing objects: 100% (2761/2761), done. + remote: Total 6815 (delta 3641), reused 6326 (delta 3286) + Receiving objects: 100% (6815/6815), 4.35 MiB | 162 KiB/s, done. + Resolving deltas: 100% (3641/3641), done. + Checking out files: 100% (1847/1847), done. + $ cd swiftmailer/ + $ ls + CHANGES LICENSE ... + $ + +Troubleshooting +--------------- + +Swift Mailer does not work when used with function overloading as implemented +by ``mbstring`` (``mbstring.func_overload`` set to ``2``). A workaround is to +temporarily change the internal encoding to ``ASCII`` when sending an email: + +.. code-block:: php + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + // Create your message and send it with Swift Mailer + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + +.. _`GitHub`: http://github.com/swiftmailer/swiftmailer diff --git a/vendor/swiftmailer/swiftmailer/doc/introduction.rst b/vendor/swiftmailer/swiftmailer/doc/introduction.rst new file mode 100644 index 0000000..a85336b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/introduction.rst @@ -0,0 +1,135 @@ +Introduction +============ + +Swift Mailer is a component-based library for sending e-mails from PHP +applications. + +Organization of this Book +------------------------- + +This book has been written so that those who need information quickly are able +to find what they need, and those who wish to learn more advanced topics can +read deeper into each chapter. + +The book begins with an overview of Swift Mailer, discussing what's included +in the package and preparing you for the remainder of the book. + +It is possible to read this user guide just like any other book (from +beginning to end). Each chapter begins with a discussion of the contents it +contains, followed by a short code sample designed to give you a head start. +As you get further into a chapter you will learn more about Swift Mailer's +capabilities, but often you will be able to head directly to the topic you +wish to learn about. + +Throughout this book you will be presented with code samples, which most +people should find ample to implement Swift Mailer appropriately in their own +projects. We will also use diagrams where appropriate, and where we believe +readers may find it helpful we will discuss some related theory, including +reference to certain documents you are able to find online. + +Code Samples +------------ + +Code samples presented in this book will be displayed on a different colored +background in a monospaced font. Samples are not to be taken as copy & paste +code snippets. + +Code examples are used through the book to clarify what is written in text. +They will sometimes be usable as-is, but they should always be taken as +outline/pseudo code only. + +A code sample will look like this:: + + class AClass + { + ... + } + + // A Comment + $obj = new AClass($arg1, $arg2, ... ); + + /* A note about another way of doing something + $obj = AClass::newInstance($arg1, $arg2, ... ); + + */ + +The presence of 3 dots ``...`` in a code sample indicates that we have left +out a chunk of the code for brevity, they are not actually part of the code. + +We will often place multi-line comments ``/* ... */`` in the code so that we +can show alternative ways of achieving the same result. + +You should read the code examples given and try to understand them. They are +kept concise so that you are not overwhelmed with information. + +History of Swift Mailer +----------------------- + +Swift Mailer began back in 2005 as a one-class project for sending mail over +SMTP. It has since grown into the flexible component-based library that is in +development today. + +Chris Corbyn first posted Swift Mailer on a web forum asking for comments from +other developers. It was never intended as a fully supported open source +project, but members of the forum began to adopt it and make use of it. + +Very quickly feature requests were coming for the ability to add attachments +and use SMTP authentication, along with a number of other "obvious" missing +features. Considering the only alternative was PHPMailer it seemed like a good +time to bring some fresh tools to the table. Chris began working towards a +more component based, PHP5-like approach unlike the existing single-class, +legacy PHP4 approach taken by PHPMailer. + +Members of the forum offered a lot of advice and critique on the code as he +worked through this project and released versions 2 and 3 of the library in +2005 and 2006, which by then had been broken down into smaller classes +offering more flexibility and supporting plugins. To this day the Swift Mailer +team still receive a lot of feature requests from users both on the forum and +in by email. + +Until 2008 Chris was the sole developer of Swift Mailer, but entering 2009 he +gained the support of two experienced developers well-known to him: Paul +Annesley and Christopher Thompson. This has been an extremely welcome change. + +As of September 2009, Chris handed over the maintenance of Swift Mailer to +Fabien Potencier. + +Now 2009 and in its fourth major version Swift Mailer is more object-oriented +and flexible than ever, both from a usability standpoint and from a +development standpoint. + +By no means is Swift Mailer ready to call "finished". There are still many +features that can be added to the library along with the constant refactoring +that happens behind the scenes. + +It's a Library! +--------------- + +Swift Mailer is not an application - it's a library. + +To most experienced developers this is probably an obvious point to make, but +it's certainly worth mentioning. Many people often contact us having gotten +the completely wrong end of the stick in terms of what Swift Mailer is +actually for. + +It's not an application. It does not have a graphical user interface. It +cannot be opened in your web browser directly. + +It's a library (or a framework if you like). It provides a whole lot of +classes that do some very complicated things, so that you don't have to. You +"use" Swift Mailer within an application so that your application can have the +ability to send emails. + +The component-based structure of the library means that you are free to +implement it in a number of different ways and that you can pick and choose +what you want to use. + +An application on the other hand (such as a blog or a forum) is already "put +together" in a particular way, (usually) provides a graphical user interface +and most likely doesn't offer a great deal of integration with your own +application. + +Embrace the structure of the library and use the components it offers to your +advantage. Learning what the components do, rather than blindly copying and +pasting existing code will put you in a great position to build a powerful +application! diff --git a/vendor/swiftmailer/swiftmailer/doc/japanese.rst b/vendor/swiftmailer/swiftmailer/doc/japanese.rst new file mode 100644 index 0000000..34afa7b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/japanese.rst @@ -0,0 +1,22 @@ +Using Swift Mailer for Japanese Emails +====================================== + +To send emails in Japanese, you need to tweak the default configuration. + +After requiring the Swift Mailer autoloader (by including the +``swift_required.php`` file), call the ``Swift::init()`` method with the +following code:: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + Swift::init(function () { + Swift_DependencyContainer::getInstance() + ->register('mime.qpheaderencoder') + ->asAliasOf('mime.base64headerencoder'); + + Swift_Preferences::getInstance()->setCharset('iso-2022-jp'); + }); + + /* rest of code goes here */ + +That's all! diff --git a/vendor/swiftmailer/swiftmailer/doc/messages.rst b/vendor/swiftmailer/swiftmailer/doc/messages.rst new file mode 100644 index 0000000..7a19253 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/messages.rst @@ -0,0 +1,1057 @@ +Creating Messages +================= + +Creating messages in Swift Mailer is done by making use of the various MIME +entities provided with the library. Complex messages can be quickly created +with very little effort. + +Quick Reference for Creating a Message +--------------------------------------- + +You can think of creating a Message as being similar to the steps you perform +when you click the Compose button in your mail client. You give it a subject, +specify some recipients, add any attachments and write your message. + +To create a Message: + +* Call the ``newInstance()`` method of ``Swift_Message``. + +* Set your sender address (``From:``) with ``setFrom()`` or ``setSender()``. + +* Set a subject line with ``setSubject()``. + +* Set recipients with ``setTo()``, ``setCc()`` and/or ``setBcc()``. + +* Set a body with ``setBody()``. + +* Add attachments with ``attach()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the message + $message = Swift_Message::newInstance() + + // Give the message a subject + ->setSubject('Your subject') + + // Set the From address with an associative array + ->setFrom(array('john@doe.com' => 'John Doe')) + + // Set the To addresses with an associative array + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + + // Give it a body + ->setBody('Here is the message itself') + + // And optionally an alternative body + ->addPart('Here is the message itself', 'text/html') + + // Optionally add any attachments + ->attach(Swift_Attachment::fromPath('my-document.pdf')) + ; + +Message Basics +-------------- + +A message is a container for anything you want to send to somebody else. There +are several basic aspects of a message that you should know. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Header-Name: A header value + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +The Structure of a Message +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Of all of the MIME entities, a message -- ``Swift_Message`` +is the largest and most complex. It has many properties that can be updated +and it can contain other MIME entities -- attachments for example -- +nested inside it. + +A Message has a lot of different Headers which are there to present +information about the message to the recipients' mail client. Most of these +headers will be familiar to the majority of users, but we'll list the basic +ones. Although it's possible to work directly with the Headers of a Message +(or other MIME entity), the standard Headers have accessor methods provided to +abstract away the complex details for you. For example, although the Date on a +message is written with a strict format, you only need to pass a UNIX +timestamp to ``setDate()``. + ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| Header | Description | Accessors | ++===============================+====================================================================================================================================+=============================================+ +| ``Message-ID`` | Identifies this message with a unique ID, usually containing the domain name and time generated | ``getId()`` / ``setId()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Return-Path`` | Specifies where bounces should go (Swift Mailer reads this for other uses) | ``getReturnPath()`` / ``setReturnPath()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``From`` | Specifies the address of the person who the message is from. This can be multiple addresses if multiple people wrote the message. | ``getFrom()`` / ``setFrom()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Sender`` | Specifies the address of the person who physically sent the message (higher precedence than ``From:``) | ``getSender()`` / ``setSender()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``To`` | Specifies the addresses of the intended recipients | ``getTo()`` / ``setTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Cc`` | Specifies the addresses of recipients who will be copied in on the message | ``getCc()`` / ``setCc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Bcc`` | Specifies the addresses of recipients who the message will be blind-copied to. Other recipients will not be aware of these copies. | ``getBcc()`` / ``setBcc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Reply-To`` | Specifies the address where replies are sent to | ``getReplyTo()`` / ``setReplyTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Subject`` | Specifies the subject line that is displayed in the recipients' mail client | ``getSubject()`` / ``setSubject()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Date`` | Specifies the date at which the message was sent | ``getDate()`` / ``setDate()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Type`` | Specifies the format of the message (usually text/plain or text/html) | ``getContentType()`` / ``setContentType()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Transfer-Encoding`` | Specifies the encoding scheme in the message | ``getEncoder()`` / ``setEncoder()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ + +Working with a Message Object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although there are a lot of available methods on a message object, you only +need to make use of a small subset of them. Usually you'll use +``setSubject()``, ``setTo()`` and +``setFrom()`` before setting the body of your message with +``setBody()``. + +Calling methods is simple. You just call them like functions, but using the +object operator "``->``" to do so. If you've created +a message object and called it ``$message`` then you'd set a +subject on it like so: + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + $message = Swift_Message::newInstance(); + $message->setSubject('My subject'); + +All MIME entities (including a message) have a ``toString()`` +method that you can call if you want to take a look at what is going to be +sent. For example, if you ``echo +$message->toString();`` you would see something like this: + +.. code-block:: bash + + Message-ID: <1230173678.4952f5eeb1432@swift.generated> + Date: Thu, 25 Dec 2008 13:54:38 +1100 + Subject: Example subject + From: Chris Corbyn + To: Receiver Name + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + + Here is the message + +We'll take a closer look at the methods you use to create your message in the +following sections. + +Adding Content to Your Message +------------------------------ + +Rich content can be added to messages in Swift Mailer with relative ease by +calling methods such as ``setSubject()``, ``setBody()``, ``addPart()`` and +``attach()``. + +Setting the Subject Line +~~~~~~~~~~~~~~~~~~~~~~~~ + +The subject line, displayed in the recipients' mail client can be set with the +``setSubject()`` method, or as a parameter to ``Swift_Message::newInstance()``. + +To set the subject of your Message: + +* Call the ``setSubject()`` method of the Message, or specify it at the time + you create the message. + + .. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('My amazing subject'); + + // Or set it after like this + $message->setSubject('My amazing subject'); + +Setting the Body Content +~~~~~~~~~~~~~~~~~~~~~~~~ + +The body of the message -- seen when the user opens the message -- +is specified by calling the ``setBody()`` method. If an alternative body is to +be included ``addPart()`` can be used. + +The body of a message is the main part that is read by the user. Often people +want to send a message in HTML format (``text/html``), other +times people want to send in plain text (``text/plain``), or +sometimes people want to send both versions and allow the recipient to choose +how they view the message. + +As a rule of thumb, if you're going to send a HTML email, always include a +plain-text equivalent of the same content so that users who prefer to read +plain text can do so. + +To set the body of your Message: + +* Call the ``setBody()`` method of the Message, or specify it at the time you + create the message. + +* Add any alternative bodies with ``addPart()``. + +If the recipient's mail client offers preferences for displaying text vs. HTML +then the mail client will present that part to the user where available. In +other cases the mail client will display the "best" part it can - usually HTML +if you've included HTML. + +.. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('Subject here', 'My amazing body'); + + // Or set it after like this + $message->setBody('My amazing body', 'text/html'); + + // Add alternative parts with addPart() + $message->addPart('My amazing body in plain text', 'text/plain'); + +Attaching Files +--------------- + +Attachments are downloadable parts of a message and can be added by calling +the ``attach()`` method on the message. You can add attachments that exist on +disk, or you can create attachments on-the-fly. + +Attachments are actually an interesting area of Swift Mailer and something +that could put a lot of power at your fingertips if you grasp the concept +behind the way a message is held together. + +Although we refer to files sent over e-mails as "attachments" -- because +they're attached to the message -- lots of other parts of the message are +actually "attached" even if we don't refer to these parts as attachments. + +File attachments are created by the ``Swift_Attachment`` class +and then attached to the message via the ``attach()`` method on +it. For all of the "every day" MIME types such as all image formats, word +documents, PDFs and spreadsheets you don't need to explicitly set the +content-type of the attachment, though it would do no harm to do so. For less +common formats you should set the content-type -- which we'll cover in a +moment. + +Attaching Existing Files +~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that already exist, either on disk or at a URL can be attached to a +message with just one line of code, using ``Swift_Attachment::fromPath()``. + +You can attach files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can attach files from other +websites. + +To attach an existing file: + +* Create an attachment with ``Swift_Attachment::fromPath()``. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file with +the same filename as the one you attached. + +.. code-block:: php + + // Create the attachment + // * Note that you can technically leave the content-type parameter out + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg', 'image/jpeg'); + + // Attach it to the message + $message->attach($attachment); + + + // The two statements above could be written in one line instead + $message->attach(Swift_Attachment::fromPath('/path/to/image.jpg')); + + + // You can attach files from a URL if allow_url_fopen is on in php.ini + $message->attach(Swift_Attachment::fromPath('http://site.tld/logo.png')); + +Setting the Filename +~~~~~~~~~~~~~~~~~~~~ + +Usually you don't need to explicitly set the filename of an attachment because +the name of the attached file will be used by default, but if you want to set +the filename you use the ``setFilename()`` method of the Attachment. + +To change the filename of an attachment: + +* Call its ``setFilename()`` method. + +The attachment will be attached in the normal way, but meta-data sent inside +the email will rename the file to something else. + +.. code-block:: php + + // Create the attachment and call its setFilename() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setFilename('cool.jpg'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setFilename('cool.jpg') + ); + +Attaching Dynamic Content +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that are generated at runtime, such as PDF documents or images created +via GD can be attached directly to a message without writing them out to disk. +Use the standard ``Swift_Attachment::newInstance()`` method. + +To attach dynamically created content: + +* Create your content as you normally would. + +* Create an attachment with ``Swift_Attachment::newInstance()``, specifying + the source data of your content along with a name and the content-type. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file +with the filename and content-type you specify. + +.. note:: + + If you would usually write the file to disk anyway you should just attach + it with ``Swift_Attachment::fromPath()`` since this will use less memory: + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $data = create_my_pdf_data(); + + // Create the attachment with your data + $attachment = Swift_Attachment::newInstance($data, 'my-file.pdf', 'application/pdf'); + + // Attach it to the message + $message->attach($attachment); + + + // You can alternatively use method chaining to build the attachment + $attachment = Swift_Attachment::newInstance() + ->setFilename('my-file.pdf') + ->setContentType('application/pdf') + ->setBody($data) + ; + +Changing the Disposition +~~~~~~~~~~~~~~~~~~~~~~~~ + +Attachments just appear as files that can be saved to the Desktop if desired. +You can make attachment appear inline where possible by using the +``setDisposition()`` method of an attachment. + +To make an attachment appear inline: + +* Call its ``setDisposition()`` method. + +The attachment will be displayed within the email viewing window if the mail +client knows how to display it. + +.. note:: + + If you try to create an inline attachment for a non-displayable file type + such as a ZIP file, the mail client should just present the attachment as + normal: + + .. code-block:: php + + // Create the attachment and call its setDisposition() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setDisposition('inline'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setDisposition('inline') + ); + +Embedding Inline Media Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Often people want to include an image or other content inline with a HTML +message. It's easy to do this with HTML linking to remote resources, but this +approach is usually blocked by mail clients. Swift Mailer allows you to embed +your media directly into the message. + +Mail clients usually block downloads from remote resources because this +technique was often abused as a mean of tracking who opened an email. If +you're sending a HTML email and you want to include an image in the message +another approach you can take is to embed the image directly. + +Swift Mailer makes embedding files into messages extremely streamlined. You +embed a file by calling the ``embed()`` method of the message, +which returns a value you can use in a ``src`` or +``href`` attribute in your HTML. + +Just like with attachments, it's possible to embed dynamically generated +content without having an existing file available. + +The embedded files are sent in the email as a special type of attachment that +has a unique ID used to reference them within your HTML attributes. On mail +clients that do not support embedded files they may appear as attachments. + +Although this is commonly done for images, in theory it will work for any +displayable (or playable) media type. Support for other media types (such as +video) is dependent on the mail client however. + +Embedding Existing Files +........................ + +Files that already exist, either on disk or at a URL can be embedded in a +message with just one line of code, using ``Swift_EmbeddedFile::fromPath()``. + +You can embed files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can embed files from other websites. + +To embed an existing file: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message with ``embed()``. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create the message + $message = Swift_Message::newInstance('My subject'); + + // Set the body + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + + // You can embed files from a URL if allow_url_fopen is on in php.ini + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::fromPath('image.png')); + + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + +Embedding Dynamic Content +......................... + +Images that are generated at runtime, such as images created via GD can be +embedded directly to a message without writing them out to disk. Use the +standard ``Swift_Image::newInstance()`` method. + +To embed dynamically created content: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message + with ``embed()``. You will need to specify a filename and a content-type. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $img_data = create_my_image_data(); + + // Create the message + $message = Swift_Message::newInstance('My subject'); + + // Set the body + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::newInstance($img_data, 'image.jpg', 'image/jpeg')); + + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + +Adding Recipients to Your Message +--------------------------------- + +Recipients are specified within the message itself via ``setTo()``, ``setCc()`` +and ``setBcc()``. Swift Mailer reads these recipients from the message when it +gets sent so that it knows where to send the message to. + +Message recipients are one of three types: + +* ``To:`` recipients -- the primary recipients (required) + +* ``Cc:`` recipients -- receive a copy of the message (optional) + +* ``Bcc:`` recipients -- hidden from other recipients (optional) + +Each type can contain one, or several addresses. It's possible to list only +the addresses of the recipients, or you can personalize the address by +providing the real name of the recipient. + +Make sure to add only valid email addresses as recipients. If you try to add an +invalid email address with ``setTo()``, ``setCc()`` or ``setBcc()``, Swift +Mailer will throw a ``Swift_RfcComplianceException``. + +If you add recipients automatically based on a data source that may contain +invalid email addresses, you can prevent possible exceptions by validating the +addresses using ``Swift_Validate::email($email)`` and only adding addresses +that validate. Another way would be to wrap your ``setTo()``, ``setCc()`` and +``setBcc()`` calls in a try-catch block and handle the +``Swift_RfcComplianceException`` in the catch block. + +.. sidebar:: Syntax for Addresses + + If you only wish to refer to a single email address (for example your + ``From:`` address) then you can just use a string. + + .. code-block:: php + + $message->setFrom('some@address.tld'); + + If you want to include a name then you must use an associative array. + + .. code-block:: php + + $message->setFrom(array('some@address.tld' => 'The Name')); + + If you want to include multiple addresses then you must use an array. + + .. code-block:: php + + $message->setTo(array('some@address.tld', 'other@address.tld')); + + You can mix personalized (addresses with a name) and non-personalized + addresses in the same list by mixing the use of associative and + non-associative array syntax. + + .. code-block:: php + + $message->setTo(array( + 'recipient-with-name@example.org' => 'Recipient Name One', + 'no-name@example.org', // Note that this is not a key-value pair + 'named-recipient@example.org' => 'Recipient Name Two' + )); + +Setting ``To:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``To:`` recipients are required in a message and are set with the +``setTo()`` or ``addTo()`` methods of the message. + +To set ``To:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, +then call the ``setTo()`` method with a complete array of addresses, or use the +``addTo()`` method to iteratively add recipients. + +The ``setTo()`` method accepts input in various formats as described earlier in +this chapter. The ``addTo()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``To:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setTo()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add + recipients, use the ``addTo()`` method. + + .. code-block:: php + + // Using setTo() to set all recipients in one go + $message->setTo(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addTo() to add recipients iteratively + $message->addTo('person1@example.org'); + $message->addTo('person2@example.org', 'Person 2 Name'); + +Setting ``Cc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Cc:`` recipients are set with the ``setCc()`` or ``addCc()`` methods of the +message. + +To set ``Cc:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call +the ``setCc()`` method with a complete array of addresses, or use the +``addCc()`` method to iteratively add recipients. + +The ``setCc()`` method accepts input in various formats as described earlier in +this chapter. The ``addCc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``Cc:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setCc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Cc: + recipients, use the ``addCc()`` method. + + .. code-block:: php + + // Using setCc() to set all recipients in one go + $message->setCc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addCc() to add recipients iteratively + $message->addCc('person1@example.org'); + $message->addCc('person2@example.org', 'Person 2 Name'); + +Setting ``Bcc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Bcc:`` recipients receive a copy of the message without anybody else knowing +it, and are set with the ``setBcc()`` or ``addBcc()`` methods of the message. + +To set ``Bcc:`` recipients, create the message object using either ``new +Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call the +``setBcc()`` method with a complete array of addresses, or use +the ``addBcc()`` method to iteratively add recipients. + +The ``setBcc()`` method accepts input in various formats as described earlier in +this chapter. The ``addBcc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +Only the individual ``Bcc:`` recipient will see their address in the message +headers. Other recipients (including other ``Bcc:`` recipients) will not see the +address. + +.. note:: + + Multiple calls to ``setBcc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Bcc: + recipients, use the ``addBcc()`` method. + + .. code-block:: php + + // Using setBcc() to set all recipients in one go + $message->setBcc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addBcc() to add recipients iteratively + $message->addBcc('person1@example.org'); + $message->addBcc('person2@example.org', 'Person 2 Name'); + +Specifying Sender Details +------------------------- + +An email must include information about who sent it. Usually this is managed +by the ``From:`` address, however there are other options. + +The sender information is contained in three possible places: + +* ``From:`` -- the address(es) of who wrote the message (required) + +* ``Sender:`` -- the address of the single person who sent the message + (optional) + +* ``Return-Path:`` -- the address where bounces should go to (optional) + +You must always include a ``From:`` address by using ``setFrom()`` on the +message. Swift Mailer will use this as the default ``Return-Path:`` unless +otherwise specified. + +The ``Sender:`` address exists because the person who actually sent the email +may not be the person who wrote the email. It has a higher precedence than the +``From:`` address and will be used as the ``Return-Path:`` unless otherwise +specified. + +Setting the ``From:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``From:`` address is required and is set with the ``setFrom()`` method of the +message. ``From:`` addresses specify who actually wrote the email, and usually who sent it. + +What most people probably don't realise is that you can have more than one +``From:`` address if more than one person wrote the email -- for example if an +email was put together by a committee. + +To set the ``From:`` address(es): + +* Call the ``setFrom()`` method on the Message. + +The ``From:`` address(es) are visible in the message headers and +will be seen by the recipients. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + + .. code-block:: php + + // Set a single From: address + $message->setFrom('your@address.tld'); + + // Set a From: address including a name + $message->setFrom(array('your@address.tld' => 'Your Name')); + + // Set multiple From: addresses if multiple people wrote the email + $message->setFrom(array( + 'person1@example.org' => 'Sender One', + 'person2@example.org' => 'Sender Two' + )); + +Setting the ``Sender:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Sender:`` address specifies who sent the message and is set with the +``setSender()`` method of the message. + +To set the ``Sender:`` address: + +* Call the ``setSender()`` method on the Message. + +The ``Sender:`` address is visible in the message headers and will be seen by +the recipients. + +This address will be used as the ``Return-Path:`` unless otherwise specified. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + +You must not set more than one sender address on a message because it's not +possible for more than one person to send a single message. + +.. code-block:: php + + $message->setSender('your@address.tld'); + +Setting the ``Return-Path:`` (Bounce) Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Return-Path:`` address specifies where bounce notifications should +be sent and is set with the ``setReturnPath()`` method of the message. + +You can only have one ``Return-Path:`` and it must not include +a personal name. + +To set the ``Return-Path:`` address: + +* Call the ``setReturnPath()`` method on the Message. + +Bounce notifications will be sent to this address. + +.. code-block:: php + + $message->setReturnPath('bounces@address.tld'); + + +Signed/Encrypted Message +------------------------ + +To increase the integrity/security of a message it is possible to sign and/or +encrypt an message using one or multiple signers. + +S/MIME +~~~~~~ + +S/MIME can sign and/or encrypt a message using the OpenSSL extension. + +When signing a message, the signer creates a signature of the entire content of the message (including attachments). + +The certificate and private key must be PEM encoded, and can be either created using for example OpenSSL or +obtained at an official Certificate Authority (CA). + +**The recipient must have the CA certificate in the list of trusted issuers in order to verify the signature.** + +**Make sure the certificate supports emailProtection.** + +When using OpenSSL this can done by the including the *-addtrust emailProtection* parameter when creating the certificate. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/certificate.pem', '/path/to/private-key.pem'); + $message->attachSigner($smimeSigner); + +When the private key is secured using a passphrase use the following instead. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/certificate.pem', array('/path/to/private-key.pem', 'passphrase')); + $message->attachSigner($smimeSigner); + +By default the signature is added as attachment, +making the message still readable for mailing agents not supporting signed messages. + +Storing the message as binary is also possible but not recommended. + +.. code-block:: php + + $smimeSigner->setSignCertificate('/path/to/certificate.pem', '/path/to/private-key.pem', PKCS7_BINARY); + +When encrypting the message (also known as enveloping), the entire message (including attachments) +is encrypted using a certificate, and the recipient can then decrypt the message using corresponding private key. + +Encrypting ensures nobody can read the contents of the message without the private key. + +Normally the recipient provides a certificate for encrypting and keeping the decryption key private. + +Using both signing and encrypting is also possible. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/sign-certificate.pem', '/path/to/private-key.pem'); + $smimeSigner->setEncryptCertificate('/path/to/encrypt-certificate.pem'); + $message->attachSigner($smimeSigner); + +The used encryption cipher can be set as the second parameter of setEncryptCertificate() + +See http://php.net/manual/openssl.ciphers for a list of supported ciphers. + +By default the message is first signed and then encrypted, this can be changed by adding. + +.. code-block:: php + + $smimeSigner->setSignThenEncrypt(false); + +**Changing this is not recommended as most mail agents don't support this none-standard way.** + +Only when having trouble with sign then encrypt method, this should be changed. + +Requesting a Read Receipt +------------------------- + +It is possible to request a read-receipt to be sent to an address when the +email is opened. To request a read receipt set the address with +``setReadReceiptTo()``. + +To request a read receipt: + +* Set the address you want the receipt to be sent to with the + ``setReadReceiptTo()`` method on the Message. + +When the email is opened, if the mail client supports it a notification will be sent to this address. + +.. note:: + + Read receipts won't work for the majority of recipients since many mail + clients auto-disable them. Those clients that will send a read receipt + will make the user aware that one has been requested. + + .. code-block:: php + + $message->setReadReceiptTo('your@address.tld'); + +Setting the Character Set +------------------------- + +The character set of the message (and it's MIME parts) is set with the +``setCharset()`` method. You can also change the global default of UTF-8 by +working with the ``Swift_Preferences`` class. + +Swift Mailer will default to the UTF-8 character set unless otherwise +overridden. UTF-8 will work in most instances since it includes all of the +standard US keyboard characters in addition to most international characters. + +It is absolutely vital however that you know what character set your message +(or it's MIME parts) are written in otherwise your message may be received +completely garbled. + +There are two places in Swift Mailer where you can change the character set: + +* In the ``Swift_Preferences`` class + +* On each individual message and/or MIME part + +To set the character set of your Message: + +* Change the global UTF-8 setting by calling + ``Swift_Preferences::setCharset()``; or + +* Call the ``setCharset()`` method on the message or the MIME part. + + .. code-block:: php + + // Approach 1: Change the global setting (suggested) + Swift_Preferences::getInstance()->setCharset('iso-8859-2'); + + // Approach 2: Call the setCharset() method of the message + $message = Swift_Message::newInstance() + ->setCharset('iso-8859-2'); + + // Approach 3: Specify the charset when setting the body + $message->setBody('My body', 'text/html', 'iso-8859-2'); + + // Approach 4: Specify the charset for each part added + $message->addPart('My part', 'text/plain', 'iso-8859-2'); + +Setting the Line Length +----------------------- + +The length of lines in a message can be changed by using the ``setMaxLineLength()`` method on the message. It should be kept to less than +1000 characters. + +Swift Mailer defaults to using 78 characters per line in a message. This is +done for historical reasons and so that the message can be easily viewed in +plain-text terminals. + +To change the maximum length of lines in your Message: + +* Call the ``setMaxLineLength()`` method on the Message. + +Lines that are longer than the line length specified will be wrapped between +words. + +.. note:: + + You should never set a maximum length longer than 1000 characters + according to RFC 2822. Doing so could have unspecified side-effects such + as truncating parts of your message when it is transported between SMTP + servers. + + .. code-block:: php + + $message->setMaxLineLength(1000); + +Setting the Message Priority +---------------------------- + +You can change the priority of the message with ``setPriority()``. Setting the +priority will not change the way your email is sent -- it is purely an +indicative setting for the recipient. + +The priority of a message is an indication to the recipient what significance +it has. Swift Mailer allows you to set the priority by calling the ``setPriority`` method. This method takes an integer value between 1 and 5: + +* Highest +* High +* Normal +* Low +* Lowest + +To set the message priority: + +* Set the priority as an integer between 1 and 5 with the ``setPriority()`` + method on the Message. + +.. code-block:: php + + // Indicate "High" priority + $message->setPriority(2); diff --git a/vendor/swiftmailer/swiftmailer/doc/overview.rst b/vendor/swiftmailer/swiftmailer/doc/overview.rst new file mode 100644 index 0000000..c912617 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/overview.rst @@ -0,0 +1,161 @@ +Library Overview +================ + +Most features (and more) of your every day mail client software are provided +by Swift Mailer, using object-oriented PHP code as the interface. + +In this chapter we will take a short tour of the various components, which put +together form the Swift Mailer library as a whole. You will learn key +terminology used throughout the rest of this book and you will gain a little +understanding of the classes you will work with as you integrate Swift Mailer +into your application. + +This chapter is intended to prepare you for the information contained in the +subsequent chapters of this book. You may choose to skip this chapter if you +are fairly technically minded, though it is likely to save you some time in +the long run if you at least read between the lines here. + +System Requirements +------------------- + +The basic requirements to operate Swift Mailer are extremely minimal and +easily achieved. Historically, Swift Mailer has supported both PHP 4 and PHP 5 +by following a parallel development workflow. Now in it's fourth major +version, and Swift Mailer operates on servers running PHP 5.2 or higher. + +The library aims to work with as many PHP 5 projects as possible: + +* PHP 5.2 or higher, with the SPL extension (standard) + +* Limited network access to connect to remote SMTP servers + +* 8 MB or more memory limit (Swift Mailer uses around 2 MB) + +Component Breakdown +------------------- + +Swift Mailer is made up of many classes. Each of these classes can be grouped +into a general "component" group which describes the task it is designed to +perform. + +We'll take a brief look at the components which form Swift Mailer in this +section of the book. + +The Mailer +~~~~~~~~~~ + +The mailer class, ``Swift_Mailer`` is the central class in the library where +all of the other components meet one another. ``Swift_Mailer`` acts as a sort +of message dispatcher, communicating with the underlying Transport to deliver +your Message to all intended recipients. + +If you were to dig around in the source code for Swift Mailer you'd notice +that ``Swift_Mailer`` itself is pretty bare. It delegates to other objects for +most tasks and in theory, if you knew the internals of Swift Mailer well you +could by-pass this class entirely. We wouldn't advise doing such a thing +however -- there are reasons this class exists: + +* for consistency, regardless of the Transport used + +* to provide abstraction from the internals in the event internal API changes + are made + +* to provide convenience wrappers around aspects of the internal API + +An instance of ``Swift_Mailer`` is created by the developer before sending any +Messages. + +Transports +~~~~~~~~~~ + +Transports are the classes in Swift Mailer that are responsible for +communicating with a service in order to deliver a Message. There are several +types of Transport in Swift Mailer, all of which implement the Swift_Transport +interface and offer underlying start(), stop() and send() methods. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| Class | Features | Pros/cons | ++=================================+=============================================================================================+===============================================================================================================================================+ +| ``Swift_SmtpTransport`` | Sends messages over SMTP; Supports Authentication; Supports Encryption | Very portable; Pleasingly predictable results; Provides good feedback | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_SendmailTransport`` | Communicates with a locally installed ``sendmail`` executable (Linux/UNIX) | Quick time-to-run; Provides less-accurate feedback than SMTP; Requires ``sendmail`` installation | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_MailTransport`` | Uses PHP's built-in ``mail()`` function | Very portable; Potentially unpredictable results; Provides extremely weak feedback | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_LoadBalancedTransport`` | Cycles through a collection of the other Transports to manage load-reduction | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down); Keeps the load on remote services down by spreading the work | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_FailoverTransport`` | Works in conjunction with a collection of the other Transports to provide high-availability | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down) | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ + +MIME Entities +~~~~~~~~~~~~~ + +Everything that forms part of a Message is called a MIME Entity. All MIME +entities in Swift Mailer share a common set of features. There are various +types of MIME entity that serve different purposes such as Attachments and +MIME parts. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +Each MIME component implements the base ``Swift_Mime_MimeEntity`` interface, +which offers methods for retrieving Headers, adding new Headers, changing the +Encoder, updating the body and so on! + +All MIME entities have one Header in common -- the Content-Type Header, +updated with the entity's ``setContentType()`` method. + +Encoders +~~~~~~~~ + +Encoders are used to transform the content of Messages generated in Swift +Mailer into a format that is safe to send across the internet and that +conforms to RFC specifications. + +Generally speaking you will not need to interact with the Encoders in Swift +Mailer -- the correct settings will be handled by the library itself. +However they are probably worth a brief mention in the event that you do want +to play with them. + +Both the Headers and the body of all MIME entities (including the Message +itself) use Encoders to ensure the data they contain can be sent over the +internet without becoming corrupted or misinterpreted. + +There are two types of Encoder: Base64 and Quoted-Printable. + +Plugins +~~~~~~~ + +Plugins exist to extend, or modify the behaviour of Swift Mailer. They respond +to Events that are fired within the Transports during sending. + +There are a number of Plugins provided as part of the base Swift Mailer +package and they all follow a common interface to respond to Events fired +within the library. Interfaces are provided to "listen" to each type of Event +fired and to act as desired when a listened-to Event occurs. + +Although several plugins are provided with Swift Mailer out-of-the-box, the +Events system has been specifically designed to make it easy for experienced +object-oriented developers to write their own plugins in order to achieve +goals that may not be possible with the base library. diff --git a/vendor/swiftmailer/swiftmailer/doc/plugins.rst b/vendor/swiftmailer/swiftmailer/doc/plugins.rst new file mode 100644 index 0000000..16ae335 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/plugins.rst @@ -0,0 +1,385 @@ +Plugins +======= + +Plugins are provided with Swift Mailer and can be used to extend the behavior +of the library in situations where using simple class inheritance would be more complex. + +AntiFlood Plugin +---------------- + +Many SMTP servers have limits on the number of messages that may be sent +during any single SMTP connection. The AntiFlood plugin provides a way to stay +within this limit while still managing a large number of emails. + +A typical limit for a single connection is 100 emails. If the server you +connect to imposes such a limit, it expects you to disconnect after that +number of emails has been sent. You could manage this manually within a loop, +but the AntiFlood plugin provides the necessary wrapper code so that you don't +need to worry about this logic. + +Regardless of limits imposed by the server, it's usually a good idea to be +conservative with the resources of the SMTP server. Sending will become +sluggish if the server is being over-used so using the AntiFlood plugin will +not be a bad idea even if no limits exist. + +The AntiFlood plugin's logic is basically to disconnect and the immediately +re-connect with the SMTP server every X number of emails sent, where X is a +number you specify to the plugin. + +You can also specify a time period in seconds that Swift Mailer should pause +for between the disconnect/re-connect process. It's a good idea to pause for a +short time (say 30 seconds every 100 emails) simply to give the SMTP server a +chance to process its queue and recover some resources. + +Using the AntiFlood Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The AntiFlood Plugin -- like all plugins -- is added with the Mailer class's +``registerPlugin()`` method. It takes two constructor parameters: the number of +emails to pause after, and optionally the number of seconds to pause for. + +To use the AntiFlood plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_AntiFloodPlugin`` class, passing + in one or two constructor parameters. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will count the number of messages that +have been sent since the last re-connect. Once the number hits your specified +threshold it will disconnect and re-connect, optionally pausing for a +specified amount of time. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Use AntiFlood to re-connect after 100 emails + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100)); + + // And specify a time in seconds to pause for (30 secs) + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Throttler Plugin +---------------- + +If your SMTP server has restrictions in place to limit the rate at which you +send emails, then your code will need to be aware of this rate-limiting. The +Throttler plugin makes Swift Mailer run at a rate-limited speed. + +Many shared hosts don't open their SMTP servers as a free-for-all. Usually +they have policies in place (probably to discourage spammers) that only allow +you to send a fixed number of emails per-hour/day. + +The Throttler plugin supports two modes of rate-limiting and with each, you +will need to do that math to figure out the values you want. The plugin can +limit based on the number of emails per minute, or the number of +bytes-transferred per-minute. + +Using the Throttler Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Throttler Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It has two required constructor parameters that +tell it how to do its rate-limiting. + +To use the Throttler plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_ThrottlerPlugin`` class, passing + the number of emails, or bytes you wish to limit by, along with the mode + you're using. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will keep track of the rate at which sending +messages is occurring. If it realises that sending is happening too fast, it +will cause your program to ``sleep()`` for enough time to average out the rate. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Rate limit to 100 emails per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE + )); + + // Rate limit to 10MB per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 1024 * 1024 * 10, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE + )); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Logger Plugin +------------- + +The Logger plugins helps with debugging during the process of sending. It can +help to identify why an SMTP server is rejecting addresses, or any other +hard-to-find problems that may arise. + +The Logger plugin comes in two parts. There's the plugin itself, along with +one of a number of possible Loggers that you may choose to use. For example, +the logger may output messages directly in realtime, or it may capture +messages in an array. + +One other notable feature is the way in which the Logger plugin changes +Exception messages. If Exceptions are being thrown but the error message does +not provide conclusive information as to the source of the problem (such as an +ambiguous SMTP error) the Logger plugin includes the entire SMTP transcript in +the error message so that debugging becomes a simpler task. + +There are a few available Loggers included with Swift Mailer, but writing your +own implementation is incredibly simple and is achieved by creating a short +class that implements the ``Swift_Plugins_Logger`` interface. + +* ``Swift_Plugins_Loggers_ArrayLogger``: Keeps a collection of log messages + inside an array. The array content can be cleared or dumped out to the + screen. + +* ``Swift_Plugins_Loggers_EchoLogger``: Prints output to the screen in + realtime. Handy for very rudimentary debug output. + +Using the Logger Plugin +~~~~~~~~~~~~~~~~~~~~~~~ + +The Logger Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It accepts an instance of ``Swift_Plugins_Logger`` +in its constructor. + +To use the Logger plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the a Logger implementation of + ``Swift_Plugins_Logger``. + +* Create an instance of the ``Swift_Plugins_LoggerPlugin`` class, passing the + created Logger instance to its constructor. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +* Dump the contents of the log with the logger's ``dump()`` method. + +When Swift Mailer sends messages it will keep a log of all the interactions +with the underlying Transport being used. Depending upon the Logger that has +been used the behaviour will differ, but all implementations offer a way to +get the contents of the log. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // To use the ArrayLogger + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Or to use the Echo Logger + $logger = new Swift_Plugins_Loggers_EchoLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + + // Dump the log contents + // NOTE: The EchoLogger dumps in realtime so dump() does nothing for it + echo $logger->dump(); + +Decorator Plugin +---------------- + +Often there's a need to send the same message to multiple recipients, but with +tiny variations such as the recipient's name being used inside the message +body. The Decorator plugin aims to provide a solution for allowing these small +differences. + +The decorator plugin works by intercepting the sending process of Swift +Mailer, reading the email address in the To: field and then looking up a set +of replacements for a template. + +While the use of this plugin is simple, it is probably the most commonly +misunderstood plugin due to the way in which it works. The typical mistake +users make is to try registering the plugin multiple times (once for each +recipient) -- inside a loop for example. This is incorrect. + +The Decorator plugin should be registered just once, but containing the list +of all recipients prior to sending. It will use this list of recipients to +find the required replacements during sending. + +Using the Decorator Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Decorator plugin, simply create an associative array of replacements +based on email addresses and then use the mailer's ``registerPlugin()`` method +to add the plugin. + +First create an associative array of replacements based on the email addresses +you'll be sending the message to. + +.. note:: + + The replacements array becomes a 2-dimensional array whose keys are the + email addresses and whose values are an associative array of replacements + for that email address. The curly braces used in this example can be any + type of syntax you choose, provided they match the placeholders in your + email template. + + .. code-block:: php + + $replacements = array(); + foreach ($users as $user) { + $replacements[$user['email']] = array( + '{username}'=>$user['username'], + '{password}'=>$user['password'] + ); + } + +Now create an instance of the Decorator plugin using this array of replacements +and then register it with the Mailer. Do this only once! + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin($replacements); + + $mailer->registerPlugin($decorator); + +When you create your message, replace elements in the body (and/or the subject +line) with your placeholders. + +.. code-block:: php + + $message = Swift_Message::newInstance() + ->setSubject('Important notice for {username}') + ->setBody( + "Hello {username}, we have reset your password to {password}\n" . + "Please log in and change it at your earliest convenience." + ) + ; + + foreach ($users as $user) { + $message->addTo($user['email']); + } + +When you send this message to each of your recipients listed in your +``$replacements`` array they will receive a message customized for just +themselves. For example, the message used above when received may appear like +this to one user: + +.. code-block:: text + + Subject: Important notice for smilingsunshine2009 + + Hello smilingsunshine2009, we have reset your password to rainyDays + Please log in and change it at your earliest convenience. + +While another use may receive the message as: + +.. code-block:: text + + Subject: Important notice for billy-bo-bob + + Hello billy-bo-bob, we have reset your password to dancingOctopus + Please log in and change it at your earliest convenience. + +While the decorator plugin provides a means to solve this problem, there are +various ways you could tackle this problem without the need for a plugin. +We're trying to come up with a better way ourselves and while we have several +(obvious) ideas we don't quite have the perfect solution to go ahead and +implement it. Watch this space. + +Providing Your Own Replacements Lookup for the Decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Filling an array with replacements may not be the best solution for providing +replacement information to the decorator. If you have a more elegant algorithm +that performs replacement lookups on-the-fly you may provide your own +implementation. + +Providing your own replacements lookup implementation for the Decorator is +simply a matter of passing an instance of ``Swift_Plugins_Decorator_Replacements`` to the decorator plugin's constructor, +rather than passing in an array. + +The Replacements interface is very simple to implement since it has just one +method: ``getReplacementsFor($address)``. + +Imagine you want to look up replacements from a database on-the-fly, you might +provide an implementation that does this. You need to create a small class. + +.. code-block:: php + + class DbReplacements implements Swift_Plugins_Decorator_Replacements { + public function getReplacementsFor($address) { + $sql = sprintf( + "SELECT * FROM user WHERE email = '%s'", + mysql_real_escape_string($address) + ); + + $result = mysql_query($sql); + + if ($row = mysql_fetch_assoc($result)) { + return array( + '{username}'=>$row['username'], + '{password}'=>$row['password'] + ); + } + } + } + +Now all you need to do is pass an instance of your class into the Decorator +plugin's constructor instead of passing an array. + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin(new DbReplacements()); + + $mailer->registerPlugin($decorator); + +For each message sent, the plugin will call your class' ``getReplacementsFor()`` +method to find the array of replacements it needs. + +.. note:: + + If your lookup algorithm is case sensitive, you should transform the + ``$address`` argument as appropriate -- for example by passing it + through ``strtolower()``. diff --git a/vendor/swiftmailer/swiftmailer/doc/sending.rst b/vendor/swiftmailer/swiftmailer/doc/sending.rst new file mode 100644 index 0000000..4460845 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/sending.rst @@ -0,0 +1,607 @@ +Sending Messages +================ + +Quick Reference for Sending a Message +------------------------------------- + +Sending a message is very straightforward. You create a Transport, use it to +create the Mailer, then you use the Mailer to send the message. + +To send a Message: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, ``Swift_MailTransport`` + or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +.. caution:: + + The ``Swift_SmtpTransport`` and ``Swift_SendmailTransport`` transports use + ``proc_*`` PHP functions, which might not be available on your PHP + installation. You can easily check if that's the case by running the + following PHP script: ``setUsername('your username') + ->setPassword('your password') + ; + + /* + You could alternatively use a different transport such as Sendmail or Mail: + + // Sendmail + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/sendmail -bs'); + + // Mail + $transport = Swift_MailTransport::newInstance(); + */ + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $result = $mailer->send($message); + +Transport Types +~~~~~~~~~~~~~~~ + +A Transport is the component which actually does the sending. You need to +provide a Transport object to the Mailer class and there are several possible +options. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + +The SMTP Transport +.................. + +The SMTP Transport sends messages over the (standardized) Simple Message +Transfer Protocol. It can deal with encryption and authentication. + +The SMTP Transport, ``Swift_SmtpTransport`` is without doubt the most commonly +used Transport because it will work on 99% of web servers (I just made that +number up, but you get the idea). All the server needs is the ability to +connect to a remote (or even local) SMTP server on the correct port number +(usually 25). + +SMTP servers often require users to authenticate with a username and password +before any mail can be sent to other domains. This is easily achieved using +Swift Mailer with the SMTP Transport. + +SMTP is a protocol -- in other words it's a "way" of communicating a job +to be done (i.e. sending a message). The SMTP protocol is the fundamental +basis on which messages are delivered all over the internet 7 days a week, 365 +days a year. For this reason it's the most "direct" method of sending messages +you can use and it's the one that will give you the most power and feedback +(such as delivery failures) when using Swift Mailer. + +Because SMTP is generally run as a remote service (i.e. you connect to it over +the network/internet) it's extremely portable from server-to-server. You can +easily store the SMTP server address and port number in a configuration file +within your application and adjust the settings accordingly if the code is +moved or if the SMTP server is changed. + +Some SMTP servers -- Google for example -- use encryption for security reasons. +Swift Mailer supports using both SSL and TLS encryption settings. + +Using the SMTP Transport +^^^^^^^^^^^^^^^^^^^^^^^^ + +The SMTP Transport is easy to use. Most configuration options can be set with +the constructor. + +To use the SMTP Transport you need to know which SMTP server your code needs +to connect to. Ask your web host if you're not sure. Lots of people ask me who +to connect to -- I really can't answer that since it's a setting that's +extremely specific to your hosting environment. + +To use the SMTP Transport: + +* Call ``Swift_SmtpTransport::newInstance()`` with the SMTP server name and + optionally with a port number (defaults to 25). + +* Use the returned object to create the Mailer. + +A connection to the SMTP server will be established upon the first call to +``send()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(25) + ; + */ + +Encrypted SMTP +^^^^^^^^^^^^^^ + +You can use SSL or TLS encryption with the SMTP Transport by specifying it as +a parameter or with a method call. + +To use encryption with the SMTP Transport: + +* Pass the encryption setting as a third parameter to + ``Swift_SmtpTransport::newInstance()``; or + +* Call the ``setEncryption()`` method on the Transport. + +A connection to the SMTP server will be established upon the first call to +``send()``. The connection will be initiated with the correct encryption +settings. + +.. note:: + + For SSL or TLS encryption to work your PHP installation must have + appropriate OpenSSL transports wrappers. You can check if "tls" and/or + "ssl" are present in your PHP installation by using the PHP function + ``stream_get_transports()`` + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 587, 'ssl'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(587) + ->setEncryption('ssl') + ; + */ + +SMTP with a Username and Password +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Some servers require authentication. You can provide a username and password +with ``setUsername()`` and ``setPassword()`` methods. + +To use a username and password with the SMTP Transport: + +* Create the Transport with ``Swift_SmtpTransport::newInstance()``. + +* Call the ``setUsername()`` and ``setPassword()`` methods on the Transport. + +Your username and password will be used to authenticate upon first connect +when ``send()`` are first used on the Mailer. + +If authentication fails, an Exception of type ``Swift_TransportException`` will +be thrown. + +.. note:: + + If you need to know early whether or not authentication has failed and an + Exception is going to be thrown, call the ``start()`` method on the + created Transport. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport the call setUsername() and setPassword() + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ->setUsername('username') + ->setPassword('password') + ; + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Sendmail Transport +...................... + +The Sendmail Transport sends messages by communicating with a locally +installed MTA -- such as ``sendmail``. + +The Sendmail Transport, ``Swift_SendmailTransport`` does not directly connect to +any remote services. It is designed for Linux servers that have ``sendmail`` +installed. The Transport starts a local ``sendmail`` process and sends messages +to it. Usually the ``sendmail`` process will respond quickly as it spools your +messages to disk before sending them. + +The Transport is named the Sendmail Transport for historical reasons +(``sendmail`` was the "standard" UNIX tool for sending e-mail for years). It +will send messages using other transfer agents such as Exim or Postfix despite +its name, provided they have the relevant sendmail wrappers so that they can be +started with the correct command-line flags. + +It's a common misconception that because the Sendmail Transport returns a +result very quickly it must therefore deliver messages to recipients quickly +-- this is not true. It's not slow by any means, but it's certainly not +faster than SMTP when it comes to getting messages to the intended recipients. +This is because sendmail itself sends the messages over SMTP once they have +been quickly spooled to disk. + +The Sendmail Transport has the potential to be just as smart of the SMTP +Transport when it comes to notifying Swift Mailer about which recipients were +rejected, but in reality the majority of locally installed ``sendmail`` +instances are not configured well enough to provide any useful feedback. As such +Swift Mailer may report successful deliveries where they did in fact fail before +they even left your server. + +You can run the Sendmail Transport in two different modes specified by command +line flags: + +* "``-bs``" runs in SMTP mode so theoretically it will act like the SMTP + Transport + +* "``-t``" runs in piped mode with no feedback, but theoretically faster, + though not advised + +You can think of the Sendmail Transport as a sort of asynchronous SMTP Transport +-- though if you have problems with delivery failures you should try using the +SMTP Transport instead. Swift Mailer isn't doing the work here, it's simply +passing the work to somebody else (i.e. ``sendmail``). + +Using the Sendmail Transport +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To use the Sendmail Transport you simply need to call +``Swift_SendmailTransport::newInstance()`` with the command as a parameter. + +To use the Sendmail Transport you need to know where ``sendmail`` or another MTA +exists on the server. Swift Mailer uses a default value of +``/usr/sbin/sendmail``, which should work on most systems. + +You specify the entire command as a parameter (i.e. including the command line +flags). Swift Mailer supports operational modes of "``-bs``" (default) and +"``-t``". + +.. note:: + + If you run sendmail in "``-t``" mode you will get no feedback as to whether + or not sending has succeeded. Use "``-bs``" unless you have a reason not to. + +To use the Sendmail Transport: + +* Call ``Swift_SendmailTransport::newInstance()`` with the command, including + the correct command line flags. The default is to use ``/usr/sbin/sendmail + -bs`` if this is not specified. + +* Use the returned object to create the Mailer. + +A sendmail process will be started upon the first call to ``send()``. If the +process cannot be started successfully an Exception of type +``Swift_TransportException`` will be thrown. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/exim -bs'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Mail Transport +.................. + +The Mail Transport sends messages by delegating to PHP's internal +``mail()`` function. + +In my experience -- and others' -- the ``mail()`` function is not particularly +predictable, or helpful. + +Quite notably, the ``mail()`` function behaves entirely differently between +Linux and Windows servers. On linux it uses ``sendmail``, but on Windows it uses +SMTP. + +In order for the ``mail()`` function to even work at all ``php.ini`` needs to be +configured correctly, specifying the location of sendmail or of an SMTP server. + +The problem with ``mail()`` is that it "tries" to simplify things to the point +that it actually makes things more complex due to poor interface design. The +developers of Swift Mailer have gone to a lot of effort to make the Mail +Transport work with a reasonable degree of consistency. + +Serious drawbacks when using this Transport are: + +* Unpredictable message headers + +* Lack of feedback regarding delivery failures + +* Lack of support for several plugins that require real-time delivery feedback + +It's a last resort, and we say that with a passion! + +Using the Mail Transport +^^^^^^^^^^^^^^^^^^^^^^^^ + +To use the Mail Transport you simply need to call +``Swift_MailTransport::newInstance()``. It's unlikely you'll need to configure +the Transport. + +To use the Mail Transport: + +* Call ``Swift_MailTransport::newInstance()``. + +* Use the returned object to create the Mailer. + +Messages will be sent using the ``mail()`` function. + +.. note:: + + The ``mail()`` function can take a ``$additional_parameters`` parameter. + Swift Mailer sets this to "``-f%s``" by default, where the "``%s``" is + substituted with the address of the sender (via a ``sprintf()``) at send + time. You may override this default by passing an argument to + ``newInstance()``. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_MailTransport::newInstance(); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +Available Methods for Sending Messages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Mailer class offers two methods for sending Messages -- ``send()``. +Each behaves in a slightly different way. + +When a message is sent in Swift Mailer, the Mailer class communicates with +whichever Transport class you have chosen to use. + +Each recipient in the message should either be accepted or rejected by the +Transport. For example, if the domain name on the email address is not +reachable the SMTP Transport may reject the address because it cannot process +it. Whichever method you use -- ``send()`` -- Swift Mailer will return +an integer indicating the number of accepted recipients. + +.. note:: + + It's possible to find out which recipients were rejected -- we'll cover that + later in this chapter. + +Using the ``send()`` Method +........................... + +The ``send()`` method of the ``Swift_Mailer`` class sends a message using +exactly the same logic as your Desktop mail client would use. Just pass it a +Message and get a result. + +To send a Message with ``send()``: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + ``Swift_MailTransport`` or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +The message will be sent just like it would be sent if you used your mail +client. An integer is returned which includes the number of successful +recipients. If none of the recipients could be sent to then zero will be +returned, which equates to a boolean ``false``. If you set two +``To:`` recipients and three ``Bcc:`` recipients in the message and all of the +recipients are delivered to successfully then the value 5 will be returned. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $numSent = $mailer->send($message); + + printf("Sent %d messages\n", $numSent); + + /* Note that often that only the boolean equivalent of the + return value is of concern (zero indicates FALSE) + + if ($mailer->send($message)) + { + echo "Sent\n"; + } + else + { + echo "Failed\n"; + } + + */ + +Sending Emails in Batch +....................... + +If you want to send a separate message to each recipient so that only their +own address shows up in the ``To:`` field, follow the following recipe: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + ``Swift_MailTransport`` or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Iterate over the recipients and send message via the ``send()`` method on + the Mailer object. + +Each recipient of the messages receives a different copy with only their own +email address on the ``To:`` field. + +Make sure to add only valid email addresses as recipients. If you try to add an +invalid email address with ``setTo()``, ``setCc()`` or ``setBcc()``, Swift +Mailer will throw a ``Swift_RfcComplianceException``. + +If you add recipients automatically based on a data source that may contain +invalid email addresses, you can prevent possible exceptions by validating the +addresses using ``Swift_Validate::email($email)`` and only adding addresses +that validate. Another way would be to wrap your ``setTo()``, ``setCc()`` and +``setBcc()`` calls in a try-catch block and handle the +``Swift_RfcComplianceException`` in the catch block. + +Handling invalid addresses properly is especially important when sending emails +in large batches since a single invalid address might cause an unhandled +exception and stop the execution or your script early. + +.. note:: + + In the following example, two emails are sent. One to each of + ``receiver@domain.org`` and ``other@domain.org``. These recipients will + not be aware of each other. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setBody('Here is the message itself') + ; + + // Send the message + $failedRecipients = array(); + $numSent = 0; + $to = array('receiver@domain.org', 'other@domain.org' => 'A name'); + + foreach ($to as $address => $name) + { + if (is_int($address)) { + $message->setTo($name); + } else { + $message->setTo(array($address => $name)); + } + + $numSent += $mailer->send($message, $failedRecipients); + } + + printf("Sent %d messages\n", $numSent); + +Finding out Rejected Addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's possible to get a list of addresses that were rejected by the Transport +by using a by-reference parameter to ``send()``. + +As Swift Mailer attempts to send the message to each address given to it, if a +recipient is rejected it will be added to the array. You can pass an existing +array, otherwise one will be created by-reference. + +Collecting the list of recipients that were rejected can be useful in +circumstances where you need to "prune" a mailing list for example when some +addresses cannot be delivered to. + +Getting Failures By-reference +............................. + +Collecting delivery failures by-reference with the ``send()`` method is as +simple as passing a variable name to the method call. + +To get failed recipients by-reference: + +* Pass a by-reference variable name to the ``send()`` method of the Mailer + class. + +If the Transport rejects any of the recipients, the culprit addresses will be +added to the array provided by-reference. + +.. note:: + + If the variable name does not yet exist, it will be initialized as an + empty array and then failures will be added to that array. If the variable + already exists it will be type-cast to an array and failures will be added + to it. + + .. code-block:: php + + $mailer = Swift_Mailer::newInstance( ... ); + + $message = Swift_Message::newInstance( ... ) + ->setFrom( ... ) + ->setTo(array( + 'receiver@bad-domain.org' => 'Receiver Name', + 'other@domain.org' => 'A name', + 'other-receiver@bad-domain.org' => 'Other Name' + )) + ->setBody( ... ) + ; + + // Pass a variable name to the send() method + if (!$mailer->send($message, $failures)) + { + echo "Failures:"; + print_r($failures); + } + + /* + Failures: + Array ( + 0 => receiver@bad-domain.org, + 1 => other-receiver@bad-domain.org + ) + */ diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle new file mode 100644 index 0000000000000000000000000000000000000000..f895752b70947592db74b6a3b8a6bd135a9fd150 GIT binary patch literal 3503 zcmV;g4N&qQiwFP!000030PS6EQ`@)}{tUmumk&GLSx6-LC3cr~Cj@BOOAAXvd$*G> z#!(V4j-A>HA=~MHzmo0vRW^_oh=XTJ?O5mN_0gjv>B!#y<91>RH`w(|$9}T|8(>Gk zw&4s+d-P`KvUj|v?)>p~d;dR&XPw^P=SRZSGCd-kUml#EbcCI~X0tt=TDaLf>>Uc{ zrzhQ>K;vjOk3Q}QJ7Yqouba*JeBRKRRKsv4OwendyUrB5WN}I(+oKAN0U7MjWP;dJ z*`ZGdra|6rZ|z^>#oM+)%o}{FFR*)J5Af}NlRc$@rcLk&yKljMb9uW|p=q0jPH2>$ zf|w&E-F0<#v$gLLmlpXg&0)it*yhM}X4A$Qy?f{C!=Z&~j`y3PP(mIetpVjk$bNGf zXOQ-cIPJkdGmk`R5_g81%kYkF-{@Y)u^cxN2#XsC%$JYjBvYv+>n4co%r!@5T*OfJ z8`u@V_oQm9(KU1nM+z?1EtZIXUCKGuY+|=9b7bc#9Of!KknT=(gC>=ZS5z_)(;uv1 z4t0XFSw;Y~_8_AF1O#6L>9q`n-yj4aR|*zMq5{Lv&e%0Qq2st$3p=ZFXnIpiU!+UE ztYE>I2yX;-$*NH>Ovi_gF`HnUWWlto1vytCzM5%x4DKB4EgL*6jiq~D)ap^LPE8w! z@g#v5=)A}JAfXaP7=)f2#$kzeL#dYAiowKDi?tLfqs2td9r`Gl>8NmV0lp~;YN&E+ zS3oV)kiVx&5ld-+2JQ+X&>BF6X{oq#h3R(5B5uEY!Ktcqjgy6s1>%ZpyRI`&2t|46 z64SU2Lz8(8R9S=c7E_$k zRgnlKRooR+P=vuC96}`v(vyWLhgXIFcpWnyON49dQNL z$~ZsQ3Bj&i6Z0oBv5EQ%;+ zHqGN{xvWvpqS?AUaAx+vSU{X4H#Guccu##o=?0T273 z=CMh7_?GZ=KVg?PrEYQ6EPe}KV9)%QYty9KB-tkvF7fyo=ciy$yJ=)`6UiVuUEP7m z(6p?iL|PaM_U6{!WXeL98r zs9TQpLAug;SwD33xyMiW<5_7c=^uV4%K{^pr>sO=%Dzj6u&>*m>HlX;M|1>5eZw&v zod#)=7TkNYEBX_)!1lNP(6I@*vid{NA9{1s^ZG+$th*j2KzNU>8_eMN`*$j7u%%%t z`g`BS10>7+k&Cg-Zmz5urdKM}pX%<)DU|1zQv=wE-kLyMpr z^zj8{P3#`id&gbmx-qx*&n+4iTUF)u9gsD<6LGA-GnyFj43svHXIeGSRF-E_do1Oh z;6*c&)&FYiWajEtKv6M4jW7xM$8leE{i+bcBK#`67BVFD$n2D}NZ+~U;C+F?RBS1f zbnYOAW>IR?aO*g>O}lvBw)^|2d++sw^ekXatw>rs+OD7|jNPHy+DvvAYj&@#$f{eB z@34(s(>OM7@!+#LAmis^9oOob7x$8qamS zqHb5Vbe!r=**s)A+e_+daYwtrbPj2If<2pDVOMa5!qtLcPauv!+=W0nnR&uCgf_ip zlERHnv&rQC#jYbvFs&kijIlq%^rw}A=`kU5z*E|TMJk5ly0a-^N>hJ3`38q-rafTZ zt2oN!#Bp()O_s$*b{k~PSNp>qL|J7$n>Of1F852IwnS=6_We9Wl_T@;-TegKg|m@D``aT-PHdAsKA@IUm-e(*Vc1&k0OX8^ zl3xEbI{+Ccv^DUGlU8v|5?6Li(ln(Zx9F%MX|Uw@iq@hMGOS&=Q~pS6?BJHg(IcE2 z5P!fK6MzQuSttb^ld4gdN1eL9s9Wvuohbi^TO|h~8K+8ws0={$p@E+XLq5Tj9U9PD zvZ!cs3n`)|DQw60c}@+8(&~d{s~l|`%vcpAmz7V5v>3C9qITDb-mFCJtsOQO&?@Gw zP3L#M1AbtXFlQS{+Q{8xq}3}d^%`!~*Km(bD_$h&5(i|Yb0vzQaE>TO+dzt` zkAu`c058`EfO$W@q7@}r*dCEF+tLr50Q%c=*CaS~0PxrTHZ7XJrioo&YZ~024TtP> zV1IjP&Ac(S!U#p6_bQN&IS!NTdBXm-IhoRm2_7#)gC>areTM^|A790{g5&+VmYxSW zo27(np2qVavzq<<%j?R6_j43kQxy?HNt6{$lezmjWmN!I_F9l-N|Z>E8weFH(90l@ zsdta7^1@8zv0WCFAHrM7W{{7y=iUkeo4W(_=vccaVf`3@poU5TlnY-609DH^L0sQg zxfc8Flt2!x>|+&B5)d0=5#}VxpGcxcOOk8fn4d{}4>hG)atlgGm7oe`O;RQ8xm;tE zA41z*{I-?@IWa&(p(7 zykR7;-qnB>k+O-jDAsQnsadh|tazY%Sdl7DilSHRs}|sH0JIF0>2Dp6J5Wg+ViXC!%g=5dK#j9IIW04jc8@_?5z6jmbJIn3-~-r%W9kNpBqxqg4=3wnR&G3)VyMf*4H>mff31ZPI3Ga}6C=q1>gJ|K>8g#~X+ zv(8?(IgBI;twi?vO)B6KT0Mee3p$PH*7OKt7Z&r+db#G~&#RC`dmHrE-didiGG!qC zYzwGp0qBXh3ZHU(U)dgI%>n&oKPa+W?Wr0=+T6yd=WwO`=tph31@==oY4gf#pE4V! zz(1RBB*fa0{gd=i^_#32h0e?Y_D&t+8b|J8Ty!L)Kbf9+6=Rcc-UPlb=lTdo*yH=G z1O=Q>!4B=xi5W?mxOc8wm=Jt-mj8?KwC8l`1ThvK>XwJ2Dmc{_v}vNisOmF=3#%iJ zDRY(|SU&0#j?v>MBfz})IsJKGYw?qb=XZ)0A%W(A4$#F|pw{|zQRC(-_JcmMF?oo* z$Ifz$F(z5zAShKgS(Urnr){uzcl)(`Vh`{wr^oQV{@tDtr#<**<|UhexHH^L*cG~c zqkA33FvVu!oMRKh0t+S??U`$iOq*lFgIxjeY<6EO1?c63A10mhd|AhuP3+A4x6nU5 z*62=kgC?4dUsSRQ9Gc$L(ihp>EQ>D~6XA`(F2&~8shO(!{ zFQ>K6W`s=yvXjCvQwO@oo1R`c^M@dwir*XVJ{9#$!pjYZ#6qo0+x$Ai{4t!9p-=b2 za}mpba0cd(eYk*D^P!#}A`t-U9zc6g5)gbXNv|bM_zgk;a_s9zlo*f0;Lg~k6St1z zUM=jb4x8EEe@Nf}f)9tKG$qRWN6x`c5pu#$myhrPZSQOE9+r*Y$#O%f-!6VVbPpUt zoXH2>O_F*bX_bZh-zP9F_cWF~09!`UN zpW-2@Uaoo6!pXKzjGRov^4QPm{LM`Ze!j1mYCX6!%_eiDd?@N@VlHIq6{nr&m zYp|t03CXabrN7XgPwm1SjVsvDJ!e{-Ove=M4>VXGoY~gmZtBX8U?nr5{OFY%y9!3G zh|Ya`GH-l$Cv;ZsXM-)n2Q$CG#=ZwV`pVQe^pDFtP&YeVmX!X|!$-7lxtMWxB)_!Y zIiqPwT%S#Q;LM9Dy)$!NS`n!UOwrULT6e*2Prr(#!-9rGQwlV(hW}Z{SjTTg1QG{s zf~`$A#YYLJH}-m0|3ic9Guw(Prx(9Mlg}SSFYqWum{BDD)WcuRWN+_)P(qwYzBtY# dlTFZwk616kA77^y1;pF!{{uPR7LWUs0009F*SY`z literal 0 HcmV?d00001 diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle new file mode 100644 index 0000000000000000000000000000000000000000..e1e33cbf16ea45d29ad48eb9b2f37959292881b2 GIT binary patch literal 5575 zcmV;&6*%f2iwFP!000030PS6CbK5r7{v7`bzWLBe-gRUE1b9i)?8vgyxa-(%tR&m6 zcXntIlGsqBLQ-+Oo&NU)NL?u2Y*Us5G02P~i97&7;+zK$4h|1q{r%g>Rld?7bbRlp zr?3T{D%9)ww&M+cditUJ=7sU}@2~e>{om1fr~BK*u`+g@Fj6i)9G;$Zl&3Ea4%*|f zOAii?x<|^z>B(hRVRalF9G^W^o(`jE{PN)7=H{klaj90%A92C(;3DwHG>C3bS!FL+ zftDTFPuXRX+LxNcj@nKydcF7L)n|J9y4{PMuk_TqrNN13({Ha1_)&J!@gh2)!E5m9 zVD@!Zgk9U|SrM!9V^Z_HkQD?L|MKKj7zON>U$Yjr{E_Dj0)H}YowIL02UfrDQr6;E z2UDSxJVa&-lGK@32eUepYfmD-ZT~R|qxm(XU_uXO=R217)e1Yl>j!gz>Fp*0H?zZw zODcAG~9ZtcYfti~ulR0Qdqy1)-M^yu_yRHwXd9-36~C^#G@5 zJHx;Um5v`=-+I|1M@~3)t=r{0pFLouH&T95_(!%FQ-<;I$nQ-?)QhspGnoUre&cjgE=_cok3^5pp#MDE0W4Yj;A~CzHAwTY-HQeIpuR|3ir456$2o2;um{ z0Q^H$V-eMiMKF#<2#7^kY5t+XSVYsA$d}&UUeZQ{m8)JKDBU4d{y}d$R*!uNnL5)@ zv8~8bCLwzmilvML=PR=k#j{2*Z(f#JH&+$6u$b%L6qR{TL+8J=s6^Vjmh1~I+gGuLrLWM7 zEN|czUgR>mb(c$}9(N+rcU(6uu`Dw0Lfc{Z)=sXI5}tuLz;hHYDA!?sG{_X3_38`I zLMDdL*jM;h4eLP5c4|r?b3Y33qTn{O65kc3T&Gaw2RHhW|8X4)`V2IF7_bBE?`m%b zHaU|jeBKu>E6hi|LOliKfgv~8GzUMJ8%TGUlJHEWP`(pX_A3D)Jbv#y^ytk-cM9 z)c>3w4sv5Te`4(*C>|O=ygNPdhBR>4@YO3Ey?AeQZ!^PQp%-#tAC}{mUJ+!TN7^!$ zdxcl?@U8}BT`hnbf?8^*)mKBPwE$B?O(iX8;8Inb$q}S&RU0qa`1^T!cIPz_k;l@+ zO?m!eM8YQI%-#fz^a5yD5_{?S+luFJ!D=_pFdvE7Wq6dRIHnRKW?$Hd7wsS<&BQXf ziXpLVJ6QI{aW{iyH7%b&G`D<+n`ilQEkn8|MRZRNXmnvk!)FV}pOD@(t=yZIGp)_I z%rwDVW@;^u8L926fEq?E6g$M!maf4vR|NznVq^}1G69XKEk27?)x5_;dNb;KGYoOv zY6#ZSRZ!clrsAnv4V#s2HG~s3QYa@Ky`s3+MA1mB*G5lMCtS^pQdI;@WfN62THM38 zO;rR9&^|2iG6N*Fv=6VQeb~HP`*Jc;^H$cpsj%1gi^QS0YHHNdQgt;gC0gl%EHrep zRVX8e>UF5>g&#Um>P?QhqX4uBQTacNfKF7bn<>A+nl!jX+rZjJbPff}JykjD+6;$+ zM67UCXyQ821OV2LS0y?JY=p|=38o@YH)J@$BZd<|0}C6}0!_V^9@dXD;6W>Q=wU1Z zr6NAQBI-N#hBq`&!aAXrOez6snPwwh_t2CpopM^a#%blD6;cf!^Q_%8*wr_>v*VSh zxTQf9Q>g^DOwdfkA`-DAVyzFcejV>TT4BMaoup1iCur6WV%Igs-jIq)$$lbWN~Zs) zOy7KH1UuH5QJOk73HxX0y1xDp_F4*T=(QBM{(MzOs9J#yfC~iVi$Ys;lWc5u8h1U4 zGYdlkTL2i1BzlINno6R#_Ngf&ToLP(UFUqpZp=`LsV=Y!na#v4IYE`Uwblu0Q!q=k zc`AQ}_Te<&K($k(03I!@X446$1RyXA1mrw*HO^1KuH_s~^dL#s*UX&5Y+56SO*w3C zB~RzB52TrKOMrp2PG%>l*WXD3nz&`aeZ??9d3qfK7YN9)bhWWm7dXs_sI}B^#ROv& zItUHTY(Z11;p3|z+&M}&gDg#py&-YxlxBKzb?Fh2)NYN`?$Q}})P6_9&>9GqG$l<; znr`Lt4K?`+i>R-#a0}?OhDKEI*I4p{Wrc-p2@|;O?dJwHF;aOd5fDw&W$B++s@)9PwaB+MNV`plxJ*V`zA@* zKGB+I`!cnQ^oxn;7dt)vczpVw?)ioCQen^QYA-p^?snUqx9^V6y35B;&Q4Fxj+c*J zemJ~nzn=<6`T`+iElQui5q@`}s9J&&cA&OM*O6FYI}+qdm7sb#6c$S*v&*m?fh;;Z6> zLgr=9&nPMgM+H=DCJ~mg8|f1Wiq~qn{w3ju$L0No=NhfX;CpLFfQl0 zWGj&}nFT;$-@-;9$$rCzI&7e3GF&J}ImvLh!f-eGdUhSt=PwjD+4NnJxgz&DH&yyh5Jo&S<-Ff#cb^;_S>O1fQ|b*E`xTR~!E(Naw!_ze zU@y+9wIT&Esh^x4L&3bnY_Q=+m~wX*(}1aQpJ%;3eL;a{abXQY={qYn8Z$ps|2+;J8K|nAvu4QsI^vyTQd*~2{wZ@Yb{iv`Uzg4$t(t! zC+vq1>IDLFZn_fFHwZ=Ag*Iuua&VL zkd<@}mqSvlbGh?U=T_D^5!hLYGNH8rSfR4X@oh_3qt^030I(X?Xr(hjji@bBAR(#t zI-sX&i2@s@ZW2RNHDsuU{y1wCn~FD%mtqlUaOj>Xb6?BIy){PeF(nQeG9=-9LZxz= z$5}MkxMf(bOyneJBBv=Z2)>(aZg?!Wt&L<{$^@&4%$qcv>?EtgMnbDh0xfyd8hO*H z6GozvCLZh8V9SPQ2O;o|DQK+$C{Z zBXQ!pPzXt!rlDyKwro{ewj@qs5~r>+qG4o>#S{Br<-dywRk| zkdAywn$}2~n7&ZwE42kAPK%Tg4Q5V2nzv+5VlpQ-pR$&`O=9|+awBB9aczV<89T9M za}ov))Fx9W>CTtbX^qs0!Q!OHz;i?7X?=i2c4x9jlKlXXh8l@j(vKz)YmJE2w(Wq1 zp|Ep3qG@yM#*N!P-oVnj4N!6B}ydGL3+wPlD2?6I*oVgiDzd*$Q(~cP8c&1JYzvmEL^GoYu&kK6qh6qCPV@ zqDvtw3n(c#G4Q^EOoPLTsuiF~737s8NimL8qDTRTR3J$CGtr8+f5>1*HFDf1vup-4 zau9wLST}{}dv;!$;&qy{c4*m4K>C<<-9|t$prOeG=8vs@8`+8UEXgp36L%BTnh0T7zG5sPY&#H^c%L={WOeLbSLb%$JU26z zQH^r(PT*Jh1Yf?(4V&eLwUOmJyb~D^Wfk#|VaAG(QJ8s=UGpl+&1 zaC3V^63R+K8CUHDB?HDgXGE*;z6!UC0~|_Hz${A7F+aTteB50J^RpAjrs1jI`%LF6 zFbgp;B>%Ykjj+kJF*>s8+Rf&femP@0E-reMXP&)4V zmyD+_g!`5o()lAewQgy!Xyp9SXT}%)jPk=v#(H8&P6-ME6ZmBmm>0ibKkqw2est)~ zouXx$z*^u>qHlZDbgDFT^0?k|TN)uvp49#2yE@uUbr?x2#( zI8reV7hp*>niS(nIieI|N`-jU2$jc{d`B@&XOo!twX&j+se;}*pG2#xwDIt9uMZT_-APO#z5aOMUR1FB4ikh8HyG{6IC`7S3 z%%mAm-oNq!-6_+Y#BSJQKV<#=IlKq6iH$5bl*auMKZnahKZ^X(J1a&fNuusuBg00_MP^o!dYbl7kE`~&=b zc>VUH+h*rG$E4duzkK=q(-~-=d^&Dleql0iKD#%apO>k^VmM9OeMVRh{nZ0;5;A#p+7$wy)JAH0ZThJBB#T*mIvimT*aSeQpGYns_YCU*i8*fRCM zs;~Cm84P!@qr3iicN)Eb=y;%^mVNHIw|7HVb_A=K3FSwx+}Kqza?Rm9=978yeK}$? z^0*n?^_a~35{`Wjc#I1(au~-4Kd@|exU9JQ4c(#92 z4(Xjq5U>ZaG=V9bTFiYeH0WB_3+d^NPKTx?rCF5t)RMqpvajeWSay6(DPKH@zNdpFz?@fN7<}%*>M(e8vBv7l*YUgWk20-X%s*zD VzxW)xOdDSB{SO2kTt$6+0RTlqtKk3u literal 0 HcmV?d00001 diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle new file mode 100644 index 0000000000000000000000000000000000000000..5670e2b65ada6e01406334a4b462f58e8f48f4fd GIT binary patch literal 3061 zcmVFR$!k`4GK0ih`&)N3_0pskCgy+3{0lD+r;eCXT4SBN~zb^h1_1#wG& zj^Ub?)BR)Xy7_Lmy!Gcne*Ztmm(}LS`iU^GEe{Lz>!Y(;RoL1s77qsl8y1Vl&10c{ zR%Kt2=7fl7mw;KqICO?N&#%hc9O;Fe+vbt7yi(}bu z;*KDGk7f;XGz{H_(+mjeHm$_JPUKvB*mn+XtLvmX9HcrtkZufggH#nSSJn~*lP`2& zj&%%^O_oHlyemq(KoKOcCrNv9N%$`SqL{i1T1cb=$EnpGvOJ;cqV}Vc%yMjb16zNL z-}$721*0$gA<#=Q8&QTq@7Ohledyq%GEI6xO?8N$%`!Zy?hM_V40s@XmDO}qo)s`Ic`Zh*EtC3bt{q2YCtXvDb<W8~h!=PoWHn5H?lmh)iHWH@*oz4 zQ)qvMn4tOF??lp|xJg9cwh%NWRc&<_`L;#7>JXurafdKUl<#hw8f|5el@3AkF$!Th z&O{knj&9l9+Qh|;nH2NX@zqMa@qKaC)8u7TPhM8_NESeTd4O*-+}%xpG$02z#oH6#%eVrxl)m(62lY;%bBHj|$vM5ZHXT7+*O2K7XQ zCyNSEnXW1r`Ksg(qD02YpzewVsiFW--Vx|WO(}^<6o?bF#1{tnVccd?;@2QZM!<{O z5oYw8jo;Egs$;058}PR-`GNek@?@#pBO^su>Mlx|6|>XpHYtHF#hALBBC1iE{bakR z#AAc)UdrtLC)j;*5j8_Qe`%!PFQvK=Q(*WDK8{>uZod)sgofWCt-Kxr2HZaJ`gm|O z>~tWaYpVf7LVMWR84C+T52ERp0kKkAP}_BFs5?IyYf>!nJ+jPG4jtjn!+c8(@)vn^ z5mHoj9f#QKg1#L#wT|E5ni8~N1qplySw&kB$zB$C%KW62Mis0-mI=8z3*)YNuR&GY320}8UK@>?Q*=JM1&N7PMFI$Z&>u_ zFMisUF~H0;9ar6cpEPWqgVhMcb_6ompTCf=CiFiU#nJVkLgo z#8TkJmnN#R%+f?!dTCN2I3<-7-qNHZX^iyj9LAa~Po7=+Z* zSHEm!yc*S8$|)oMkW)rdW}iRlsVTPwa5Shprlty_^c&bsByx--7D zhJ1@O_0;w7gi!U!=QsW~Se-O?oAjVS;Uf6y#4*t0fWAdyA`Ts6Eq9wzlPTX2SHrTv zdhIqT(h@#bMP1hC3N*DKN_59buJHV>Kr9#3vVRD;l=)tt(f7*V+Ie}`e`N_ZluFC0 zp+r=y1hkoIC~2hvpgx~W4cAo-SG4AoDHef>Pf8_e6PU+219)Gh#{e?Q18Fi2dxrpZB6k~@@LHnU}_r)m|_N)8DMTOU|tUKMj&Ql zVX>x&SsJg1>C>;Ew26QzmomW20CSTW=6Hj8t@cxVT+vEXT-m(s6ghi4$LqzFY}4jP zH*GA(!j?|{*~DzNFWdhR_5S=t82Aqsy=mo%9`!B9e-?;78Kj%kRp@W#c<_F`&&Q+~ z1K;LXvU8JgTdwo@7_B_rK;J+nnLS3HG09D4l52c4rxKpKQ?-o)mh3Tzj7t8(RMKl; za(gg%41WBg&Q{)|KT`JSVRztPp<`~JL(kK@Q0NC&nuGN5TGB}F)k*yH9a z*u9}|BneJnmQ_G8A3QhGa{w5uco%w=IJWe>ONT>}{MW0})9&R}jfJwgW08^7nPk;3 z+CrRZV#-dvS!h2CwVAL8I6K>k;BFN0Yfi_FMQ)phc6&dxOz54t#vL@1A;x0*5s|*J zJgc3KNEQe5;E-0G%yixY!PshjB3LCh9id*c#J#$1L!6GrmTvN25DuDdJcL?Dx4kgB zogb#pn;Y?r_R+kTGzO9vdY|#yQokAjq{Zvx>!S1HjRi+@MR}S)THp>bUl-0S=Z-hH z>5e_uyAH%pk@v{pHhK`^q;QZF>zibj`QyWjxyUCvD)~%Yn(m!sA9A=^pDa^_4q%ipCT1h1K1MqNasetzm%JIM=mf-47vq z4reszW-)!{q5QdPTAgtu*;xTgC0PJqPcH2#3OitFP!N1l#L^2F!^NirhC;B_VIoa{ za`D0$y2I0)z-~ylACd9C^A=z>zLBM-(rG~4i^h?QvD-h_Q54)Gz!1jrH7WCo>dd5( zhxlLf^n?BJ7KreFNA{=Mse7pWee`#&and{NU0#bSXy1OYjc%2EH!rWjr(XN?#y%wJ z)f2VZls?>ly1ft&Yquwdje8>V?#>=n-^Zy!IGqOVK7$?n`dZC`7DlsuU}Q8J%r3o7 zR&XeW#Ei0o3$yrZ2UE;27pSG-J78zjl6t59H1MRRRSGl^fFh}&P>DZ)k*@8^>h@mY zKsVjN>uGd6@bwkc%}d9AoDW^e8EnQ%C_Q_zLs+M0t|^@Rd@@bG8-21a_nSc-kHN|> z;Mfbmqo0|OL;v}kC+1B~mox7E+Jh&=wh*M)&5m7uv*;4^)^bdD#E10i5FyeLu>}lo zX)*P@K-ARRso?12sVSy3VUAnN#Jw<&KZ=rA3AsqHhl65x6mxzB$H+lN*%3-gV$*3% z`TR-r3U*_FIW2@b&`KaEr9>b_lO;wepIo<}XlEoM`&jqG>%b=ZaFG8$W3gsW&3^y@ D6q4x9 literal 0 HcmV?d00001 diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php new file mode 100644 index 0000000..72419b3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php @@ -0,0 +1,80 @@ +createDependenciesFor('mime.attachment') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new Attachment. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * + * @return Swift_Mime_Attachment + */ + public static function newInstance($data = null, $filename = null, $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new Attachment from a filesystem path. + * + * @param string $path + * @param string $contentType optional + * + * @return Swift_Mime_Attachment + */ + public static function fromPath($path, $contentType = null) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path), + $contentType + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php new file mode 100644 index 0000000..c397c8b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php @@ -0,0 +1,179 @@ +_filters[$key] = $filter; + } + + /** + * Remove an already present StreamFilter based on its $key. + * + * @param string $key + */ + public function removeFilter($key) + { + unset($this->_filters[$key]); + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + */ + public function write($bytes) + { + $this->_writeBuffer .= $bytes; + foreach ($this->_filters as $filter) { + if ($filter->shouldBuffer($this->_writeBuffer)) { + return; + } + } + $this->_doWrite($this->_writeBuffer); + + return ++$this->_sequence; + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit() + { + $this->_doWrite($this->_writeBuffer); + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + if ($this->_writeBuffer !== '') { + $stream->write($this->_writeBuffer); + } + unset($this->_mirrors[$k]); + } + } + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + */ + public function flushBuffers() + { + if ($this->_writeBuffer !== '') { + $this->_doWrite($this->_writeBuffer); + } + $this->_flush(); + + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } + + /** Run $bytes through all filters */ + private function _filter($bytes) + { + foreach ($this->_filters as $filter) { + $bytes = $filter->filter($bytes); + } + + return $bytes; + } + + /** Just write the bytes to the stream */ + private function _doWrite($bytes) + { + $this->_commit($this->_filter($bytes)); + + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + + $this->_writeBuffer = ''; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php new file mode 100644 index 0000000..561cda8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php @@ -0,0 +1,184 @@ +_array = $stack; + $this->_arraySize = count($stack); + } elseif (is_string($stack)) { + $this->write($stack); + } else { + $this->_array = array(); + } + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_arraySize) { + return false; + } + + // Don't use array slice + $end = $length + $this->_offset; + $end = $this->_arraySize < $end + ? $this->_arraySize + : $end; + $ret = ''; + for (; $this->_offset < $end; ++$this->_offset) { + $ret .= $this->_array[$this->_offset]; + } + + return $ret; + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + */ + public function write($bytes) + { + $to_add = str_split($bytes); + foreach ($to_add as $value) { + $this->_array[] = $value; + } + $this->_arraySize = count($this->_array); + + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + unset($this->_mirrors[$k]); + } + } + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param int $byteOffset + * + * @return bool + */ + public function setReadPointer($byteOffset) + { + if ($byteOffset > $this->_arraySize) { + $byteOffset = $this->_arraySize; + } elseif ($byteOffset < 0) { + $byteOffset = 0; + } + + $this->_offset = $byteOffset; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_offset = 0; + $this->_array = array(); + $this->_arraySize = 0; + + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php new file mode 100644 index 0000000..4061043 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php @@ -0,0 +1,229 @@ +_path = $path; + $this->_mode = $writable ? 'w+b' : 'rb'; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { + $this->_quotes = true; + } + } + + /** + * Get the complete path to the file. + * + * @return string + */ + public function getPath() + { + return $this->_path; + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * + * @param int $length + * + * @throws Swift_IoException + * + * @return string|bool + */ + public function read($length) + { + $fp = $this->_getReadHandle(); + if (!feof($fp)) { + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + $bytes = fread($fp, $length); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_offset = ftell($fp); + + // If we read one byte after reaching the end of the file + // feof() will return false and an empty string is returned + if ($bytes === '' && feof($fp)) { + $this->_resetReadHandle(); + + return false; + } + + return $bytes; + } + + $this->_resetReadHandle(); + + return false; + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param int $byteOffset + * + * @return bool + */ + public function setReadPointer($byteOffset) + { + if (isset($this->_reader)) { + $this->_seekReadStreamToPosition($byteOffset); + } + $this->_offset = $byteOffset; + } + + /** Just write the bytes to the file */ + protected function _commit($bytes) + { + fwrite($this->_getWriteHandle(), $bytes); + $this->_resetReadHandle(); + } + + /** Not used */ + protected function _flush() + { + } + + /** Get the resource for reading */ + private function _getReadHandle() + { + if (!isset($this->_reader)) { + if (!$this->_reader = fopen($this->_path, 'rb')) { + throw new Swift_IoException( + 'Unable to open file for reading ['.$this->_path.']' + ); + } + if ($this->_offset != 0) { + $this->_getReadStreamSeekableStatus(); + $this->_seekReadStreamToPosition($this->_offset); + } + } + + return $this->_reader; + } + + /** Get the resource for writing */ + private function _getWriteHandle() + { + if (!isset($this->_writer)) { + if (!$this->_writer = fopen($this->_path, $this->_mode)) { + throw new Swift_IoException( + 'Unable to open file for writing ['.$this->_path.']' + ); + } + } + + return $this->_writer; + } + + /** Force a reload of the resource for reading */ + private function _resetReadHandle() + { + if (isset($this->_reader)) { + fclose($this->_reader); + $this->_reader = null; + } + } + + /** Check if ReadOnly Stream is seekable */ + private function _getReadStreamSeekableStatus() + { + $metas = stream_get_meta_data($this->_reader); + $this->_seekable = $metas['seekable']; + } + + /** Streams in a readOnly stream ensuring copy if needed */ + private function _seekReadStreamToPosition($offset) + { + if ($this->_seekable === null) { + $this->_getReadStreamSeekableStatus(); + } + if ($this->_seekable === false) { + $currentPos = ftell($this->_reader); + if ($currentPos < $offset) { + $toDiscard = $offset - $currentPos; + fread($this->_reader, $toDiscard); + + return; + } + $this->_copyReadStream(); + } + fseek($this->_reader, $offset, SEEK_SET); + } + + /** Copy a readOnly Stream to ensure seekability */ + private function _copyReadStream() + { + if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) { + /* We have opened a php:// Stream Should work without problem */ + } elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) { + /* We have opened a tmpfile */ + } else { + throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available'); + } + $currentPos = ftell($this->_reader); + fclose($this->_reader); + $source = fopen($this->_path, 'rb'); + if (!$source) { + throw new Swift_IoException('Unable to open file for copying ['.$this->_path.']'); + } + fseek($tmpFile, 0, SEEK_SET); + while (!feof($source)) { + fwrite($tmpFile, fread($source, 4096)); + } + fseek($tmpFile, $currentPos, SEEK_SET); + fclose($source); + $this->_reader = $tmpFile; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php new file mode 100644 index 0000000..1c9a80c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php @@ -0,0 +1,42 @@ +getPath())) === false) { + throw new Swift_IoException('Failed to get temporary file content.'); + } + + return $content; + } + + public function __destruct() + { + if (file_exists($this->getPath())) { + @unlink($this->getPath()); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php new file mode 100644 index 0000000..3d5e854 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php @@ -0,0 +1,67 @@ + + */ +interface Swift_CharacterReader +{ + const MAP_TYPE_INVALID = 0x01; + const MAP_TYPE_FIXED_LEN = 0x02; + const MAP_TYPE_POSITIONS = 0x03; + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars); + + /** + * Returns the mapType, see constants. + * + * @return int + */ + public function getMapType(); + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param integer[] $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size); + + /** + * Returns the number of bytes which should be read to start each character. + * + * For fixed width character sets this should be the number of octets-per-character. + * For multibyte character sets this will probably be 1. + * + * @return int + */ + public function getInitialByteSize(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php new file mode 100644 index 0000000..c1029b9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php @@ -0,0 +1,97 @@ + + */ +class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader +{ + /** + * The number of bytes in a single character. + * + * @var int + */ + private $_width; + + /** + * Creates a new GenericFixedWidthReader using $width bytes per character. + * + * @param int $width + */ + public function __construct($width) + { + $this->_width = $width; + } + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + $strlen = strlen($string); + // % and / are CPU intensive, so, maybe find a better way + $ignored = $strlen % $this->_width; + $ignoredChars = substr($string, -$ignored); + $currentMap = $this->_width; + + return ($strlen - $ignored) / $this->_width; + } + + /** + * Returns the mapType. + * + * @return int + */ + public function getMapType() + { + return self::MAP_TYPE_FIXED_LEN; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $needed = $this->_width - $size; + + return ($needed > -1) ? $needed : -1; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return $this->_width; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php new file mode 100644 index 0000000..ddc34ca --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php @@ -0,0 +1,84 @@ + "\x07F") { + // Invalid char + $currentMap[$i + $startOffset] = $string[$i]; + } + } + + return $strlen; + } + + /** + * Returns mapType. + * + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_INVALID; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $byte = reset($bytes); + if (1 == count($bytes) && $byte >= 0x00 && $byte <= 0x7F) { + return 0; + } else { + return -1; + } + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return 1; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php new file mode 100644 index 0000000..d5fa9c9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php @@ -0,0 +1,179 @@ + + */ +class Swift_CharacterReader_Utf8Reader implements Swift_CharacterReader +{ + /** Pre-computed for optimization */ + private static $length_map = array( + // N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x0N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x2N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x4N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x6N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x8N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xAN + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xCN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDN + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEN + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0, // 0xFN + ); + + private static $s_length_map = array( + "\x00" => 1, "\x01" => 1, "\x02" => 1, "\x03" => 1, "\x04" => 1, "\x05" => 1, "\x06" => 1, "\x07" => 1, + "\x08" => 1, "\x09" => 1, "\x0a" => 1, "\x0b" => 1, "\x0c" => 1, "\x0d" => 1, "\x0e" => 1, "\x0f" => 1, + "\x10" => 1, "\x11" => 1, "\x12" => 1, "\x13" => 1, "\x14" => 1, "\x15" => 1, "\x16" => 1, "\x17" => 1, + "\x18" => 1, "\x19" => 1, "\x1a" => 1, "\x1b" => 1, "\x1c" => 1, "\x1d" => 1, "\x1e" => 1, "\x1f" => 1, + "\x20" => 1, "\x21" => 1, "\x22" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1, + "\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1, + "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1, + "\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1, + "\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1, + "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1, + "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1, + "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5c" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1, + "\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1, + "\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1, + "\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1, + "\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x7f" => 1, + "\x80" => 0, "\x81" => 0, "\x82" => 0, "\x83" => 0, "\x84" => 0, "\x85" => 0, "\x86" => 0, "\x87" => 0, + "\x88" => 0, "\x89" => 0, "\x8a" => 0, "\x8b" => 0, "\x8c" => 0, "\x8d" => 0, "\x8e" => 0, "\x8f" => 0, + "\x90" => 0, "\x91" => 0, "\x92" => 0, "\x93" => 0, "\x94" => 0, "\x95" => 0, "\x96" => 0, "\x97" => 0, + "\x98" => 0, "\x99" => 0, "\x9a" => 0, "\x9b" => 0, "\x9c" => 0, "\x9d" => 0, "\x9e" => 0, "\x9f" => 0, + "\xa0" => 0, "\xa1" => 0, "\xa2" => 0, "\xa3" => 0, "\xa4" => 0, "\xa5" => 0, "\xa6" => 0, "\xa7" => 0, + "\xa8" => 0, "\xa9" => 0, "\xaa" => 0, "\xab" => 0, "\xac" => 0, "\xad" => 0, "\xae" => 0, "\xaf" => 0, + "\xb0" => 0, "\xb1" => 0, "\xb2" => 0, "\xb3" => 0, "\xb4" => 0, "\xb5" => 0, "\xb6" => 0, "\xb7" => 0, + "\xb8" => 0, "\xb9" => 0, "\xba" => 0, "\xbb" => 0, "\xbc" => 0, "\xbd" => 0, "\xbe" => 0, "\xbf" => 0, + "\xc0" => 2, "\xc1" => 2, "\xc2" => 2, "\xc3" => 2, "\xc4" => 2, "\xc5" => 2, "\xc6" => 2, "\xc7" => 2, + "\xc8" => 2, "\xc9" => 2, "\xca" => 2, "\xcb" => 2, "\xcc" => 2, "\xcd" => 2, "\xce" => 2, "\xcf" => 2, + "\xd0" => 2, "\xd1" => 2, "\xd2" => 2, "\xd3" => 2, "\xd4" => 2, "\xd5" => 2, "\xd6" => 2, "\xd7" => 2, + "\xd8" => 2, "\xd9" => 2, "\xda" => 2, "\xdb" => 2, "\xdc" => 2, "\xdd" => 2, "\xde" => 2, "\xdf" => 2, + "\xe0" => 3, "\xe1" => 3, "\xe2" => 3, "\xe3" => 3, "\xe4" => 3, "\xe5" => 3, "\xe6" => 3, "\xe7" => 3, + "\xe8" => 3, "\xe9" => 3, "\xea" => 3, "\xeb" => 3, "\xec" => 3, "\xed" => 3, "\xee" => 3, "\xef" => 3, + "\xf0" => 4, "\xf1" => 4, "\xf2" => 4, "\xf3" => 4, "\xf4" => 4, "\xf5" => 4, "\xf6" => 4, "\xf7" => 4, + "\xf8" => 5, "\xf9" => 5, "\xfa" => 5, "\xfb" => 5, "\xfc" => 6, "\xfd" => 6, "\xfe" => 0, "\xff" => 0, + ); + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + if (!isset($currentMap['i']) || !isset($currentMap['p'])) { + $currentMap['p'] = $currentMap['i'] = array(); + } + + $strlen = strlen($string); + $charPos = count($currentMap['p']); + $foundChars = 0; + $invalid = false; + for ($i = 0; $i < $strlen; ++$i) { + $char = $string[$i]; + $size = self::$s_length_map[$char]; + if ($size == 0) { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue; + } else { + if ($invalid == true) { + /* We mark the chars as invalid and start a new char */ + $currentMap['p'][$charPos + $foundChars] = $startOffset + $i; + $currentMap['i'][$charPos + $foundChars] = true; + ++$foundChars; + $invalid = false; + } + if (($i + $size) > $strlen) { + $ignoredChars = substr($string, $i); + break; + } + for ($j = 1; $j < $size; ++$j) { + $char = $string[$i + $j]; + if ($char > "\x7F" && $char < "\xC0") { + // Valid - continue parsing + } else { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue 2; + } + } + /* Ok we got a complete char here */ + $currentMap['p'][$charPos + $foundChars] = $startOffset + $i + $size; + $i += $j - 1; + ++$foundChars; + } + } + + return $foundChars; + } + + /** + * Returns mapType. + * + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_POSITIONS; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + if ($size < 1) { + return -1; + } + $needed = self::$length_map[$bytes[0]] - $size; + + return ($needed > -1) + ? $needed + : -1 + ; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return 1; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php new file mode 100644 index 0000000..15b6c69 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php @@ -0,0 +1,26 @@ +init(); + } + + public function __wakeup() + { + $this->init(); + } + + public function init() + { + if (count(self::$_map) > 0) { + return; + } + + $prefix = 'Swift_CharacterReader_'; + + $singleByte = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(1), + ); + + $doubleByte = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(2), + ); + + $fourBytes = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(4), + ); + + // Utf-8 + self::$_map['utf-?8'] = array( + 'class' => $prefix.'Utf8Reader', + 'constructor' => array(), + ); + + //7-8 bit charsets + self::$_map['(us-)?ascii'] = $singleByte; + self::$_map['(iso|iec)-?8859-?[0-9]+'] = $singleByte; + self::$_map['windows-?125[0-9]'] = $singleByte; + self::$_map['cp-?[0-9]+'] = $singleByte; + self::$_map['ansi'] = $singleByte; + self::$_map['macintosh'] = $singleByte; + self::$_map['koi-?7'] = $singleByte; + self::$_map['koi-?8-?.+'] = $singleByte; + self::$_map['mik'] = $singleByte; + self::$_map['(cork|t1)'] = $singleByte; + self::$_map['v?iscii'] = $singleByte; + + //16 bits + self::$_map['(ucs-?2|utf-?16)'] = $doubleByte; + + //32 bits + self::$_map['(ucs-?4|utf-?32)'] = $fourBytes; + + // Fallback + self::$_map['.*'] = $singleByte; + } + + /** + * Returns a CharacterReader suitable for the charset applied. + * + * @param string $charset + * + * @return Swift_CharacterReader + */ + public function getReaderFor($charset) + { + $charset = trim(strtolower($charset)); + foreach (self::$_map as $pattern => $spec) { + $re = '/^'.$pattern.'$/D'; + if (preg_match($re, $charset)) { + if (!array_key_exists($pattern, self::$_loaded)) { + $reflector = new ReflectionClass($spec['class']); + if ($reflector->getConstructor()) { + $reader = $reflector->newInstanceArgs($spec['constructor']); + } else { + $reader = $reflector->newInstance(); + } + self::$_loaded[$pattern] = $reader; + } + + return self::$_loaded[$pattern]; + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php new file mode 100644 index 0000000..717924f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php @@ -0,0 +1,89 @@ +setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /** + * Set the character set used in this CharacterStream. + * + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * Overwrite this character stream using the byte sequence in the byte stream. + * + * @param Swift_OutputByteStream $os output stream to read from + */ + public function importByteStream(Swift_OutputByteStream $os) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory + ->getReaderFor($this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + while (false !== $bytes = $os->read($startLength)) { + $c = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $c[] = self::$_byteMap[$bytes[$i]]; + } + $size = count($c); + $need = $this->_charReader + ->validateByteSequence($c, $size); + if ($need > 0 && + false !== $bytes = $os->read($need)) { + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $c[] = self::$_byteMap[$bytes[$i]]; + } + } + $this->_array[] = $c; + ++$this->_array_size; + } + } + + /** + * Import a string a bytes into this CharacterStream, overwriting any existing + * data in the stream. + * + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * Read $length characters from the stream and move the internal pointer + * $length further into the stream. + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_array_size) { + return false; + } + + // Don't use array slice + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) { + if (!isset($this->_array[$i])) { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += $i - $this->_offset; // Limit function calls + $chars = false; + foreach ($arrays as $array) { + $chars .= implode('', array_map('chr', $array)); + } + + return $chars; + } + + /** + * Read $length characters from the stream and return a 1-dimensional array + * containing there octet values. + * + * @param int $length + * + * @return integer[] + */ + public function readBytes($length) + { + if ($this->_offset == $this->_array_size) { + return false; + } + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) { + if (!isset($this->_array[$i])) { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += ($i - $this->_offset); // Limit function calls + + return call_user_func_array('array_merge', $arrays); + } + + /** + * Write $chars to the end of the stream. + * + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + + $fp = fopen('php://memory', 'w+b'); + fwrite($fp, $chars); + unset($chars); + fseek($fp, 0, SEEK_SET); + + $buffer = array(0); + $buf_pos = 1; + $buf_len = 1; + $has_datas = true; + do { + $bytes = array(); + // Buffer Filing + if ($buf_len - $buf_pos < $startLength) { + $buf = array_splice($buffer, $buf_pos); + $new = $this->_reloadBuffer($fp, 100); + if ($new) { + $buffer = array_merge($buf, $new); + $buf_len = count($buffer); + $buf_pos = 0; + } else { + $has_datas = false; + } + } + if ($buf_len - $buf_pos > 0) { + $size = 0; + for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) { + ++$size; + $bytes[] = $buffer[$buf_pos++]; + } + $need = $this->_charReader->validateByteSequence( + $bytes, $size); + if ($need > 0) { + if ($buf_len - $buf_pos < $need) { + $new = $this->_reloadBuffer($fp, $need); + + if ($new) { + $buffer = array_merge($buffer, $new); + $buf_len = count($buffer); + } + } + for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) { + $bytes[] = $buffer[$buf_pos++]; + } + } + $this->_array[] = $bytes; + ++$this->_array_size; + } + } while ($has_datas); + + fclose($fp); + } + + /** + * Move the internal pointer to $charOffset in the stream. + * + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($charOffset > $this->_array_size) { + $charOffset = $this->_array_size; + } elseif ($charOffset < 0) { + $charOffset = 0; + } + $this->_offset = $charOffset; + } + + /** + * Empty the stream and reset the internal pointer. + */ + public function flushContents() + { + $this->_offset = 0; + $this->_array = array(); + $this->_array_size = 0; + } + + private function _reloadBuffer($fp, $len) + { + if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) { + $buf = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $buf[] = self::$_byteMap[$bytes[$i]]; + } + + return $buf; + } + + return false; + } + + private static function _initializeMaps() + { + if (!isset(self::$_charMap)) { + self::$_charMap = array(); + for ($byte = 0; $byte < 256; ++$byte) { + self::$_charMap[$byte] = chr($byte); + } + self::$_byteMap = array_flip(self::$_charMap); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php new file mode 100644 index 0000000..7620d0e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php @@ -0,0 +1,275 @@ + + */ +class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream +{ + /** + * The char reader (lazy-loaded) for the current charset. + * + * @var Swift_CharacterReader + */ + private $_charReader; + + /** + * A factory for creating CharacterReader instances. + * + * @var Swift_CharacterReaderFactory + */ + private $_charReaderFactory; + + /** + * The character set this stream is using. + * + * @var string + */ + private $_charset; + + /** + * The data's stored as-is. + * + * @var string + */ + private $_datas = ''; + + /** + * Number of bytes in the stream. + * + * @var int + */ + private $_datasSize = 0; + + /** + * Map. + * + * @var mixed + */ + private $_map; + + /** + * Map Type. + * + * @var int + */ + private $_mapType = 0; + + /** + * Number of characters in the stream. + * + * @var int + */ + private $_charCount = 0; + + /** + * Position in the stream. + * + * @var int + */ + private $_currentPos = 0; + + /** + * Constructor. + * + * @param Swift_CharacterReaderFactory $factory + * @param string $charset + */ + public function __construct(Swift_CharacterReaderFactory $factory, $charset) + { + $this->setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /* -- Changing parameters of the stream -- */ + + /** + * Set the character set used in this CharacterStream. + * + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + $this->_mapType = 0; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * @see Swift_CharacterStream::flushContents() + */ + public function flushContents() + { + $this->_datas = null; + $this->_map = null; + $this->_charCount = 0; + $this->_currentPos = 0; + $this->_datasSize = 0; + } + + /** + * @see Swift_CharacterStream::importByteStream() + * + * @param Swift_OutputByteStream $os + */ + public function importByteStream(Swift_OutputByteStream $os) + { + $this->flushContents(); + $blocks = 512; + $os->setReadPointer(0); + while (false !== ($read = $os->read($blocks))) { + $this->write($read); + } + } + + /** + * @see Swift_CharacterStream::importString() + * + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * @see Swift_CharacterStream::read() + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_currentPos >= $this->_charCount) { + return false; + } + $ret = false; + $length = ($this->_currentPos + $length > $this->_charCount) + ? $this->_charCount - $this->_currentPos + : $length; + switch ($this->_mapType) { + case Swift_CharacterReader::MAP_TYPE_FIXED_LEN: + $len = $length * $this->_map; + $ret = substr($this->_datas, + $this->_currentPos * $this->_map, + $len); + $this->_currentPos += $length; + break; + + case Swift_CharacterReader::MAP_TYPE_INVALID: + $end = $this->_currentPos + $length; + $end = $end > $this->_charCount + ? $this->_charCount + : $end; + $ret = ''; + for (; $this->_currentPos < $length; ++$this->_currentPos) { + if (isset($this->_map[$this->_currentPos])) { + $ret .= '?'; + } else { + $ret .= $this->_datas[$this->_currentPos]; + } + } + break; + + case Swift_CharacterReader::MAP_TYPE_POSITIONS: + $end = $this->_currentPos + $length; + $end = $end > $this->_charCount + ? $this->_charCount + : $end; + $ret = ''; + $start = 0; + if ($this->_currentPos > 0) { + $start = $this->_map['p'][$this->_currentPos - 1]; + } + $to = $start; + for (; $this->_currentPos < $end; ++$this->_currentPos) { + if (isset($this->_map['i'][$this->_currentPos])) { + $ret .= substr($this->_datas, $start, $to - $start).'?'; + $start = $this->_map['p'][$this->_currentPos]; + } else { + $to = $this->_map['p'][$this->_currentPos]; + } + } + $ret .= substr($this->_datas, $start, $to - $start); + break; + } + + return $ret; + } + + /** + * @see Swift_CharacterStream::readBytes() + * + * @param int $length + * + * @return integer[] + */ + public function readBytes($length) + { + $read = $this->read($length); + if ($read !== false) { + $ret = array_map('ord', str_split($read, 1)); + + return $ret; + } + + return false; + } + + /** + * @see Swift_CharacterStream::setPointer() + * + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($this->_charCount < $charOffset) { + $charOffset = $this->_charCount; + } + $this->_currentPos = $charOffset; + } + + /** + * @see Swift_CharacterStream::write() + * + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + $this->_map = array(); + $this->_mapType = $this->_charReader->getMapType(); + } + $ignored = ''; + $this->_datas .= $chars; + $this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored); + if ($ignored !== false) { + $this->_datasSize = strlen($this->_datas) - strlen($ignored); + } else { + $this->_datasSize = strlen($this->_datas); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php new file mode 100644 index 0000000..4ae5bac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for Spools (implements time and message limits). + * + * @author Fabien Potencier + */ +abstract class Swift_ConfigurableSpool implements Swift_Spool +{ + /** The maximum number of messages to send per flush */ + private $_message_limit; + + /** The time limit per flush */ + private $_time_limit; + + /** + * Sets the maximum number of messages to send per flush. + * + * @param int $limit + */ + public function setMessageLimit($limit) + { + $this->_message_limit = (int) $limit; + } + + /** + * Gets the maximum number of messages to send per flush. + * + * @return int The limit + */ + public function getMessageLimit() + { + return $this->_message_limit; + } + + /** + * Sets the time limit (in seconds) per flush. + * + * @param int $limit The limit + */ + public function setTimeLimit($limit) + { + $this->_time_limit = (int) $limit; + } + + /** + * Gets the time limit (in seconds) per flush. + * + * @return int The limit + */ + public function getTimeLimit() + { + return $this->_time_limit; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php new file mode 100644 index 0000000..660cc84 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php @@ -0,0 +1,373 @@ +_store); + } + + /** + * Test if an item is registered in this container with the given name. + * + * @see register() + * + * @param string $itemName + * + * @return bool + */ + public function has($itemName) + { + return array_key_exists($itemName, $this->_store) + && isset($this->_store[$itemName]['lookupType']); + } + + /** + * Lookup the item with the given $itemName. + * + * @see register() + * + * @param string $itemName + * + * @throws Swift_DependencyException If the dependency is not found + * + * @return mixed + */ + public function lookup($itemName) + { + if (!$this->has($itemName)) { + throw new Swift_DependencyException( + 'Cannot lookup dependency "'.$itemName.'" since it is not registered.' + ); + } + + switch ($this->_store[$itemName]['lookupType']) { + case self::TYPE_ALIAS: + return $this->_createAlias($itemName); + case self::TYPE_VALUE: + return $this->_getValue($itemName); + case self::TYPE_INSTANCE: + return $this->_createNewInstance($itemName); + case self::TYPE_SHARED: + return $this->_createSharedInstance($itemName); + } + } + + /** + * Create an array of arguments passed to the constructor of $itemName. + * + * @param string $itemName + * + * @return array + */ + public function createDependenciesFor($itemName) + { + $args = array(); + if (isset($this->_store[$itemName]['args'])) { + $args = $this->_resolveArgs($this->_store[$itemName]['args']); + } + + return $args; + } + + /** + * Register a new dependency with $itemName. + * + * This method returns the current DependencyContainer instance because it + * requires the use of the fluid interface to set the specific details for the + * dependency. + * + * @see asNewInstanceOf(), asSharedInstanceOf(), asValue() + * + * @param string $itemName + * + * @return Swift_DependencyContainer + */ + public function register($itemName) + { + $this->_store[$itemName] = array(); + $this->_endPoint = &$this->_store[$itemName]; + + return $this; + } + + /** + * Specify the previously registered item as a literal value. + * + * {@link register()} must be called before this will work. + * + * @param mixed $value + * + * @return Swift_DependencyContainer + */ + public function asValue($value) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_VALUE; + $endPoint['value'] = $value; + + return $this; + } + + /** + * Specify the previously registered item as an alias of another item. + * + * @param string $lookup + * + * @return Swift_DependencyContainer + */ + public function asAliasOf($lookup) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_ALIAS; + $endPoint['ref'] = $lookup; + + return $this; + } + + /** + * Specify the previously registered item as a new instance of $className. + * + * {@link register()} must be called before this will work. + * Any arguments can be set with {@link withDependencies()}, + * {@link addConstructorValue()} or {@link addConstructorLookup()}. + * + * @see withDependencies(), addConstructorValue(), addConstructorLookup() + * + * @param string $className + * + * @return Swift_DependencyContainer + */ + public function asNewInstanceOf($className) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_INSTANCE; + $endPoint['className'] = $className; + + return $this; + } + + /** + * Specify the previously registered item as a shared instance of $className. + * + * {@link register()} must be called before this will work. + * + * @param string $className + * + * @return Swift_DependencyContainer + */ + public function asSharedInstanceOf($className) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_SHARED; + $endPoint['className'] = $className; + + return $this; + } + + /** + * Specify a list of injected dependencies for the previously registered item. + * + * This method takes an array of lookup names. + * + * @see addConstructorValue(), addConstructorLookup() + * + * @param array $lookups + * + * @return Swift_DependencyContainer + */ + public function withDependencies(array $lookups) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['args'] = array(); + foreach ($lookups as $lookup) { + $this->addConstructorLookup($lookup); + } + + return $this; + } + + /** + * Specify a literal (non looked up) value for the constructor of the + * previously registered item. + * + * @see withDependencies(), addConstructorLookup() + * + * @param mixed $value + * + * @return Swift_DependencyContainer + */ + public function addConstructorValue($value) + { + $endPoint = &$this->_getEndPoint(); + if (!isset($endPoint['args'])) { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'value', 'item' => $value); + + return $this; + } + + /** + * Specify a dependency lookup for the constructor of the previously + * registered item. + * + * @see withDependencies(), addConstructorValue() + * + * @param string $lookup + * + * @return Swift_DependencyContainer + */ + public function addConstructorLookup($lookup) + { + $endPoint = &$this->_getEndPoint(); + if (!isset($this->_endPoint['args'])) { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup); + + return $this; + } + + /** Get the literal value with $itemName */ + private function _getValue($itemName) + { + return $this->_store[$itemName]['value']; + } + + /** Resolve an alias to another item */ + private function _createAlias($itemName) + { + return $this->lookup($this->_store[$itemName]['ref']); + } + + /** Create a fresh instance of $itemName */ + private function _createNewInstance($itemName) + { + $reflector = new ReflectionClass($this->_store[$itemName]['className']); + if ($reflector->getConstructor()) { + return $reflector->newInstanceArgs( + $this->createDependenciesFor($itemName) + ); + } else { + return $reflector->newInstance(); + } + } + + /** Create and register a shared instance of $itemName */ + private function _createSharedInstance($itemName) + { + if (!isset($this->_store[$itemName]['instance'])) { + $this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName); + } + + return $this->_store[$itemName]['instance']; + } + + /** Get the current endpoint in the store */ + private function &_getEndPoint() + { + if (!isset($this->_endPoint)) { + throw new BadMethodCallException( + 'Component must first be registered by calling register()' + ); + } + + return $this->_endPoint; + } + + /** Get an argument list with dependencies resolved */ + private function _resolveArgs(array $args) + { + $resolved = array(); + foreach ($args as $argDefinition) { + switch ($argDefinition['type']) { + case 'lookup': + $resolved[] = $this->_lookupRecursive($argDefinition['item']); + break; + case 'value': + $resolved[] = $argDefinition['item']; + break; + } + } + + return $resolved; + } + + /** Resolve a single dependency with an collections */ + private function _lookupRecursive($item) + { + if (is_array($item)) { + $collection = array(); + foreach ($item as $k => $v) { + $collection[$k] = $this->_lookupRecursive($v); + } + + return $collection; + } else { + return $this->lookup($item); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php new file mode 100644 index 0000000..799d38d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php @@ -0,0 +1,27 @@ +createDependenciesFor('mime.embeddedfile') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new EmbeddedFile. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * + * @return Swift_Mime_EmbeddedFile + */ + public static function newInstance($data = null, $filename = null, $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new EmbeddedFile from a filesystem path. + * + * @param string $path + * + * @return Swift_Mime_EmbeddedFile + */ + public static function fromPath($path) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path) + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php new file mode 100644 index 0000000..2073abc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php @@ -0,0 +1,28 @@ += $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $encodedString = base64_encode($string); + $firstLine = ''; + + if (0 != $firstLineOffset) { + $firstLine = substr( + $encodedString, 0, $maxLineLength - $firstLineOffset + )."\r\n"; + $encodedString = substr( + $encodedString, $maxLineLength - $firstLineOffset + ); + } + + return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n")); + } + + /** + * Does nothing. + */ + public function charsetChanged($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php new file mode 100644 index 0000000..f989c8d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php @@ -0,0 +1,289 @@ + '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04', + 5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09', + 10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E', + 15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13', + 20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18', + 25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D', + 30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22', + 35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27', + 40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C', + 45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31', + 50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36', + 55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B', + 60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40', + 65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45', + 70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A', + 75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F', + 80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54', + 85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59', + 90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E', + 95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63', + 100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68', + 105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D', + 110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72', + 115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77', + 120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C', + 125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81', + 130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86', + 135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B', + 140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90', + 145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95', + 150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A', + 155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F', + 160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4', + 165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9', + 170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE', + 175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3', + 180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8', + 185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD', + 190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2', + 195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7', + 200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC', + 205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1', + 210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6', + 215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB', + 220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0', + 225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5', + 230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA', + 235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF', + 240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4', + 245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9', + 250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE', + 255 => '=FF', + ); + + protected static $_safeMapShare = array(); + + /** + * A map of non-encoded ascii characters. + * + * @var string[] + */ + protected $_safeMap = array(); + + /** + * Creates a new QpEncoder for the given CharacterStream. + * + * @param Swift_CharacterStream $charStream to use for reading characters + * @param Swift_StreamFilter $filter if input should be canonicalized + */ + public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null) + { + $this->_charStream = $charStream; + if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } else { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + $this->_filter = $filter; + } + + public function __sleep() + { + return array('_charStream', '_filter'); + } + + public function __wakeup() + { + if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } else { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + } + + protected function getSafeMapShareId() + { + return get_class($this); + } + + protected function initSafeMap() + { + foreach (array_merge( + array(0x09, 0x20), range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) { + $this->_safeMap[$byte] = chr($byte); + } + } + + /** + * Takes an unencoded string and produces a QP encoded string from it. + * + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * + * @param string $string to encode + * @param int $firstLineOffset, optional + * @param int $maxLineLength, optional 0 indicates the default of 76 chars + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $lines = array(); + $lNo = 0; + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $size = $lineLen = 0; + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + // Fetching more than 4 chars at one is slower, as is fetching fewer bytes + // Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6 + // bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes + while (false !== $bytes = $this->_nextSequence()) { + // If we're filtering the input + if (isset($this->_filter)) { + // If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) { + // Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) { + break; + } + + foreach ($moreBytes as $b) { + $bytes[] = $b; + } + } + // And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + if ($currentLine && $lineLen + $size >= $thisLineLength) { + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $thisLineLength = $maxLineLength; + $lineLen = 0; + } + $lineLen += $size; + $currentLine .= $enc; + } + + return $this->_standardize(implode("=\r\n", $lines)); + } + + /** + * Updates the charset used. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + + /** + * Encode the given byte array into a verbatim QP form. + * + * @param integer[] $bytes + * @param int $size + * + * @return string + */ + protected function _encodeByteSequence(array $bytes, &$size) + { + $ret = ''; + $size = 0; + foreach ($bytes as $b) { + if (isset($this->_safeMap[$b])) { + $ret .= $this->_safeMap[$b]; + ++$size; + } else { + $ret .= self::$_qpMap[$b]; + $size += 3; + } + } + + return $ret; + } + + /** + * Get the next sequence of bytes to read from the char stream. + * + * @param int $size number of bytes to read + * + * @return integer[] + */ + protected function _nextSequence($size = 4) + { + return $this->_charStream->readBytes($size); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + * + * @param string $string + * + * @return string + */ + protected function _standardize($string) + { + $string = str_replace(array("\t=0D=0A", ' =0D=0A', '=0D=0A'), + array("=09\r\n", "=20\r\n", "\r\n"), $string + ); + switch ($end = ord(substr($string, -1))) { + case 0x09: + case 0x20: + $string = substr_replace($string, self::$_qpMap[$end], -1); + } + + return $string; + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_charStream = clone $this->_charStream; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php new file mode 100644 index 0000000..b0215e8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php @@ -0,0 +1,92 @@ +_charStream = $charStream; + } + + /** + * Takes an unencoded string and produces a string encoded according to + * RFC 2231 from it. + * + * @param string $string + * @param int $firstLineOffset + * @param int $maxLineLength optional, 0 indicates the default of 75 bytes + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + $lines = array(); + $lineCount = 0; + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + + if (0 >= $maxLineLength) { + $maxLineLength = 75; + } + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + $thisLineLength = $maxLineLength - $firstLineOffset; + + while (false !== $char = $this->_charStream->read(4)) { + $encodedChar = rawurlencode($char); + if (0 != strlen($currentLine) + && strlen($currentLine.$encodedChar) > $thisLineLength) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + $thisLineLength = $maxLineLength; + } + $currentLine .= $encodedChar; + } + + return implode("\r\n", $lines); + } + + /** + * Updates the charset used. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_charStream = clone $this->_charStream; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php new file mode 100644 index 0000000..253977b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php @@ -0,0 +1,64 @@ +lookup($key); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php new file mode 100644 index 0000000..7dc381d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php @@ -0,0 +1,65 @@ +_command = $command; + $this->_successCodes = $successCodes; + } + + /** + * Get the command which was sent to the server. + * + * @return string + */ + public function getCommand() + { + return $this->_command; + } + + /** + * Get the numeric response codes which indicate success for this command. + * + * @return integer[] + */ + public function getSuccessCodes() + { + return $this->_successCodes; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php new file mode 100644 index 0000000..7545404 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php @@ -0,0 +1,24 @@ +_source = $source; + } + + /** + * Get the source object of this event. + * + * @return object + */ + public function getSource() + { + return $this->_source; + } + + /** + * Prevent this Event from bubbling any further up the stack. + * + * @param bool $cancel, optional + */ + public function cancelBubble($cancel = true) + { + $this->_bubbleCancelled = $cancel; + } + + /** + * Returns true if this Event will not bubble any further up the stack. + * + * @return bool + */ + public function bubbleCancelled() + { + return $this->_bubbleCancelled; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php new file mode 100644 index 0000000..2e92ba9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php @@ -0,0 +1,65 @@ +_response = $response; + $this->_valid = $valid; + } + + /** + * Get the response which was received from the server. + * + * @return string + */ + public function getResponse() + { + return $this->_response; + } + + /** + * Get the success status of this Event. + * + * @return bool + */ + public function isValid() + { + return $this->_valid; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php new file mode 100644 index 0000000..c40919d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php @@ -0,0 +1,24 @@ +_message = $message; + $this->_result = self::RESULT_PENDING; + } + + /** + * Get the Transport used to send the Message. + * + * @return Swift_Transport + */ + public function getTransport() + { + return $this->getSource(); + } + + /** + * Get the Message being sent. + * + * @return Swift_Mime_Message + */ + public function getMessage() + { + return $this->_message; + } + + /** + * Set the array of addresses that failed in sending. + * + * @param array $recipients + */ + public function setFailedRecipients($recipients) + { + $this->_failedRecipients = $recipients; + } + + /** + * Get an recipient addresses which were not accepted for delivery. + * + * @return string[] + */ + public function getFailedRecipients() + { + return $this->_failedRecipients; + } + + /** + * Set the result of sending. + * + * @param int $result + */ + public function setResult($result) + { + $this->_result = $result; + } + + /** + * Get the result of this Event. + * + * The return value is a bitmask from + * {@see RESULT_PENDING, RESULT_SUCCESS, RESULT_TENTATIVE, RESULT_FAILED} + * + * @return int + */ + public function getResult() + { + return $this->_result; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php new file mode 100644 index 0000000..d922e1b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php @@ -0,0 +1,31 @@ +_eventMap = array( + 'Swift_Events_CommandEvent' => 'Swift_Events_CommandListener', + 'Swift_Events_ResponseEvent' => 'Swift_Events_ResponseListener', + 'Swift_Events_SendEvent' => 'Swift_Events_SendListener', + 'Swift_Events_TransportChangeEvent' => 'Swift_Events_TransportChangeListener', + 'Swift_Events_TransportExceptionEvent' => 'Swift_Events_TransportExceptionListener', + ); + } + + /** + * Create a new SendEvent for $source and $message. + * + * @param Swift_Transport $source + * @param Swift_Mime_Message + * + * @return Swift_Events_SendEvent + */ + public function createSendEvent(Swift_Transport $source, Swift_Mime_Message $message) + { + return new Swift_Events_SendEvent($source, $message); + } + + /** + * Create a new CommandEvent for $source and $command. + * + * @param Swift_Transport $source + * @param string $command That will be executed + * @param array $successCodes That are needed + * + * @return Swift_Events_CommandEvent + */ + public function createCommandEvent(Swift_Transport $source, $command, $successCodes = array()) + { + return new Swift_Events_CommandEvent($source, $command, $successCodes); + } + + /** + * Create a new ResponseEvent for $source and $response. + * + * @param Swift_Transport $source + * @param string $response + * @param bool $valid If the response is valid + * + * @return Swift_Events_ResponseEvent + */ + public function createResponseEvent(Swift_Transport $source, $response, $valid) + { + return new Swift_Events_ResponseEvent($source, $response, $valid); + } + + /** + * Create a new TransportChangeEvent for $source. + * + * @param Swift_Transport $source + * + * @return Swift_Events_TransportChangeEvent + */ + public function createTransportChangeEvent(Swift_Transport $source) + { + return new Swift_Events_TransportChangeEvent($source); + } + + /** + * Create a new TransportExceptionEvent for $source. + * + * @param Swift_Transport $source + * @param Swift_TransportException $ex + * + * @return Swift_Events_TransportExceptionEvent + */ + public function createTransportExceptionEvent(Swift_Transport $source, Swift_TransportException $ex) + { + return new Swift_Events_TransportExceptionEvent($source, $ex); + } + + /** + * Bind an event listener to this dispatcher. + * + * @param Swift_Events_EventListener $listener + */ + public function bindEventListener(Swift_Events_EventListener $listener) + { + foreach ($this->_listeners as $l) { + // Already loaded + if ($l === $listener) { + return; + } + } + $this->_listeners[] = $listener; + } + + /** + * Dispatch the given Event to all suitable listeners. + * + * @param Swift_Events_EventObject $evt + * @param string $target method + */ + public function dispatchEvent(Swift_Events_EventObject $evt, $target) + { + $this->_prepareBubbleQueue($evt); + $this->_bubble($evt, $target); + } + + /** Queue listeners on a stack ready for $evt to be bubbled up it */ + private function _prepareBubbleQueue(Swift_Events_EventObject $evt) + { + $this->_bubbleQueue = array(); + $evtClass = get_class($evt); + foreach ($this->_listeners as $listener) { + if (array_key_exists($evtClass, $this->_eventMap) + && ($listener instanceof $this->_eventMap[$evtClass])) { + $this->_bubbleQueue[] = $listener; + } + } + } + + /** Bubble $evt up the stack calling $target() on each listener */ + private function _bubble(Swift_Events_EventObject $evt, $target) + { + if (!$evt->bubbleCancelled() && $listener = array_shift($this->_bubbleQueue)) { + $listener->$target($evt); + $this->_bubble($evt, $target); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php new file mode 100644 index 0000000..a8972fd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php @@ -0,0 +1,27 @@ +getSource(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php new file mode 100644 index 0000000..253165d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php @@ -0,0 +1,45 @@ +_exception = $ex; + } + + /** + * Get the TransportException thrown. + * + * @return Swift_TransportException + */ + public function getException() + { + return $this->_exception; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php new file mode 100644 index 0000000..cc3c099 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php @@ -0,0 +1,24 @@ +createDependenciesFor('transport.failover') + ); + + $this->setTransports($transports); + } + + /** + * Create a new FailoverTransport instance. + * + * @param Swift_Transport[] $transports + * + * @return Swift_FailoverTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php new file mode 100644 index 0000000..2208539 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php @@ -0,0 +1,208 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages on the filesystem. + * + * @author Fabien Potencier + * @author Xavier De Cock + */ +class Swift_FileSpool extends Swift_ConfigurableSpool +{ + /** The spool directory */ + private $_path; + + /** + * File WriteRetry Limit. + * + * @var int + */ + private $_retryLimit = 10; + + /** + * Create a new FileSpool. + * + * @param string $path + * + * @throws Swift_IoException + */ + public function __construct($path) + { + $this->_path = $path; + + if (!file_exists($this->_path)) { + if (!mkdir($this->_path, 0777, true)) { + throw new Swift_IoException('Unable to create Path ['.$this->_path.']'); + } + } + } + + /** + * Tests if this Spool mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Spool mechanism. + */ + public function start() + { + } + + /** + * Stops this Spool mechanism. + */ + public function stop() + { + } + + /** + * Allow to manage the enqueuing retry limit. + * + * Default, is ten and allows over 64^20 different fileNames + * + * @param int $limit + */ + public function setRetryLimit($limit) + { + $this->_retryLimit = $limit; + } + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @throws Swift_IoException + * + * @return bool + */ + public function queueMessage(Swift_Mime_Message $message) + { + $ser = serialize($message); + $fileName = $this->_path.'/'.$this->getRandomString(10); + for ($i = 0; $i < $this->_retryLimit; ++$i) { + /* We try an exclusive creation of the file. This is an atomic operation, it avoid locking mechanism */ + $fp = @fopen($fileName.'.message', 'x'); + if (false !== $fp) { + if (false === fwrite($fp, $ser)) { + return false; + } + + return fclose($fp); + } else { + /* The file already exists, we try a longer fileName */ + $fileName .= $this->getRandomString(1); + } + } + + throw new Swift_IoException('Unable to create a file for enqueuing Message'); + } + + /** + * Execute a recovery if for any reason a process is sending for too long. + * + * @param int $timeout in second Defaults is for very slow smtp responses + */ + public function recover($timeout = 900) + { + foreach (new DirectoryIterator($this->_path) as $file) { + $file = $file->getRealPath(); + + if (substr($file, -16) == '.message.sending') { + $lockedtime = filectime($file); + if ((time() - $lockedtime) > $timeout) { + rename($file, substr($file, 0, -8)); + } + } + } + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent e-mail's + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + $directoryIterator = new DirectoryIterator($this->_path); + + /* Start the transport only if there are queued files to send */ + if (!$transport->isStarted()) { + foreach ($directoryIterator as $file) { + if (substr($file->getRealPath(), -8) == '.message') { + $transport->start(); + break; + } + } + } + + $failedRecipients = (array) $failedRecipients; + $count = 0; + $time = time(); + foreach ($directoryIterator as $file) { + $file = $file->getRealPath(); + + if (substr($file, -8) != '.message') { + continue; + } + + /* We try a rename, it's an atomic operation, and avoid locking the file */ + if (rename($file, $file.'.sending')) { + $message = unserialize(file_get_contents($file.'.sending')); + + $count += $transport->send($message, $failedRecipients); + + unlink($file.'.sending'); + } else { + /* This message has just been catched by another process */ + continue; + } + + if ($this->getMessageLimit() && $count >= $this->getMessageLimit()) { + break; + } + + if ($this->getTimeLimit() && (time() - $time) >= $this->getTimeLimit()) { + break; + } + } + + return $count; + } + + /** + * Returns a random string needed to generate a fileName for the queue. + * + * @param int $count + * + * @return string + */ + protected function getRandomString($count) + { + // This string MUST stay FS safe, avoid special chars + $base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'; + $ret = ''; + $strlen = strlen($base); + for ($i = 0; $i < $count; ++$i) { + $ret .= $base[((int) rand(0, $strlen - 1))]; + } + + return $ret; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php new file mode 100644 index 0000000..0b24db1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php @@ -0,0 +1,24 @@ +setFile( + new Swift_ByteStream_FileByteStream($path) + ); + + return $image; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php new file mode 100644 index 0000000..56efc75 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php @@ -0,0 +1,75 @@ +_stream = $stream; + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $this->_contents[$nsKey][$itemKey] = $string; + break; + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) { + $this->_contents[$nsKey][$itemKey] = ''; + } + $this->_contents[$nsKey][$itemKey] .= $string; + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + } + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $this->clearKey($nsKey, $itemKey); + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) { + $this->_contents[$nsKey][$itemKey] = ''; + } + while (false !== $bytes = $os->read(8192)) { + $this->_contents[$nsKey][$itemKey] .= $bytes; + } + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + } + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $writeThrough + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) { + $is->setWriteThroughStream($writeThrough); + } + + return $is; + } + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @return string + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) { + return $this->_contents[$nsKey][$itemKey]; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + $this->_prepareCache($nsKey); + $is->write($this->getString($nsKey, $itemKey)); + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + + return array_key_exists($itemKey, $this->_contents[$nsKey]); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + unset($this->_contents[$nsKey][$itemKey]); + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey) + { + unset($this->_contents[$nsKey]); + } + + /** + * Initialize the namespace of $nsKey if needed. + * + * @param string $nsKey + */ + private function _prepareCache($nsKey) + { + if (!array_key_exists($nsKey, $this->_contents)) { + $this->_contents[$nsKey] = array(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php new file mode 100644 index 0000000..dc1515a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php @@ -0,0 +1,324 @@ +_stream = $stream; + $this->_path = $path; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { + $this->_quotes = true; + } + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + * + * @throws Swift_IoException + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + break; + } + fwrite($fp, $string); + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + * + * @throws Swift_IoException + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + break; + } + while (false !== $bytes = $os->read(8192)) { + fwrite($fp, $bytes); + } + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $writeThrough + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) { + $is->setWriteThroughStream($writeThrough); + } + + return $is; + } + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @throws Swift_IoException + * + * @return string + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + $str = ''; + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { + $str .= $bytes; + } + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + + return $str; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + if ($this->hasKey($nsKey, $itemKey)) { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { + $is->write($bytes); + } + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + } + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey) + { + return is_file($this->_path.'/'.$nsKey.'/'.$itemKey); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + if ($this->hasKey($nsKey, $itemKey)) { + $this->_freeHandle($nsKey, $itemKey); + unlink($this->_path.'/'.$nsKey.'/'.$itemKey); + } + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey) + { + if (array_key_exists($nsKey, $this->_keys)) { + foreach ($this->_keys[$nsKey] as $itemKey => $null) { + $this->clearKey($nsKey, $itemKey); + } + if (is_dir($this->_path.'/'.$nsKey)) { + rmdir($this->_path.'/'.$nsKey); + } + unset($this->_keys[$nsKey]); + } + } + + /** + * Initialize the namespace of $nsKey if needed. + * + * @param string $nsKey + */ + private function _prepareCache($nsKey) + { + $cacheDir = $this->_path.'/'.$nsKey; + if (!is_dir($cacheDir)) { + if (!mkdir($cacheDir)) { + throw new Swift_IoException('Failed to create cache directory '.$cacheDir); + } + $this->_keys[$nsKey] = array(); + } + } + + /** + * Get a file handle on the cache item. + * + * @param string $nsKey + * @param string $itemKey + * @param int $position + * + * @return resource + */ + private function _getHandle($nsKey, $itemKey, $position) + { + if (!isset($this->_keys[$nsKey][$itemKey])) { + $openMode = $this->hasKey($nsKey, $itemKey) + ? 'r+b' + : 'w+b' + ; + $fp = fopen($this->_path.'/'.$nsKey.'/'.$itemKey, $openMode); + $this->_keys[$nsKey][$itemKey] = $fp; + } + if (self::POSITION_START == $position) { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_SET); + } elseif (self::POSITION_END == $position) { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_END); + } + + return $this->_keys[$nsKey][$itemKey]; + } + + private function _freeHandle($nsKey, $itemKey) + { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_CURRENT); + fclose($fp); + $this->_keys[$nsKey][$itemKey] = null; + } + + /** + * Destructor. + */ + public function __destruct() + { + foreach ($this->_keys as $nsKey => $null) { + $this->clearAll($nsKey); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php new file mode 100644 index 0000000..af80bdc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php @@ -0,0 +1,51 @@ +_keyCache = $keyCache; + } + + /** + * Specify a stream to write through for each write(). + * + * @param Swift_InputByteStream $is + */ + public function setWriteThroughStream(Swift_InputByteStream $is) + { + $this->_writeThrough = $is; + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + * @param Swift_InputByteStream $is optional + */ + public function write($bytes, Swift_InputByteStream $is = null) + { + $this->_keyCache->setString( + $this->_nsKey, $this->_itemKey, $bytes, Swift_KeyCache::MODE_APPEND + ); + if (isset($is)) { + $is->write($bytes); + } + if (isset($this->_writeThrough)) { + $this->_writeThrough->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Not used. + */ + public function bind(Swift_InputByteStream $is) + { + } + + /** + * Not used. + */ + public function unbind(Swift_InputByteStream $is) + { + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_keyCache->clearKey($this->_nsKey, $this->_itemKey); + } + + /** + * Set the nsKey which will be written to. + * + * @param string $nsKey + */ + public function setNsKey($nsKey) + { + $this->_nsKey = $nsKey; + } + + /** + * Set the itemKey which will be written to. + * + * @param string $itemKey + */ + public function setItemKey($itemKey) + { + $this->_itemKey = $itemKey; + } + + /** + * Any implementation should be cloneable, allowing the clone to access a + * separate $nsKey and $itemKey. + */ + public function __clone() + { + $this->_writeThrough = null; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php new file mode 100644 index 0000000..fdba9df --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php @@ -0,0 +1,45 @@ +createDependenciesFor('transport.loadbalanced') + ); + + $this->setTransports($transports); + } + + /** + * Create a new LoadBalancedTransport instance. + * + * @param array $transports + * + * @return Swift_LoadBalancedTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php new file mode 100644 index 0000000..858ca81 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php @@ -0,0 +1,45 @@ +createDependenciesFor('transport.mail') + ); + + $this->setExtraParams($extraParams); + } + + /** + * Create a new MailTransport instance. + * + * @param string $extraParams To be passed to mail() + * + * @return Swift_MailTransport + */ + public static function newInstance($extraParams = '-f%s') + { + return new self($extraParams); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php new file mode 100644 index 0000000..34a78d4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php @@ -0,0 +1,114 @@ +_transport = $transport; + } + + /** + * Create a new Mailer instance. + * + * @param Swift_Transport $transport + * + * @return Swift_Mailer + */ + public static function newInstance(Swift_Transport $transport) + { + return new self($transport); + } + + /** + * Create a new class instance of one of the message services. + * + * For example 'mimepart' would create a 'message.mimepart' instance + * + * @param string $service + * + * @return object + */ + public function createMessage($service = 'message') + { + return Swift_DependencyContainer::getInstance() + ->lookup('message.'.$service); + } + + /** + * Send the given Message like it would be sent in a mail client. + * + * All recipients (with the exception of Bcc) will be able to see the other + * recipients this message was sent to. + * + * Recipient/sender data will be retrieved from the Message object. + * + * The return value is the number of recipients who were accepted for + * delivery. + * + * @param Swift_Mime_Message $message + * @param array $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + + if (!$this->_transport->isStarted()) { + $this->_transport->start(); + } + + $sent = 0; + + try { + $sent = $this->_transport->send($message, $failedRecipients); + } catch (Swift_RfcComplianceException $e) { + foreach ($message->getTo() as $address => $name) { + $failedRecipients[] = $address; + } + } + + return $sent; + } + + /** + * Register a plugin using a known unique key (e.g. myPlugin). + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_transport->registerPlugin($plugin); + } + + /** + * The Transport used to send messages. + * + * @return Swift_Transport + */ + public function getTransport() + { + return $this->_transport; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php new file mode 100644 index 0000000..e3e6cad --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php @@ -0,0 +1,55 @@ +_recipients = $recipients; + } + + /** + * Returns true only if there are more recipients to send to. + * + * @return bool + */ + public function hasNext() + { + return !empty($this->_recipients); + } + + /** + * Returns an array where the keys are the addresses of recipients and the + * values are the names. e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL). + * + * @return array + */ + public function nextRecipient() + { + return array_splice($this->_recipients, 0, 1); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php new file mode 100644 index 0000000..650f3ec --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php @@ -0,0 +1,32 @@ + 'Foo') or ('foo@bar' => NULL). + * + * @return array + */ + public function nextRecipient(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php new file mode 100644 index 0000000..5b23969 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in memory. + * + * @author Fabien Potencier + */ +class Swift_MemorySpool implements Swift_Spool +{ + protected $messages = array(); + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Stores a message in the queue. + * + * @param Swift_Mime_Message $message The message to store + * + * @return bool Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message) + { + //clone the message to make sure it is not changed while in the queue + $this->messages[] = clone $message; + + return true; + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + if (!$this->messages) { + return 0; + } + + if (!$transport->isStarted()) { + $transport->start(); + } + + $count = 0; + while ($message = array_pop($this->messages)) { + $count += $transport->send($message, $failedRecipients); + } + + return $count; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php new file mode 100644 index 0000000..11aa5a9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php @@ -0,0 +1,291 @@ +createDependenciesFor('mime.message') + ); + + if (!isset($charset)) { + $charset = Swift_DependencyContainer::getInstance() + ->lookup('properties.charset'); + } + $this->setSubject($subject); + $this->setBody($body); + $this->setCharset($charset); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new Message. + * + * @param string $subject + * @param string $body + * @param string $contentType + * @param string $charset + * + * @return Swift_Message + */ + public static function newInstance($subject = null, $body = null, $contentType = null, $charset = null) + { + return new self($subject, $body, $contentType, $charset); + } + + /** + * Add a MimePart to this Message. + * + * @param string|Swift_OutputByteStream $body + * @param string $contentType + * @param string $charset + * + * @return Swift_Mime_SimpleMessage + */ + public function addPart($body, $contentType = null, $charset = null) + { + return $this->attach(Swift_MimePart::newInstance( + $body, $contentType, $charset + )); + } + + /** + * Attach a new signature handler to the message. + * + * @param Swift_Signer $signer + * + * @return Swift_Message + */ + public function attachSigner(Swift_Signer $signer) + { + if ($signer instanceof Swift_Signers_HeaderSigner) { + $this->headerSigners[] = $signer; + } elseif ($signer instanceof Swift_Signers_BodySigner) { + $this->bodySigners[] = $signer; + } + + return $this; + } + + /** + * Attach a new signature handler to the message. + * + * @param Swift_Signer $signer + * + * @return Swift_Message + */ + public function detachSigner(Swift_Signer $signer) + { + if ($signer instanceof Swift_Signers_HeaderSigner) { + foreach ($this->headerSigners as $k => $headerSigner) { + if ($headerSigner === $signer) { + unset($this->headerSigners[$k]); + + return $this; + } + } + } elseif ($signer instanceof Swift_Signers_BodySigner) { + foreach ($this->bodySigners as $k => $bodySigner) { + if ($bodySigner === $signer) { + unset($this->bodySigners[$k]); + + return $this; + } + } + } + + return $this; + } + + /** + * Get this message as a complete string. + * + * @return string + */ + public function toString() + { + if (empty($this->headerSigners) && empty($this->bodySigners)) { + return parent::toString(); + } + + $this->saveMessage(); + + $this->doSign(); + + $string = parent::toString(); + + $this->restoreMessage(); + + return $string; + } + + /** + * Write this message to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream $is + */ + public function toByteStream(Swift_InputByteStream $is) + { + if (empty($this->headerSigners) && empty($this->bodySigners)) { + parent::toByteStream($is); + + return; + } + + $this->saveMessage(); + + $this->doSign(); + + parent::toByteStream($is); + + $this->restoreMessage(); + } + + public function __wakeup() + { + Swift_DependencyContainer::getInstance()->createDependenciesFor('mime.message'); + } + + /** + * loops through signers and apply the signatures. + */ + protected function doSign() + { + foreach ($this->bodySigners as $signer) { + $altered = $signer->getAlteredHeaders(); + $this->saveHeaders($altered); + $signer->signMessage($this); + } + + foreach ($this->headerSigners as $signer) { + $altered = $signer->getAlteredHeaders(); + $this->saveHeaders($altered); + $signer->reset(); + + $signer->setHeaders($this->getHeaders()); + + $signer->startBody(); + $this->_bodyToByteStream($signer); + $signer->endBody(); + + $signer->addSignature($this->getHeaders()); + } + } + + /** + * save the message before any signature is applied. + */ + protected function saveMessage() + { + $this->savedMessage = array('headers' => array()); + $this->savedMessage['body'] = $this->getBody(); + $this->savedMessage['children'] = $this->getChildren(); + if (count($this->savedMessage['children']) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $this->savedMessage['children'])); + $this->setBody(''); + } + } + + /** + * save the original headers. + * + * @param array $altered + */ + protected function saveHeaders(array $altered) + { + foreach ($altered as $head) { + $lc = strtolower($head); + + if (!isset($this->savedMessage['headers'][$lc])) { + $this->savedMessage['headers'][$lc] = $this->getHeaders()->getAll($head); + } + } + } + + /** + * Remove or restore altered headers. + */ + protected function restoreHeaders() + { + foreach ($this->savedMessage['headers'] as $name => $savedValue) { + $headers = $this->getHeaders()->getAll($name); + + foreach ($headers as $key => $value) { + if (!isset($savedValue[$key])) { + $this->getHeaders()->remove($name, $key); + } + } + } + } + + /** + * Restore message body. + */ + protected function restoreMessage() + { + $this->setBody($this->savedMessage['body']); + $this->setChildren($this->savedMessage['children']); + + $this->restoreHeaders(); + $this->savedMessage = array(); + } + + /** + * Clone Message Signers. + * + * @see Swift_Mime_SimpleMimeEntity::__clone() + */ + public function __clone() + { + parent::__clone(); + foreach ($this->bodySigners as $key => $bodySigner) { + $this->bodySigners[$key] = clone($bodySigner); + } + + foreach ($this->headerSigners as $key => $headerSigner) { + $this->headerSigners[$key] = clone($headerSigner); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php new file mode 100644 index 0000000..0a72606 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php @@ -0,0 +1,153 @@ +setDisposition('attachment'); + $this->setContentType('application/octet-stream'); + $this->_mimeTypes = $mimeTypes; + } + + /** + * Get the nesting level used for this attachment. + * + * Always returns {@link LEVEL_MIXED}. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_MIXED; + } + + /** + * Get the Content-Disposition of this attachment. + * + * By default attachments have a disposition of "attachment". + * + * @return string + */ + public function getDisposition() + { + return $this->_getHeaderFieldModel('Content-Disposition'); + } + + /** + * Set the Content-Disposition of this attachment. + * + * @param string $disposition + * + * @return Swift_Mime_Attachment + */ + public function setDisposition($disposition) + { + if (!$this->_setHeaderFieldModel('Content-Disposition', $disposition)) { + $this->getHeaders()->addParameterizedHeader( + 'Content-Disposition', $disposition + ); + } + + return $this; + } + + /** + * Get the filename of this attachment when downloaded. + * + * @return string + */ + public function getFilename() + { + return $this->_getHeaderParameter('Content-Disposition', 'filename'); + } + + /** + * Set the filename of this attachment. + * + * @param string $filename + * + * @return Swift_Mime_Attachment + */ + public function setFilename($filename) + { + $this->_setHeaderParameter('Content-Disposition', 'filename', $filename); + $this->_setHeaderParameter('Content-Type', 'name', $filename); + + return $this; + } + + /** + * Get the file size of this attachment. + * + * @return int + */ + public function getSize() + { + return $this->_getHeaderParameter('Content-Disposition', 'size'); + } + + /** + * Set the file size of this attachment. + * + * @param int $size + * + * @return Swift_Mime_Attachment + */ + public function setSize($size) + { + $this->_setHeaderParameter('Content-Disposition', 'size', $size); + + return $this; + } + + /** + * Set the file that this attachment is for. + * + * @param Swift_FileStream $file + * @param string $contentType optional + * + * @return Swift_Mime_Attachment + */ + public function setFile(Swift_FileStream $file, $contentType = null) + { + $this->setFilename(basename($file->getPath())); + $this->setBody($file, $contentType); + if (!isset($contentType)) { + $extension = strtolower(substr( + $file->getPath(), strrpos($file->getPath(), '.') + 1 + )); + + if (array_key_exists($extension, $this->_mimeTypes)) { + $this->setContentType($this->_mimeTypes[$extension]); + } + } + + return $this; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php new file mode 100644 index 0000000..b49c3a8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php @@ -0,0 +1,24 @@ += $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $remainder = 0; + $base64ReadBufferRemainderBytes = null; + + // To reduce memory usage, the output buffer is streamed to the input buffer like so: + // Output Stream => base64encode => wrap line length => Input Stream + // HOWEVER it's important to note that base64_encode() should only be passed whole triplets of data (except for the final chunk of data) + // otherwise it will assume the input data has *ended* and it will incorrectly pad/terminate the base64 data mid-stream. + // We use $base64ReadBufferRemainderBytes to carry over 1-2 "remainder" bytes from the each chunk from OutputStream and pre-pend those onto the + // chunk of bytes read in the next iteration. + // When the OutputStream is empty, we must flush any remainder bytes. + while (true) { + $readBytes = $os->read(8192); + $atEOF = ($readBytes === false); + + if ($atEOF) { + $streamTheseBytes = $base64ReadBufferRemainderBytes; + } else { + $streamTheseBytes = $base64ReadBufferRemainderBytes.$readBytes; + } + $base64ReadBufferRemainderBytes = null; + $bytesLength = strlen($streamTheseBytes); + + if ($bytesLength === 0) { // no data left to encode + break; + } + + // if we're not on the last block of the ouput stream, make sure $streamTheseBytes ends with a complete triplet of data + // and carry over remainder 1-2 bytes to the next loop iteration + if (!$atEOF) { + $excessBytes = $bytesLength % 3; + if ($excessBytes !== 0) { + $base64ReadBufferRemainderBytes = substr($streamTheseBytes, -$excessBytes); + $streamTheseBytes = substr($streamTheseBytes, 0, $bytesLength - $excessBytes); + } + } + + $encoded = base64_encode($streamTheseBytes); + $encodedTransformed = ''; + $thisMaxLineLength = $maxLineLength - $remainder - $firstLineOffset; + + while ($thisMaxLineLength < strlen($encoded)) { + $encodedTransformed .= substr($encoded, 0, $thisMaxLineLength)."\r\n"; + $firstLineOffset = 0; + $encoded = substr($encoded, $thisMaxLineLength); + $thisMaxLineLength = $maxLineLength; + $remainder = 0; + } + + if (0 < $remainingLength = strlen($encoded)) { + $remainder += $remainingLength; + $encodedTransformed .= $encoded; + $encoded = null; + } + + $is->write($encodedTransformed); + + if ($atEOF) { + break; + } + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'base64'. + * + * @return string + */ + public function getName() + { + return 'base64'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php new file mode 100644 index 0000000..710b5ac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php @@ -0,0 +1,123 @@ +charset = $charset ? $charset : 'utf-8'; + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->charset = $charset; + } + + /** + * Encode $in to $out. + * + * @param Swift_OutputByteStream $os to read from + * @param Swift_InputByteStream $is to write to + * @param int $firstLineOffset + * @param int $maxLineLength 0 indicates the default length for this encoding + * + * @throws RuntimeException + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->charset !== 'utf-8') { + throw new RuntimeException( + sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset)); + } + + $string = ''; + + while (false !== $bytes = $os->read(8192)) { + $string .= $bytes; + } + + $is->write($this->encodeString($string)); + } + + /** + * Get the MIME name of this content encoding scheme. + * + * @return string + */ + public function getName() + { + return 'quoted-printable'; + } + + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset if first line needs to be shorter + * @param int $maxLineLength 0 indicates the default length for this encoding + * + * @throws RuntimeException + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->charset !== 'utf-8') { + throw new RuntimeException( + sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset)); + } + + return $this->_standardize(quoted_printable_encode($string)); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + * + * @param string $string + * + * @return string + */ + protected function _standardize($string) + { + // transform CR or LF to CRLF + $string = preg_replace('~=0D(?!=0A)|(?_name = $name; + $this->_canonical = $canonical; + } + + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset ignored + * @param int $maxLineLength - 0 means no wrapping will occur + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->_canonical) { + $string = $this->_canonicalize($string); + } + + return $this->_safeWordWrap($string, $maxLineLength, "\r\n"); + } + + /** + * Encode stream $in to stream $out. + * + * @param Swift_OutputByteStream $os + * @param Swift_InputByteStream $is + * @param int $firstLineOffset ignored + * @param int $maxLineLength optional, 0 means no wrapping will occur + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + $leftOver = ''; + while (false !== $bytes = $os->read(8192)) { + $toencode = $leftOver.$bytes; + if ($this->_canonical) { + $toencode = $this->_canonicalize($toencode); + } + $wrapped = $this->_safeWordWrap($toencode, $maxLineLength, "\r\n"); + $lastLinePos = strrpos($wrapped, "\r\n"); + $leftOver = substr($wrapped, $lastLinePos); + $wrapped = substr($wrapped, 0, $lastLinePos); + + $is->write($wrapped); + } + if (strlen($leftOver)) { + $is->write($leftOver); + } + } + + /** + * Get the name of this encoding scheme. + * + * @return string + */ + public function getName() + { + return $this->_name; + } + + /** + * Not used. + */ + public function charsetChanged($charset) + { + } + + /** + * A safer (but weaker) wordwrap for unicode. + * + * @param string $string + * @param int $length + * @param string $le + * + * @return string + */ + private function _safeWordwrap($string, $length = 75, $le = "\r\n") + { + if (0 >= $length) { + return $string; + } + + $originalLines = explode($le, $string); + + $lines = array(); + $lineCount = 0; + + foreach ($originalLines as $originalLine) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + + //$chunks = preg_split('/(?<=[\ \t,\.!\?\-&\+\/])/', $originalLine); + $chunks = preg_split('/(?<=\s)/', $originalLine); + + foreach ($chunks as $chunk) { + if (0 != strlen($currentLine) + && strlen($currentLine.$chunk) > $length) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + } + $currentLine .= $chunk; + } + } + + return implode("\r\n", $lines); + } + + /** + * Canonicalize string input (fix CRLF). + * + * @param string $string + * + * @return string + */ + private function _canonicalize($string) + { + return str_replace( + array("\r\n", "\r", "\n"), + array("\n", "\n", "\r\n"), + $string + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php new file mode 100644 index 0000000..8ee4c75 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php @@ -0,0 +1,123 @@ +_dotEscape = $dotEscape; + parent::__construct($charStream, $filter); + } + + public function __sleep() + { + return array('_charStream', '_filter', '_dotEscape'); + } + + protected function getSafeMapShareId() + { + return get_class($this).($this->_dotEscape ? '.dotEscape' : ''); + } + + protected function initSafeMap() + { + parent::initSafeMap(); + if ($this->_dotEscape) { + /* Encode . as =2e for buggy remote servers */ + unset($this->_safeMap[0x2e]); + } + } + + /** + * Encode stream $in to stream $out. + * + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * + * @param Swift_OutputByteStream $os output stream + * @param Swift_InputByteStream $is input stream + * @param int $firstLineOffset + * @param int $maxLineLength + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $this->_charStream->flushContents(); + $this->_charStream->importByteStream($os); + + $currentLine = ''; + $prepend = ''; + $size = $lineLen = 0; + + while (false !== $bytes = $this->_nextSequence()) { + // If we're filtering the input + if (isset($this->_filter)) { + // If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) { + // Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) { + break; + } + + foreach ($moreBytes as $b) { + $bytes[] = $b; + } + } + // And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + if ($currentLine && $lineLen + $size >= $thisLineLength) { + $is->write($prepend.$this->_standardize($currentLine)); + $currentLine = ''; + $prepend = "=\r\n"; + $thisLineLength = $maxLineLength; + $lineLen = 0; + } + $lineLen += $size; + $currentLine .= $enc; + } + if (strlen($currentLine)) { + $is->write($prepend.$this->_standardize($currentLine)); + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'quoted-printable'. + * + * @return string + */ + public function getName() + { + return 'quoted-printable'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php new file mode 100644 index 0000000..8113e52 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php @@ -0,0 +1,97 @@ + + */ +class Swift_Mime_ContentEncoder_QpContentEncoderProxy implements Swift_Mime_ContentEncoder +{ + /** + * @var Swift_Mime_ContentEncoder_QpContentEncoder + */ + private $safeEncoder; + + /** + * @var Swift_Mime_ContentEncoder_NativeQpContentEncoder + */ + private $nativeEncoder; + + /** + * @var null|string + */ + private $charset; + + /** + * Constructor. + * + * @param Swift_Mime_ContentEncoder_QpContentEncoder $safeEncoder + * @param Swift_Mime_ContentEncoder_NativeQpContentEncoder $nativeEncoder + * @param string|null $charset + */ + public function __construct(Swift_Mime_ContentEncoder_QpContentEncoder $safeEncoder, Swift_Mime_ContentEncoder_NativeQpContentEncoder $nativeEncoder, $charset) + { + $this->safeEncoder = $safeEncoder; + $this->nativeEncoder = $nativeEncoder; + $this->charset = $charset; + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->safeEncoder = clone $this->safeEncoder; + $this->nativeEncoder = clone $this->nativeEncoder; + } + + /** + * {@inheritdoc} + */ + public function charsetChanged($charset) + { + $this->charset = $charset; + } + + /** + * {@inheritdoc} + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + $this->getEncoder()->encodeByteStream($os, $is, $firstLineOffset, $maxLineLength); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'quoted-printable'; + } + + /** + * {@inheritdoc} + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return $this->getEncoder()->encodeString($string, $firstLineOffset, $maxLineLength); + } + + /** + * @return Swift_Mime_ContentEncoder + */ + private function getEncoder() + { + return 'utf-8' === $this->charset ? $this->nativeEncoder : $this->safeEncoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php new file mode 100644 index 0000000..0b8526e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php @@ -0,0 +1,64 @@ + + */ +class Swift_Mime_ContentEncoder_RawContentEncoder implements Swift_Mime_ContentEncoder +{ + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset ignored + * @param int $maxLineLength ignored + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return $string; + } + + /** + * Encode stream $in to stream $out. + * + * @param Swift_OutputByteStream $in + * @param Swift_InputByteStream $out + * @param int $firstLineOffset ignored + * @param int $maxLineLength ignored + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + while (false !== ($bytes = $os->read(8192))) { + $is->write($bytes); + } + } + + /** + * Get the name of this encoding scheme. + * + * @return string + */ + public function getName() + { + return 'raw'; + } + + /** + * Not used. + */ + public function charsetChanged($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php new file mode 100644 index 0000000..6af7571 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php @@ -0,0 +1,45 @@ +setDisposition('inline'); + $this->setId($this->getId()); + } + + /** + * Get the nesting level of this EmbeddedFile. + * + * Returns {@see LEVEL_RELATED}. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_RELATED; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php new file mode 100644 index 0000000..cc44a6e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php @@ -0,0 +1,24 @@ +init(); + } + + public function __wakeup() + { + $this->init(); + } + + protected function init() + { + if (count(self::$_specials) > 0) { + return; + } + + self::$_specials = array( + '(', ')', '<', '>', '[', ']', + ':', ';', '@', ',', '.', '"', + ); + + /*** Refer to RFC 2822 for ABNF grammar ***/ + + // All basic building blocks + self::$_grammar['NO-WS-CTL'] = '[\x01-\x08\x0B\x0C\x0E-\x19\x7F]'; + self::$_grammar['WSP'] = '[ \t]'; + self::$_grammar['CRLF'] = '(?:\r\n)'; + self::$_grammar['FWS'] = '(?:(?:'.self::$_grammar['WSP'].'*'. + self::$_grammar['CRLF'].')?'.self::$_grammar['WSP'].')'; + self::$_grammar['text'] = '[\x00-\x08\x0B\x0C\x0E-\x7F]'; + self::$_grammar['quoted-pair'] = '(?:\\\\'.self::$_grammar['text'].')'; + self::$_grammar['ctext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21-\x27\x2A-\x5B\x5D-\x7E])'; + // Uses recursive PCRE (?1) -- could be a weak point?? + self::$_grammar['ccontent'] = '(?:'.self::$_grammar['ctext'].'|'. + self::$_grammar['quoted-pair'].'|(?1))'; + self::$_grammar['comment'] = '(\((?:'.self::$_grammar['FWS'].'|'. + self::$_grammar['ccontent'].')*'.self::$_grammar['FWS'].'?\))'; + self::$_grammar['CFWS'] = '(?:(?:'.self::$_grammar['FWS'].'?'. + self::$_grammar['comment'].')*(?:(?:'.self::$_grammar['FWS'].'?'. + self::$_grammar['comment'].')|'.self::$_grammar['FWS'].'))'; + self::$_grammar['qtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21\x23-\x5B\x5D-\x7E])'; + self::$_grammar['qcontent'] = '(?:'.self::$_grammar['qtext'].'|'. + self::$_grammar['quoted-pair'].')'; + self::$_grammar['quoted-string'] = '(?:'.self::$_grammar['CFWS'].'?"'. + '('.self::$_grammar['FWS'].'?'.self::$_grammar['qcontent'].')*'. + self::$_grammar['FWS'].'?"'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['atext'] = '[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]'; + self::$_grammar['atom'] = '(?:'.self::$_grammar['CFWS'].'?'. + self::$_grammar['atext'].'+'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['dot-atom-text'] = '(?:'.self::$_grammar['atext'].'+'. + '(\.'.self::$_grammar['atext'].'+)*)'; + self::$_grammar['dot-atom'] = '(?:'.self::$_grammar['CFWS'].'?'. + self::$_grammar['dot-atom-text'].'+'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['word'] = '(?:'.self::$_grammar['atom'].'|'. + self::$_grammar['quoted-string'].')'; + self::$_grammar['phrase'] = '(?:'.self::$_grammar['word'].'+?)'; + self::$_grammar['no-fold-quote'] = '(?:"(?:'.self::$_grammar['qtext']. + '|'.self::$_grammar['quoted-pair'].')*")'; + self::$_grammar['dtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21-\x5A\x5E-\x7E])'; + self::$_grammar['no-fold-literal'] = '(?:\[(?:'.self::$_grammar['dtext']. + '|'.self::$_grammar['quoted-pair'].')*\])'; + + // Message IDs + self::$_grammar['id-left'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. + self::$_grammar['no-fold-quote'].')'; + self::$_grammar['id-right'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. + self::$_grammar['no-fold-literal'].')'; + + // Addresses, mailboxes and paths + self::$_grammar['local-part'] = '(?:'.self::$_grammar['dot-atom'].'|'. + self::$_grammar['quoted-string'].')'; + self::$_grammar['dcontent'] = '(?:'.self::$_grammar['dtext'].'|'. + self::$_grammar['quoted-pair'].')'; + self::$_grammar['domain-literal'] = '(?:'.self::$_grammar['CFWS'].'?\[('. + self::$_grammar['FWS'].'?'.self::$_grammar['dcontent'].')*?'. + self::$_grammar['FWS'].'?\]'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['domain'] = '(?:'.self::$_grammar['dot-atom'].'|'. + self::$_grammar['domain-literal'].')'; + self::$_grammar['addr-spec'] = '(?:'.self::$_grammar['local-part'].'@'. + self::$_grammar['domain'].')'; + } + + /** + * Get the grammar defined for $name token. + * + * @param string $name exactly as written in the RFC + * + * @return string + */ + public function getDefinition($name) + { + if (array_key_exists($name, self::$_grammar)) { + return self::$_grammar[$name]; + } else { + throw new Swift_RfcComplianceException( + "No such grammar '".$name."' defined." + ); + } + } + + /** + * Returns the tokens defined in RFC 2822 (and some related RFCs). + * + * @return array + */ + public function getGrammarDefinitions() + { + return self::$_grammar; + } + + /** + * Returns the current special characters used in the syntax which need to be escaped. + * + * @return array + */ + public function getSpecials() + { + return self::$_specials; + } + + /** + * Escape special characters in a string (convert to quoted-pairs). + * + * @param string $token + * @param string[] $include additional chars to escape + * @param string[] $exclude chars from escaping + * + * @return string + */ + public function escapeSpecials($token, $include = array(), $exclude = array()) + { + foreach (array_merge(array('\\'), array_diff(self::$_specials, $exclude), $include) as $char) { + $token = str_replace($char, '\\'.$char, $token); + } + + return $token; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php new file mode 100644 index 0000000..a8ddd27 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php @@ -0,0 +1,93 @@ +getName(), "\r\n"); + mb_internal_encoding($old); + + return $newstring; + } + + return parent::encodeString($string, $firstLineOffset, $maxLineLength); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php new file mode 100644 index 0000000..510dd66 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php @@ -0,0 +1,65 @@ +_safeMap[$byte] = chr($byte); + } + } + + /** + * Get the name of this encoding scheme. + * + * Returns the string 'Q'. + * + * @return string + */ + public function getName() + { + return 'Q'; + } + + /** + * Takes an unencoded string and produces a QP encoded string from it. + * + * @param string $string string to encode + * @param int $firstLineOffset optional + * @param int $maxLineLength optional, 0 indicates the default of 76 chars + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return str_replace(array(' ', '=20', "=\r\n"), array('_', '_', "\r\n"), + parent::encodeString($string, $firstLineOffset, $maxLineLength) + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php new file mode 100644 index 0000000..c65f26d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php @@ -0,0 +1,78 @@ +setGrammar($grammar); + } + + /** + * Set the character set used in this Header. + * + * @param string $charset + */ + public function setCharset($charset) + { + $this->clearCachedValueIf($charset != $this->_charset); + $this->_charset = $charset; + if (isset($this->_encoder)) { + $this->_encoder->charsetChanged($charset); + } + } + + /** + * Get the character set used in this Header. + * + * @return string + */ + public function getCharset() + { + return $this->_charset; + } + + /** + * Set the language used in this Header. + * + * For example, for US English, 'en-us'. + * This can be unspecified. + * + * @param string $lang + */ + public function setLanguage($lang) + { + $this->clearCachedValueIf($this->_lang != $lang); + $this->_lang = $lang; + } + + /** + * Get the language used in this Header. + * + * @return string + */ + public function getLanguage() + { + return $this->_lang; + } + + /** + * Set the encoder used for encoding the header. + * + * @param Swift_Mime_HeaderEncoder $encoder + */ + public function setEncoder(Swift_Mime_HeaderEncoder $encoder) + { + $this->_encoder = $encoder; + $this->setCachedValue(null); + } + + /** + * Get the encoder used for encoding this Header. + * + * @return Swift_Mime_HeaderEncoder + */ + public function getEncoder() + { + return $this->_encoder; + } + + /** + * Set the grammar used for the header. + * + * @param Swift_Mime_Grammar $grammar + */ + public function setGrammar(Swift_Mime_Grammar $grammar) + { + $this->_grammar = $grammar; + $this->setCachedValue(null); + } + + /** + * Get the grammar used for this Header. + * + * @return Swift_Mime_Grammar + */ + public function getGrammar() + { + return $this->_grammar; + } + + /** + * Get the name of this header (e.g. charset). + * + * @return string + */ + public function getFieldName() + { + return $this->_name; + } + + /** + * Set the maximum length of lines in the header (excluding EOL). + * + * @param int $lineLength + */ + public function setMaxLineLength($lineLength) + { + $this->clearCachedValueIf($this->_lineLength != $lineLength); + $this->_lineLength = $lineLength; + } + + /** + * Get the maximum permitted length of lines in this Header. + * + * @return int + */ + public function getMaxLineLength() + { + return $this->_lineLength; + } + + /** + * Get this Header rendered as a RFC 2822 compliant string. + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function toString() + { + return $this->_tokensToString($this->toTokens()); + } + + /** + * Returns a string representation of this object. + * + * @return string + * + * @see toString() + */ + public function __toString() + { + return $this->toString(); + } + + // -- Points of extension + + /** + * Set the name of this Header field. + * + * @param string $name + */ + protected function setFieldName($name) + { + $this->_name = $name; + } + + /** + * Produces a compliant, formatted RFC 2822 'phrase' based on the string given. + * + * @param Swift_Mime_Header $header + * @param string $string as displayed + * @param string $charset of the text + * @param Swift_Mime_HeaderEncoder $encoder + * @param bool $shorten the first line to make remove for header name + * + * @return string + */ + protected function createPhrase(Swift_Mime_Header $header, $string, $charset, Swift_Mime_HeaderEncoder $encoder = null, $shorten = false) + { + // Treat token as exactly what was given + $phraseStr = $string; + // If it's not valid + if (!preg_match('/^'.$this->getGrammar()->getDefinition('phrase').'$/D', $phraseStr)) { + // .. but it is just ascii text, try escaping some characters + // and make it a quoted-string + if (preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $phraseStr)) { + $phraseStr = $this->getGrammar()->escapeSpecials( + $phraseStr, array('"'), $this->getGrammar()->getSpecials() + ); + $phraseStr = '"'.$phraseStr.'"'; + } else { + // ... otherwise it needs encoding + // Determine space remaining on line if first line + if ($shorten) { + $usedLength = strlen($header->getFieldName().': '); + } else { + $usedLength = 0; + } + $phraseStr = $this->encodeWords($header, $string, $usedLength); + } + } + + return $phraseStr; + } + + /** + * Encode needed word tokens within a string of input. + * + * @param Swift_Mime_Header $header + * @param string $input + * @param string $usedLength optional + * + * @return string + */ + protected function encodeWords(Swift_Mime_Header $header, $input, $usedLength = -1) + { + $value = ''; + + $tokens = $this->getEncodableWordTokens($input); + + foreach ($tokens as $token) { + // See RFC 2822, Sect 2.2 (really 2.2 ??) + if ($this->tokenNeedsEncoding($token)) { + // Don't encode starting WSP + $firstChar = substr($token, 0, 1); + switch ($firstChar) { + case ' ': + case "\t": + $value .= $firstChar; + $token = substr($token, 1); + } + + if (-1 == $usedLength) { + $usedLength = strlen($header->getFieldName().': ') + strlen($value); + } + $value .= $this->getTokenAsEncodedWord($token, $usedLength); + + $header->setMaxLineLength(76); // Forcefully override + } else { + $value .= $token; + } + } + + return $value; + } + + /** + * Test if a token needs to be encoded or not. + * + * @param string $token + * + * @return bool + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token); + } + + /** + * Splits a string into tokens in blocks of words which can be encoded quickly. + * + * @param string $string + * + * @return string[] + */ + protected function getEncodableWordTokens($string) + { + $tokens = array(); + + $encodedToken = ''; + // Split at all whitespace boundaries + foreach (preg_split('~(?=[\t ])~', $string) as $token) { + if ($this->tokenNeedsEncoding($token)) { + $encodedToken .= $token; + } else { + if (strlen($encodedToken) > 0) { + $tokens[] = $encodedToken; + $encodedToken = ''; + } + $tokens[] = $token; + } + } + if (strlen($encodedToken)) { + $tokens[] = $encodedToken; + } + + return $tokens; + } + + /** + * Get a token as an encoded word for safe insertion into headers. + * + * @param string $token token to encode + * @param int $firstLineOffset optional + * + * @return string + */ + protected function getTokenAsEncodedWord($token, $firstLineOffset = 0) + { + // Adjust $firstLineOffset to account for space needed for syntax + $charsetDecl = $this->_charset; + if (isset($this->_lang)) { + $charsetDecl .= '*'.$this->_lang; + } + $encodingWrapperLength = strlen( + '=?'.$charsetDecl.'?'.$this->_encoder->getName().'??=' + ); + + if ($firstLineOffset >= 75) { + //Does this logic need to be here? + $firstLineOffset = 0; + } + + $encodedTextLines = explode("\r\n", + $this->_encoder->encodeString( + $token, $firstLineOffset, 75 - $encodingWrapperLength, $this->_charset + ) + ); + + if (strtolower($this->_charset) !== 'iso-2022-jp') { + // special encoding for iso-2022-jp using mb_encode_mimeheader + foreach ($encodedTextLines as $lineNum => $line) { + $encodedTextLines[$lineNum] = '=?'.$charsetDecl. + '?'.$this->_encoder->getName(). + '?'.$line.'?='; + } + } + + return implode("\r\n ", $encodedTextLines); + } + + /** + * Generates tokens from the given string which include CRLF as individual tokens. + * + * @param string $token + * + * @return string[] + */ + protected function generateTokenLines($token) + { + return preg_split('~(\r\n)~', $token, -1, PREG_SPLIT_DELIM_CAPTURE); + } + + /** + * Set a value into the cache. + * + * @param string $value + */ + protected function setCachedValue($value) + { + $this->_cachedValue = $value; + } + + /** + * Get the value in the cache. + * + * @return string + */ + protected function getCachedValue() + { + return $this->_cachedValue; + } + + /** + * Clear the cached value if $condition is met. + * + * @param bool $condition + */ + protected function clearCachedValueIf($condition) + { + if ($condition) { + $this->setCachedValue(null); + } + } + + /** + * Generate a list of all tokens in the final header. + * + * @param string $string The string to tokenize + * + * @return array An array of tokens as strings + */ + protected function toTokens($string = null) + { + if (is_null($string)) { + $string = $this->getFieldBody(); + } + + $tokens = array(); + + // Generate atoms; split at all invisible boundaries followed by WSP + foreach (preg_split('~(?=[ \t])~', $string) as $token) { + $newTokens = $this->generateTokenLines($token); + foreach ($newTokens as $newToken) { + $tokens[] = $newToken; + } + } + + return $tokens; + } + + /** + * Takes an array of tokens which appear in the header and turns them into + * an RFC 2822 compliant string, adding FWSP where needed. + * + * @param string[] $tokens + * + * @return string + */ + private function _tokensToString(array $tokens) + { + $lineCount = 0; + $headerLines = array(); + $headerLines[] = $this->_name.': '; + $currentLine = &$headerLines[$lineCount++]; + + // Build all tokens back into compliant header + foreach ($tokens as $i => $token) { + // Line longer than specified maximum or token was just a new line + if (("\r\n" == $token) || + ($i > 0 && strlen($currentLine.$token) > $this->_lineLength) + && 0 < strlen($currentLine)) { + $headerLines[] = ''; + $currentLine = &$headerLines[$lineCount++]; + } + + // Append token to the line + if ("\r\n" != $token) { + $currentLine .= $token; + } + } + + // Implode with FWS (RFC 2822, 2.2.3) + return implode("\r\n", $headerLines)."\r\n"; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php new file mode 100644 index 0000000..4fd6674 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php @@ -0,0 +1,125 @@ + + * + * + * + * @param string $name of Header + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_Grammar $grammar) + { + $this->setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_DATE; + } + + /** + * Set the model for the field body. + * + * This method takes a UNIX timestamp. + * + * @param int $model + */ + public function setFieldBodyModel($model) + { + $this->setTimestamp($model); + } + + /** + * Get the model for the field body. + * + * This method returns a UNIX timestamp. + * + * @return mixed + */ + public function getFieldBodyModel() + { + return $this->getTimestamp(); + } + + /** + * Get the UNIX timestamp of the Date in this Header. + * + * @return int + */ + public function getTimestamp() + { + return $this->_timestamp; + } + + /** + * Set the UNIX timestamp of the Date in this Header. + * + * @param int $timestamp + */ + public function setTimestamp($timestamp) + { + if (!is_null($timestamp)) { + $timestamp = (int) $timestamp; + } + $this->clearCachedValueIf($this->_timestamp != $timestamp); + $this->_timestamp = $timestamp; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + if (isset($this->_timestamp)) { + $this->setCachedValue(date('r', $this->_timestamp)); + } + } + + return $this->getCachedValue(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php new file mode 100644 index 0000000..b114506 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php @@ -0,0 +1,180 @@ +setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_ID; + } + + /** + * Set the model for the field body. + * + * This method takes a string ID, or an array of IDs. + * + * @param mixed $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setId($model); + } + + /** + * Get the model for the field body. + * + * This method returns an array of IDs + * + * @return array + */ + public function getFieldBodyModel() + { + return $this->getIds(); + } + + /** + * Set the ID used in the value of this header. + * + * @param string|array $id + * + * @throws Swift_RfcComplianceException + */ + public function setId($id) + { + $this->setIds(is_array($id) ? $id : array($id)); + } + + /** + * Get the ID used in the value of this Header. + * + * If multiple IDs are set only the first is returned. + * + * @return string + */ + public function getId() + { + if (count($this->_ids) > 0) { + return $this->_ids[0]; + } + } + + /** + * Set a collection of IDs to use in the value of this Header. + * + * @param string[] $ids + * + * @throws Swift_RfcComplianceException + */ + public function setIds(array $ids) + { + $actualIds = array(); + + foreach ($ids as $id) { + $this->_assertValidId($id); + $actualIds[] = $id; + } + + $this->clearCachedValueIf($this->_ids != $actualIds); + $this->_ids = $actualIds; + } + + /** + * Get the list of IDs used in this Header. + * + * @return string[] + */ + public function getIds() + { + return $this->_ids; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@see toString()} for that). + * + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + $angleAddrs = array(); + + foreach ($this->_ids as $id) { + $angleAddrs[] = '<'.$id.'>'; + } + + $this->setCachedValue(implode(' ', $angleAddrs)); + } + + return $this->getCachedValue(); + } + + /** + * Throws an Exception if the id passed does not comply with RFC 2822. + * + * @param string $id + * + * @throws Swift_RfcComplianceException + */ + private function _assertValidId($id) + { + if (!preg_match( + '/^'.$this->getGrammar()->getDefinition('id-left').'@'. + $this->getGrammar()->getDefinition('id-right').'$/D', + $id + )) { + throw new Swift_RfcComplianceException( + 'Invalid ID given <'.$id.'>' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php new file mode 100644 index 0000000..798e7f4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php @@ -0,0 +1,354 @@ +setFieldName($name); + $this->setEncoder($encoder); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_MAILBOX; + } + + /** + * Set the model for the field body. + * + * This method takes a string, or an array of addresses. + * + * @param mixed $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setNameAddresses($model); + } + + /** + * Get the model for the field body. + * + * This method returns an associative array like {@link getNameAddresses()} + * + * @throws Swift_RfcComplianceException + * + * @return array + */ + public function getFieldBodyModel() + { + return $this->getNameAddresses(); + } + + /** + * Set a list of mailboxes to be shown in this Header. + * + * The mailboxes can be a simple array of addresses, or an array of + * key=>value pairs where (email => personalName). + * Example: + * + * setNameAddresses(array( + * 'chris@swiftmailer.org' => 'Chris Corbyn', + * 'mark@swiftmailer.org' //No associated personal name + * )); + * ?> + * + * + * @see __construct() + * @see setAddresses() + * @see setValue() + * + * @param string|string[] $mailboxes + * + * @throws Swift_RfcComplianceException + */ + public function setNameAddresses($mailboxes) + { + $this->_mailboxes = $this->normalizeMailboxes((array) $mailboxes); + $this->setCachedValue(null); //Clear any cached value + } + + /** + * Get the full mailbox list of this Header as an array of valid RFC 2822 strings. + * + * Example: + * + * 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddressStrings()); + * // array ( + * // 0 => Chris Corbyn , + * // 1 => Mark Corbyn + * // ) + * ?> + * + * + * @see getNameAddresses() + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string[] + */ + public function getNameAddressStrings() + { + return $this->_createNameAddressStrings($this->getNameAddresses()); + } + + /** + * Get all mailboxes in this Header as key=>value pairs. + * + * The key is the address and the value is the name (or null if none set). + * Example: + * + * 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddresses()); + * // array ( + * // chris@swiftmailer.org => Chris Corbyn, + * // mark@swiftmailer.org => Mark Corbyn + * // ) + * ?> + * + * + * @see getAddresses() + * @see getNameAddressStrings() + * + * @return string[] + */ + public function getNameAddresses() + { + return $this->_mailboxes; + } + + /** + * Makes this Header represent a list of plain email addresses with no names. + * + * Example: + * + * setAddresses( + * array('one@domain.tld', 'two@domain.tld', 'three@domain.tld') + * ); + * ?> + * + * + * @see setNameAddresses() + * @see setValue() + * + * @param string[] $addresses + * + * @throws Swift_RfcComplianceException + */ + public function setAddresses($addresses) + { + $this->setNameAddresses(array_values((array) $addresses)); + } + + /** + * Get all email addresses in this Header. + * + * @see getNameAddresses() + * + * @return string[] + */ + public function getAddresses() + { + return array_keys($this->_mailboxes); + } + + /** + * Remove one or more addresses from this Header. + * + * @param string|string[] $addresses + */ + public function removeAddresses($addresses) + { + $this->setCachedValue(null); + foreach ((array) $addresses as $address) { + unset($this->_mailboxes[$address]); + } + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function getFieldBody() + { + // Compute the string value of the header only if needed + if (is_null($this->getCachedValue())) { + $this->setCachedValue($this->createMailboxListString($this->_mailboxes)); + } + + return $this->getCachedValue(); + } + + // -- Points of extension + + /** + * Normalizes a user-input list of mailboxes into consistent key=>value pairs. + * + * @param string[] $mailboxes + * + * @return string[] + */ + protected function normalizeMailboxes(array $mailboxes) + { + $actualMailboxes = array(); + + foreach ($mailboxes as $key => $value) { + if (is_string($key)) { + //key is email addr + $address = $key; + $name = $value; + } else { + $address = $value; + $name = null; + } + $this->_assertValidAddress($address); + $actualMailboxes[$address] = $name; + } + + return $actualMailboxes; + } + + /** + * Produces a compliant, formatted display-name based on the string given. + * + * @param string $displayName as displayed + * @param bool $shorten the first line to make remove for header name + * + * @return string + */ + protected function createDisplayNameString($displayName, $shorten = false) + { + return $this->createPhrase($this, $displayName, + $this->getCharset(), $this->getEncoder(), $shorten + ); + } + + /** + * Creates a string form of all the mailboxes in the passed array. + * + * @param string[] $mailboxes + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + protected function createMailboxListString(array $mailboxes) + { + return implode(', ', $this->_createNameAddressStrings($mailboxes)); + } + + /** + * Redefine the encoding requirements for mailboxes. + * + * Commas and semicolons are used to separate + * multiple addresses, and should therefore be encoded + * + * @param string $token + * + * @return bool + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('/[,;]/', $token) || parent::tokenNeedsEncoding($token); + } + + /** + * Return an array of strings conforming the the name-addr spec of RFC 2822. + * + * @param string[] $mailboxes + * + * @return string[] + */ + private function _createNameAddressStrings(array $mailboxes) + { + $strings = array(); + + foreach ($mailboxes as $email => $name) { + $mailboxStr = $email; + if (!is_null($name)) { + $nameStr = $this->createDisplayNameString($name, empty($strings)); + $mailboxStr = $nameStr.' <'.$mailboxStr.'>'; + } + $strings[] = $mailboxStr; + } + + return $strings; + } + + /** + * Throws an Exception if the address passed does not comply with RFC 2822. + * + * @param string $address + * + * @throws Swift_RfcComplianceException If invalid. + */ + private function _assertValidAddress($address) + { + if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', + $address)) { + throw new Swift_RfcComplianceException( + 'Address in mailbox given ['.$address. + '] does not comply with RFC 2822, 3.6.2.' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php new file mode 100644 index 0000000..b52b964 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php @@ -0,0 +1,137 @@ + + */ +class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header +{ + /** + * The value of this Header. + * + * @var string + */ + private $_value; + + /** + * The name of this Header. + * + * @var string + */ + private $_fieldName; + + /** + * Creates a new SimpleHeader with $name. + * + * @param string $name + * @param Swift_Mime_HeaderEncoder $encoder + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name) + { + $this->_fieldName = $name; + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_TEXT; + } + + /** + * Set the model for the field body. + * + * This method takes a string for the field value. + * + * @param string $model + */ + public function setFieldBodyModel($model) + { + $this->setValue($model); + } + + /** + * Get the model for the field body. + * + * This method returns a string. + * + * @return string + */ + public function getFieldBodyModel() + { + return $this->getValue(); + } + + /** + * Get the (unencoded) value of this header. + * + * @return string + */ + public function getValue() + { + return $this->_value; + } + + /** + * Set the (unencoded) value of this header. + * + * @param string $value + */ + public function setValue($value) + { + $this->_value = $value; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() + { + return $this->_value; + } + + /** + * Get this Header rendered as a RFC 2822 compliant string. + * + * @return string + */ + public function toString() + { + return $this->_fieldName.': '.$this->_value; + } + + /** + * Set the Header FieldName. + * + * @see Swift_Mime_Header::getFieldName() + */ + public function getFieldName() + { + return $this->_fieldName; + } + + /** + * Ignored. + */ + public function setCharset($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php new file mode 100644 index 0000000..0dcf1fc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php @@ -0,0 +1,260 @@ +_paramEncoder = $paramEncoder; + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_PARAMETERIZED; + } + + /** + * Set the character set used in this Header. + * + * @param string $charset + */ + public function setCharset($charset) + { + parent::setCharset($charset); + if (isset($this->_paramEncoder)) { + $this->_paramEncoder->charsetChanged($charset); + } + } + + /** + * Set the value of $parameter. + * + * @param string $parameter + * @param string $value + */ + public function setParameter($parameter, $value) + { + $this->setParameters(array_merge($this->getParameters(), array($parameter => $value))); + } + + /** + * Get the value of $parameter. + * + * @param string $parameter + * + * @return string + */ + public function getParameter($parameter) + { + $params = $this->getParameters(); + + return array_key_exists($parameter, $params) + ? $params[$parameter] + : null; + } + + /** + * Set an associative array of parameter names mapped to values. + * + * @param string[] $parameters + */ + public function setParameters(array $parameters) + { + $this->clearCachedValueIf($this->_params != $parameters); + $this->_params = $parameters; + } + + /** + * Returns an associative array of parameter names mapped to values. + * + * @return string[] + */ + public function getParameters() + { + return $this->_params; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() //TODO: Check caching here + { + $body = parent::getFieldBody(); + foreach ($this->_params as $name => $value) { + if (!is_null($value)) { + // Add the parameter + $body .= '; '.$this->_createParameter($name, $value); + } + } + + return $body; + } + + /** + * Generate a list of all tokens in the final header. + * + * This doesn't need to be overridden in theory, but it is for implementation + * reasons to prevent potential breakage of attributes. + * + * @param string $string The string to tokenize + * + * @return array An array of tokens as strings + */ + protected function toTokens($string = null) + { + $tokens = parent::toTokens(parent::getFieldBody()); + + // Try creating any parameters + foreach ($this->_params as $name => $value) { + if (!is_null($value)) { + // Add the semi-colon separator + $tokens[count($tokens) - 1] .= ';'; + $tokens = array_merge($tokens, $this->generateTokenLines( + ' '.$this->_createParameter($name, $value) + )); + } + } + + return $tokens; + } + + /** + * Render a RFC 2047 compliant header parameter from the $name and $value. + * + * @param string $name + * @param string $value + * + * @return string + */ + private function _createParameter($name, $value) + { + $origValue = $value; + + $encoded = false; + // Allow room for parameter name, indices, "=" and DQUOTEs + $maxValueLength = $this->getMaxLineLength() - strlen($name.'=*N"";') - 1; + $firstLineOffset = 0; + + // If it's not already a valid parameter value... + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + // TODO: text, or something else?? + // ... and it's not ascii + if (!preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $value)) { + $encoded = true; + // Allow space for the indices, charset and language + $maxValueLength = $this->getMaxLineLength() - strlen($name.'*N*="";') - 1; + $firstLineOffset = strlen( + $this->getCharset()."'".$this->getLanguage()."'" + ); + } + } + + // Encode if we need to + if ($encoded || strlen($value) > $maxValueLength) { + if (isset($this->_paramEncoder)) { + $value = $this->_paramEncoder->encodeString( + $origValue, $firstLineOffset, $maxValueLength, $this->getCharset() + ); + } else { + // We have to go against RFC 2183/2231 in some areas for interoperability + $value = $this->getTokenAsEncodedWord($origValue); + $encoded = false; + } + } + + $valueLines = isset($this->_paramEncoder) ? explode("\r\n", $value) : array($value); + + // Need to add indices + if (count($valueLines) > 1) { + $paramLines = array(); + foreach ($valueLines as $i => $line) { + $paramLines[] = $name.'*'.$i. + $this->_getEndOfParameterValue($line, true, $i == 0); + } + + return implode(";\r\n ", $paramLines); + } else { + return $name.$this->_getEndOfParameterValue( + $valueLines[0], $encoded, true + ); + } + } + + /** + * Returns the parameter value from the "=" and beyond. + * + * @param string $value to append + * @param bool $encoded + * @param bool $firstLine + * + * @return string + */ + private function _getEndOfParameterValue($value, $encoded = false, $firstLine = false) + { + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + $value = '"'.$value.'"'; + } + $prepend = '='; + if ($encoded) { + $prepend = '*='; + if ($firstLine) { + $prepend = '*='.$this->getCharset()."'".$this->getLanguage(). + "'"; + } + } + + return $prepend.$value; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php new file mode 100644 index 0000000..2fffc7b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php @@ -0,0 +1,143 @@ +setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_PATH; + } + + /** + * Set the model for the field body. + * This method takes a string for an address. + * + * @param string $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setAddress($model); + } + + /** + * Get the model for the field body. + * This method returns a string email address. + * + * @return mixed + */ + public function getFieldBodyModel() + { + return $this->getAddress(); + } + + /** + * Set the Address which should appear in this Header. + * + * @param string $address + * + * @throws Swift_RfcComplianceException + */ + public function setAddress($address) + { + if (is_null($address)) { + $this->_address = null; + } elseif ('' == $address) { + $this->_address = ''; + } else { + $this->_assertValidAddress($address); + $this->_address = $address; + } + $this->setCachedValue(null); + } + + /** + * Get the address which is used in this Header (if any). + * + * Null is returned if no address is set. + * + * @return string + */ + public function getAddress() + { + return $this->_address; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + if (isset($this->_address)) { + $this->setCachedValue('<'.$this->_address.'>'); + } + } + + return $this->getCachedValue(); + } + + /** + * Throws an Exception if the address passed does not comply with RFC 2822. + * + * @param string $address + * + * @throws Swift_RfcComplianceException If address is invalid + */ + private function _assertValidAddress($address) + { + if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', + $address)) { + throw new Swift_RfcComplianceException( + 'Address set in PathHeader does not comply with addr-spec of RFC 2822.' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php new file mode 100644 index 0000000..86177f1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php @@ -0,0 +1,112 @@ +setFieldName($name); + $this->setEncoder($encoder); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_TEXT; + } + + /** + * Set the model for the field body. + * + * This method takes a string for the field value. + * + * @param string $model + */ + public function setFieldBodyModel($model) + { + $this->setValue($model); + } + + /** + * Get the model for the field body. + * + * This method returns a string. + * + * @return string + */ + public function getFieldBodyModel() + { + return $this->getValue(); + } + + /** + * Get the (unencoded) value of this header. + * + * @return string + */ + public function getValue() + { + return $this->_value; + } + + /** + * Set the (unencoded) value of this header. + * + * @param string $value + */ + public function setValue($value) + { + $this->clearCachedValueIf($this->_value != $value); + $this->_value = $value; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + $this->setCachedValue( + $this->encodeWords($this, $this->_value) + ); + } + + return $this->getCachedValue(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php new file mode 100644 index 0000000..9b36d21 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php @@ -0,0 +1,223 @@ + 'Real Name'). + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $address + * @param string $name optional + */ + public function setSender($address, $name = null); + + /** + * Get the sender address for this message. + * + * This has a higher significance than the From address. + * + * @return string + */ + public function getSender(); + + /** + * Set the From address of this message. + * + * It is permissible for multiple From addresses to be set using an array. + * + * If multiple From addresses are used, you SHOULD set the Sender address and + * according to RFC 2822, MUST set the sender address. + * + * An array can be used if display names are to be provided: i.e. + * array('email@address.com' => 'Real Name'). + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setFrom($addresses, $name = null); + + /** + * Get the From address(es) of this message. + * + * This method always returns an associative array where the keys are the + * addresses. + * + * @return string[] + */ + public function getFrom(); + + /** + * Set the Reply-To address(es). + * + * Any replies from the receiver will be sent to this address. + * + * It is permissible for multiple reply-to addresses to be set using an array. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setReplyTo($addresses, $name = null); + + /** + * Get the Reply-To addresses for this message. + * + * This method always returns an associative array where the keys provide the + * email addresses. + * + * @return string[] + */ + public function getReplyTo(); + + /** + * Set the To address(es). + * + * Recipients set in this field will receive a copy of this message. + * + * This method has the same synopsis as {@link setFrom()} and {@link setCc()}. + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setTo($addresses, $name = null); + + /** + * Get the To addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getTo(); + + /** + * Set the Cc address(es). + * + * Recipients set in this field will receive a 'carbon-copy' of this message. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setCc($addresses, $name = null); + + /** + * Get the Cc addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getCc(); + + /** + * Set the Bcc address(es). + * + * Recipients set in this field will receive a 'blind-carbon-copy' of this + * message. + * + * In other words, they will get the message, but any other recipients of the + * message will have no such knowledge of their receipt of it. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setBcc($addresses, $name = null); + + /** + * Get the Bcc addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getBcc(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php new file mode 100644 index 0000000..30f460c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php @@ -0,0 +1,117 @@ +setContentType('text/plain'); + if (!is_null($charset)) { + $this->setCharset($charset); + } + } + + /** + * Set the body of this entity, either as a string, or as an instance of + * {@link Swift_OutputByteStream}. + * + * @param mixed $body + * @param string $contentType optional + * @param string $charset optional + * + * @return Swift_Mime_MimePart + */ + public function setBody($body, $contentType = null, $charset = null) + { + if (isset($charset)) { + $this->setCharset($charset); + } + $body = $this->_convertString($body); + + parent::setBody($body, $contentType); + + return $this; + } + + /** + * Get the character set of this entity. + * + * @return string + */ + public function getCharset() + { + return $this->_getHeaderParameter('Content-Type', 'charset'); + } + + /** + * Set the character set of this entity. + * + * @param string $charset + * + * @return Swift_Mime_MimePart + */ + public function setCharset($charset) + { + $this->_setHeaderParameter('Content-Type', 'charset', $charset); + if ($charset !== $this->_userCharset) { + $this->_clearCache(); + } + $this->_userCharset = $charset; + parent::charsetChanged($charset); + + return $this; + } + + /** + * Get the format of this entity (i.e. flowed or fixed). + * + * @return string + */ + public function getFormat() + { + return $this->_getHeaderParameter('Content-Type', 'format'); + } + + /** + * Set the format of this entity (flowed or fixed). + * + * @param string $format + * + * @return Swift_Mime_MimePart + */ + public function setFormat($format) + { + $this->_setHeaderParameter('Content-Type', 'format', $format); + $this->_userFormat = $format; + + return $this; + } + + /** + * Test if delsp is being used for this entity. + * + * @return bool + */ + public function getDelSp() + { + return ($this->_getHeaderParameter('Content-Type', 'delsp') == 'yes') + ? true + : false; + } + + /** + * Turn delsp on or off for this entity. + * + * @param bool $delsp + * + * @return Swift_Mime_MimePart + */ + public function setDelSp($delsp = true) + { + $this->_setHeaderParameter('Content-Type', 'delsp', $delsp ? 'yes' : null); + $this->_userDelSp = $delsp; + + return $this; + } + + /** + * Get the nesting level of this entity. + * + * @see LEVEL_TOP, LEVEL_ALTERNATIVE, LEVEL_MIXED, LEVEL_RELATED + * + * @return int + */ + public function getNestingLevel() + { + return $this->_nestingLevel; + } + + /** + * Receive notification that the charset has changed on this document, or a + * parent document. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->setCharset($charset); + } + + /** Fix the content-type and encoding of this entity */ + protected function _fixHeaders() + { + parent::_fixHeaders(); + if (count($this->getChildren())) { + $this->_setHeaderParameter('Content-Type', 'charset', null); + $this->_setHeaderParameter('Content-Type', 'format', null); + $this->_setHeaderParameter('Content-Type', 'delsp', null); + } else { + $this->setCharset($this->_userCharset); + $this->setFormat($this->_userFormat); + $this->setDelSp($this->_userDelSp); + } + } + + /** Set the nesting level of this entity */ + protected function _setNestingLevel($level) + { + $this->_nestingLevel = $level; + } + + /** Encode charset when charset is not utf-8 */ + protected function _convertString($string) + { + $charset = strtolower($this->getCharset()); + if (!in_array($charset, array('utf-8', 'iso-8859-1', ''))) { + // mb_convert_encoding must be the first one to check, since iconv cannot convert some words. + if (function_exists('mb_convert_encoding')) { + $string = mb_convert_encoding($string, $charset, 'utf-8'); + } elseif (function_exists('iconv')) { + $string = iconv('utf-8//TRANSLIT//IGNORE', $charset, $string); + } else { + throw new Swift_SwiftException('No suitable convert encoding function (use UTF-8 as your charset or install the mbstring or iconv extension).'); + } + + return $string; + } + + return $string; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php new file mode 100644 index 0000000..e15c6ef --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php @@ -0,0 +1,34 @@ +_encoder = $encoder; + $this->_paramEncoder = $paramEncoder; + $this->_grammar = $grammar; + $this->_charset = $charset; + } + + /** + * Create a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string|null $addresses + * + * @return Swift_Mime_Header + */ + public function createMailboxHeader($name, $addresses = null) + { + $header = new Swift_Mime_Headers_MailboxHeader($name, $this->_encoder, $this->_grammar); + if (isset($addresses)) { + $header->setFieldBodyModel($addresses); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int|null $timestamp + * + * @return Swift_Mime_Header + */ + public function createDateHeader($name, $timestamp = null) + { + $header = new Swift_Mime_Headers_DateHeader($name, $this->_grammar); + if (isset($timestamp)) { + $header->setFieldBodyModel($timestamp); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + * + * @return Swift_Mime_Header + */ + public function createTextHeader($name, $value = null) + { + $header = new Swift_Mime_Headers_UnstructuredHeader($name, $this->_encoder, $this->_grammar); + if (isset($value)) { + $header->setFieldBodyModel($value); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + * + * @return Swift_Mime_ParameterizedHeader + */ + public function createParameterizedHeader($name, $value = null, + $params = array()) + { + $header = new Swift_Mime_Headers_ParameterizedHeader($name, + $this->_encoder, (strtolower($name) == 'content-disposition') + ? $this->_paramEncoder + : null, + $this->_grammar + ); + if (isset($value)) { + $header->setFieldBodyModel($value); + } + foreach ($params as $k => $v) { + $header->setParameter($k, $v); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + * + * @return Swift_Mime_Header + */ + public function createIdHeader($name, $ids = null) + { + $header = new Swift_Mime_Headers_IdentificationHeader($name, $this->_grammar); + if (isset($ids)) { + $header->setFieldBodyModel($ids); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + * + * @return Swift_Mime_Header + */ + public function createPathHeader($name, $path = null) + { + $header = new Swift_Mime_Headers_PathHeader($name, $this->_grammar); + if (isset($path)) { + $header->setFieldBodyModel($path); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charset = $charset; + $this->_encoder->charsetChanged($charset); + $this->_paramEncoder->charsetChanged($charset); + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_encoder = clone $this->_encoder; + $this->_paramEncoder = clone $this->_paramEncoder; + } + + /** Apply the charset to the Header */ + private function _setHeaderCharset(Swift_Mime_Header $header) + { + if (isset($this->_charset)) { + $header->setCharset($this->_charset); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php new file mode 100644 index 0000000..0776240 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php @@ -0,0 +1,396 @@ +_factory = $factory; + if (isset($charset)) { + $this->setCharset($charset); + } + } + + /** + * Set the charset used by these headers. + * + * @param string $charset + */ + public function setCharset($charset) + { + $this->_charset = $charset; + $this->_factory->charsetChanged($charset); + $this->_notifyHeadersOfCharset($charset); + } + + /** + * Add a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string $addresses + */ + public function addMailboxHeader($name, $addresses = null) + { + $this->_storeHeader($name, + $this->_factory->createMailboxHeader($name, $addresses)); + } + + /** + * Add a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int $timestamp + */ + public function addDateHeader($name, $timestamp = null) + { + $this->_storeHeader($name, + $this->_factory->createDateHeader($name, $timestamp)); + } + + /** + * Add a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + */ + public function addTextHeader($name, $value = null) + { + $this->_storeHeader($name, + $this->_factory->createTextHeader($name, $value)); + } + + /** + * Add a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + */ + public function addParameterizedHeader($name, $value = null, $params = array()) + { + $this->_storeHeader($name, $this->_factory->createParameterizedHeader($name, $value, $params)); + } + + /** + * Add a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + */ + public function addIdHeader($name, $ids = null) + { + $this->_storeHeader($name, $this->_factory->createIdHeader($name, $ids)); + } + + /** + * Add a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + */ + public function addPathHeader($name, $path = null) + { + $this->_storeHeader($name, $this->_factory->createPathHeader($name, $path)); + } + + /** + * Returns true if at least one header with the given $name exists. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + * + * @return bool + */ + public function has($name, $index = 0) + { + $lowerName = strtolower($name); + + return array_key_exists($lowerName, $this->_headers) && array_key_exists($index, $this->_headers[$lowerName]); + } + + /** + * Set a header in the HeaderSet. + * + * The header may be a previously fetched header via {@link get()} or it may + * be one that has been created separately. + * + * If $index is specified, the header will be inserted into the set at this + * offset. + * + * @param Swift_Mime_Header $header + * @param int $index + */ + public function set(Swift_Mime_Header $header, $index = 0) + { + $this->_storeHeader($header->getFieldName(), $header, $index); + } + + /** + * Get the header with the given $name. + * + * If multiple headers match, the actual one may be specified by $index. + * Returns NULL if none present. + * + * @param string $name + * @param int $index + * + * @return Swift_Mime_Header + */ + public function get($name, $index = 0) + { + if ($this->has($name, $index)) { + $lowerName = strtolower($name); + + return $this->_headers[$lowerName][$index]; + } + } + + /** + * Get all headers with the given $name. + * + * @param string $name + * + * @return array + */ + public function getAll($name = null) + { + if (!isset($name)) { + $headers = array(); + foreach ($this->_headers as $collection) { + $headers = array_merge($headers, $collection); + } + + return $headers; + } + + $lowerName = strtolower($name); + if (!array_key_exists($lowerName, $this->_headers)) { + return array(); + } + + return $this->_headers[$lowerName]; + } + + /** + * Return the name of all Headers. + * + * @return array + */ + public function listAll() + { + $headers = $this->_headers; + if ($this->_canSort()) { + uksort($headers, array($this, '_sortHeaders')); + } + + return array_keys($headers); + } + + /** + * Remove the header with the given $name if it's set. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + */ + public function remove($name, $index = 0) + { + $lowerName = strtolower($name); + unset($this->_headers[$lowerName][$index]); + } + + /** + * Remove all headers with the given $name. + * + * @param string $name + */ + public function removeAll($name) + { + $lowerName = strtolower($name); + unset($this->_headers[$lowerName]); + } + + /** + * Create a new instance of this HeaderSet. + * + * @return Swift_Mime_HeaderSet + */ + public function newInstance() + { + return new self($this->_factory); + } + + /** + * Define a list of Header names as an array in the correct order. + * + * These Headers will be output in the given order where present. + * + * @param array $sequence + */ + public function defineOrdering(array $sequence) + { + $this->_order = array_flip(array_map('strtolower', $sequence)); + } + + /** + * Set a list of header names which must always be displayed when set. + * + * Usually headers without a field value won't be output unless set here. + * + * @param array $names + */ + public function setAlwaysDisplayed(array $names) + { + $this->_required = array_flip(array_map('strtolower', $names)); + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->setCharset($charset); + } + + /** + * Returns a string with a representation of all headers. + * + * @return string + */ + public function toString() + { + $string = ''; + $headers = $this->_headers; + if ($this->_canSort()) { + uksort($headers, array($this, '_sortHeaders')); + } + foreach ($headers as $collection) { + foreach ($collection as $header) { + if ($this->_isDisplayed($header) || $header->getFieldBody() != '') { + $string .= $header->toString(); + } + } + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @return string + * + * @see toString() + */ + public function __toString() + { + return $this->toString(); + } + + /** Save a Header to the internal collection */ + private function _storeHeader($name, Swift_Mime_Header $header, $offset = null) + { + if (!isset($this->_headers[strtolower($name)])) { + $this->_headers[strtolower($name)] = array(); + } + if (!isset($offset)) { + $this->_headers[strtolower($name)][] = $header; + } else { + $this->_headers[strtolower($name)][$offset] = $header; + } + } + + /** Test if the headers can be sorted */ + private function _canSort() + { + return count($this->_order) > 0; + } + + /** uksort() algorithm for Header ordering */ + private function _sortHeaders($a, $b) + { + $lowerA = strtolower($a); + $lowerB = strtolower($b); + $aPos = array_key_exists($lowerA, $this->_order) + ? $this->_order[$lowerA] + : -1; + $bPos = array_key_exists($lowerB, $this->_order) + ? $this->_order[$lowerB] + : -1; + + if ($aPos == -1) { + return 1; + } elseif ($bPos == -1) { + return -1; + } + + return ($aPos < $bPos) ? -1 : 1; + } + + /** Test if the given Header is always displayed */ + private function _isDisplayed(Swift_Mime_Header $header) + { + return array_key_exists(strtolower($header->getFieldName()), $this->_required); + } + + /** Notify all Headers of the new charset */ + private function _notifyHeadersOfCharset($charset) + { + foreach ($this->_headers as $headerGroup) { + foreach ($headerGroup as $header) { + $header->setCharset($charset); + } + } + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_factory = clone $this->_factory; + foreach ($this->_headers as $groupKey => $headerGroup) { + foreach ($headerGroup as $key => $header) { + $this->_headers[$groupKey][$key] = clone $header; + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php new file mode 100644 index 0000000..72b6ad9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php @@ -0,0 +1,649 @@ +getHeaders()->defineOrdering(array( + 'Return-Path', + 'Received', + 'DKIM-Signature', + 'DomainKey-Signature', + 'Sender', + 'Message-ID', + 'Date', + 'Subject', + 'From', + 'Reply-To', + 'To', + 'Cc', + 'Bcc', + 'MIME-Version', + 'Content-Type', + 'Content-Transfer-Encoding', + )); + $this->getHeaders()->setAlwaysDisplayed(array('Date', 'Message-ID', 'From')); + $this->getHeaders()->addTextHeader('MIME-Version', '1.0'); + $this->setDate(time()); + $this->setId($this->getId()); + $this->getHeaders()->addMailboxHeader('From'); + } + + /** + * Always returns {@link LEVEL_TOP} for a message instance. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_TOP; + } + + /** + * Set the subject of this message. + * + * @param string $subject + * + * @return Swift_Mime_SimpleMessage + */ + public function setSubject($subject) + { + if (!$this->_setHeaderFieldModel('Subject', $subject)) { + $this->getHeaders()->addTextHeader('Subject', $subject); + } + + return $this; + } + + /** + * Get the subject of this message. + * + * @return string + */ + public function getSubject() + { + return $this->_getHeaderFieldModel('Subject'); + } + + /** + * Set the date at which this message was created. + * + * @param int $date + * + * @return Swift_Mime_SimpleMessage + */ + public function setDate($date) + { + if (!$this->_setHeaderFieldModel('Date', $date)) { + $this->getHeaders()->addDateHeader('Date', $date); + } + + return $this; + } + + /** + * Get the date at which this message was created. + * + * @return int + */ + public function getDate() + { + return $this->_getHeaderFieldModel('Date'); + } + + /** + * Set the return-path (the bounce address) of this message. + * + * @param string $address + * + * @return Swift_Mime_SimpleMessage + */ + public function setReturnPath($address) + { + if (!$this->_setHeaderFieldModel('Return-Path', $address)) { + $this->getHeaders()->addPathHeader('Return-Path', $address); + } + + return $this; + } + + /** + * Get the return-path (bounce address) of this message. + * + * @return string + */ + public function getReturnPath() + { + return $this->_getHeaderFieldModel('Return-Path'); + } + + /** + * Set the sender of this message. + * + * This does not override the From field, but it has a higher significance. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setSender($address, $name = null) + { + if (!is_array($address) && isset($name)) { + $address = array($address => $name); + } + + if (!$this->_setHeaderFieldModel('Sender', (array) $address)) { + $this->getHeaders()->addMailboxHeader('Sender', (array) $address); + } + + return $this; + } + + /** + * Get the sender of this message. + * + * @return string + */ + public function getSender() + { + return $this->_getHeaderFieldModel('Sender'); + } + + /** + * Add a From: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addFrom($address, $name = null) + { + $current = $this->getFrom(); + $current[$address] = $name; + + return $this->setFrom($current); + } + + /** + * Set the from address of this message. + * + * You may pass an array of addresses if this message is from multiple people. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param string|array $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setFrom($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('From', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('From', (array) $addresses); + } + + return $this; + } + + /** + * Get the from address of this message. + * + * @return mixed + */ + public function getFrom() + { + return $this->_getHeaderFieldModel('From'); + } + + /** + * Add a Reply-To: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addReplyTo($address, $name = null) + { + $current = $this->getReplyTo(); + $current[$address] = $name; + + return $this->setReplyTo($current); + } + + /** + * Set the reply-to address of this message. + * + * You may pass an array of addresses if replies will go to multiple people. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param string $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setReplyTo($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Reply-To', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Reply-To', (array) $addresses); + } + + return $this; + } + + /** + * Get the reply-to address of this message. + * + * @return string + */ + public function getReplyTo() + { + return $this->_getHeaderFieldModel('Reply-To'); + } + + /** + * Add a To: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addTo($address, $name = null) + { + $current = $this->getTo(); + $current[$address] = $name; + + return $this->setTo($current); + } + + /** + * Set the to addresses of this message. + * + * If multiple recipients will receive the message an array should be used. + * Example: array('receiver@domain.org', 'other@domain.org' => 'A name') + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setTo($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('To', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('To', (array) $addresses); + } + + return $this; + } + + /** + * Get the To addresses of this message. + * + * @return array + */ + public function getTo() + { + return $this->_getHeaderFieldModel('To'); + } + + /** + * Add a Cc: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addCc($address, $name = null) + { + $current = $this->getCc(); + $current[$address] = $name; + + return $this->setCc($current); + } + + /** + * Set the Cc addresses of this message. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setCc($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Cc', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Cc', (array) $addresses); + } + + return $this; + } + + /** + * Get the Cc address of this message. + * + * @return array + */ + public function getCc() + { + return $this->_getHeaderFieldModel('Cc'); + } + + /** + * Add a Bcc: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addBcc($address, $name = null) + { + $current = $this->getBcc(); + $current[$address] = $name; + + return $this->setBcc($current); + } + + /** + * Set the Bcc addresses of this message. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setBcc($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Bcc', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Bcc', (array) $addresses); + } + + return $this; + } + + /** + * Get the Bcc addresses of this message. + * + * @return array + */ + public function getBcc() + { + return $this->_getHeaderFieldModel('Bcc'); + } + + /** + * Set the priority of this message. + * + * The value is an integer where 1 is the highest priority and 5 is the lowest. + * + * @param int $priority + * + * @return Swift_Mime_SimpleMessage + */ + public function setPriority($priority) + { + $priorityMap = array( + 1 => 'Highest', + 2 => 'High', + 3 => 'Normal', + 4 => 'Low', + 5 => 'Lowest', + ); + $pMapKeys = array_keys($priorityMap); + if ($priority > max($pMapKeys)) { + $priority = max($pMapKeys); + } elseif ($priority < min($pMapKeys)) { + $priority = min($pMapKeys); + } + if (!$this->_setHeaderFieldModel('X-Priority', + sprintf('%d (%s)', $priority, $priorityMap[$priority]))) { + $this->getHeaders()->addTextHeader('X-Priority', + sprintf('%d (%s)', $priority, $priorityMap[$priority])); + } + + return $this; + } + + /** + * Get the priority of this message. + * + * The returned value is an integer where 1 is the highest priority and 5 + * is the lowest. + * + * @return int + */ + public function getPriority() + { + list($priority) = sscanf($this->_getHeaderFieldModel('X-Priority'), + '%[1-5]' + ); + + return isset($priority) ? $priority : 3; + } + + /** + * Ask for a delivery receipt from the recipient to be sent to $addresses. + * + * @param array $addresses + * + * @return Swift_Mime_SimpleMessage + */ + public function setReadReceiptTo($addresses) + { + if (!$this->_setHeaderFieldModel('Disposition-Notification-To', $addresses)) { + $this->getHeaders() + ->addMailboxHeader('Disposition-Notification-To', $addresses); + } + + return $this; + } + + /** + * Get the addresses to which a read-receipt will be sent. + * + * @return string + */ + public function getReadReceiptTo() + { + return $this->_getHeaderFieldModel('Disposition-Notification-To'); + } + + /** + * Attach a {@link Swift_Mime_MimeEntity} such as an Attachment or MimePart. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return Swift_Mime_SimpleMessage + */ + public function attach(Swift_Mime_MimeEntity $entity) + { + $this->setChildren(array_merge($this->getChildren(), array($entity))); + + return $this; + } + + /** + * Remove an already attached entity. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return Swift_Mime_SimpleMessage + */ + public function detach(Swift_Mime_MimeEntity $entity) + { + $newChildren = array(); + foreach ($this->getChildren() as $child) { + if ($entity !== $child) { + $newChildren[] = $child; + } + } + $this->setChildren($newChildren); + + return $this; + } + + /** + * Attach a {@link Swift_Mime_MimeEntity} and return it's CID source. + * This method should be used when embedding images or other data in a message. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return string + */ + public function embed(Swift_Mime_MimeEntity $entity) + { + $this->attach($entity); + + return 'cid:'.$entity->getId(); + } + + /** + * Get this message as a complete string. + * + * @return string + */ + public function toString() + { + if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + $string = parent::toString(); + $this->setChildren($children); + } else { + $string = parent::toString(); + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @see toString() + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Write this message to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream $is + */ + public function toByteStream(Swift_InputByteStream $is) + { + if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + parent::toByteStream($is); + $this->setChildren($children); + } else { + parent::toByteStream($is); + } + } + + /** @see Swift_Mime_SimpleMimeEntity::_getIdField() */ + protected function _getIdField() + { + return 'Message-ID'; + } + + /** Turn the body of this message into a child of itself if needed */ + protected function _becomeMimePart() + { + $part = new parent($this->getHeaders()->newInstance(), $this->getEncoder(), + $this->_getCache(), $this->_getGrammar(), $this->_userCharset + ); + $part->setContentType($this->_userContentType); + $part->setBody($this->getBody()); + $part->setFormat($this->_userFormat); + $part->setDelSp($this->_userDelSp); + $part->_setNestingLevel($this->_getTopNestingLevel()); + + return $part; + } + + /** Get the highest nesting level nested inside this message */ + private function _getTopNestingLevel() + { + $highestLevel = $this->getNestingLevel(); + foreach ($this->getChildren() as $child) { + $childLevel = $child->getNestingLevel(); + if ($highestLevel < $childLevel) { + $highestLevel = $childLevel; + } + } + + return $highestLevel; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php new file mode 100644 index 0000000..8e14ba8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php @@ -0,0 +1,867 @@ + array(self::LEVEL_TOP, self::LEVEL_MIXED), + 'multipart/alternative' => array(self::LEVEL_MIXED, self::LEVEL_ALTERNATIVE), + 'multipart/related' => array(self::LEVEL_ALTERNATIVE, self::LEVEL_RELATED), + ); + + /** A set of filter rules to define what level an entity should be nested at */ + private $_compoundLevelFilters = array(); + + /** The nesting level of this entity */ + private $_nestingLevel = self::LEVEL_ALTERNATIVE; + + /** A KeyCache instance used during encoding and streaming */ + private $_cache; + + /** Direct descendants of this entity */ + private $_immediateChildren = array(); + + /** All descendants of this entity */ + private $_children = array(); + + /** The maximum line length of the body of this entity */ + private $_maxLineLength = 78; + + /** The order in which alternative mime types should appear */ + private $_alternativePartOrder = array( + 'text/plain' => 1, + 'text/html' => 2, + 'multipart/related' => 3, + ); + + /** The CID of this entity */ + private $_id; + + /** The key used for accessing the cache */ + private $_cacheKey; + + protected $_userContentType; + + /** + * Create a new SimpleMimeEntity with $headers, $encoder and $cache. + * + * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_Mime_Grammar $grammar + */ + public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar) + { + $this->_cacheKey = md5(uniqid(getmypid().mt_rand(), true)); + $this->_cache = $cache; + $this->_headers = $headers; + $this->_grammar = $grammar; + $this->setEncoder($encoder); + $this->_headers->defineOrdering(array('Content-Type', 'Content-Transfer-Encoding')); + + // This array specifies that, when the entire MIME document contains + // $compoundLevel, then for each child within $level, if its Content-Type + // is $contentType then it should be treated as if it's level is + // $neededLevel instead. I tried to write that unambiguously! :-\ + // Data Structure: + // array ( + // $compoundLevel => array( + // $level => array( + // $contentType => $neededLevel + // ) + // ) + // ) + + $this->_compoundLevelFilters = array( + (self::LEVEL_ALTERNATIVE + self::LEVEL_RELATED) => array( + self::LEVEL_ALTERNATIVE => array( + 'text/plain' => self::LEVEL_ALTERNATIVE, + 'text/html' => self::LEVEL_RELATED, + ), + ), + ); + + $this->_id = $this->getRandomId(); + } + + /** + * Generate a new Content-ID or Message-ID for this MIME entity. + * + * @return string + */ + public function generateId() + { + $this->setId($this->getRandomId()); + + return $this->_id; + } + + /** + * Get the {@link Swift_Mime_HeaderSet} for this entity. + * + * @return Swift_Mime_HeaderSet + */ + public function getHeaders() + { + return $this->_headers; + } + + /** + * Get the nesting level of this entity. + * + * @see LEVEL_TOP, LEVEL_MIXED, LEVEL_RELATED, LEVEL_ALTERNATIVE + * + * @return int + */ + public function getNestingLevel() + { + return $this->_nestingLevel; + } + + /** + * Get the Content-type of this entity. + * + * @return string + */ + public function getContentType() + { + return $this->_getHeaderFieldModel('Content-Type'); + } + + /** + * Set the Content-type of this entity. + * + * @param string $type + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setContentType($type) + { + $this->_setContentTypeInHeaders($type); + // Keep track of the value so that if the content-type changes automatically + // due to added child entities, it can be restored if they are later removed + $this->_userContentType = $type; + + return $this; + } + + /** + * Get the CID of this entity. + * + * The CID will only be present in headers if a Content-ID header is present. + * + * @return string + */ + public function getId() + { + $tmp = (array) $this->_getHeaderFieldModel($this->_getIdField()); + + return $this->_headers->has($this->_getIdField()) ? current($tmp) : $this->_id; + } + + /** + * Set the CID of this entity. + * + * @param string $id + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setId($id) + { + if (!$this->_setHeaderFieldModel($this->_getIdField(), $id)) { + $this->_headers->addIdHeader($this->_getIdField(), $id); + } + $this->_id = $id; + + return $this; + } + + /** + * Get the description of this entity. + * + * This value comes from the Content-Description header if set. + * + * @return string + */ + public function getDescription() + { + return $this->_getHeaderFieldModel('Content-Description'); + } + + /** + * Set the description of this entity. + * + * This method sets a value in the Content-ID header. + * + * @param string $description + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setDescription($description) + { + if (!$this->_setHeaderFieldModel('Content-Description', $description)) { + $this->_headers->addTextHeader('Content-Description', $description); + } + + return $this; + } + + /** + * Get the maximum line length of the body of this entity. + * + * @return int + */ + public function getMaxLineLength() + { + return $this->_maxLineLength; + } + + /** + * Set the maximum line length of lines in this body. + * + * Though not enforced by the library, lines should not exceed 1000 chars. + * + * @param int $length + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setMaxLineLength($length) + { + $this->_maxLineLength = $length; + + return $this; + } + + /** + * Get all children added to this entity. + * + * @return Swift_Mime_MimeEntity[] + */ + public function getChildren() + { + return $this->_children; + } + + /** + * Set all children of this entity. + * + * @param Swift_Mime_MimeEntity[] $children + * @param int $compoundLevel For internal use only + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setChildren(array $children, $compoundLevel = null) + { + // TODO: Try to refactor this logic + + $compoundLevel = isset($compoundLevel) + ? $compoundLevel + : $this->_getCompoundLevel($children) + ; + + $immediateChildren = array(); + $grandchildren = array(); + $newContentType = $this->_userContentType; + + foreach ($children as $child) { + $level = $this->_getNeededChildLevel($child, $compoundLevel); + if (empty($immediateChildren)) { + //first iteration + $immediateChildren = array($child); + } else { + $nextLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + if ($nextLevel == $level) { + $immediateChildren[] = $child; + } elseif ($level < $nextLevel) { + // Re-assign immediateChildren to grandchildren + $grandchildren = array_merge($grandchildren, $immediateChildren); + // Set new children + $immediateChildren = array($child); + } else { + $grandchildren[] = $child; + } + } + } + + if (!empty($immediateChildren)) { + $lowestLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + + // Determine which composite media type is needed to accommodate the + // immediate children + foreach ($this->_compositeRanges as $mediaType => $range) { + if ($lowestLevel > $range[0] + && $lowestLevel <= $range[1]) { + $newContentType = $mediaType; + break; + } + } + + // Put any grandchildren in a subpart + if (!empty($grandchildren)) { + $subentity = $this->_createChild(); + $subentity->_setNestingLevel($lowestLevel); + $subentity->setChildren($grandchildren, $compoundLevel); + array_unshift($immediateChildren, $subentity); + } + } + + $this->_immediateChildren = $immediateChildren; + $this->_children = $children; + $this->_setContentTypeInHeaders($newContentType); + $this->_fixHeaders(); + $this->_sortChildren(); + + return $this; + } + + /** + * Get the body of this entity as a string. + * + * @return string + */ + public function getBody() + { + return ($this->_body instanceof Swift_OutputByteStream) + ? $this->_readStream($this->_body) + : $this->_body; + } + + /** + * Set the body of this entity, either as a string, or as an instance of + * {@link Swift_OutputByteStream}. + * + * @param mixed $body + * @param string $contentType optional + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setBody($body, $contentType = null) + { + if ($body !== $this->_body) { + $this->_clearCache(); + } + + $this->_body = $body; + if (isset($contentType)) { + $this->setContentType($contentType); + } + + return $this; + } + + /** + * Get the encoder used for the body of this entity. + * + * @return Swift_Mime_ContentEncoder + */ + public function getEncoder() + { + return $this->_encoder; + } + + /** + * Set the encoder used for the body of this entity. + * + * @param Swift_Mime_ContentEncoder $encoder + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setEncoder(Swift_Mime_ContentEncoder $encoder) + { + if ($encoder !== $this->_encoder) { + $this->_clearCache(); + } + + $this->_encoder = $encoder; + $this->_setEncoding($encoder->getName()); + $this->_notifyEncoderChanged($encoder); + + return $this; + } + + /** + * Get the boundary used to separate children in this entity. + * + * @return string + */ + public function getBoundary() + { + if (!isset($this->_boundary)) { + $this->_boundary = '_=_swift_v4_'.time().'_'.md5(getmypid().mt_rand().uniqid('', true)).'_=_'; + } + + return $this->_boundary; + } + + /** + * Set the boundary used to separate children in this entity. + * + * @param string $boundary + * + * @throws Swift_RfcComplianceException + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setBoundary($boundary) + { + $this->_assertValidBoundary($boundary); + $this->_boundary = $boundary; + + return $this; + } + + /** + * Receive notification that the charset of this entity, or a parent entity + * has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_notifyCharsetChanged($charset); + } + + /** + * Receive notification that the encoder of this entity or a parent entity + * has changed. + * + * @param Swift_Mime_ContentEncoder $encoder + */ + public function encoderChanged(Swift_Mime_ContentEncoder $encoder) + { + $this->_notifyEncoderChanged($encoder); + } + + /** + * Get this entire entity as a string. + * + * @return string + */ + public function toString() + { + $string = $this->_headers->toString(); + $string .= $this->_bodyToString(); + + return $string; + } + + /** + * Get this entire entity as a string. + * + * @return string + */ + protected function _bodyToString() + { + $string = ''; + + if (isset($this->_body) && empty($this->_immediateChildren)) { + if ($this->_cache->hasKey($this->_cacheKey, 'body')) { + $body = $this->_cache->getString($this->_cacheKey, 'body'); + } else { + $body = "\r\n".$this->_encoder->encodeString($this->getBody(), 0, + $this->getMaxLineLength() + ); + $this->_cache->setString($this->_cacheKey, 'body', $body, + Swift_KeyCache::MODE_WRITE + ); + } + $string .= $body; + } + + if (!empty($this->_immediateChildren)) { + foreach ($this->_immediateChildren as $child) { + $string .= "\r\n\r\n--".$this->getBoundary()."\r\n"; + $string .= $child->toString(); + } + $string .= "\r\n\r\n--".$this->getBoundary()."--\r\n"; + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @see toString() + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Write this entire entity to a {@see Swift_InputByteStream}. + * + * @param Swift_InputByteStream + */ + public function toByteStream(Swift_InputByteStream $is) + { + $is->write($this->_headers->toString()); + $is->commit(); + + $this->_bodyToByteStream($is); + } + + /** + * Write this entire entity to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream + */ + protected function _bodyToByteStream(Swift_InputByteStream $is) + { + if (empty($this->_immediateChildren)) { + if (isset($this->_body)) { + if ($this->_cache->hasKey($this->_cacheKey, 'body')) { + $this->_cache->exportToByteStream($this->_cacheKey, 'body', $is); + } else { + $cacheIs = $this->_cache->getInputByteStream($this->_cacheKey, 'body'); + if ($cacheIs) { + $is->bind($cacheIs); + } + + $is->write("\r\n"); + + if ($this->_body instanceof Swift_OutputByteStream) { + $this->_body->setReadPointer(0); + + $this->_encoder->encodeByteStream($this->_body, $is, 0, $this->getMaxLineLength()); + } else { + $is->write($this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength())); + } + + if ($cacheIs) { + $is->unbind($cacheIs); + } + } + } + } + + if (!empty($this->_immediateChildren)) { + foreach ($this->_immediateChildren as $child) { + $is->write("\r\n\r\n--".$this->getBoundary()."\r\n"); + $child->toByteStream($is); + } + $is->write("\r\n\r\n--".$this->getBoundary()."--\r\n"); + } + } + + /** + * Get the name of the header that provides the ID of this entity. + */ + protected function _getIdField() + { + return 'Content-ID'; + } + + /** + * Get the model data (usually an array or a string) for $field. + */ + protected function _getHeaderFieldModel($field) + { + if ($this->_headers->has($field)) { + return $this->_headers->get($field)->getFieldBodyModel(); + } + } + + /** + * Set the model data for $field. + */ + protected function _setHeaderFieldModel($field, $model) + { + if ($this->_headers->has($field)) { + $this->_headers->get($field)->setFieldBodyModel($model); + + return true; + } else { + return false; + } + } + + /** + * Get the parameter value of $parameter on $field header. + */ + protected function _getHeaderParameter($field, $parameter) + { + if ($this->_headers->has($field)) { + return $this->_headers->get($field)->getParameter($parameter); + } + } + + /** + * Set the parameter value of $parameter on $field header. + */ + protected function _setHeaderParameter($field, $parameter, $value) + { + if ($this->_headers->has($field)) { + $this->_headers->get($field)->setParameter($parameter, $value); + + return true; + } else { + return false; + } + } + + /** + * Re-evaluate what content type and encoding should be used on this entity. + */ + protected function _fixHeaders() + { + if (count($this->_immediateChildren)) { + $this->_setHeaderParameter('Content-Type', 'boundary', + $this->getBoundary() + ); + $this->_headers->remove('Content-Transfer-Encoding'); + } else { + $this->_setHeaderParameter('Content-Type', 'boundary', null); + $this->_setEncoding($this->_encoder->getName()); + } + } + + /** + * Get the KeyCache used in this entity. + * + * @return Swift_KeyCache + */ + protected function _getCache() + { + return $this->_cache; + } + + /** + * Get the grammar used for validation. + * + * @return Swift_Mime_Grammar + */ + protected function _getGrammar() + { + return $this->_grammar; + } + + /** + * Empty the KeyCache for this entity. + */ + protected function _clearCache() + { + $this->_cache->clearKey($this->_cacheKey, 'body'); + } + + /** + * Returns a random Content-ID or Message-ID. + * + * @return string + */ + protected function getRandomId() + { + $idLeft = md5(getmypid().'.'.time().'.'.uniqid(mt_rand(), true)); + $idRight = !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'swift.generated'; + $id = $idLeft.'@'.$idRight; + + try { + $this->_assertValidId($id); + } catch (Swift_RfcComplianceException $e) { + $id = $idLeft.'@swift.generated'; + } + + return $id; + } + + private function _readStream(Swift_OutputByteStream $os) + { + $string = ''; + while (false !== $bytes = $os->read(8192)) { + $string .= $bytes; + } + + $os->setReadPointer(0); + + return $string; + } + + private function _setEncoding($encoding) + { + if (!$this->_setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) { + $this->_headers->addTextHeader('Content-Transfer-Encoding', $encoding); + } + } + + private function _assertValidBoundary($boundary) + { + if (!preg_match( + '/^[a-z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-z0-9\'\(\)\+_\-,\.\/:=\?]$/Di', + $boundary)) { + throw new Swift_RfcComplianceException('Mime boundary set is not RFC 2046 compliant.'); + } + } + + private function _setContentTypeInHeaders($type) + { + if (!$this->_setHeaderFieldModel('Content-Type', $type)) { + $this->_headers->addParameterizedHeader('Content-Type', $type); + } + } + + private function _setNestingLevel($level) + { + $this->_nestingLevel = $level; + } + + private function _getCompoundLevel($children) + { + $level = 0; + foreach ($children as $child) { + $level |= $child->getNestingLevel(); + } + + return $level; + } + + private function _getNeededChildLevel($child, $compoundLevel) + { + $filter = array(); + foreach ($this->_compoundLevelFilters as $bitmask => $rules) { + if (($compoundLevel & $bitmask) === $bitmask) { + $filter = $rules + $filter; + } + } + + $realLevel = $child->getNestingLevel(); + $lowercaseType = strtolower($child->getContentType()); + + if (isset($filter[$realLevel]) + && isset($filter[$realLevel][$lowercaseType])) { + return $filter[$realLevel][$lowercaseType]; + } else { + return $realLevel; + } + } + + private function _createChild() + { + return new self($this->_headers->newInstance(), + $this->_encoder, $this->_cache, $this->_grammar); + } + + private function _notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder) + { + foreach ($this->_immediateChildren as $child) { + $child->encoderChanged($encoder); + } + } + + private function _notifyCharsetChanged($charset) + { + $this->_encoder->charsetChanged($charset); + $this->_headers->charsetChanged($charset); + foreach ($this->_immediateChildren as $child) { + $child->charsetChanged($charset); + } + } + + private function _sortChildren() + { + $shouldSort = false; + foreach ($this->_immediateChildren as $child) { + // NOTE: This include alternative parts moved into a related part + if ($child->getNestingLevel() == self::LEVEL_ALTERNATIVE) { + $shouldSort = true; + break; + } + } + + // Sort in order of preference, if there is one + if ($shouldSort) { + usort($this->_immediateChildren, array($this, '_childSortAlgorithm')); + } + } + + private function _childSortAlgorithm($a, $b) + { + $typePrefs = array(); + $types = array( + strtolower($a->getContentType()), + strtolower($b->getContentType()), + ); + foreach ($types as $type) { + $typePrefs[] = (array_key_exists($type, $this->_alternativePartOrder)) + ? $this->_alternativePartOrder[$type] + : (max($this->_alternativePartOrder) + 1); + } + + return ($typePrefs[0] >= $typePrefs[1]) ? 1 : -1; + } + + // -- Destructor + + /** + * Empties it's own contents from the cache. + */ + public function __destruct() + { + $this->_cache->clearAll($this->_cacheKey); + } + + /** + * Throws an Exception if the id passed does not comply with RFC 2822. + * + * @param string $id + * + * @throws Swift_RfcComplianceException + */ + private function _assertValidId($id) + { + if (!preg_match( + '/^'.$this->_grammar->getDefinition('id-left').'@'. + $this->_grammar->getDefinition('id-right').'$/D', + $id + )) { + throw new Swift_RfcComplianceException( + 'Invalid ID given <'.$id.'>' + ); + } + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_headers = clone $this->_headers; + $this->_encoder = clone $this->_encoder; + $this->_cacheKey = uniqid(); + $children = array(); + foreach ($this->_children as $pos => $child) { + $children[$pos] = clone $child; + } + $this->setChildren($children); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php new file mode 100644 index 0000000..215f8db --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php @@ -0,0 +1,59 @@ +createDependenciesFor('mime.part') + ); + + if (!isset($charset)) { + $charset = Swift_DependencyContainer::getInstance() + ->lookup('properties.charset'); + } + $this->setBody($body); + $this->setCharset($charset); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new MimePart. + * + * @param string $body + * @param string $contentType + * @param string $charset + * + * @return Swift_Mime_MimePart + */ + public static function newInstance($body = null, $contentType = null, $charset = null) + { + return new self($body, $contentType, $charset); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php new file mode 100644 index 0000000..b38e1cf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pretends messages have been sent, but just ignores them. + * + * @author Fabien Potencier + */ +class Swift_NullTransport extends Swift_Transport_NullTransport +{ + /** + * Create a new NullTransport. + */ + public function __construct() + { + call_user_func_array( + array($this, 'Swift_Transport_NullTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.null') + ); + } + + /** + * Create a new NullTransport instance. + * + * @return Swift_NullTransport + */ + public static function newInstance() + { + return new self(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php new file mode 100644 index 0000000..1f26f9b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php @@ -0,0 +1,46 @@ +setThreshold($threshold); + $this->setSleepTime($sleep); + $this->_sleeper = $sleeper; + } + + /** + * Set the number of emails to send before restarting. + * + * @param int $threshold + */ + public function setThreshold($threshold) + { + $this->_threshold = $threshold; + } + + /** + * Get the number of emails to send before restarting. + * + * @return int + */ + public function getThreshold() + { + return $this->_threshold; + } + + /** + * Set the number of seconds to sleep for during a restart. + * + * @param int $sleep time + */ + public function setSleepTime($sleep) + { + $this->_sleep = $sleep; + } + + /** + * Get the number of seconds to sleep for during a restart. + * + * @return int + */ + public function getSleepTime() + { + return $this->_sleep; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + ++$this->_counter; + if ($this->_counter >= $this->_threshold) { + $transport = $evt->getTransport(); + $transport->stop(); + if ($this->_sleep) { + $this->sleep($this->_sleep); + } + $transport->start(); + $this->_counter = 0; + } + } + + /** + * Sleep for $seconds. + * + * @param int $seconds + */ + public function sleep($seconds) + { + if (isset($this->_sleeper)) { + $this->_sleeper->sleep($seconds); + } else { + sleep($seconds); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php new file mode 100644 index 0000000..f7e18d0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php @@ -0,0 +1,164 @@ +getMessage(); + $message->toByteStream($this); + } + + /** + * Invoked immediately following a command being sent. + * + * @param Swift_Events_CommandEvent $evt + */ + public function commandSent(Swift_Events_CommandEvent $evt) + { + $command = $evt->getCommand(); + $this->_out += strlen($command); + } + + /** + * Invoked immediately following a response coming back. + * + * @param Swift_Events_ResponseEvent $evt + */ + public function responseReceived(Swift_Events_ResponseEvent $evt) + { + $response = $evt->getResponse(); + $this->_in += strlen($response); + } + + /** + * Called when a message is sent so that the outgoing counter can be increased. + * + * @param string $bytes + */ + public function write($bytes) + { + $this->_out += strlen($bytes); + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + unset($this->_mirrors[$k]); + } + } + } + + /** + * Not used. + */ + public function flushBuffers() + { + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } + + /** + * Get the total number of bytes sent to the server. + * + * @return int + */ + public function getBytesOut() + { + return $this->_out; + } + + /** + * Get the total number of bytes received from the server. + * + * @return int + */ + public function getBytesIn() + { + return $this->_in; + } + + /** + * Reset the internal counters to zero. + */ + public function reset() + { + $this->_out = 0; + $this->_in = 0; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php new file mode 100644 index 0000000..9f9f08b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php @@ -0,0 +1,31 @@ + + * $replacements = array( + * "address1@domain.tld" => array("{a}" => "b", "{c}" => "d"), + * "address2@domain.tld" => array("{a}" => "x", "{c}" => "y") + * ) + * + * + * When using an instance of {@link Swift_Plugins_Decorator_Replacements}, + * the object should return just the array of replacements for the address + * given to {@link Swift_Plugins_Decorator_Replacements::getReplacementsFor()}. + * + * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements + */ + public function __construct($replacements) + { + $this->setReplacements($replacements); + } + + /** + * Sets replacements. + * + * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements + * + * @see __construct() + */ + public function setReplacements($replacements) + { + if (!($replacements instanceof Swift_Plugins_Decorator_Replacements)) { + $this->_replacements = (array) $replacements; + } else { + $this->_replacements = $replacements; + } + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $this->_restoreMessage($message); + $to = array_keys($message->getTo()); + $address = array_shift($to); + if ($replacements = $this->getReplacementsFor($address)) { + $body = $message->getBody(); + $search = array_keys($replacements); + $replace = array_values($replacements); + $bodyReplaced = str_replace( + $search, $replace, $body + ); + if ($body != $bodyReplaced) { + $this->_originalBody = $body; + $message->setBody($bodyReplaced); + } + + foreach ($message->getHeaders()->getAll() as $header) { + $body = $header->getFieldBodyModel(); + $count = 0; + if (is_array($body)) { + $bodyReplaced = array(); + foreach ($body as $key => $value) { + $count1 = 0; + $count2 = 0; + $key = is_string($key) ? str_replace($search, $replace, $key, $count1) : $key; + $value = is_string($value) ? str_replace($search, $replace, $value, $count2) : $value; + $bodyReplaced[$key] = $value; + + if (!$count && ($count1 || $count2)) { + $count = 1; + } + } + } else { + $bodyReplaced = str_replace($search, $replace, $body, $count); + } + + if ($count) { + $this->_originalHeaders[$header->getFieldName()] = $body; + $header->setFieldBodyModel($bodyReplaced); + } + } + + $children = (array) $message->getChildren(); + foreach ($children as $child) { + list($type) = sscanf($child->getContentType(), '%[^/]/%s'); + if ('text' == $type) { + $body = $child->getBody(); + $bodyReplaced = str_replace( + $search, $replace, $body + ); + if ($body != $bodyReplaced) { + $child->setBody($bodyReplaced); + $this->_originalChildBodies[$child->getId()] = $body; + } + } + } + $this->_lastMessage = $message; + } + } + + /** + * Find a map of replacements for the address. + * + * If this plugin was provided with a delegate instance of + * {@link Swift_Plugins_Decorator_Replacements} then the call will be + * delegated to it. Otherwise, it will attempt to find the replacements + * from the array provided in the constructor. + * + * If no replacements can be found, an empty value (NULL) is returned. + * + * @param string $address + * + * @return array + */ + public function getReplacementsFor($address) + { + if ($this->_replacements instanceof Swift_Plugins_Decorator_Replacements) { + return $this->_replacements->getReplacementsFor($address); + } else { + return isset($this->_replacements[$address]) + ? $this->_replacements[$address] + : null + ; + } + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $this->_restoreMessage($evt->getMessage()); + } + + /** Restore a changed message back to its original state */ + private function _restoreMessage(Swift_Mime_Message $message) + { + if ($this->_lastMessage === $message) { + if (isset($this->_originalBody)) { + $message->setBody($this->_originalBody); + $this->_originalBody = null; + } + if (!empty($this->_originalHeaders)) { + foreach ($message->getHeaders()->getAll() as $header) { + if (array_key_exists($header->getFieldName(), $this->_originalHeaders)) { + $header->setFieldBodyModel($this->_originalHeaders[$header->getFieldName()]); + } + } + $this->_originalHeaders = array(); + } + if (!empty($this->_originalChildBodies)) { + $children = (array) $message->getChildren(); + foreach ($children as $child) { + $id = $child->getId(); + if (array_key_exists($id, $this->_originalChildBodies)) { + $child->setBody($this->_originalChildBodies[$id]); + } + } + $this->_originalChildBodies = array(); + } + $this->_lastMessage = null; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php new file mode 100644 index 0000000..7552b67 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php @@ -0,0 +1,69 @@ +_sender = $sender; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $headers = $message->getHeaders(); + + // save current recipients + $headers->addPathHeader('X-Swift-Return-Path', $message->getReturnPath()); + + // replace them with the one to send to + $message->setReturnPath($this->_sender); + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + + // restore original headers + $headers = $message->getHeaders(); + + if ($headers->has('X-Swift-Return-Path')) { + $message->setReturnPath($headers->get('X-Swift-Return-Path')->getAddress()); + $headers->removeAll('X-Swift-Return-Path'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php new file mode 100644 index 0000000..d9bce89 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php @@ -0,0 +1,36 @@ +_logger = $logger; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + $this->_logger->add($entry); + } + + /** + * Clear the log contents. + */ + public function clear() + { + $this->_logger->clear(); + } + + /** + * Get this log as a string. + * + * @return string + */ + public function dump() + { + return $this->_logger->dump(); + } + + /** + * Invoked immediately following a command being sent. + * + * @param Swift_Events_CommandEvent $evt + */ + public function commandSent(Swift_Events_CommandEvent $evt) + { + $command = $evt->getCommand(); + $this->_logger->add(sprintf('>> %s', $command)); + } + + /** + * Invoked immediately following a response coming back. + * + * @param Swift_Events_ResponseEvent $evt + */ + public function responseReceived(Swift_Events_ResponseEvent $evt) + { + $response = $evt->getResponse(); + $this->_logger->add(sprintf('<< %s', $response)); + } + + /** + * Invoked just before a Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ Starting %s', $transportName)); + } + + /** + * Invoked immediately after the Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStarted(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ %s started', $transportName)); + } + + /** + * Invoked just before a Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ Stopping %s', $transportName)); + } + + /** + * Invoked immediately after the Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStopped(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ %s stopped', $transportName)); + } + + /** + * Invoked as a TransportException is thrown in the Transport system. + * + * @param Swift_Events_TransportExceptionEvent $evt + */ + public function exceptionThrown(Swift_Events_TransportExceptionEvent $evt) + { + $e = $evt->getException(); + $message = $e->getMessage(); + $code = $e->getCode(); + $this->_logger->add(sprintf('!! %s (code: %s)', $message, $code)); + $message .= PHP_EOL; + $message .= 'Log data:'.PHP_EOL; + $message .= $this->_logger->dump(); + $evt->cancelBubble(); + throw new Swift_TransportException($message, $code, $e->getPrevious()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php new file mode 100644 index 0000000..865bb0a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php @@ -0,0 +1,72 @@ +_size = $size; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + $this->_log[] = $entry; + while (count($this->_log) > $this->_size) { + array_shift($this->_log); + } + } + + /** + * Clear the log contents. + */ + public function clear() + { + $this->_log = array(); + } + + /** + * Get this log as a string. + * + * @return string + */ + public function dump() + { + return implode(PHP_EOL, $this->_log); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php new file mode 100644 index 0000000..3583297 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php @@ -0,0 +1,58 @@ +_isHtml = $isHtml; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + if ($this->_isHtml) { + printf('%s%s%s', htmlspecialchars($entry, ENT_QUOTES), '
', PHP_EOL); + } else { + printf('%s%s', $entry, PHP_EOL); + } + } + + /** + * Not implemented. + */ + public function clear() + { + } + + /** + * Not implemented. + */ + public function dump() + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php new file mode 100644 index 0000000..e622cb3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php @@ -0,0 +1,74 @@ +messages = array(); + } + + /** + * Get the message list. + * + * @return array + */ + public function getMessages() + { + return $this->messages; + } + + /** + * Get the message count. + * + * @return int count + */ + public function countMessages() + { + return count($this->messages); + } + + /** + * Empty the message list. + */ + public function clear() + { + $this->messages = array(); + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $this->messages[] = clone $evt->getMessage(); + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php new file mode 100644 index 0000000..fb99e4c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php @@ -0,0 +1,31 @@ +_host = $host; + $this->_port = $port; + $this->_crypto = $crypto; + } + + /** + * Create a new PopBeforeSmtpPlugin for $host and $port. + * + * @param string $host + * @param int $port + * @param string $crypto as "tls" or "ssl" + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public static function newInstance($host, $port = 110, $crypto = null) + { + return new self($host, $port, $crypto); + } + + /** + * Set a Pop3Connection to delegate to instead of connecting directly. + * + * @param Swift_Plugins_Pop_Pop3Connection $connection + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setConnection(Swift_Plugins_Pop_Pop3Connection $connection) + { + $this->_connection = $connection; + + return $this; + } + + /** + * Bind this plugin to a specific SMTP transport instance. + * + * @param Swift_Transport + */ + public function bindSmtp(Swift_Transport $smtp) + { + $this->_transport = $smtp; + } + + /** + * Set the connection timeout in seconds (default 10). + * + * @param int $timeout + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setTimeout($timeout) + { + $this->_timeout = (int) $timeout; + + return $this; + } + + /** + * Set the username to use when connecting (if needed). + * + * @param string $username + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setUsername($username) + { + $this->_username = $username; + + return $this; + } + + /** + * Set the password to use when connecting (if needed). + * + * @param string $password + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setPassword($password) + { + $this->_password = $password; + + return $this; + } + + /** + * Connect to the POP3 host and authenticate. + * + * @throws Swift_Plugins_Pop_Pop3Exception if connection fails + */ + public function connect() + { + if (isset($this->_connection)) { + $this->_connection->connect(); + } else { + if (!isset($this->_socket)) { + if (!$socket = fsockopen( + $this->_getHostString(), $this->_port, $errno, $errstr, $this->_timeout)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to connect to POP3 host [%s]: %s', $this->_host, $errstr) + ); + } + $this->_socket = $socket; + + if (false === $greeting = fgets($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to connect to POP3 host [%s]', trim($greeting)) + ); + } + + $this->_assertOk($greeting); + + if ($this->_username) { + $this->_command(sprintf("USER %s\r\n", $this->_username)); + $this->_command(sprintf("PASS %s\r\n", $this->_password)); + } + } + } + } + + /** + * Disconnect from the POP3 host. + */ + public function disconnect() + { + if (isset($this->_connection)) { + $this->_connection->disconnect(); + } else { + $this->_command("QUIT\r\n"); + if (!fclose($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('POP3 host [%s] connection could not be stopped', $this->_host) + ); + } + $this->_socket = null; + } + } + + /** + * Invoked just before a Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) + { + if (isset($this->_transport)) { + if ($this->_transport !== $evt->getTransport()) { + return; + } + } + + $this->connect(); + $this->disconnect(); + } + + /** + * Not used. + */ + public function transportStarted(Swift_Events_TransportChangeEvent $evt) + { + } + + /** + * Not used. + */ + public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt) + { + } + + /** + * Not used. + */ + public function transportStopped(Swift_Events_TransportChangeEvent $evt) + { + } + + private function _command($command) + { + if (!fwrite($this->_socket, $command)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to write command [%s] to POP3 host', trim($command)) + ); + } + + if (false === $response = fgets($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to read from POP3 host after command [%s]', trim($command)) + ); + } + + $this->_assertOk($response); + + return $response; + } + + private function _assertOk($response) + { + if (substr($response, 0, 3) != '+OK') { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('POP3 command failed [%s]', trim($response)) + ); + } + } + + private function _getHostString() + { + $host = $this->_host; + switch (strtolower($this->_crypto)) { + case 'ssl': + $host = 'ssl://'.$host; + break; + + case 'tls': + $host = 'tls://'.$host; + break; + } + + return $host; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php new file mode 100644 index 0000000..c3a1f86 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php @@ -0,0 +1,213 @@ +_recipient = $recipient; + $this->_whitelist = $whitelist; + } + + /** + * Set the recipient of all messages. + * + * @param mixed $recipient + */ + public function setRecipient($recipient) + { + $this->_recipient = $recipient; + } + + /** + * Get the recipient of all messages. + * + * @return mixed + */ + public function getRecipient() + { + return $this->_recipient; + } + + /** + * Set a list of regular expressions to whitelist certain recipients. + * + * @param array $whitelist + */ + public function setWhitelist(array $whitelist) + { + $this->_whitelist = $whitelist; + } + + /** + * Get the whitelist. + * + * @return array + */ + public function getWhitelist() + { + return $this->_whitelist; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $headers = $message->getHeaders(); + + // conditionally save current recipients + + if ($headers->has('to')) { + $headers->addMailboxHeader('X-Swift-To', $message->getTo()); + } + + if ($headers->has('cc')) { + $headers->addMailboxHeader('X-Swift-Cc', $message->getCc()); + } + + if ($headers->has('bcc')) { + $headers->addMailboxHeader('X-Swift-Bcc', $message->getBcc()); + } + + // Filter remaining headers against whitelist + $this->_filterHeaderSet($headers, 'To'); + $this->_filterHeaderSet($headers, 'Cc'); + $this->_filterHeaderSet($headers, 'Bcc'); + + // Add each hard coded recipient + $to = $message->getTo(); + if (null === $to) { + $to = array(); + } + + foreach ((array) $this->_recipient as $recipient) { + if (!array_key_exists($recipient, $to)) { + $message->addTo($recipient); + } + } + } + + /** + * Filter header set against a whitelist of regular expressions. + * + * @param Swift_Mime_HeaderSet $headerSet + * @param string $type + */ + private function _filterHeaderSet(Swift_Mime_HeaderSet $headerSet, $type) + { + foreach ($headerSet->getAll($type) as $headers) { + $headers->setNameAddresses($this->_filterNameAddresses($headers->getNameAddresses())); + } + } + + /** + * Filtered list of addresses => name pairs. + * + * @param array $recipients + * + * @return array + */ + private function _filterNameAddresses(array $recipients) + { + $filtered = array(); + + foreach ($recipients as $address => $name) { + if ($this->_isWhitelisted($address)) { + $filtered[$address] = $name; + } + } + + return $filtered; + } + + /** + * Matches address against whitelist of regular expressions. + * + * @param $recipient + * + * @return bool + */ + protected function _isWhitelisted($recipient) + { + if (in_array($recipient, (array) $this->_recipient)) { + return true; + } + + foreach ($this->_whitelist as $pattern) { + if (preg_match($pattern, $recipient)) { + return true; + } + } + + return false; + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $this->_restoreMessage($evt->getMessage()); + } + + private function _restoreMessage(Swift_Mime_Message $message) + { + // restore original headers + $headers = $message->getHeaders(); + + if ($headers->has('X-Swift-To')) { + $message->setTo($headers->get('X-Swift-To')->getNameAddresses()); + $headers->removeAll('X-Swift-To'); + } else { + $message->setTo(null); + } + + if ($headers->has('X-Swift-Cc')) { + $message->setCc($headers->get('X-Swift-Cc')->getNameAddresses()); + $headers->removeAll('X-Swift-Cc'); + } + + if ($headers->has('X-Swift-Bcc')) { + $message->setBcc($headers->get('X-Swift-Bcc')->getNameAddresses()); + $headers->removeAll('X-Swift-Bcc'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php new file mode 100644 index 0000000..0f21b7d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php @@ -0,0 +1,32 @@ +_reporter = $reporter; + } + + /** + * Not used. + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $failures = array_flip($evt->getFailedRecipients()); + foreach ((array) $message->getTo() as $address => $null) { + $this->_reporter->notify( + $message, $address, (array_key_exists($address, $failures) + ? Swift_Plugins_Reporter::RESULT_FAIL + : Swift_Plugins_Reporter::RESULT_PASS) + ); + } + foreach ((array) $message->getCc() as $address => $null) { + $this->_reporter->notify( + $message, $address, (array_key_exists($address, $failures) + ? Swift_Plugins_Reporter::RESULT_FAIL + : Swift_Plugins_Reporter::RESULT_PASS) + ); + } + foreach ((array) $message->getBcc() as $address => $null) { + $this->_reporter->notify( + $message, $address, (array_key_exists($address, $failures) + ? Swift_Plugins_Reporter::RESULT_FAIL + : Swift_Plugins_Reporter::RESULT_PASS) + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php new file mode 100644 index 0000000..cad9d16 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php @@ -0,0 +1,59 @@ +_failures_cache[$address])) { + $this->_failures[] = $address; + $this->_failures_cache[$address] = true; + } + } + + /** + * Get an array of addresses for which delivery failed. + * + * @return array + */ + public function getFailedRecipients() + { + return $this->_failures; + } + + /** + * Clear the buffer (empty the list). + */ + public function clear() + { + $this->_failures = $this->_failures_cache = array(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php new file mode 100644 index 0000000..c625935 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php @@ -0,0 +1,39 @@ +'.PHP_EOL; + echo 'PASS '.$address.PHP_EOL; + echo ''.PHP_EOL; + flush(); + } else { + echo '
'.PHP_EOL; + echo 'FAIL '.$address.PHP_EOL; + echo '
'.PHP_EOL; + flush(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php new file mode 100644 index 0000000..595c0f6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php @@ -0,0 +1,24 @@ +_rate = $rate; + $this->_mode = $mode; + $this->_sleeper = $sleeper; + $this->_timer = $timer; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $time = $this->getTimestamp(); + if (!isset($this->_start)) { + $this->_start = $time; + } + $duration = $time - $this->_start; + + switch ($this->_mode) { + case self::BYTES_PER_MINUTE : + $sleep = $this->_throttleBytesPerMinute($duration); + break; + case self::MESSAGES_PER_SECOND : + $sleep = $this->_throttleMessagesPerSecond($duration); + break; + case self::MESSAGES_PER_MINUTE : + $sleep = $this->_throttleMessagesPerMinute($duration); + break; + default : + $sleep = 0; + break; + } + + if ($sleep > 0) { + $this->sleep($sleep); + } + } + + /** + * Invoked when a Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + parent::sendPerformed($evt); + ++$this->_messages; + } + + /** + * Sleep for $seconds. + * + * @param int $seconds + */ + public function sleep($seconds) + { + if (isset($this->_sleeper)) { + $this->_sleeper->sleep($seconds); + } else { + sleep($seconds); + } + } + + /** + * Get the current UNIX timestamp. + * + * @return int + */ + public function getTimestamp() + { + if (isset($this->_timer)) { + return $this->_timer->getTimestamp(); + } else { + return time(); + } + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleBytesPerMinute($timePassed) + { + $expectedDuration = $this->getBytesOut() / ($this->_rate / 60); + + return (int) ceil($expectedDuration - $timePassed); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleMessagesPerSecond($timePassed) + { + $expectedDuration = $this->_messages / ($this->_rate); + + return (int) ceil($expectedDuration - $timePassed); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleMessagesPerMinute($timePassed) + { + $expectedDuration = $this->_messages / ($this->_rate / 60); + + return (int) ceil($expectedDuration - $timePassed); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php new file mode 100644 index 0000000..9c8deb3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php @@ -0,0 +1,24 @@ +register('properties.charset')->asValue($charset); + + return $this; + } + + /** + * Set the directory where temporary files can be saved. + * + * @param string $dir + * + * @return Swift_Preferences + */ + public function setTempDir($dir) + { + Swift_DependencyContainer::getInstance() + ->register('tempdir')->asValue($dir); + + return $this; + } + + /** + * Set the type of cache to use (i.e. "disk" or "array"). + * + * @param string $type + * + * @return Swift_Preferences + */ + public function setCacheType($type) + { + Swift_DependencyContainer::getInstance() + ->register('cache')->asAliasOf(sprintf('cache.%s', $type)); + + return $this; + } + + /** + * Set the QuotedPrintable dot escaper preference. + * + * @param bool $dotEscape + * + * @return Swift_Preferences + */ + public function setQPDotEscape($dotEscape) + { + $dotEscape = !empty($dotEscape); + Swift_DependencyContainer::getInstance() + ->register('mime.qpcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoder') + ->withDependencies(array('mime.charstream', 'mime.bytecanonicalizer')) + ->addConstructorValue($dotEscape); + + return $this; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php new file mode 100644 index 0000000..2897474 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php @@ -0,0 +1,27 @@ +createDependenciesFor('transport.sendmail') + ); + + $this->setCommand($command); + } + + /** + * Create a new SendmailTransport instance. + * + * @param string $command + * + * @return Swift_SendmailTransport + */ + public static function newInstance($command = '/usr/sbin/sendmail -bs') + { + return new self($command); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php new file mode 100644 index 0000000..2e7a872 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php @@ -0,0 +1,23 @@ + + * + * @deprecated + */ +class Swift_SignedMessage extends Swift_Message +{ +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php new file mode 100644 index 0000000..2d8176d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php @@ -0,0 +1,20 @@ + + */ +interface Swift_Signer +{ + public function reset(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php new file mode 100644 index 0000000..9ffcef3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php @@ -0,0 +1,33 @@ + + */ +interface Swift_Signers_BodySigner extends Swift_Signer +{ + /** + * Change the Swift_Signed_Message to apply the singing. + * + * @param Swift_Message $message + * + * @return Swift_Signers_BodySigner + */ + public function signMessage(Swift_Message $message); + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php new file mode 100644 index 0000000..514b61a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php @@ -0,0 +1,702 @@ + + */ +class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner +{ + /** + * PrivateKey. + * + * @var string + */ + protected $_privateKey; + + /** + * DomainName. + * + * @var string + */ + protected $_domainName; + + /** + * Selector. + * + * @var string + */ + protected $_selector; + + /** + * Hash algorithm used. + * + * @var string + */ + protected $_hashAlgorithm = 'rsa-sha1'; + + /** + * Body canon method. + * + * @var string + */ + protected $_bodyCanon = 'simple'; + + /** + * Header canon method. + * + * @var string + */ + protected $_headerCanon = 'simple'; + + /** + * Headers not being signed. + * + * @var array + */ + protected $_ignoredHeaders = array(); + + /** + * Signer identity. + * + * @var unknown_type + */ + protected $_signerIdentity; + + /** + * BodyLength. + * + * @var int + */ + protected $_bodyLen = 0; + + /** + * Maximum signedLen. + * + * @var int + */ + protected $_maxLen = PHP_INT_MAX; + + /** + * Embbed bodyLen in signature. + * + * @var bool + */ + protected $_showLen = false; + + /** + * When the signature has been applied (true means time()), false means not embedded. + * + * @var mixed + */ + protected $_signatureTimestamp = true; + + /** + * When will the signature expires false means not embedded, if sigTimestamp is auto + * Expiration is relative, otherwhise it's absolute. + * + * @var int + */ + protected $_signatureExpiration = false; + + /** + * Must we embed signed headers? + * + * @var bool + */ + protected $_debugHeaders = false; + + // work variables + /** + * Headers used to generate hash. + * + * @var array + */ + protected $_signedHeaders = array(); + + /** + * If debugHeaders is set store debugDatas here. + * + * @var string + */ + private $_debugHeadersData = ''; + + /** + * Stores the bodyHash. + * + * @var string + */ + private $_bodyHash = ''; + + /** + * Stores the signature header. + * + * @var Swift_Mime_Headers_ParameterizedHeader + */ + protected $_dkimHeader; + + /** + * Hash Handler. + * + * @var hash_ressource + */ + private $_headerHashHandler; + + private $_bodyHashHandler; + + private $_headerHash; + + private $_headerCanonData = ''; + + private $_bodyCanonEmptyCounter = 0; + + private $_bodyCanonIgnoreStart = 2; + + private $_bodyCanonSpace = false; + + private $_bodyCanonLastChar = null; + + private $_bodyCanonLine = ''; + + private $_bound = array(); + + /** + * Constructor. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + */ + public function __construct($privateKey, $domainName, $selector) + { + $this->_privateKey = $privateKey; + $this->_domainName = $domainName; + $this->_signerIdentity = '@'.$domainName; + $this->_selector = $selector; + } + + /** + * Instanciate DKIMSigner. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + * + * @return Swift_Signers_DKIMSigner + */ + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + /** + * Reset the Signer. + * + * @see Swift_Signer::reset() + */ + public function reset() + { + $this->_headerHash = null; + $this->_signedHeaders = array(); + $this->_headerHashHandler = null; + $this->_bodyHash = null; + $this->_bodyHashHandler = null; + $this->_bodyCanonIgnoreStart = 2; + $this->_bodyCanonEmptyCounter = 0; + $this->_bodyCanonLastChar = null; + $this->_bodyCanonSpace = false; + } + + /** + * Writes $bytes to the end of the stream. + * + * Writing may not happen immediately if the stream chooses to buffer. If + * you want to write these bytes with immediate effect, call {@link commit()} + * after calling write(). + * + * This method returns the sequence ID of the write (i.e. 1 for first, 2 for + * second, etc etc). + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + */ + public function write($bytes) + { + $this->_canonicalizeBody($bytes); + foreach ($this->_bound as $is) { + $is->write($bytes); + } + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit() + { + // Nothing to do + return; + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + $this->_bound[] = $is; + + return; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + foreach ($this->_bound as $k => $stream) { + if ($stream === $is) { + unset($this->_bound[$k]); + + return; + } + } + + return; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + */ + public function flushBuffers() + { + $this->reset(); + } + + /** + * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256. + * + * @param string $hash + * + * @return Swift_Signers_DKIMSigner + */ + public function setHashAlgorithm($hash) + { + // Unable to sign with rsa-sha256 + if ($hash == 'rsa-sha1') { + $this->_hashAlgorithm = 'rsa-sha1'; + } else { + $this->_hashAlgorithm = 'rsa-sha256'; + } + + return $this; + } + + /** + * Set the body canonicalization algorithm. + * + * @param string $canon + * + * @return Swift_Signers_DKIMSigner + */ + public function setBodyCanon($canon) + { + if ($canon == 'relaxed') { + $this->_bodyCanon = 'relaxed'; + } else { + $this->_bodyCanon = 'simple'; + } + + return $this; + } + + /** + * Set the header canonicalization algorithm. + * + * @param string $canon + * + * @return Swift_Signers_DKIMSigner + */ + public function setHeaderCanon($canon) + { + if ($canon == 'relaxed') { + $this->_headerCanon = 'relaxed'; + } else { + $this->_headerCanon = 'simple'; + } + + return $this; + } + + /** + * Set the signer identity. + * + * @param string $identity + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignerIdentity($identity) + { + $this->_signerIdentity = $identity; + + return $this; + } + + /** + * Set the length of the body to sign. + * + * @param mixed $len (bool or int) + * + * @return Swift_Signers_DKIMSigner + */ + public function setBodySignedLen($len) + { + if ($len === true) { + $this->_showLen = true; + $this->_maxLen = PHP_INT_MAX; + } elseif ($len === false) { + $this->showLen = false; + $this->_maxLen = PHP_INT_MAX; + } else { + $this->_showLen = true; + $this->_maxLen = (int) $len; + } + + return $this; + } + + /** + * Set the signature timestamp. + * + * @param timestamp $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureTimestamp($time) + { + $this->_signatureTimestamp = $time; + + return $this; + } + + /** + * Set the signature expiration timestamp. + * + * @param timestamp $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureExpiration($time) + { + $this->_signatureExpiration = $time; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DKIMSigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + /** + * Start Body. + */ + public function startBody() + { + // Init + switch ($this->_hashAlgorithm) { + case 'rsa-sha256' : + $this->_bodyHashHandler = hash_init('sha256'); + break; + case 'rsa-sha1' : + $this->_bodyHashHandler = hash_init('sha1'); + break; + } + $this->_bodyCanonLine = ''; + } + + /** + * End Body. + */ + public function endBody() + { + $this->_endOfBody(); + } + + /** + * Returns the list of Headers Tampered by this plugin. + * + * @return array + */ + public function getAlteredHeaders() + { + if ($this->_debugHeaders) { + return array('DKIM-Signature', 'X-DebugHash'); + } else { + return array('DKIM-Signature'); + } + } + + /** + * Adds an ignored Header. + * + * @param string $header_name + * + * @return Swift_Signers_DKIMSigner + */ + public function ignoreHeader($header_name) + { + $this->_ignoredHeaders[strtolower($header_name)] = true; + + return $this; + } + + /** + * Set the headers to sign. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DKIMSigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $this->_headerCanonData = ''; + // Loop through Headers + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if ($headers->has($hName)) { + $tmp = $headers->getAll($hName); + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $this->_addHeader($header->toString()); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + + return $this; + } + + /** + * Add the signature to the given Headers. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DKIMSigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers) + { + // Prepare the DKIM-Signature + $params = array('v' => '1', 'a' => $this->_hashAlgorithm, 'bh' => base64_encode($this->_bodyHash), 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'i' => $this->_signerIdentity, 's' => $this->_selector); + if ($this->_bodyCanon != 'simple') { + $params['c'] = $this->_headerCanon.'/'.$this->_bodyCanon; + } elseif ($this->_headerCanon != 'simple') { + $params['c'] = $this->_headerCanon; + } + if ($this->_showLen) { + $params['l'] = $this->_bodyLen; + } + if ($this->_signatureTimestamp === true) { + $params['t'] = time(); + if ($this->_signatureExpiration !== false) { + $params['x'] = $params['t'] + $this->_signatureExpiration; + } + } else { + if ($this->_signatureTimestamp !== false) { + $params['t'] = $this->_signatureTimestamp; + } + if ($this->_signatureExpiration !== false) { + $params['x'] = $this->_signatureExpiration; + } + } + if ($this->_debugHeaders) { + $params['z'] = implode('|', $this->_debugHeadersData); + } + $string = ''; + foreach ($params as $k => $v) { + $string .= $k.'='.$v.'; '; + } + $string = trim($string); + $headers->addTextHeader('DKIM-Signature', $string); + // Add the last DKIM-Signature + $tmp = $headers->getAll('DKIM-Signature'); + $this->_dkimHeader = end($tmp); + $this->_addHeader(trim($this->_dkimHeader->toString())."\r\n b=", true); + $this->_endOfHeaders(); + if ($this->_debugHeaders) { + $headers->addTextHeader('X-DebugHash', base64_encode($this->_headerHash)); + } + $this->_dkimHeader->setValue($string.' b='.trim(chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '))); + + return $this; + } + + /* Private helpers */ + + protected function _addHeader($header, $is_sig = false) + { + switch ($this->_headerCanon) { + case 'relaxed' : + // Prepare Header and cascade + $exploded = explode(':', $header, 2); + $name = strtolower(trim($exploded[0])); + $value = str_replace("\r\n", '', $exploded[1]); + $value = preg_replace("/[ \t][ \t]+/", ' ', $value); + $header = $name.':'.trim($value).($is_sig ? '' : "\r\n"); + case 'simple' : + // Nothing to do + } + $this->_addToHeaderHash($header); + } + + protected function _endOfHeaders() + { + //$this->_headerHash=hash_final($this->_headerHashHandler, true); + } + + protected function _canonicalizeBody($string) + { + $len = strlen($string); + $canon = ''; + $method = ($this->_bodyCanon == 'relaxed'); + for ($i = 0; $i < $len; ++$i) { + if ($this->_bodyCanonIgnoreStart > 0) { + --$this->_bodyCanonIgnoreStart; + continue; + } + switch ($string[$i]) { + case "\r" : + $this->_bodyCanonLastChar = "\r"; + break; + case "\n" : + if ($this->_bodyCanonLastChar == "\r") { + if ($method) { + $this->_bodyCanonSpace = false; + } + if ($this->_bodyCanonLine == '') { + ++$this->_bodyCanonEmptyCounter; + } else { + $this->_bodyCanonLine = ''; + $canon .= "\r\n"; + } + } else { + // Wooops Error + // todo handle it but should never happen + } + break; + case ' ' : + case "\t" : + if ($method) { + $this->_bodyCanonSpace = true; + break; + } + default : + if ($this->_bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); + $this->_bodyCanonEmptyCounter = 0; + } + if ($this->_bodyCanonSpace) { + $this->_bodyCanonLine .= ' '; + $canon .= ' '; + $this->_bodyCanonSpace = false; + } + $this->_bodyCanonLine .= $string[$i]; + $canon .= $string[$i]; + } + } + $this->_addToBodyHash($canon); + } + + protected function _endOfBody() + { + // Add trailing Line return if last line is non empty + if (strlen($this->_bodyCanonLine) > 0) { + $this->_addToBodyHash("\r\n"); + } + $this->_bodyHash = hash_final($this->_bodyHashHandler, true); + } + + private function _addToBodyHash($string) + { + $len = strlen($string); + if ($len > ($new_len = ($this->_maxLen - $this->_bodyLen))) { + $string = substr($string, 0, $new_len); + $len = $new_len; + } + hash_update($this->_bodyHashHandler, $string); + $this->_bodyLen += $len; + } + + private function _addToHeaderHash($header) + { + if ($this->_debugHeaders) { + $this->_debugHeadersData[] = trim($header); + } + $this->_headerCanonData .= $header; + } + + /** + * @throws Swift_SwiftException + * + * @return string + */ + private function _getEncryptedHash() + { + $signature = ''; + switch ($this->_hashAlgorithm) { + case 'rsa-sha1': + $algorithm = OPENSSL_ALGO_SHA1; + break; + case 'rsa-sha256': + $algorithm = OPENSSL_ALGO_SHA256; + break; + } + $pkeyId = openssl_get_privatekey($this->_privateKey); + if (!$pkeyId) { + throw new Swift_SwiftException('Unable to load DKIM Private Key ['.openssl_error_string().']'); + } + if (openssl_sign($this->_headerCanonData, $signature, $pkeyId, $algorithm)) { + return $signature; + } + throw new Swift_SwiftException('Unable to sign DKIM Hash ['.openssl_error_string().']'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php new file mode 100644 index 0000000..bceda3d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php @@ -0,0 +1,525 @@ + + */ +class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner +{ + /** + * PrivateKey. + * + * @var string + */ + protected $_privateKey; + + /** + * DomainName. + * + * @var string + */ + protected $_domainName; + + /** + * Selector. + * + * @var string + */ + protected $_selector; + + /** + * Hash algorithm used. + * + * @var string + */ + protected $_hashAlgorithm = 'rsa-sha1'; + + /** + * Canonisation method. + * + * @var string + */ + protected $_canon = 'simple'; + + /** + * Headers not being signed. + * + * @var array + */ + protected $_ignoredHeaders = array(); + + /** + * Signer identity. + * + * @var string + */ + protected $_signerIdentity; + + /** + * Must we embed signed headers? + * + * @var bool + */ + protected $_debugHeaders = false; + + // work variables + /** + * Headers used to generate hash. + * + * @var array + */ + private $_signedHeaders = array(); + + /** + * Stores the signature header. + * + * @var Swift_Mime_Headers_ParameterizedHeader + */ + protected $_domainKeyHeader; + + /** + * Hash Handler. + * + * @var resource|null + */ + private $_hashHandler; + + private $_hash; + + private $_canonData = ''; + + private $_bodyCanonEmptyCounter = 0; + + private $_bodyCanonIgnoreStart = 2; + + private $_bodyCanonSpace = false; + + private $_bodyCanonLastChar = null; + + private $_bodyCanonLine = ''; + + private $_bound = array(); + + /** + * Constructor. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + */ + public function __construct($privateKey, $domainName, $selector) + { + $this->_privateKey = $privateKey; + $this->_domainName = $domainName; + $this->_signerIdentity = '@'.$domainName; + $this->_selector = $selector; + } + + /** + * Instanciate DomainKeySigner. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + * + * @return Swift_Signers_DomainKeySigner + */ + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + /** + * Resets internal states. + * + * @return Swift_Signers_DomainKeysSigner + */ + public function reset() + { + $this->_hash = null; + $this->_hashHandler = null; + $this->_bodyCanonIgnoreStart = 2; + $this->_bodyCanonEmptyCounter = 0; + $this->_bodyCanonLastChar = null; + $this->_bodyCanonSpace = false; + + return $this; + } + + /** + * Writes $bytes to the end of the stream. + * + * Writing may not happen immediately if the stream chooses to buffer. If + * you want to write these bytes with immediate effect, call {@link commit()} + * after calling write(). + * + * This method returns the sequence ID of the write (i.e. 1 for first, 2 for + * second, etc etc). + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + * @return Swift_Signers_DomainKeysSigner + */ + public function write($bytes) + { + $this->_canonicalizeBody($bytes); + foreach ($this->_bound as $is) { + $is->write($bytes); + } + + return $this; + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + * + * @return Swift_Signers_DomainKeysSigner + */ + public function commit() + { + // Nothing to do + return $this; + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + * + * @return Swift_Signers_DomainKeysSigner + */ + public function bind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + $this->_bound[] = $is; + + return $this; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + * + * @return Swift_Signers_DomainKeysSigner + */ + public function unbind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + foreach ($this->_bound as $k => $stream) { + if ($stream === $is) { + unset($this->_bound[$k]); + + return; + } + } + + return $this; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + * + * @return Swift_Signers_DomainKeysSigner + */ + public function flushBuffers() + { + $this->reset(); + + return $this; + } + + /** + * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256. + * + * @param string $hash + * + * @return Swift_Signers_DomainKeysSigner + */ + public function setHashAlgorithm($hash) + { + $this->_hashAlgorithm = 'rsa-sha1'; + + return $this; + } + + /** + * Set the canonicalization algorithm. + * + * @param string $canon simple | nofws defaults to simple + * + * @return Swift_Signers_DomainKeysSigner + */ + public function setCanon($canon) + { + if ($canon == 'nofws') { + $this->_canon = 'nofws'; + } else { + $this->_canon = 'simple'; + } + + return $this; + } + + /** + * Set the signer identity. + * + * @param string $identity + * + * @return Swift_Signers_DomainKeySigner + */ + public function setSignerIdentity($identity) + { + $this->_signerIdentity = $identity; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DomainKeySigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + /** + * Start Body. + */ + public function startBody() + { + } + + /** + * End Body. + */ + public function endBody() + { + $this->_endOfBody(); + } + + /** + * Returns the list of Headers Tampered by this plugin. + * + * @return array + */ + public function getAlteredHeaders() + { + if ($this->_debugHeaders) { + return array('DomainKey-Signature', 'X-DebugHash'); + } else { + return array('DomainKey-Signature'); + } + } + + /** + * Adds an ignored Header. + * + * @param string $header_name + * + * @return Swift_Signers_DomainKeySigner + */ + public function ignoreHeader($header_name) + { + $this->_ignoredHeaders[strtolower($header_name)] = true; + + return $this; + } + + /** + * Set the headers to sign. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DomainKeySigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $this->_startHash(); + $this->_canonData = ''; + // Loop through Headers + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if ($headers->has($hName)) { + $tmp = $headers->getAll($hName); + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $this->_addHeader($header->toString()); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + $this->_endOfHeaders(); + + return $this; + } + + /** + * Add the signature to the given Headers. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DomainKeySigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers) + { + // Prepare the DomainKey-Signature Header + $params = array('a' => $this->_hashAlgorithm, 'b' => chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '), 'c' => $this->_canon, 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'q' => 'dns', 's' => $this->_selector); + $string = ''; + foreach ($params as $k => $v) { + $string .= $k.'='.$v.'; '; + } + $string = trim($string); + $headers->addTextHeader('DomainKey-Signature', $string); + + return $this; + } + + /* Private helpers */ + + protected function _addHeader($header) + { + switch ($this->_canon) { + case 'nofws' : + // Prepare Header and cascade + $exploded = explode(':', $header, 2); + $name = strtolower(trim($exploded[0])); + $value = str_replace("\r\n", '', $exploded[1]); + $value = preg_replace("/[ \t][ \t]+/", ' ', $value); + $header = $name.':'.trim($value)."\r\n"; + case 'simple' : + // Nothing to do + } + $this->_addToHash($header); + } + + protected function _endOfHeaders() + { + $this->_bodyCanonEmptyCounter = 1; + } + + protected function _canonicalizeBody($string) + { + $len = strlen($string); + $canon = ''; + $nofws = ($this->_canon == 'nofws'); + for ($i = 0; $i < $len; ++$i) { + if ($this->_bodyCanonIgnoreStart > 0) { + --$this->_bodyCanonIgnoreStart; + continue; + } + switch ($string[$i]) { + case "\r" : + $this->_bodyCanonLastChar = "\r"; + break; + case "\n" : + if ($this->_bodyCanonLastChar == "\r") { + if ($nofws) { + $this->_bodyCanonSpace = false; + } + if ($this->_bodyCanonLine == '') { + ++$this->_bodyCanonEmptyCounter; + } else { + $this->_bodyCanonLine = ''; + $canon .= "\r\n"; + } + } else { + // Wooops Error + throw new Swift_SwiftException('Invalid new line sequence in mail found \n without preceding \r'); + } + break; + case ' ' : + case "\t" : + case "\x09": //HTAB + if ($nofws) { + $this->_bodyCanonSpace = true; + break; + } + default : + if ($this->_bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); + $this->_bodyCanonEmptyCounter = 0; + } + $this->_bodyCanonLine .= $string[$i]; + $canon .= $string[$i]; + } + } + $this->_addToHash($canon); + } + + protected function _endOfBody() + { + if (strlen($this->_bodyCanonLine) > 0) { + $this->_addToHash("\r\n"); + } + $this->_hash = hash_final($this->_hashHandler, true); + } + + private function _addToHash($string) + { + $this->_canonData .= $string; + hash_update($this->_hashHandler, $string); + } + + private function _startHash() + { + // Init + switch ($this->_hashAlgorithm) { + case 'rsa-sha1' : + $this->_hashHandler = hash_init('sha1'); + break; + } + $this->_canonLine = ''; + } + + /** + * @throws Swift_SwiftException + * + * @return string + */ + private function _getEncryptedHash() + { + $signature = ''; + $pkeyId = openssl_get_privatekey($this->_privateKey); + if (!$pkeyId) { + throw new Swift_SwiftException('Unable to load DomainKey Private Key ['.openssl_error_string().']'); + } + if (openssl_sign($this->_canonData, $signature, $pkeyId, OPENSSL_ALGO_SHA1)) { + return $signature; + } + throw new Swift_SwiftException('Unable to sign DomainKey Hash ['.openssl_error_string().']'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php new file mode 100644 index 0000000..c75cb08 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php @@ -0,0 +1,65 @@ + + */ +interface Swift_Signers_HeaderSigner extends Swift_Signer, Swift_InputByteStream +{ + /** + * Exclude an header from the signed headers. + * + * @param string $header_name + * + * @return Swift_Signers_HeaderSigner + */ + public function ignoreHeader($header_name); + + /** + * Prepare the Signer to get a new Body. + * + * @return Swift_Signers_HeaderSigner + */ + public function startBody(); + + /** + * Give the signal that the body has finished streaming. + * + * @return Swift_Signers_HeaderSigner + */ + public function endBody(); + + /** + * Give the headers already given. + * + * @param Swift_Mime_SimpleHeaderSet $headers + * + * @return Swift_Signers_HeaderSigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers); + + /** + * Add the header(s) to the headerSet. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_HeaderSigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers); + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php new file mode 100644 index 0000000..9f1022c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php @@ -0,0 +1,189 @@ + + */ +class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner +{ + private $_peclLoaded = false; + + private $_dkimHandler = null; + + private $dropFirstLF = true; + + const CANON_RELAXED = 1; + const CANON_SIMPLE = 2; + const SIG_RSA_SHA1 = 3; + const SIG_RSA_SHA256 = 4; + + public function __construct($privateKey, $domainName, $selector) + { + if (extension_loaded('opendkim')) { + $this->_peclLoaded = true; + } else { + throw new Swift_SwiftException('php-opendkim extension not found'); + } + parent::__construct($privateKey, $domainName, $selector); + } + + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + public function addSignature(Swift_Mime_HeaderSet $headers) + { + $header = new Swift_Mime_Headers_OpenDKIMHeader('DKIM-Signature'); + $headerVal = $this->_dkimHandler->getSignatureHeader(); + if (!$headerVal) { + throw new Swift_SwiftException('OpenDKIM Error: '.$this->_dkimHandler->getError()); + } + $header->setValue($headerVal); + $headers->set($header); + + return $this; + } + + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $bodyLen = $this->_bodyLen; + if (is_bool($bodyLen)) { + $bodyLen = -1; + } + $hash = ($this->_hashAlgorithm == 'rsa-sha1') ? OpenDKIMSign::ALG_RSASHA1 : OpenDKIMSign::ALG_RSASHA256; + $bodyCanon = ($this->_bodyCanon == 'simple') ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $headerCanon = ($this->_headerCanon == 'simple') ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $this->_dkimHandler = new OpenDKIMSign($this->_privateKey, $this->_selector, $this->_domainName, $headerCanon, $bodyCanon, $hash, $bodyLen); + // Hardcode signature Margin for now + $this->_dkimHandler->setMargin(78); + + if (!is_numeric($this->_signatureTimestamp)) { + OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, time()); + } else { + if (!OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, $this->_signatureTimestamp)) { + throw new Swift_SwiftException('Unable to force signature timestamp ['.openssl_error_string().']'); + } + } + if (isset($this->_signerIdentity)) { + $this->_dkimHandler->setSigner($this->_signerIdentity); + } + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + $tmp = $headers->getAll($hName); + if ($headers->has($hName)) { + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $htosign = $header->toString(); + $this->_dkimHandler->header($htosign); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + + return $this; + } + + public function startBody() + { + if (!$this->_peclLoaded) { + return parent::startBody(); + } + $this->dropFirstLF = true; + $this->_dkimHandler->eoh(); + + return $this; + } + + public function endBody() + { + if (!$this->_peclLoaded) { + return parent::endBody(); + } + $this->_dkimHandler->eom(); + + return $this; + } + + public function reset() + { + $this->_dkimHandler = null; + parent::reset(); + + return $this; + } + + /** + * Set the signature timestamp. + * + * @param timestamp $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureTimestamp($time) + { + $this->_signatureTimestamp = $time; + + return $this; + } + + /** + * Set the signature expiration timestamp. + * + * @param timestamp $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureExpiration($time) + { + $this->_signatureExpiration = $time; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DKIMSigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + // Protected + + protected function _canonicalizeBody($string) + { + if (!$this->_peclLoaded) { + return parent::_canonicalizeBody($string); + } + if (false && $this->dropFirstLF === true) { + if ($string[0] == "\r" && $string[1] == "\n") { + $string = substr($string, 2); + } + } + $this->dropFirstLF = false; + if (strlen($string)) { + $this->_dkimHandler->body($string); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php new file mode 100644 index 0000000..d4c77e7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php @@ -0,0 +1,437 @@ + + */ +class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner +{ + protected $signCertificate; + protected $signPrivateKey; + protected $encryptCert; + protected $signThenEncrypt = true; + protected $signLevel; + protected $encryptLevel; + protected $signOptions; + protected $encryptOptions; + protected $encryptCipher; + protected $extraCerts = null; + + /** + * @var Swift_StreamFilters_StringReplacementFilterFactory + */ + protected $replacementFactory; + + /** + * @var Swift_Mime_HeaderFactory + */ + protected $headerFactory; + + /** + * Constructor. + * + * @param string $certificate + * @param string $privateKey + * @param string $encryptCertificate + */ + public function __construct($signCertificate = null, $signPrivateKey = null, $encryptCertificate = null) + { + if (null !== $signPrivateKey) { + $this->setSignCertificate($signCertificate, $signPrivateKey); + } + + if (null !== $encryptCertificate) { + $this->setEncryptCertificate($encryptCertificate); + } + + $this->replacementFactory = Swift_DependencyContainer::getInstance() + ->lookup('transport.replacementfactory'); + + $this->signOptions = PKCS7_DETACHED; + + // Supported since php5.4 + if (defined('OPENSSL_CIPHER_AES_128_CBC')) { + $this->encryptCipher = OPENSSL_CIPHER_AES_128_CBC; + } else { + $this->encryptCipher = OPENSSL_CIPHER_RC2_128; + } + } + + /** + * Returns an new Swift_Signers_SMimeSigner instance. + * + * @param string $certificate + * @param string $privateKey + * + * @return Swift_Signers_SMimeSigner + */ + public static function newInstance($certificate = null, $privateKey = null) + { + return new self($certificate, $privateKey); + } + + /** + * Set the certificate location to use for signing. + * + * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php + * + * @param string $certificate + * @param string|array $privateKey If the key needs an passphrase use array('file-location', 'passphrase') instead + * @param int $signOptions Bitwise operator options for openssl_pkcs7_sign() + * @param string $extraCerts A file containing intermediate certificates needed by the signing certificate + * + * @return Swift_Signers_SMimeSigner + */ + public function setSignCertificate($certificate, $privateKey = null, $signOptions = PKCS7_DETACHED, $extraCerts = null) + { + $this->signCertificate = 'file://'.str_replace('\\', '/', realpath($certificate)); + + if (null !== $privateKey) { + if (is_array($privateKey)) { + $this->signPrivateKey = $privateKey; + $this->signPrivateKey[0] = 'file://'.str_replace('\\', '/', realpath($privateKey[0])); + } else { + $this->signPrivateKey = 'file://'.str_replace('\\', '/', realpath($privateKey)); + } + } + + $this->signOptions = $signOptions; + if (null !== $extraCerts) { + $this->extraCerts = str_replace('\\', '/', realpath($extraCerts)); + } + + return $this; + } + + /** + * Set the certificate location to use for encryption. + * + * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php + * @link http://nl3.php.net/manual/en/openssl.ciphers.php + * + * @param string|array $recipientCerts Either an single X.509 certificate, or an assoc array of X.509 certificates. + * @param int $cipher + * + * @return Swift_Signers_SMimeSigner + */ + public function setEncryptCertificate($recipientCerts, $cipher = null) + { + if (is_array($recipientCerts)) { + $this->encryptCert = array(); + + foreach ($recipientCerts as $cert) { + $this->encryptCert[] = 'file://'.str_replace('\\', '/', realpath($cert)); + } + } else { + $this->encryptCert = 'file://'.str_replace('\\', '/', realpath($recipientCerts)); + } + + if (null !== $cipher) { + $this->encryptCipher = $cipher; + } + + return $this; + } + + /** + * @return string + */ + public function getSignCertificate() + { + return $this->signCertificate; + } + + /** + * @return string + */ + public function getSignPrivateKey() + { + return $this->signPrivateKey; + } + + /** + * Set perform signing before encryption. + * + * The default is to first sign the message and then encrypt. + * But some older mail clients, namely Microsoft Outlook 2000 will work when the message first encrypted. + * As this goes against the official specs, its recommended to only use 'encryption -> signing' when specifically targeting these 'broken' clients. + * + * @param string $signThenEncrypt + * + * @return Swift_Signers_SMimeSigner + */ + public function setSignThenEncrypt($signThenEncrypt = true) + { + $this->signThenEncrypt = $signThenEncrypt; + + return $this; + } + + /** + * @return bool + */ + public function isSignThenEncrypt() + { + return $this->signThenEncrypt; + } + + /** + * Resets internal states. + * + * @return Swift_Signers_SMimeSigner + */ + public function reset() + { + return $this; + } + + /** + * Change the Swift_Message to apply the signing. + * + * @param Swift_Message $message + * + * @return Swift_Signers_SMimeSigner + */ + public function signMessage(Swift_Message $message) + { + if (null === $this->signCertificate && null === $this->encryptCert) { + return $this; + } + + // Store the message using ByteStream to a file{1} + // Remove all Children + // Sign file{1}, parse the new MIME headers and set them on the primary MimeEntity + // Set the singed-body as the new body (without boundary) + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $this->toSMimeByteStream($messageStream, $message); + $message->setEncoder(Swift_DependencyContainer::getInstance()->lookup('mime.rawcontentencoder')); + + $message->setChildren(array()); + $this->streamToMime($messageStream, $message); + } + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders() + { + return array('Content-Type', 'Content-Transfer-Encoding', 'Content-Disposition'); + } + + /** + * @param Swift_InputByteStream $inputStream + * @param Swift_Message $mimeEntity + */ + protected function toSMimeByteStream(Swift_InputByteStream $inputStream, Swift_Message $message) + { + $mimeEntity = $this->createMessage($message); + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + $mimeEntity->toByteStream($messageStream); + $messageStream->commit(); + + if (null !== $this->signCertificate && null !== $this->encryptCert) { + $temporaryStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if ($this->signThenEncrypt) { + $this->messageStreamToSignedByteStream($messageStream, $temporaryStream); + $this->messageStreamToEncryptedByteStream($temporaryStream, $inputStream); + } else { + $this->messageStreamToEncryptedByteStream($messageStream, $temporaryStream); + $this->messageStreamToSignedByteStream($temporaryStream, $inputStream); + } + } elseif ($this->signCertificate !== null) { + $this->messageStreamToSignedByteStream($messageStream, $inputStream); + } else { + $this->messageStreamToEncryptedByteStream($messageStream, $inputStream); + } + } + + /** + * @param Swift_Message $message + * + * @return Swift_Message + */ + protected function createMessage(Swift_Message $message) + { + $mimeEntity = new Swift_Message('', $message->getBody(), $message->getContentType(), $message->getCharset()); + $mimeEntity->setChildren($message->getChildren()); + + $messageHeaders = $mimeEntity->getHeaders(); + $messageHeaders->remove('Message-ID'); + $messageHeaders->remove('Date'); + $messageHeaders->remove('Subject'); + $messageHeaders->remove('MIME-Version'); + $messageHeaders->remove('To'); + $messageHeaders->remove('From'); + + return $mimeEntity; + } + + /** + * @param Swift_FileStream $outputStream + * @param Swift_InputByteStream $inputStream + * + * @throws Swift_IoException + */ + protected function messageStreamToSignedByteStream(Swift_FileStream $outputStream, Swift_InputByteStream $inputStream) + { + $signedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + $args = array($outputStream->getPath(), $signedMessageStream->getPath(), $this->signCertificate, $this->signPrivateKey, array(), $this->signOptions); + if (null !== $this->extraCerts) { + $args[] = $this->extraCerts; + } + + if (!call_user_func_array('openssl_pkcs7_sign', $args)) { + throw new Swift_IoException(sprintf('Failed to sign S/Mime message. Error: "%s".', openssl_error_string())); + } + + $this->copyFromOpenSSLOutput($signedMessageStream, $inputStream); + } + + /** + * @param Swift_FileStream $outputStream + * @param Swift_InputByteStream $is + * + * @throws Swift_IoException + */ + protected function messageStreamToEncryptedByteStream(Swift_FileStream $outputStream, Swift_InputByteStream $is) + { + $encryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_encrypt($outputStream->getPath(), $encryptedMessageStream->getPath(), $this->encryptCert, array(), 0, $this->encryptCipher)) { + throw new Swift_IoException(sprintf('Failed to encrypt S/Mime message. Error: "%s".', openssl_error_string())); + } + + $this->copyFromOpenSSLOutput($encryptedMessageStream, $is); + } + + /** + * @param Swift_OutputByteStream $fromStream + * @param Swift_InputByteStream $toStream + */ + protected function copyFromOpenSSLOutput(Swift_OutputByteStream $fromStream, Swift_InputByteStream $toStream) + { + $bufferLength = 4096; + $filteredStream = new Swift_ByteStream_TemporaryFileByteStream(); + $filteredStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $filteredStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $filteredStream->write($buffer); + } + + $filteredStream->flushBuffers(); + + while (false !== ($buffer = $filteredStream->read($bufferLength))) { + $toStream->write($buffer); + } + + $toStream->commit(); + } + + /** + * Merges an OutputByteStream to Swift_Message. + * + * @param Swift_OutputByteStream $fromStream + * @param Swift_Message $message + */ + protected function streamToMime(Swift_OutputByteStream $fromStream, Swift_Message $message) + { + $bufferLength = 78; + $headerData = ''; + + $fromStream->setReadPointer(0); + + while (($buffer = $fromStream->read($bufferLength)) !== false) { + $headerData .= $buffer; + + if (false !== strpos($buffer, "\r\n\r\n")) { + break; + } + } + + $headersPosEnd = strpos($headerData, "\r\n\r\n"); + $headerData = trim($headerData); + $headerData = substr($headerData, 0, $headersPosEnd); + $headerLines = explode("\r\n", $headerData); + unset($headerData); + + $headers = array(); + $currentHeaderName = ''; + + foreach ($headerLines as $headerLine) { + // Line separated + if (ctype_space($headerLines[0]) || false === strpos($headerLine, ':')) { + $headers[$currentHeaderName] .= ' '.trim($headerLine); + continue; + } + + $header = explode(':', $headerLine, 2); + $currentHeaderName = strtolower($header[0]); + $headers[$currentHeaderName] = trim($header[1]); + } + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $messageStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + $messageHeaders = $message->getHeaders(); + + // No need to check for 'application/pkcs7-mime', as this is always base64 + if ('multipart/signed;' === substr($headers['content-type'], 0, 17)) { + if (!preg_match('/boundary=("[^"]+"|(?:[^\s]+|$))/is', $headers['content-type'], $contentTypeData)) { + throw new Swift_SwiftException('Failed to find Boundary parameter'); + } + + $boundary = trim($contentTypeData['1'], '"'); + $boundaryLen = strlen($boundary); + + // Skip the header and CRLF CRLF + $fromStream->setReadPointer($headersPosEnd + 4); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $messageStream->write($buffer); + } + + $messageStream->commit(); + + $messageHeaders->remove('Content-Transfer-Encoding'); + $message->setContentType($headers['content-type']); + $message->setBoundary($boundary); + $message->setBody($messageStream); + } else { + $fromStream->setReadPointer($headersPosEnd + 4); + + if (null === $this->headerFactory) { + $this->headerFactory = Swift_DependencyContainer::getInstance()->lookup('mime.headerfactory'); + } + + $message->setContentType($headers['content-type']); + $messageHeaders->set($this->headerFactory->createTextHeader('Content-Transfer-Encoding', $headers['content-transfer-encoding'])); + $messageHeaders->set($this->headerFactory->createTextHeader('Content-Disposition', $headers['content-disposition'])); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $messageStream->write($buffer); + } + + $messageStream->commit(); + $message->setBody($messageStream); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php new file mode 100644 index 0000000..6251611 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php @@ -0,0 +1,58 @@ +createDependenciesFor('transport.smtp') + ); + + $this->setHost($host); + $this->setPort($port); + $this->setEncryption($security); + } + + /** + * Create a new SmtpTransport instance. + * + * @param string $host + * @param int $port + * @param string $security + * + * @return Swift_SmtpTransport + */ + public static function newInstance($host = 'localhost', $port = 25, $security = null) + { + return new self($host, $port, $security); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php new file mode 100644 index 0000000..c16ab4b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for spools. + * + * @author Fabien Potencier + */ +interface Swift_Spool +{ + /** + * Starts this Spool mechanism. + */ + public function start(); + + /** + * Stops this Spool mechanism. + */ + public function stop(); + + /** + * Tests if this Spool mechanism has started. + * + * @return bool + */ + public function isStarted(); + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @return bool Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message); + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php new file mode 100644 index 0000000..cf9bf78 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @author Fabien Potencier + */ +class Swift_SpoolTransport extends Swift_Transport_SpoolTransport +{ + /** + * Create a new SpoolTransport. + * + * @param Swift_Spool $spool + */ + public function __construct(Swift_Spool $spool) + { + $arguments = Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.spool'); + + $arguments[] = $spool; + + call_user_func_array( + array($this, 'Swift_Transport_SpoolTransport::__construct'), + $arguments + ); + } + + /** + * Create a new SpoolTransport instance. + * + * @param Swift_Spool $spool + * + * @return Swift_SpoolTransport + */ + public static function newInstance(Swift_Spool $spool) + { + return new self($spool); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php new file mode 100644 index 0000000..362be2e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php @@ -0,0 +1,35 @@ +_search = $search; + $this->_index = array(); + $this->_tree = array(); + $this->_replace = array(); + $this->_repSize = array(); + + $tree = null; + $i = null; + $last_size = $size = 0; + foreach ($search as $i => $search_element) { + if ($tree !== null) { + $tree[-1] = min(count($replace) - 1, $i - 1); + $tree[-2] = $last_size; + } + $tree = &$this->_tree; + if (is_array($search_element)) { + foreach ($search_element as $k => $char) { + $this->_index[$char] = true; + if (!isset($tree[$char])) { + $tree[$char] = array(); + } + $tree = &$tree[$char]; + } + $last_size = $k + 1; + $size = max($size, $last_size); + } else { + $last_size = 1; + if (!isset($tree[$search_element])) { + $tree[$search_element] = array(); + } + $tree = &$tree[$search_element]; + $size = max($last_size, $size); + $this->_index[$search_element] = true; + } + } + if ($i !== null) { + $tree[-1] = min(count($replace) - 1, $i); + $tree[-2] = $last_size; + $this->_treeMaxLen = $size; + } + foreach ($replace as $rep) { + if (!is_array($rep)) { + $rep = array($rep); + } + $this->_replace[] = $rep; + } + for ($i = count($this->_replace) - 1; $i >= 0; --$i) { + $this->_replace[$i] = $rep = $this->filter($this->_replace[$i], $i); + $this->_repSize[$i] = count($rep); + } + } + + /** + * Returns true if based on the buffer passed more bytes should be buffered. + * + * @param array $buffer + * + * @return bool + */ + public function shouldBuffer($buffer) + { + $endOfBuffer = end($buffer); + + return isset($this->_index[$endOfBuffer]); + } + + /** + * Perform the actual replacements on $buffer and return the result. + * + * @param array $buffer + * @param int $_minReplaces + * + * @return array + */ + public function filter($buffer, $_minReplaces = -1) + { + if ($this->_treeMaxLen == 0) { + return $buffer; + } + + $newBuffer = array(); + $buf_size = count($buffer); + for ($i = 0; $i < $buf_size; ++$i) { + $search_pos = $this->_tree; + $last_found = PHP_INT_MAX; + // We try to find if the next byte is part of a search pattern + for ($j = 0; $j <= $this->_treeMaxLen; ++$j) { + // We have a new byte for a search pattern + if (isset($buffer [$p = $i + $j]) && isset($search_pos[$buffer[$p]])) { + $search_pos = $search_pos[$buffer[$p]]; + // We have a complete pattern, save, in case we don't find a better match later + if (isset($search_pos[-1]) && $search_pos[-1] < $last_found + && $search_pos[-1] > $_minReplaces) { + $last_found = $search_pos[-1]; + $last_size = $search_pos[-2]; + } + } + // We got a complete pattern + elseif ($last_found !== PHP_INT_MAX) { + // Adding replacement datas to output buffer + $rep_size = $this->_repSize[$last_found]; + for ($j = 0; $j < $rep_size; ++$j) { + $newBuffer[] = $this->_replace[$last_found][$j]; + } + // We Move cursor forward + $i += $last_size - 1; + // Edge Case, last position in buffer + if ($i >= $buf_size) { + $newBuffer[] = $buffer[$i]; + } + + // We start the next loop + continue 2; + } else { + // this byte is not in a pattern and we haven't found another pattern + break; + } + } + // Normal byte, move it to output buffer + $newBuffer[] = $buffer[$i]; + } + + return $newBuffer; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php new file mode 100644 index 0000000..d0db8b9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php @@ -0,0 +1,66 @@ +_search = $search; + $this->_replace = $replace; + } + + /** + * Returns true if based on the buffer passed more bytes should be buffered. + * + * @param string $buffer + * + * @return bool + */ + public function shouldBuffer($buffer) + { + $endOfBuffer = substr($buffer, -1); + foreach ((array) $this->_search as $needle) { + if (false !== strpos($needle, $endOfBuffer)) { + return true; + } + } + + return false; + } + + /** + * Perform the actual replacements on $buffer and return the result. + * + * @param string $buffer + * + * @return string + */ + public function filter($buffer) + { + return str_replace($this->_search, $this->_replace, $buffer); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php new file mode 100644 index 0000000..e98240b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php @@ -0,0 +1,45 @@ +_filters[$search][$replace])) { + if (!isset($this->_filters[$search])) { + $this->_filters[$search] = array(); + } + + if (!isset($this->_filters[$search][$replace])) { + $this->_filters[$search][$replace] = array(); + } + + $this->_filters[$search][$replace] = new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } + + return $this->_filters[$search][$replace]; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php new file mode 100644 index 0000000..db3d310 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php @@ -0,0 +1,29 @@ +_eventDispatcher = $dispatcher; + $this->_buffer = $buf; + $this->_lookupHostname(); + } + + /** + * Set the name of the local domain which Swift will identify itself as. + * + * This should be a fully-qualified domain name and should be truly the domain + * you're using. + * + * If your server doesn't have a domain name, use the IP in square + * brackets (i.e. [127.0.0.1]). + * + * @param string $domain + * + * @return Swift_Transport_AbstractSmtpTransport + */ + public function setLocalDomain($domain) + { + $this->_domain = $domain; + + return $this; + } + + /** + * Get the name of the domain Swift will identify as. + * + * @return string + */ + public function getLocalDomain() + { + return $this->_domain; + } + + /** + * Sets the source IP. + * + * @param string $source + */ + public function setSourceIp($source) + { + $this->_sourceIp = $source; + } + + /** + * Returns the IP used to connect to the destination. + * + * @return string + */ + public function getSourceIp() + { + return $this->_sourceIp; + } + + /** + * Start the SMTP connection. + */ + public function start() + { + if (!$this->_started) { + if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted'); + if ($evt->bubbleCancelled()) { + return; + } + } + + try { + $this->_buffer->initialize($this->_getBufferParams()); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + $this->_readGreeting(); + $this->_doHeloCommand(); + + if ($evt) { + $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted'); + } + + $this->_started = true; + } + } + + /** + * Test if an SMTP connection has been established. + * + * @return bool + */ + public function isStarted() + { + return $this->_started; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $sent = 0; + $failedRecipients = (array) $failedRecipients; + + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if (!$reversePath = $this->_getReversePath($message)) { + $this->_throwException(new Swift_TransportException( + 'Cannot send message without a sender address' + ) + ); + } + + $to = (array) $message->getTo(); + $cc = (array) $message->getCc(); + $tos = array_merge($to, $cc); + $bcc = (array) $message->getBcc(); + + $message->setBcc(array()); + + try { + $sent += $this->_sendTo($message, $reversePath, $tos, $failedRecipients); + $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients); + } catch (Exception $e) { + $message->setBcc($bcc); + throw $e; + } + + $message->setBcc($bcc); + + if ($evt) { + if ($sent == count($to) + count($cc) + count($bcc)) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + } elseif ($sent > 0) { + $evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE); + } else { + $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); + } + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); //Make sure a new Message ID is used + + return $sent; + } + + /** + * Stop the SMTP connection. + */ + public function stop() + { + if ($this->_started) { + if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped'); + if ($evt->bubbleCancelled()) { + return; + } + } + + try { + $this->executeCommand("QUIT\r\n", array(221)); + } catch (Swift_TransportException $e) { + } + + try { + $this->_buffer->terminate(); + + if ($evt) { + $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped'); + } + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + } + $this->_started = false; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } + + /** + * Reset the current mail transaction. + */ + public function reset() + { + $this->executeCommand("RSET\r\n", array(250)); + } + + /** + * Get the IoBuffer where read/writes are occurring. + * + * @return Swift_Transport_IoBuffer + */ + public function getBuffer() + { + return $this->_buffer; + } + + /** + * Run a command against the buffer, expecting the given response codes. + * + * If no response codes are given, the response will not be validated. + * If codes are given, an exception will be thrown on an invalid response. + * + * @param string $command + * @param int[] $codes + * @param string[] $failures An array of failures by-reference + * + * @return string + */ + public function executeCommand($command, $codes = array(), &$failures = null) + { + $failures = (array) $failures; + $seq = $this->_buffer->write($command); + $response = $this->_getFullResponse($seq); + if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)) { + $this->_eventDispatcher->dispatchEvent($evt, 'commandSent'); + } + $this->_assertResponseCode($response, $codes); + + return $response; + } + + /** Read the opening SMTP greeting */ + protected function _readGreeting() + { + $this->_assertResponseCode($this->_getFullResponse(0), array(220)); + } + + /** Send the HELO welcome */ + protected function _doHeloCommand() + { + $this->executeCommand( + sprintf("HELO %s\r\n", $this->_domain), array(250) + ); + } + + /** Send the MAIL FROM command */ + protected function _doMailFromCommand($address) + { + $this->executeCommand( + sprintf("MAIL FROM:<%s>\r\n", $address), array(250) + ); + } + + /** Send the RCPT TO command */ + protected function _doRcptToCommand($address) + { + $this->executeCommand( + sprintf("RCPT TO:<%s>\r\n", $address), array(250, 251, 252) + ); + } + + /** Send the DATA command */ + protected function _doDataCommand() + { + $this->executeCommand("DATA\r\n", array(354)); + } + + /** Stream the contents of the message over the buffer */ + protected function _streamMessage(Swift_Mime_Message $message) + { + $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n..")); + try { + $message->toByteStream($this->_buffer); + $this->_buffer->flushBuffers(); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + $this->_buffer->setWriteTranslations(array()); + $this->executeCommand("\r\n.\r\n", array(250)); + } + + /** Determine the best-use reverse path for this message */ + protected function _getReversePath(Swift_Mime_Message $message) + { + $return = $message->getReturnPath(); + $sender = $message->getSender(); + $from = $message->getFrom(); + $path = null; + if (!empty($return)) { + $path = $return; + } elseif (!empty($sender)) { + // Don't use array_keys + reset($sender); // Reset Pointer to first pos + $path = key($sender); // Get key + } elseif (!empty($from)) { + reset($from); // Reset Pointer to first pos + $path = key($from); // Get key + } + + return $path; + } + + /** Throw a TransportException, first sending it to any listeners */ + protected function _throwException(Swift_TransportException $e) + { + if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { + $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); + if (!$evt->bubbleCancelled()) { + throw $e; + } + } else { + throw $e; + } + } + + /** Throws an Exception if a response code is incorrect */ + protected function _assertResponseCode($response, $wanted) + { + list($code) = sscanf($response, '%3d'); + $valid = (empty($wanted) || in_array($code, $wanted)); + + if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response, + $valid)) { + $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived'); + } + + if (!$valid) { + $this->_throwException( + new Swift_TransportException( + 'Expected response code '.implode('/', $wanted).' but got code '. + '"'.$code.'", with message "'.$response.'"', + $code) + ); + } + } + + /** Get an entire multi-line response using its sequence number */ + protected function _getFullResponse($seq) + { + $response = ''; + try { + do { + $line = $this->_buffer->readLine($seq); + $response .= $line; + } while (null !== $line && false !== $line && ' ' != $line{3}); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } catch (Swift_IoException $e) { + $this->_throwException( + new Swift_TransportException( + $e->getMessage()) + ); + } + + return $response; + } + + /** Send an email to the given recipients from the given reverse path */ + private function _doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients) + { + $sent = 0; + $this->_doMailFromCommand($reversePath); + foreach ($recipients as $forwardPath) { + try { + $this->_doRcptToCommand($forwardPath); + $sent++; + } catch (Swift_TransportException $e) { + $failedRecipients[] = $forwardPath; + } + } + + if ($sent != 0) { + $this->_doDataCommand(); + $this->_streamMessage($message); + } else { + $this->reset(); + } + + return $sent; + } + + /** Send a message to the given To: recipients */ + private function _sendTo(Swift_Mime_Message $message, $reversePath, array $to, array &$failedRecipients) + { + if (empty($to)) { + return 0; + } + + return $this->_doMailTransaction($message, $reversePath, array_keys($to), + $failedRecipients); + } + + /** Send a message to all Bcc: recipients */ + private function _sendBcc(Swift_Mime_Message $message, $reversePath, array $bcc, array &$failedRecipients) + { + $sent = 0; + foreach ($bcc as $forwardPath => $name) { + $message->setBcc(array($forwardPath => $name)); + $sent += $this->_doMailTransaction( + $message, $reversePath, array($forwardPath), $failedRecipients + ); + } + + return $sent; + } + + /** Try to determine the hostname of the server this is run on */ + private function _lookupHostname() + { + if (!empty($_SERVER['SERVER_NAME']) + && $this->_isFqdn($_SERVER['SERVER_NAME'])) { + $this->_domain = $_SERVER['SERVER_NAME']; + } elseif (!empty($_SERVER['SERVER_ADDR'])) { + $this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']); + } + } + + /** Determine is the $hostname is a fully-qualified name */ + private function _isFqdn($hostname) + { + // We could do a really thorough check, but there's really no point + if (false !== $dotPos = strpos($hostname, '.')) { + return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1); + } else { + return false; + } + } + + /** + * Destructor. + */ + public function __destruct() + { + $this->stop(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php new file mode 100644 index 0000000..53f721d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php @@ -0,0 +1,81 @@ +executeCommand("AUTH CRAM-MD5\r\n", array(334)); + $challenge = base64_decode(substr($challenge, 4)); + $message = base64_encode( + $username.' '.$this->_getResponse($password, $challenge) + ); + $agent->executeCommand(sprintf("%s\r\n", $message), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + /** + * Generate a CRAM-MD5 response from a server challenge. + * + * @param string $secret + * @param string $challenge + * + * @return string + */ + private function _getResponse($secret, $challenge) + { + if (strlen($secret) > 64) { + $secret = pack('H32', md5($secret)); + } + + if (strlen($secret) < 64) { + $secret = str_pad($secret, 64, chr(0)); + } + + $k_ipad = substr($secret, 0, 64) ^ str_repeat(chr(0x36), 64); + $k_opad = substr($secret, 0, 64) ^ str_repeat(chr(0x5C), 64); + + $inner = pack('H32', md5($k_ipad.$challenge)); + $digest = md5($k_opad.$inner); + + return $digest; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php new file mode 100644 index 0000000..6ab6e33 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php @@ -0,0 +1,51 @@ +executeCommand("AUTH LOGIN\r\n", array(334)); + $agent->executeCommand(sprintf("%s\r\n", base64_encode($username)), array(334)); + $agent->executeCommand(sprintf("%s\r\n", base64_encode($password)), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php new file mode 100644 index 0000000..f1102bb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php @@ -0,0 +1,726 @@ + + */ +class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Esmtp_Authenticator +{ + const NTLMSIG = "NTLMSSP\x00"; + const DESCONST = 'KGS!@#$%'; + + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'NTLM'; + } + + /** + * Try to authenticate the user with $username and $password. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $username + * @param string $password + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password) + { + if (!function_exists('mcrypt_module_open')) { + throw new LogicException('The mcrypt functions need to be enabled to use the NTLM authenticator.'); + } + + if (!function_exists('openssl_random_pseudo_bytes')) { + throw new LogicException('The OpenSSL extension must be enabled to use the NTLM authenticator.'); + } + + if (!function_exists('bcmul')) { + throw new LogicException('The BCMatch functions must be enabled to use the NTLM authenticator.'); + } + + try { + // execute AUTH command and filter out the code at the beginning + // AUTH NTLM xxxx + $response = base64_decode(substr(trim($this->sendMessage1($agent)), 4)); + + // extra parameters for our unit cases + $timestamp = func_num_args() > 3 ? func_get_arg(3) : $this->getCorrectTimestamp(bcmul(microtime(true), '1000')); + $client = func_num_args() > 4 ? func_get_arg(4) : $this->getRandomBytes(8); + + // Message 3 response + $this->sendMessage3($response, $username, $password, $timestamp, $client, $agent); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + protected function si2bin($si, $bits = 32) + { + $bin = null; + if ($si >= -pow(2, $bits - 1) && ($si <= pow(2, $bits - 1))) { + // positive or zero + if ($si >= 0) { + $bin = base_convert($si, 10, 2); + // pad to $bits bit + $bin_length = strlen($bin); + if ($bin_length < $bits) { + $bin = str_repeat('0', $bits - $bin_length).$bin; + } + } else { + // negative + $si = -$si - pow(2, $bits); + $bin = base_convert($si, 10, 2); + $bin_length = strlen($bin); + if ($bin_length > $bits) { + $bin = str_repeat('1', $bits - $bin_length).$bin; + } + } + } + + return $bin; + } + + /** + * Send our auth message and returns the response. + * + * @param Swift_Transport_SmtpAgent $agent + * + * @return string SMTP Response + */ + protected function sendMessage1(Swift_Transport_SmtpAgent $agent) + { + $message = $this->createMessage1(); + + return $agent->executeCommand(sprintf("AUTH %s %s\r\n", $this->getAuthKeyword(), base64_encode($message)), array(334)); + } + + /** + * Fetch all details of our response (message 2). + * + * @param string $response + * + * @return array our response parsed + */ + protected function parseMessage2($response) + { + $responseHex = bin2hex($response); + $length = floor(hexdec(substr($responseHex, 28, 4)) / 256) * 2; + $offset = floor(hexdec(substr($responseHex, 32, 4)) / 256) * 2; + $challenge = $this->hex2bin(substr($responseHex, 48, 16)); + $context = $this->hex2bin(substr($responseHex, 64, 16)); + $targetInfoH = $this->hex2bin(substr($responseHex, 80, 16)); + $targetName = $this->hex2bin(substr($responseHex, $offset, $length)); + $offset = floor(hexdec(substr($responseHex, 88, 4)) / 256) * 2; + $targetInfoBlock = substr($responseHex, $offset); + list($domainName, $serverName, $DNSDomainName, $DNSServerName, $terminatorByte) = $this->readSubBlock($targetInfoBlock); + + return array( + $challenge, + $context, + $targetInfoH, + $targetName, + $domainName, + $serverName, + $DNSDomainName, + $DNSServerName, + $this->hex2bin($targetInfoBlock), + $terminatorByte, + ); + } + + /** + * Read the blob information in from message2. + * + * @param $block + * + * @return array + */ + protected function readSubBlock($block) + { + // remove terminatorByte cause it's always the same + $block = substr($block, 0, -8); + + $length = strlen($block); + $offset = 0; + $data = array(); + while ($offset < $length) { + $blockLength = hexdec(substr(substr($block, $offset, 8), -4)) / 256; + $offset += 8; + $data[] = $this->hex2bin(substr($block, $offset, $blockLength * 2)); + $offset += $blockLength * 2; + } + + if (count($data) == 3) { + $data[] = $data[2]; + $data[2] = ''; + } + + $data[] = $this->createByte('00'); + + return $data; + } + + /** + * Send our final message with all our data. + * + * @param string $response Message 1 response (message 2) + * @param string $username + * @param string $password + * @param string $timestamp + * @param string $client + * @param Swift_Transport_SmtpAgent $agent + * @param bool $v2 Use version2 of the protocol + * + * @return string + */ + protected function sendMessage3($response, $username, $password, $timestamp, $client, Swift_Transport_SmtpAgent $agent, $v2 = true) + { + list($domain, $username) = $this->getDomainAndUsername($username); + //$challenge, $context, $targetInfoH, $targetName, $domainName, $workstation, $DNSDomainName, $DNSServerName, $blob, $ter + list($challenge, , , , , $workstation, , , $blob) = $this->parseMessage2($response); + + if (!$v2) { + // LMv1 + $lmResponse = $this->createLMPassword($password, $challenge); + // NTLMv1 + $ntlmResponse = $this->createNTLMPassword($password, $challenge); + } else { + // LMv2 + $lmResponse = $this->createLMv2Password($password, $username, $domain, $challenge, $client); + // NTLMv2 + $ntlmResponse = $this->createNTLMv2Hash($password, $username, $domain, $challenge, $blob, $timestamp, $client); + } + + $message = $this->createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse); + + return $agent->executeCommand(sprintf("%s\r\n", base64_encode($message)), array(235)); + } + + /** + * Create our message 1. + * + * @return string + */ + protected function createMessage1() + { + return self::NTLMSIG + .$this->createByte('01') // Message 1 +.$this->createByte('0702'); // Flags + } + + /** + * Create our message 3. + * + * @param string $domain + * @param string $username + * @param string $workstation + * @param string $lmResponse + * @param string $ntlmResponse + * + * @return string + */ + protected function createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse) + { + // Create security buffers + $domainSec = $this->createSecurityBuffer($domain, 64); + $domainInfo = $this->readSecurityBuffer(bin2hex($domainSec)); + $userSec = $this->createSecurityBuffer($username, ($domainInfo[0] + $domainInfo[1]) / 2); + $userInfo = $this->readSecurityBuffer(bin2hex($userSec)); + $workSec = $this->createSecurityBuffer($workstation, ($userInfo[0] + $userInfo[1]) / 2); + $workInfo = $this->readSecurityBuffer(bin2hex($workSec)); + $lmSec = $this->createSecurityBuffer($lmResponse, ($workInfo[0] + $workInfo[1]) / 2, true); + $lmInfo = $this->readSecurityBuffer(bin2hex($lmSec)); + $ntlmSec = $this->createSecurityBuffer($ntlmResponse, ($lmInfo[0] + $lmInfo[1]) / 2, true); + + return self::NTLMSIG + .$this->createByte('03') // TYPE 3 message +.$lmSec // LM response header +.$ntlmSec // NTLM response header +.$domainSec // Domain header +.$userSec // User header +.$workSec // Workstation header +.$this->createByte('000000009a', 8) // session key header (empty) +.$this->createByte('01020000') // FLAGS +.$this->convertTo16bit($domain) // domain name +.$this->convertTo16bit($username) // username +.$this->convertTo16bit($workstation) // workstation +.$lmResponse + .$ntlmResponse; + } + + /** + * @param string $timestamp Epoch timestamp in microseconds + * @param string $client Random bytes + * @param string $targetInfo + * + * @return string + */ + protected function createBlob($timestamp, $client, $targetInfo) + { + return $this->createByte('0101') + .$this->createByte('00') + .$timestamp + .$client + .$this->createByte('00') + .$targetInfo + .$this->createByte('00'); + } + + /** + * Get domain and username from our username. + * + * @example DOMAIN\username + * + * @param string $name + * + * @return array + */ + protected function getDomainAndUsername($name) + { + if (strpos($name, '\\') !== false) { + return explode('\\', $name); + } + + list($user, $domain) = explode('@', $name); + + return array($domain, $user); + } + + /** + * Create LMv1 response. + * + * @param string $password + * @param string $challenge + * + * @return string + */ + protected function createLMPassword($password, $challenge) + { + // FIRST PART + $password = $this->createByte(strtoupper($password), 14, false); + list($key1, $key2) = str_split($password, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + + $constantDecrypt = $this->createByte($this->desEncrypt(self::DESCONST, $desKey1).$this->desEncrypt(self::DESCONST, $desKey2), 21, false); + + // SECOND PART + list($key1, $key2, $key3) = str_split($constantDecrypt, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + $desKey3 = $this->createDesKey($key3); + + return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); + } + + /** + * Create NTLMv1 response. + * + * @param string $password + * @param string $challenge + * + * @return string + */ + protected function createNTLMPassword($password, $challenge) + { + // FIRST PART + $ntlmHash = $this->createByte($this->md4Encrypt($password), 21, false); + list($key1, $key2, $key3) = str_split($ntlmHash, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + $desKey3 = $this->createDesKey($key3); + + return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); + } + + /** + * Convert a normal timestamp to a tenth of a microtime epoch time. + * + * @param string $time + * + * @return string + */ + protected function getCorrectTimestamp($time) + { + // Get our timestamp (tricky!) + bcscale(0); + + $time = number_format($time, 0, '.', ''); // save microtime to string + $time = bcadd($time, '11644473600000'); // add epoch time + $time = bcmul($time, 10000); // tenths of a microsecond. + + $binary = $this->si2bin($time, 64); // create 64 bit binary string + $timestamp = ''; + for ($i = 0; $i < 8; $i++) { + $timestamp .= chr(bindec(substr($binary, -(($i + 1) * 8), 8))); + } + + return $timestamp; + } + + /** + * Create LMv2 response. + * + * @param string $password + * @param string $username + * @param string $domain + * @param string $challenge NTLM Challenge + * @param string $client Random string + * + * @return string + */ + protected function createLMv2Password($password, $username, $domain, $challenge, $client) + { + $lmPass = '00'; // by default 00 + // if $password > 15 than we can't use this method + if (strlen($password) <= 15) { + $ntlmHash = $this->md4Encrypt($password); + $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); + + $lmPass = bin2hex($this->md5Encrypt($ntml2Hash, $challenge.$client).$client); + } + + return $this->createByte($lmPass, 24); + } + + /** + * Create NTLMv2 response. + * + * @param string $password + * @param string $username + * @param string $domain + * @param string $challenge Hex values + * @param string $targetInfo Hex values + * @param string $timestamp + * @param string $client Random bytes + * + * @return string + * + * @see http://davenport.sourceforge.net/ntlm.html#theNtlmResponse + */ + protected function createNTLMv2Hash($password, $username, $domain, $challenge, $targetInfo, $timestamp, $client) + { + $ntlmHash = $this->md4Encrypt($password); + $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); + + // create blob + $blob = $this->createBlob($timestamp, $client, $targetInfo); + + $ntlmv2Response = $this->md5Encrypt($ntml2Hash, $challenge.$blob); + + return $ntlmv2Response.$blob; + } + + protected function createDesKey($key) + { + $material = array(bin2hex($key[0])); + $len = strlen($key); + for ($i = 1; $i < $len; $i++) { + list($high, $low) = str_split(bin2hex($key[$i])); + $v = $this->castToByte(ord($key[$i - 1]) << (7 + 1 - $i) | $this->uRShift(hexdec(dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xf)), $i)); + $material[] = str_pad(substr(dechex($v), -2), 2, '0', STR_PAD_LEFT); // cast to byte + } + $material[] = str_pad(substr(dechex($this->castToByte(ord($key[6]) << 1)), -2), 2, '0'); + + // odd parity + foreach ($material as $k => $v) { + $b = $this->castToByte(hexdec($v)); + $needsParity = (($this->uRShift($b, 7) ^ $this->uRShift($b, 6) ^ $this->uRShift($b, 5) + ^ $this->uRShift($b, 4) ^ $this->uRShift($b, 3) ^ $this->uRShift($b, 2) + ^ $this->uRShift($b, 1)) & 0x01) == 0; + + list($high, $low) = str_split($v); + if ($needsParity) { + $material[$k] = dechex(hexdec($high) | 0x0).dechex(hexdec($low) | 0x1); + } else { + $material[$k] = dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xe); + } + } + + return $this->hex2bin(implode('', $material)); + } + + /** HELPER FUNCTIONS */ + /** + * Create our security buffer depending on length and offset. + * + * @param string $value Value we want to put in + * @param int $offset start of value + * @param bool $is16 Do we 16bit string or not? + * + * @return string + */ + protected function createSecurityBuffer($value, $offset, $is16 = false) + { + $length = strlen(bin2hex($value)); + $length = $is16 ? $length / 2 : $length; + $length = $this->createByte(str_pad(dechex($length), 2, '0', STR_PAD_LEFT), 2); + + return $length.$length.$this->createByte(dechex($offset), 4); + } + + /** + * Read our security buffer to fetch length and offset of our value. + * + * @param string $value Securitybuffer in hex + * + * @return array array with length and offset + */ + protected function readSecurityBuffer($value) + { + $length = floor(hexdec(substr($value, 0, 4)) / 256) * 2; + $offset = floor(hexdec(substr($value, 8, 4)) / 256) * 2; + + return array($length, $offset); + } + + /** + * Cast to byte java equivalent to (byte). + * + * @param int $v + * + * @return int + */ + protected function castToByte($v) + { + return (($v + 128) % 256) - 128; + } + + /** + * Java unsigned right bitwise + * $a >>> $b. + * + * @param int $a + * @param int $b + * + * @return int + */ + protected function uRShift($a, $b) + { + if ($b == 0) { + return $a; + } + + return ($a >> $b) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($b - 1)); + } + + /** + * Right padding with 0 to certain length. + * + * @param string $input + * @param int $bytes Length of bytes + * @param bool $isHex Did we provided hex value + * + * @return string + */ + protected function createByte($input, $bytes = 4, $isHex = true) + { + if ($isHex) { + $byte = $this->hex2bin(str_pad($input, $bytes * 2, '00')); + } else { + $byte = str_pad($input, $bytes, "\x00"); + } + + return $byte; + } + + /** + * Create random bytes. + * + * @param $length + * + * @return string + */ + protected function getRandomBytes($length) + { + $bytes = openssl_random_pseudo_bytes($length, $strong); + + if (false !== $bytes && true === $strong) { + return $bytes; + } + + throw new RuntimeException('OpenSSL did not produce a secure random number.'); + } + + /** ENCRYPTION ALGORITHMS */ + /** + * DES Encryption. + * + * @param string $value + * @param string $key + * + * @return string + */ + protected function desEncrypt($value, $key) + { + $cipher = mcrypt_module_open(MCRYPT_DES, '', 'ecb', ''); + mcrypt_generic_init($cipher, $key, mcrypt_create_iv(mcrypt_enc_get_iv_size($cipher), MCRYPT_DEV_RANDOM)); + + return mcrypt_generic($cipher, $value); + } + + /** + * MD5 Encryption. + * + * @param string $key Encryption key + * @param string $msg Message to encrypt + * + * @return string + */ + protected function md5Encrypt($key, $msg) + { + $blocksize = 64; + if (strlen($key) > $blocksize) { + $key = pack('H*', md5($key)); + } + + $key = str_pad($key, $blocksize, "\0"); + $ipadk = $key ^ str_repeat("\x36", $blocksize); + $opadk = $key ^ str_repeat("\x5c", $blocksize); + + return pack('H*', md5($opadk.pack('H*', md5($ipadk.$msg)))); + } + + /** + * MD4 Encryption. + * + * @param string $input + * + * @return string + * + * @see http://php.net/manual/en/ref.hash.php + */ + protected function md4Encrypt($input) + { + $input = $this->convertTo16bit($input); + + return function_exists('hash') ? $this->hex2bin(hash('md4', $input)) : mhash(MHASH_MD4, $input); + } + + /** + * Convert UTF-8 to UTF-16. + * + * @param string $input + * + * @return string + */ + protected function convertTo16bit($input) + { + return iconv('UTF-8', 'UTF-16LE', $input); + } + + /** + * Hex2bin replacement for < PHP 5.4. + * + * @param string $hex + * + * @return string Binary + */ + protected function hex2bin($hex) + { + if (function_exists('hex2bin')) { + return hex2bin($hex); + } else { + return pack('H*', $hex); + } + } + + /** + * @param string $message + */ + protected function debug($message) + { + $message = bin2hex($message); + $messageId = substr($message, 16, 8); + echo substr($message, 0, 16)." NTLMSSP Signature
\n"; + echo $messageId." Type Indicator
\n"; + + if ($messageId == '02000000') { + $map = array( + 'Challenge', + 'Context', + 'Target Information Security Buffer', + 'Target Name Data', + 'NetBIOS Domain Name', + 'NetBIOS Server Name', + 'DNS Domain Name', + 'DNS Server Name', + 'BLOB', + 'Target Information Terminator', + ); + + $data = $this->parseMessage2($this->hex2bin($message)); + + foreach ($map as $key => $value) { + echo bin2hex($data[$key]).' - '.$data[$key].' ||| '.$value."
\n"; + } + } elseif ($messageId == '03000000') { + $i = 0; + $data[$i++] = substr($message, 24, 16); + list($lmLength, $lmOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 40, 16); + list($ntmlLength, $ntmlOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 56, 16); + list($targetLength, $targetOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 72, 16); + list($userLength, $userOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 88, 16); + list($workLength, $workOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 104, 16); + $data[$i++] = substr($message, 120, 8); + $data[$i++] = substr($message, $targetOffset, $targetLength); + $data[$i++] = substr($message, $userOffset, $userLength); + $data[$i++] = substr($message, $workOffset, $workLength); + $data[$i++] = substr($message, $lmOffset, $lmLength); + $data[$i] = substr($message, $ntmlOffset, $ntmlLength); + + $map = array( + 'LM Response Security Buffer', + 'NTLM Response Security Buffer', + 'Target Name Security Buffer', + 'User Name Security Buffer', + 'Workstation Name Security Buffer', + 'Session Key Security Buffer', + 'Flags', + 'Target Name Data', + 'User Name Data', + 'Workstation Name Data', + 'LM Response Data', + 'NTLM Response Data', + ); + + foreach ($map as $key => $value) { + echo $data[$key].' - '.$this->hex2bin($data[$key]).' ||| '.$value."
\n"; + } + } + + echo '

'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php new file mode 100644 index 0000000..43219f9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php @@ -0,0 +1,50 @@ +executeCommand(sprintf("AUTH PLAIN %s\r\n", $message), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php new file mode 100644 index 0000000..ca35e7b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php @@ -0,0 +1,70 @@ + + * $transport = Swift_SmtpTransport::newInstance('smtp.gmail.com', 587, 'tls') + * ->setAuthMode('XOAUTH2') + * ->setUsername('YOUR_EMAIL_ADDRESS') + * ->setPassword('YOUR_ACCESS_TOKEN'); + * + * + * @author xu.li + * + * @see https://developers.google.com/google-apps/gmail/xoauth2_protocol + */ +class Swift_Transport_Esmtp_Auth_XOAuth2Authenticator implements Swift_Transport_Esmtp_Authenticator +{ + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'XOAUTH2'; + } + + /** + * Try to authenticate the user with $email and $token. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $email + * @param string $token + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $email, $token) + { + try { + $param = $this->constructXOAuth2Params($email, $token); + $agent->executeCommand('AUTH XOAUTH2 '.$param."\r\n", array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + /** + * Construct the auth parameter. + * + * @see https://developers.google.com/google-apps/gmail/xoauth2_protocol#the_sasl_xoauth2_mechanism + */ + protected function constructXOAuth2Params($email, $token) + { + return base64_encode("user=$email\1auth=Bearer $token\1\1"); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php new file mode 100644 index 0000000..2b0e682 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php @@ -0,0 +1,263 @@ +setAuthenticators($authenticators); + } + + /** + * Set the Authenticators which can process a login request. + * + * @param Swift_Transport_Esmtp_Authenticator[] $authenticators + */ + public function setAuthenticators(array $authenticators) + { + $this->_authenticators = $authenticators; + } + + /** + * Get the Authenticators which can process a login request. + * + * @return Swift_Transport_Esmtp_Authenticator[] + */ + public function getAuthenticators() + { + return $this->_authenticators; + } + + /** + * Set the username to authenticate with. + * + * @param string $username + */ + public function setUsername($username) + { + $this->_username = $username; + } + + /** + * Get the username to authenticate with. + * + * @return string + */ + public function getUsername() + { + return $this->_username; + } + + /** + * Set the password to authenticate with. + * + * @param string $password + */ + public function setPassword($password) + { + $this->_password = $password; + } + + /** + * Get the password to authenticate with. + * + * @return string + */ + public function getPassword() + { + return $this->_password; + } + + /** + * Set the auth mode to use to authenticate. + * + * @param string $mode + */ + public function setAuthMode($mode) + { + $this->_auth_mode = $mode; + } + + /** + * Get the auth mode to use to authenticate. + * + * @return string + */ + public function getAuthMode() + { + return $this->_auth_mode; + } + + /** + * Get the name of the ESMTP extension this handles. + * + * @return bool + */ + public function getHandledKeyword() + { + return 'AUTH'; + } + + /** + * Set the parameters which the EHLO greeting indicated. + * + * @param string[] $parameters + */ + public function setKeywordParams(array $parameters) + { + $this->_esmtpParams = $parameters; + } + + /** + * Runs immediately after a EHLO has been issued. + * + * @param Swift_Transport_SmtpAgent $agent to read/write + */ + public function afterEhlo(Swift_Transport_SmtpAgent $agent) + { + if ($this->_username) { + $count = 0; + foreach ($this->_getAuthenticatorsForAgent() as $authenticator) { + if (in_array(strtolower($authenticator->getAuthKeyword()), + array_map('strtolower', $this->_esmtpParams))) { + $count++; + if ($authenticator->authenticate($agent, $this->_username, $this->_password)) { + return; + } + } + } + throw new Swift_TransportException( + 'Failed to authenticate on SMTP server with username "'. + $this->_username.'" using '.$count.' possible authenticators' + ); + } + } + + /** + * Not used. + */ + public function getMailParams() + { + return array(); + } + + /** + * Not used. + */ + public function getRcptParams() + { + return array(); + } + + /** + * Not used. + */ + public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false) + { + } + + /** + * Returns +1, -1 or 0 according to the rules for usort(). + * + * This method is called to ensure extensions can be execute in an appropriate order. + * + * @param string $esmtpKeyword to compare with + * + * @return int + */ + public function getPriorityOver($esmtpKeyword) + { + return 0; + } + + /** + * Returns an array of method names which are exposed to the Esmtp class. + * + * @return string[] + */ + public function exposeMixinMethods() + { + return array('setUsername', 'getUsername', 'setPassword', 'getPassword', 'setAuthMode', 'getAuthMode'); + } + + /** + * Not used. + */ + public function resetState() + { + } + + /** + * Returns the authenticator list for the given agent. + * + * @param Swift_Transport_SmtpAgent $agent + * + * @return array + */ + protected function _getAuthenticatorsForAgent() + { + if (!$mode = strtolower($this->_auth_mode)) { + return $this->_authenticators; + } + + foreach ($this->_authenticators as $authenticator) { + if (strtolower($authenticator->getAuthKeyword()) == $mode) { + return array($authenticator); + } + } + + throw new Swift_TransportException('Auth mode '.$mode.' is invalid'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php new file mode 100644 index 0000000..12a9abf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php @@ -0,0 +1,35 @@ +. + * + * @return string[] + */ + public function getMailParams(); + + /** + * Get params which are appended to RCPT TO:<>. + * + * @return string[] + */ + public function getRcptParams(); + + /** + * Runs when a command is due to be sent. + * + * @param Swift_Transport_SmtpAgent $agent to read/write + * @param string $command to send + * @param int[] $codes expected in response + * @param string[] $failedRecipients to collect failures + * @param bool $stop to be set true by-reference if the command is now sent + */ + public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false); + + /** + * Returns +1, -1 or 0 according to the rules for usort(). + * + * This method is called to ensure extensions can be execute in an appropriate order. + * + * @param string $esmtpKeyword to compare with + * + * @return int + */ + public function getPriorityOver($esmtpKeyword); + + /** + * Returns an array of method names which are exposed to the Esmtp class. + * + * @return string[] + */ + public function exposeMixinMethods(); + + /** + * Tells this handler to clear any buffers and reset its state. + */ + public function resetState(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php new file mode 100644 index 0000000..5b4f50c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php @@ -0,0 +1,386 @@ + 'tcp', + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'blocking' => 1, + 'tls' => false, + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + ); + + /** + * Creates a new EsmtpTransport using the given I/O buffer. + * + * @param Swift_Transport_IoBuffer $buf + * @param Swift_Transport_EsmtpHandler[] $extensionHandlers + * @param Swift_Events_EventDispatcher $dispatcher + */ + public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher) + { + parent::__construct($buf, $dispatcher); + $this->setExtensionHandlers($extensionHandlers); + } + + /** + * Set the host to connect to. + * + * @param string $host + * + * @return Swift_Transport_EsmtpTransport + */ + public function setHost($host) + { + $this->_params['host'] = $host; + + return $this; + } + + /** + * Get the host to connect to. + * + * @return string + */ + public function getHost() + { + return $this->_params['host']; + } + + /** + * Set the port to connect to. + * + * @param int $port + * + * @return Swift_Transport_EsmtpTransport + */ + public function setPort($port) + { + $this->_params['port'] = (int) $port; + + return $this; + } + + /** + * Get the port to connect to. + * + * @return int + */ + public function getPort() + { + return $this->_params['port']; + } + + /** + * Set the connection timeout. + * + * @param int $timeout seconds + * + * @return Swift_Transport_EsmtpTransport + */ + public function setTimeout($timeout) + { + $this->_params['timeout'] = (int) $timeout; + $this->_buffer->setParam('timeout', (int) $timeout); + + return $this; + } + + /** + * Get the connection timeout. + * + * @return int + */ + public function getTimeout() + { + return $this->_params['timeout']; + } + + /** + * Set the encryption type (tls or ssl). + * + * @param string $encryption + * + * @return Swift_Transport_EsmtpTransport + */ + public function setEncryption($encryption) + { + if ('tls' == $encryption) { + $this->_params['protocol'] = 'tcp'; + $this->_params['tls'] = true; + } else { + $this->_params['protocol'] = $encryption; + $this->_params['tls'] = false; + } + + return $this; + } + + /** + * Get the encryption type. + * + * @return string + */ + public function getEncryption() + { + return $this->_params['tls'] ? 'tls' : $this->_params['protocol']; + } + + /** + * Sets the source IP. + * + * @param string $source + * + * @return Swift_Transport_EsmtpTransport + */ + public function setSourceIp($source) + { + $this->_params['sourceIp'] = $source; + + return $this; + } + + /** + * Returns the IP used to connect to the destination. + * + * @return string + */ + public function getSourceIp() + { + return isset($this->_params['sourceIp']) ? $this->_params['sourceIp'] : null; + } + + /** + * Set ESMTP extension handlers. + * + * @param Swift_Transport_EsmtpHandler[] $handlers + * + * @return Swift_Transport_EsmtpTransport + */ + public function setExtensionHandlers(array $handlers) + { + $assoc = array(); + foreach ($handlers as $handler) { + $assoc[$handler->getHandledKeyword()] = $handler; + } + uasort($assoc, array($this, '_sortHandlers')); + $this->_handlers = $assoc; + $this->_setHandlerParams(); + + return $this; + } + + /** + * Get ESMTP extension handlers. + * + * @return Swift_Transport_EsmtpHandler[] + */ + public function getExtensionHandlers() + { + return array_values($this->_handlers); + } + + /** + * Run a command against the buffer, expecting the given response codes. + * + * If no response codes are given, the response will not be validated. + * If codes are given, an exception will be thrown on an invalid response. + * + * @param string $command + * @param int[] $codes + * @param string[] $failures An array of failures by-reference + * + * @return string + */ + public function executeCommand($command, $codes = array(), &$failures = null) + { + $failures = (array) $failures; + $stopSignal = false; + $response = null; + foreach ($this->_getActiveHandlers() as $handler) { + $response = $handler->onCommand( + $this, $command, $codes, $failures, $stopSignal + ); + if ($stopSignal) { + return $response; + } + } + + return parent::executeCommand($command, $codes, $failures); + } + + // -- Mixin invocation code + + /** Mixin handling method for ESMTP handlers */ + public function __call($method, $args) + { + foreach ($this->_handlers as $handler) { + if (in_array(strtolower($method), + array_map('strtolower', (array) $handler->exposeMixinMethods()) + )) { + $return = call_user_func_array(array($handler, $method), $args); + // Allow fluid method calls + if (is_null($return) && substr($method, 0, 3) == 'set') { + return $this; + } else { + return $return; + } + } + } + trigger_error('Call to undefined method '.$method, E_USER_ERROR); + } + + /** Get the params to initialize the buffer */ + protected function _getBufferParams() + { + return $this->_params; + } + + /** Overridden to perform EHLO instead */ + protected function _doHeloCommand() + { + try { + $response = $this->executeCommand( + sprintf("EHLO %s\r\n", $this->_domain), array(250) + ); + } catch (Swift_TransportException $e) { + return parent::_doHeloCommand(); + } + + if ($this->_params['tls']) { + try { + $this->executeCommand("STARTTLS\r\n", array(220)); + + if (!$this->_buffer->startTLS()) { + throw new Swift_TransportException('Unable to connect with TLS encryption'); + } + + try { + $response = $this->executeCommand( + sprintf("EHLO %s\r\n", $this->_domain), array(250) + ); + } catch (Swift_TransportException $e) { + return parent::_doHeloCommand(); + } + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + } + + $this->_capabilities = $this->_getCapabilities($response); + $this->_setHandlerParams(); + foreach ($this->_getActiveHandlers() as $handler) { + $handler->afterEhlo($this); + } + } + + /** Overridden to add Extension support */ + protected function _doMailFromCommand($address) + { + $handlers = $this->_getActiveHandlers(); + $params = array(); + foreach ($handlers as $handler) { + $params = array_merge($params, (array) $handler->getMailParams()); + } + $paramStr = !empty($params) ? ' '.implode(' ', $params) : ''; + $this->executeCommand( + sprintf("MAIL FROM:<%s>%s\r\n", $address, $paramStr), array(250) + ); + } + + /** Overridden to add Extension support */ + protected function _doRcptToCommand($address) + { + $handlers = $this->_getActiveHandlers(); + $params = array(); + foreach ($handlers as $handler) { + $params = array_merge($params, (array) $handler->getRcptParams()); + } + $paramStr = !empty($params) ? ' '.implode(' ', $params) : ''; + $this->executeCommand( + sprintf("RCPT TO:<%s>%s\r\n", $address, $paramStr), array(250, 251, 252) + ); + } + + /** Determine ESMTP capabilities by function group */ + private function _getCapabilities($ehloResponse) + { + $capabilities = array(); + $ehloResponse = trim($ehloResponse); + $lines = explode("\r\n", $ehloResponse); + array_shift($lines); + foreach ($lines as $line) { + if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) { + $keyword = strtoupper($matches[1]); + $paramStr = strtoupper(ltrim($matches[2], ' =')); + $params = !empty($paramStr) ? explode(' ', $paramStr) : array(); + $capabilities[$keyword] = $params; + } + } + + return $capabilities; + } + + /** Set parameters which are used by each extension handler */ + private function _setHandlerParams() + { + foreach ($this->_handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->_capabilities)) { + $handler->setKeywordParams($this->_capabilities[$keyword]); + } + } + } + + /** Get ESMTP handlers which are currently ok to use */ + private function _getActiveHandlers() + { + $handlers = array(); + foreach ($this->_handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->_capabilities)) { + $handlers[] = $handler; + } + } + + return $handlers; + } + + /** Custom sort for extension handler ordering */ + private function _sortHandlers($a, $b) + { + return $a->getPriorityOver($b->getHandledKeyword()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php new file mode 100644 index 0000000..59c7302 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php @@ -0,0 +1,85 @@ +_transports); + $sent = 0; + + for ($i = 0; $i < $maxTransports + && $transport = $this->_getNextTransport(); ++$i) { + try { + if (!$transport->isStarted()) { + $transport->start(); + } + + return $transport->send($message, $failedRecipients); + } catch (Swift_TransportException $e) { + $this->_killCurrentTransport(); + } + } + + if (count($this->_transports) == 0) { + throw new Swift_TransportException( + 'All Transports in FailoverTransport failed, or no Transports available' + ); + } + + return $sent; + } + + protected function _getNextTransport() + { + if (!isset($this->_currentTransport)) { + $this->_currentTransport = parent::_getNextTransport(); + } + + return $this->_currentTransport; + } + + protected function _killCurrentTransport() + { + $this->_currentTransport = null; + parent::_killCurrentTransport(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php new file mode 100644 index 0000000..af97adf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php @@ -0,0 +1,67 @@ +_transports = $transports; + $this->_deadTransports = array(); + } + + /** + * Get $transports to delegate to. + * + * @return Swift_Transport[] + */ + public function getTransports() + { + return array_merge($this->_transports, $this->_deadTransports); + } + + /** + * Test if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return count($this->_transports) > 0; + } + + /** + * Start this Transport mechanism. + */ + public function start() + { + $this->_transports = array_merge($this->_transports, $this->_deadTransports); + } + + /** + * Stop this Transport mechanism. + */ + public function stop() + { + foreach ($this->_transports as $transport) { + $transport->stop(); + } + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $maxTransports = count($this->_transports); + $sent = 0; + + for ($i = 0; $i < $maxTransports + && $transport = $this->_getNextTransport(); ++$i) { + try { + if (!$transport->isStarted()) { + $transport->start(); + } + if ($sent = $transport->send($message, $failedRecipients)) { + break; + } + } catch (Swift_TransportException $e) { + $this->_killCurrentTransport(); + } + } + + if (count($this->_transports) == 0) { + throw new Swift_TransportException( + 'All Transports in LoadBalancedTransport failed, or no Transports available' + ); + } + + return $sent; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + foreach ($this->_transports as $transport) { + $transport->registerPlugin($plugin); + } + } + + /** + * Rotates the transport list around and returns the first instance. + * + * @return Swift_Transport + */ + protected function _getNextTransport() + { + if ($next = array_shift($this->_transports)) { + $this->_transports[] = $next; + } + + return $next; + } + + /** + * Tag the currently used (top of stack) transport as dead/useless. + */ + protected function _killCurrentTransport() + { + if ($transport = array_pop($this->_transports)) { + try { + $transport->stop(); + } catch (Exception $e) { + } + $this->_deadTransports[] = $transport; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php new file mode 100644 index 0000000..77489ce --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php @@ -0,0 +1,32 @@ +_invoker = $invoker; + $this->_eventDispatcher = $eventDispatcher; + } + + /** + * Not used. + */ + public function isStarted() + { + return false; + } + + /** + * Not used. + */ + public function start() + { + } + + /** + * Not used. + */ + public function stop() + { + } + + /** + * Set the additional parameters used on the mail() function. + * + * This string is formatted for sprintf() where %s is the sender address. + * + * @param string $params + * + * @return Swift_Transport_MailTransport + */ + public function setExtraParams($params) + { + $this->_extraParams = $params; + + return $this; + } + + /** + * Get the additional parameters used on the mail() function. + * + * This string is formatted for sprintf() where %s is the sender address. + * + * @return string + */ + public function getExtraParams() + { + return $this->_extraParams; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + $count = ( + count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ); + + $toHeader = $message->getHeaders()->get('To'); + $subjectHeader = $message->getHeaders()->get('Subject'); + + if (!$toHeader) { + $this->_throwException(new Swift_TransportException('Cannot send message without a recipient')); + } + $to = $toHeader->getFieldBody(); + $subject = $subjectHeader ? $subjectHeader->getFieldBody() : ''; + + $reversePath = $this->_getReversePath($message); + + // Remove headers that would otherwise be duplicated + $message->getHeaders()->remove('To'); + $message->getHeaders()->remove('Subject'); + + $messageStr = $message->toString(); + + $message->getHeaders()->set($toHeader); + $message->getHeaders()->set($subjectHeader); + + // Separate headers from body + if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n")) { + $headers = substr($messageStr, 0, $endHeaders)."\r\n"; //Keep last EOL + $body = substr($messageStr, $endHeaders + 4); + } else { + $headers = $messageStr."\r\n"; + $body = ''; + } + + unset($messageStr); + + if ("\r\n" != PHP_EOL) { + // Non-windows (not using SMTP) + $headers = str_replace("\r\n", PHP_EOL, $headers); + $body = str_replace("\r\n", PHP_EOL, $body); + } else { + // Windows, using SMTP + $headers = str_replace("\r\n.", "\r\n..", $headers); + $body = str_replace("\r\n.", "\r\n..", $body); + } + + if ($this->_invoker->mail($to, $subject, $body, $headers, + sprintf($this->_extraParams, $reversePath))) { + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + } else { + $failedRecipients = array_merge( + $failedRecipients, + array_keys((array) $message->getTo()), + array_keys((array) $message->getCc()), + array_keys((array) $message->getBcc()) + ); + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); + + $count = 0; + } + + return $count; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } + + /** Throw a TransportException, first sending it to any listeners */ + protected function _throwException(Swift_TransportException $e) + { + if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { + $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); + if (!$evt->bubbleCancelled()) { + throw $e; + } + } else { + throw $e; + } + } + + /** Determine the best-use reverse path for this message */ + private function _getReversePath(Swift_Mime_Message $message) + { + $return = $message->getReturnPath(); + $sender = $message->getSender(); + $from = $message->getFrom(); + $path = null; + if (!empty($return)) { + $path = $return; + } elseif (!empty($sender)) { + $keys = array_keys($sender); + $path = array_shift($keys); + } elseif (!empty($from)) { + $keys = array_keys($from); + $path = array_shift($keys); + } + + return $path; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php new file mode 100644 index 0000000..ad20e0e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pretends messages have been sent, but just ignores them. + * + * @author Fabien Potencier + */ +class Swift_Transport_NullTransport implements Swift_Transport +{ + /** The event dispatcher from the plugin API */ + private $_eventDispatcher; + + /** + * Constructor. + */ + public function __construct(Swift_Events_EventDispatcher $eventDispatcher) + { + $this->_eventDispatcher = $eventDispatcher; + } + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Sends the given message. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $count = ( + count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ); + + return $count; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php new file mode 100644 index 0000000..3ef7dec --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php @@ -0,0 +1,159 @@ + 30, + 'blocking' => 1, + 'command' => '/usr/sbin/sendmail -bs', + 'type' => Swift_Transport_IoBuffer::TYPE_PROCESS, + ); + + /** + * Create a new SendmailTransport with $buf for I/O. + * + * @param Swift_Transport_IoBuffer $buf + * @param Swift_Events_EventDispatcher $dispatcher + */ + public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher) + { + parent::__construct($buf, $dispatcher); + } + + /** + * Start the standalone SMTP session if running in -bs mode. + */ + public function start() + { + if (false !== strpos($this->getCommand(), ' -bs')) { + parent::start(); + } + } + + /** + * Set the command to invoke. + * + * If using -t mode you are strongly advised to include -oi or -i in the flags. + * For example: /usr/sbin/sendmail -oi -t + * Swift will append a -f flag if one is not present. + * + * The recommended mode is "-bs" since it is interactive and failure notifications + * are hence possible. + * + * @param string $command + * + * @return Swift_Transport_SendmailTransport + */ + public function setCommand($command) + { + $this->_params['command'] = $command; + + return $this; + } + + /** + * Get the sendmail command which will be invoked. + * + * @return string + */ + public function getCommand() + { + return $this->_params['command']; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * + * The return value is the number of recipients who were accepted for delivery. + * NOTE: If using 'sendmail -t' you will not be aware of any failures until + * they bounce (i.e. send() will always return 100% success). + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + $command = $this->getCommand(); + $buffer = $this->getBuffer(); + + if (false !== strpos($command, ' -t')) { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if (false === strpos($command, ' -f')) { + $command .= ' -f'.escapeshellarg($this->_getReversePath($message)); + } + + $buffer->initialize(array_merge($this->_params, array('command' => $command))); + + if (false === strpos($command, ' -i') && false === strpos($command, ' -oi')) { + $buffer->setWriteTranslations(array("\r\n" => "\n", "\n." => "\n..")); + } else { + $buffer->setWriteTranslations(array("\r\n" => "\n")); + } + + $count = count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ; + $message->toByteStream($buffer); + $buffer->flushBuffers(); + $buffer->setWriteTranslations(array()); + $buffer->terminate(); + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); + } elseif (false !== strpos($command, ' -bs')) { + $count = parent::send($message, $failedRecipients); + } else { + $this->_throwException(new Swift_TransportException( + 'Unsupported sendmail command flags ['.$command.']. '. + 'Must be one of "-bs" or "-t" but can include additional flags.' + )); + } + + return $count; + } + + /** Get the params to initialize the buffer */ + protected function _getBufferParams() + { + return $this->_params; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php new file mode 100644 index 0000000..21e629a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @author Fabien Potencier + */ +class Swift_Transport_SpoolTransport implements Swift_Transport +{ + /** The spool instance */ + private $_spool; + + /** The event dispatcher from the plugin API */ + private $_eventDispatcher; + + /** + * Constructor. + */ + public function __construct(Swift_Events_EventDispatcher $eventDispatcher, Swift_Spool $spool = null) + { + $this->_eventDispatcher = $eventDispatcher; + $this->_spool = $spool; + } + + /** + * Sets the spool object. + * + * @param Swift_Spool $spool + * + * @return Swift_Transport_SpoolTransport + */ + public function setSpool(Swift_Spool $spool) + { + $this->_spool = $spool; + + return $this; + } + + /** + * Get the spool object. + * + * @return Swift_Spool + */ + public function getSpool() + { + return $this->_spool; + } + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Sends the given message. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent e-mail's + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + $success = $this->_spool->queueMessage($message); + + if ($evt) { + $evt->setResult($success ? Swift_Events_SendEvent::RESULT_SPOOLED : Swift_Events_SendEvent::RESULT_FAILED); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + return 1; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php new file mode 100644 index 0000000..84ebbee --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php @@ -0,0 +1,321 @@ +_replacementFactory = $replacementFactory; + } + + /** + * Perform any initialization needed, using the given $params. + * + * Parameters will vary depending upon the type of IoBuffer used. + * + * @param array $params + */ + public function initialize(array $params) + { + $this->_params = $params; + switch ($params['type']) { + case self::TYPE_PROCESS: + $this->_establishProcessConnection(); + break; + case self::TYPE_SOCKET: + default: + $this->_establishSocketConnection(); + break; + } + } + + /** + * Set an individual param on the buffer (e.g. switching to SSL). + * + * @param string $param + * @param mixed $value + */ + public function setParam($param, $value) + { + if (isset($this->_stream)) { + switch ($param) { + case 'timeout': + if ($this->_stream) { + stream_set_timeout($this->_stream, $value); + } + break; + + case 'blocking': + if ($this->_stream) { + stream_set_blocking($this->_stream, 1); + } + + } + } + $this->_params[$param] = $value; + } + + public function startTLS() + { + return stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); + } + + /** + * Perform any shutdown logic needed. + */ + public function terminate() + { + if (isset($this->_stream)) { + switch ($this->_params['type']) { + case self::TYPE_PROCESS: + fclose($this->_in); + fclose($this->_out); + proc_close($this->_stream); + break; + case self::TYPE_SOCKET: + default: + fclose($this->_stream); + break; + } + } + $this->_stream = null; + $this->_out = null; + $this->_in = null; + } + + /** + * Set an array of string replacements which should be made on data written + * to the buffer. + * + * This could replace LF with CRLF for example. + * + * @param string[] $replacements + */ + public function setWriteTranslations(array $replacements) + { + foreach ($this->_translations as $search => $replace) { + if (!isset($replacements[$search])) { + $this->removeFilter($search); + unset($this->_translations[$search]); + } + } + + foreach ($replacements as $search => $replace) { + if (!isset($this->_translations[$search])) { + $this->addFilter( + $this->_replacementFactory->createFilter($search, $replace), $search + ); + $this->_translations[$search] = true; + } + } + } + + /** + * Get a line of output (including any CRLF). + * + * The $sequence number comes from any writes and may or may not be used + * depending upon the implementation. + * + * @param int $sequence of last write to scan from + * + * @throws Swift_IoException + * + * @return string + */ + public function readLine($sequence) + { + if (isset($this->_out) && !feof($this->_out)) { + $line = fgets($this->_out); + if (strlen($line) == 0) { + $metas = stream_get_meta_data($this->_out); + if ($metas['timed_out']) { + throw new Swift_IoException( + 'Connection to '. + $this->_getReadConnectionDescription(). + ' Timed Out' + ); + } + } + + return $line; + } + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the remaining bytes are given instead. + * If no bytes are remaining at all, boolean false is returned. + * + * @param int $length + * + * @throws Swift_IoException + * + * @return string|bool + */ + public function read($length) + { + if (isset($this->_out) && !feof($this->_out)) { + $ret = fread($this->_out, $length); + if (strlen($ret) == 0) { + $metas = stream_get_meta_data($this->_out); + if ($metas['timed_out']) { + throw new Swift_IoException( + 'Connection to '. + $this->_getReadConnectionDescription(). + ' Timed Out' + ); + } + } + + return $ret; + } + } + + /** Not implemented */ + public function setReadPointer($byteOffset) + { + } + + /** Flush the stream contents */ + protected function _flush() + { + if (isset($this->_in)) { + fflush($this->_in); + } + } + + /** Write this bytes to the stream */ + protected function _commit($bytes) + { + if (isset($this->_in)) { + $bytesToWrite = strlen($bytes); + $totalBytesWritten = 0; + + while ($totalBytesWritten < $bytesToWrite) { + $bytesWritten = fwrite($this->_in, substr($bytes, $totalBytesWritten)); + if (false === $bytesWritten || 0 === $bytesWritten) { + break; + } + + $totalBytesWritten += $bytesWritten; + } + + if ($totalBytesWritten > 0) { + return ++$this->_sequence; + } + } + } + + /** + * Establishes a connection to a remote server. + */ + private function _establishSocketConnection() + { + $host = $this->_params['host']; + if (!empty($this->_params['protocol'])) { + $host = $this->_params['protocol'].'://'.$host; + } + $timeout = 15; + if (!empty($this->_params['timeout'])) { + $timeout = $this->_params['timeout']; + } + $options = array(); + if (!empty($this->_params['sourceIp'])) { + $options['socket']['bindto'] = $this->_params['sourceIp'].':0'; + } + $this->_stream = @stream_socket_client($host.':'.$this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, stream_context_create($options)); + if (false === $this->_stream) { + throw new Swift_TransportException( + 'Connection could not be established with host '.$this->_params['host']. + ' ['.$errstr.' #'.$errno.']' + ); + } + if (!empty($this->_params['blocking'])) { + stream_set_blocking($this->_stream, 1); + } else { + stream_set_blocking($this->_stream, 0); + } + stream_set_timeout($this->_stream, $timeout); + $this->_in = &$this->_stream; + $this->_out = &$this->_stream; + } + + /** + * Opens a process for input/output. + */ + private function _establishProcessConnection() + { + $command = $this->_params['command']; + $descriptorSpec = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w'), + ); + $this->_stream = proc_open($command, $descriptorSpec, $pipes); + stream_set_blocking($pipes[2], 0); + if ($err = stream_get_contents($pipes[2])) { + throw new Swift_TransportException( + 'Process could not be started ['.$err.']' + ); + } + $this->_in = &$pipes[0]; + $this->_out = &$pipes[1]; + } + + private function _getReadConnectionDescription() + { + switch ($this->_params['type']) { + case self::TYPE_PROCESS: + return 'Process '.$this->_params['command']; + break; + + case self::TYPE_SOCKET: + default: + $host = $this->_params['host']; + if (!empty($this->_params['protocol'])) { + $host = $this->_params['protocol'].'://'.$host; + } + $host .= ':'.$this->_params['port']; + + return $host; + break; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php new file mode 100644 index 0000000..4ae2412 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php @@ -0,0 +1,29 @@ + + */ +class Swift_Validate +{ + /** + * Grammar Object. + * + * @var Swift_Mime_Grammar + */ + private static $grammar = null; + + /** + * Checks if an e-mail address matches the current grammars. + * + * @param string $email + * + * @return bool + */ + public static function email($email) + { + if (self::$grammar === null) { + self::$grammar = Swift_DependencyContainer::getInstance() + ->lookup('mime.grammar'); + } + + return (bool) preg_match( + '/^'.self::$grammar->getDefinition('addr-spec').'$/D', + $email + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php new file mode 100644 index 0000000..6023448 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php @@ -0,0 +1,23 @@ +register('cache') + ->asAliasOf('cache.array') + + ->register('tempdir') + ->asValue('/tmp') + + ->register('cache.null') + ->asSharedInstanceOf('Swift_KeyCache_NullKeyCache') + + ->register('cache.array') + ->asSharedInstanceOf('Swift_KeyCache_ArrayKeyCache') + ->withDependencies(array('cache.inputstream')) + + ->register('cache.disk') + ->asSharedInstanceOf('Swift_KeyCache_DiskKeyCache') + ->withDependencies(array('cache.inputstream', 'tempdir')) + + ->register('cache.inputstream') + ->asNewInstanceOf('Swift_KeyCache_SimpleKeyCacheInputStream') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php new file mode 100644 index 0000000..64d69d2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php @@ -0,0 +1,9 @@ +register('message.message') + ->asNewInstanceOf('Swift_Message') + + ->register('message.mimepart') + ->asNewInstanceOf('Swift_MimePart') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php new file mode 100644 index 0000000..04f394b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php @@ -0,0 +1,123 @@ +register('properties.charset') + ->asValue('utf-8') + + ->register('mime.grammar') + ->asSharedInstanceOf('Swift_Mime_Grammar') + + ->register('mime.message') + ->asNewInstanceOf('Swift_Mime_SimpleMessage') + ->withDependencies(array( + 'mime.headerset', + 'mime.qpcontentencoder', + 'cache', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.part') + ->asNewInstanceOf('Swift_Mime_MimePart') + ->withDependencies(array( + 'mime.headerset', + 'mime.qpcontentencoder', + 'cache', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.attachment') + ->asNewInstanceOf('Swift_Mime_Attachment') + ->withDependencies(array( + 'mime.headerset', + 'mime.base64contentencoder', + 'cache', + 'mime.grammar', + )) + ->addConstructorValue($swift_mime_types) + + ->register('mime.embeddedfile') + ->asNewInstanceOf('Swift_Mime_EmbeddedFile') + ->withDependencies(array( + 'mime.headerset', + 'mime.base64contentencoder', + 'cache', + 'mime.grammar', + )) + ->addConstructorValue($swift_mime_types) + + ->register('mime.headerfactory') + ->asNewInstanceOf('Swift_Mime_SimpleHeaderFactory') + ->withDependencies(array( + 'mime.qpheaderencoder', + 'mime.rfc2231encoder', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.headerset') + ->asNewInstanceOf('Swift_Mime_SimpleHeaderSet') + ->withDependencies(array('mime.headerfactory', 'properties.charset')) + + ->register('mime.qpheaderencoder') + ->asNewInstanceOf('Swift_Mime_HeaderEncoder_QpHeaderEncoder') + ->withDependencies(array('mime.charstream')) + + ->register('mime.base64headerencoder') + ->asNewInstanceOf('Swift_Mime_HeaderEncoder_Base64HeaderEncoder') + ->withDependencies(array('mime.charstream')) + + ->register('mime.charstream') + ->asNewInstanceOf('Swift_CharacterStream_NgCharacterStream') + ->withDependencies(array('mime.characterreaderfactory', 'properties.charset')) + + ->register('mime.bytecanonicalizer') + ->asSharedInstanceOf('Swift_StreamFilters_ByteArrayReplacementFilter') + ->addConstructorValue(array(array(0x0D, 0x0A), array(0x0D), array(0x0A))) + ->addConstructorValue(array(array(0x0A), array(0x0A), array(0x0D, 0x0A))) + + ->register('mime.characterreaderfactory') + ->asSharedInstanceOf('Swift_CharacterReaderFactory_SimpleCharacterReaderFactory') + + ->register('mime.safeqpcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoder') + ->withDependencies(array('mime.charstream', 'mime.bytecanonicalizer')) + + ->register('mime.rawcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_RawContentEncoder') + + ->register('mime.nativeqpcontentencoder') + ->withDependencies(array('properties.charset')) + ->asNewInstanceOf('Swift_Mime_ContentEncoder_NativeQpContentEncoder') + + ->register('mime.qpcontentencoderproxy') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoderProxy') + ->withDependencies(array('mime.safeqpcontentencoder', 'mime.nativeqpcontentencoder', 'properties.charset')) + + ->register('mime.7bitcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_PlainContentEncoder') + ->addConstructorValue('7bit') + ->addConstructorValue(true) + + ->register('mime.8bitcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_PlainContentEncoder') + ->addConstructorValue('8bit') + ->addConstructorValue(true) + + ->register('mime.base64contentencoder') + ->asSharedInstanceOf('Swift_Mime_ContentEncoder_Base64ContentEncoder') + + ->register('mime.rfc2231encoder') + ->asNewInstanceOf('Swift_Encoder_Rfc2231Encoder') + ->withDependencies(array('mime.charstream')) + + // As of PHP 5.4.7, the quoted_printable_encode() function behaves correctly. + // see https://github.com/php/php-src/commit/18bb426587d62f93c54c40bf8535eb8416603629 + ->register('mime.qpcontentencoder') + ->asAliasOf(version_compare(phpversion(), '5.4.7', '>=') ? 'mime.qpcontentencoderproxy' : 'mime.safeqpcontentencoder') +; + +unset($swift_mime_types); diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php new file mode 100644 index 0000000..77e432c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php @@ -0,0 +1,76 @@ +register('transport.smtp') + ->asNewInstanceOf('Swift_Transport_EsmtpTransport') + ->withDependencies(array( + 'transport.buffer', + array('transport.authhandler'), + 'transport.eventdispatcher', + )) + + ->register('transport.sendmail') + ->asNewInstanceOf('Swift_Transport_SendmailTransport') + ->withDependencies(array( + 'transport.buffer', + 'transport.eventdispatcher', + )) + + ->register('transport.mail') + ->asNewInstanceOf('Swift_Transport_MailTransport') + ->withDependencies(array('transport.mailinvoker', 'transport.eventdispatcher')) + + ->register('transport.loadbalanced') + ->asNewInstanceOf('Swift_Transport_LoadBalancedTransport') + + ->register('transport.failover') + ->asNewInstanceOf('Swift_Transport_FailoverTransport') + + ->register('transport.spool') + ->asNewInstanceOf('Swift_Transport_SpoolTransport') + ->withDependencies(array('transport.eventdispatcher')) + + ->register('transport.null') + ->asNewInstanceOf('Swift_Transport_NullTransport') + ->withDependencies(array('transport.eventdispatcher')) + + ->register('transport.mailinvoker') + ->asSharedInstanceOf('Swift_Transport_SimpleMailInvoker') + + ->register('transport.buffer') + ->asNewInstanceOf('Swift_Transport_StreamBuffer') + ->withDependencies(array('transport.replacementfactory')) + + ->register('transport.authhandler') + ->asNewInstanceOf('Swift_Transport_Esmtp_AuthHandler') + ->withDependencies(array( + array( + 'transport.crammd5auth', + 'transport.loginauth', + 'transport.plainauth', + 'transport.ntlmauth', + 'transport.xoauth2auth', + ), + )) + + ->register('transport.crammd5auth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_CramMd5Authenticator') + + ->register('transport.loginauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_LoginAuthenticator') + + ->register('transport.plainauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_PlainAuthenticator') + + ->register('transport.xoauth2auth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_XOAuth2Authenticator') + + ->register('transport.ntlmauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_NTLMAuthenticator') + + ->register('transport.eventdispatcher') + ->asNewInstanceOf('Swift_Events_SimpleEventDispatcher') + + ->register('transport.replacementfactory') + ->asSharedInstanceOf('Swift_StreamFilters_StringReplacementFilterFactory') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/mime_types.php b/vendor/swiftmailer/swiftmailer/lib/mime_types.php new file mode 100644 index 0000000..2d7b98d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/mime_types.php @@ -0,0 +1,1007 @@ + 'text/vnd.in3d.3dml', + '3ds' => 'image/x-3ds', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'appcache' => 'text/cache-manifest', + 'apr' => 'application/vnd.lotus-approach', + 'aps' => 'application/postscript', + 'arc' => 'application/x-freearc', + 'asc' => 'application/pgp-signature', + 'asf' => 'video/x-ms-asf', + 'asm' => 'text/x-asm', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'blb' => 'application/x-blorb', + 'blorb' => 'application/x-blorb', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'caf' => 'audio/x-caf', + 'cap' => 'application/vnd.tcpdump.pcap', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cb7' => 'application/x-cbr', + 'cba' => 'application/x-cbr', + 'cbr' => 'application/x-cbr', + 'cbt' => 'application/x-cbr', + 'cbz' => 'application/x-cbr', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfs' => 'application/x-cfs-compressed', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dart' => 'application/vnd.dart', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dbk' => 'application/docbook+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dgc' => 'application/x-dgc-compressed', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/x-apple-diskimage', + 'dmp' => 'application/vnd.tcpdump.pcap', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvb' => 'video/vnd.dvb.file', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'emf' => 'application/x-msmetafile', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'emz' => 'application/x-msmetafile', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esa' => 'application/vnd.osgi.subsystem', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'eva' => 'application/x-eva', + 'evy' => 'application/x-envoy', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcdt' => 'application/vnd.adobe.formscentral.fcdt', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'flac' => 'audio/x-flac', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gam' => 'application/x-tads', + 'gbr' => 'application/rpki-ghostbusters', + 'gca' => 'application/x-gca-compressed', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gml' => 'application/gml+xml', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gpx' => 'application/gpx+xml', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gramps' => 'application/x-gramps-xml', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxf' => 'application/gxf', + 'gxt' => 'application/vnd.geonext', + 'gz' => 'application/x-gzip', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ink' => 'application/inkml+xml', + 'inkml' => 'application/inkml+xml', + 'install' => 'application/x-install-instructions', + 'iota' => 'application/vnd.astraea-software.iota', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/x-iso9660-image', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'jsonml' => 'application/jsonml+json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'kpxx' => 'application/vnd.ds-keypoint', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/x-lzh-compressed', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'lnk' => 'application/x-ms-shortcut', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/x-lzh-compressed', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/x-m4v', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mar' => 'application/octet-stream', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'metalink' => 'application/metalink+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mft' => 'application/rpki-manifest', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mie' => 'application/x-mie', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mk3d' => 'video/x-matroska', + 'mka' => 'audio/x-matroska', + 'mks' => 'video/x-matroska', + 'mkv' => 'video/x-matroska', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mng' => 'video/x-mng', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'nfo' => 'text/x-nfo', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nitf' => 'application/vnd.nitf', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsc' => 'application/x-conference', + 'nsf' => 'application/vnd.lotus-notes', + 'ntf' => 'application/vnd.nitf', + 'nzb' => 'application/x-nzb', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'obj' => 'application/x-tgif', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'omdoc' => 'application/omdoc+xml', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'opml' => 'text/x-opml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxps' => 'application/oxps', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcap' => 'application/vnd.tcpdump.pcap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'application/x-php', + 'php3' => 'application/x-php', + 'php4' => 'application/x-php', + 'php5' => 'application/x-php', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'ris' => 'application/x-research-info-systems', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rmvb' => 'application/vnd.rn-realmedia-vbr', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roa' => 'application/rpki-roa', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 's3m' => 'audio/s3m', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sfv' => 'text/x-sfv', + 'sgi' => 'image/sgi', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sid' => 'image/x-mrsid-image', + 'sig' => 'application/pgp-signature', + 'sil' => 'audio/silk', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'smv' => 'video/x-smv', + 'smzip' => 'application/vnd.stepmania.package', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'sql' => 'application/x-sql', + 'src' => 'application/x-wais-source', + 'srt' => 'application/x-subrip', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'ssdl' => 'application/ssdl+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'text/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 't3' => 'application/x-t3vm-image', + 'taglet' => 'application/vnd.mynfc', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'tga' => 'image/x-tga', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'ulx' => 'application/x-glulx', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvvz' => 'application/vnd.dece.zip', + 'uvx' => 'application/vnd.dece.unspecified', + 'uvz' => 'application/vnd.dece.zip', + 'vcard' => 'text/vcard', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vob' => 'video/x-ms-vob', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'wdp' => 'image/vnd.ms-photo', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-msmetafile', + 'woff' => 'application/font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'model/x3d+xml', + 'x3db' => 'model/x3d+binary', + 'x3dbz' => 'model/x3d+binary', + 'x3dv' => 'model/x3d+vrml', + 'x3dvz' => 'model/x3d+vrml', + 'x3dz' => 'model/x3d+xml', + 'xaml' => 'application/xaml+xml', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlf' => 'application/x-xliff+xml', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xm' => 'audio/xm', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpl' => 'application/xproc+xml', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'xz' => 'application/x-xz', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'z1' => 'application/x-zmachine', + 'z2' => 'application/x-zmachine', + 'z3' => 'application/x-zmachine', + 'z4' => 'application/x-zmachine', + 'z5' => 'application/x-zmachine', + 'z6' => 'application/x-zmachine', + 'z7' => 'application/x-zmachine', + 'z8' => 'application/x-zmachine', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml', + '123' => 'application/vnd.lotus-1-2-3', +); diff --git a/vendor/swiftmailer/swiftmailer/lib/preferences.php b/vendor/swiftmailer/swiftmailer/lib/preferences.php new file mode 100644 index 0000000..e519501 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/preferences.php @@ -0,0 +1,25 @@ +setCharset('utf-8'); + +// Without these lines the default caching mechanism is "array" but this uses a lot of memory. +// If possible, use a disk cache to enable attaching large attachments etc. +// You can override the default temporary directory by setting the TMPDIR environment variable. +if (@is_writable($tmpDir = sys_get_temp_dir())) { + $preferences->setTempDir($tmpDir)->setCacheType('disk'); +} + +// this should only be done when Swiftmailer won't use the native QP content encoder +// see mime_deps.php +if (version_compare(phpversion(), '5.4.7', '<')) { + $preferences->setQPDotEscape(false); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/swift_init.php b/vendor/swiftmailer/swiftmailer/lib/swift_init.php new file mode 100644 index 0000000..5c4bae4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/swift_init.php @@ -0,0 +1,28 @@ + 'application/x-php', + 'php3' => 'application/x-php', + 'php4' => 'application/x-php', + 'php5' => 'application/x-php', + 'zip' => 'application/zip', + 'gif' => 'image/gif', + 'png' => 'image/png', + 'css' => 'text/css', + 'js' => 'text/javascript', + 'txt' => 'text/plain', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'avi' => 'video/avi', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bz2', + 'csv' => 'text/csv', + 'dmg' => 'application/x-apple-diskimage', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'eml' => 'message/rfc822', + 'aps' => 'application/postscript', + 'exe' => 'application/x-ms-dos-executable', + 'flv' => 'video/x-flv', + 'gz' => 'application/x-gzip', + 'hqx' => 'application/stuffit', + 'htm' => 'text/html', + 'html' => 'text/html', + 'jar' => 'application/x-java-archive', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'm3u' => 'audio/x-mpegurl', + 'm4a' => 'audio/mp4', + 'mdb' => 'application/x-msaccess', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'odg' => 'vnd.oasis.opendocument.graphics', + 'odp' => 'vnd.oasis.opendocument.presentation', + 'odt' => 'vnd.oasis.opendocument.text', + 'ods' => 'vnd.oasis.opendocument.spreadsheet', + 'ogg' => 'audio/ogg', + 'pdf' => 'application/pdf', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'rar' => 'application/x-rar-compressed', + 'rtf' => 'application/rtf', + 'tar' => 'application/x-tar', + 'sit' => 'application/x-stuffit', + 'svg' => 'image/svg+xml', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'ttf' => 'application/x-font-truetype', + 'vcf' => 'text/x-vcard', + 'wav' => 'audio/wav', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'audio/x-ms-wmv', + 'xls' => 'application/excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + ); + + // wrap array for generating file + foreach ($valid_mime_types_preset as $extension => $mime_type) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + + // collect extensions + $valid_extensions = array(); + + // all extensions from second match + foreach ($matches[2] as $i => $extensions) { + // explode multiple extensions from string + $extensions = explode(' ', strtolower($extensions)); + + // force array for foreach + if (!is_array($extensions)) { + $extensions = array($extensions); + } + + foreach ($extensions as $extension) { + // get mime type + $mime_type = $matches[1][$i]; + + // check if string length lower than 10 + if (strlen($extension) < 10) { + // add extension + $valid_extensions[] = $extension; + + if (!isset($valid_mime_types[$mime_type])) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + } + } + } + } + + $xml = simplexml_load_string($mime_xml); + + foreach ($xml as $node) { + // check if there is no pattern + if (!isset($node->glob['pattern'])) { + continue; + } + + // get all matching extensions from match + foreach ((array) $node->glob['pattern'] as $extension) { + // skip none glob extensions + if (strpos($extension, '.') === false) { + continue; + } + + // remove get only last part + $extension = explode('.', strtolower($extension)); + $extension = end($extension); + + // maximum length in database column + if (strlen($extension) <= 9) { + $valid_extensions[] = $extension; + } + } + + if (isset($node->glob['pattern'][0])) { + // mime type + $mime_type = strtolower((string) $node['type']); + + // get first extension + $extension = strtolower(trim($node->glob['ddpattern'][0], '*.')); + + // skip none glob extensions and check if string length between 1 and 10 + if (strpos($extension, '.') !== false || strlen($extension) < 1 || strlen($extension) > 9) { + continue; + } + + // check if string length lower than 10 + if (!isset($valid_mime_types[$mime_type])) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + } + } + + // full list of valid extensions only + $valid_mime_types = array_unique($valid_mime_types); + ksort($valid_mime_types); + + // combine mime types and extensions array + $output = "$preamble\$swift_mime_types = array(\n ".implode($valid_mime_types, ",\n ")."\n);"; + + // write mime_types.php config file + @file_put_contents('./mime_types.php', $output); +} + +generateUpToDateMimeArray(); diff --git a/vendor/swiftmailer/swiftmailer/phpunit.xml.dist b/vendor/swiftmailer/swiftmailer/phpunit.xml.dist new file mode 100644 index 0000000..2420586 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/phpunit.xml.dist @@ -0,0 +1,37 @@ + + + + + + + + + + + tests/unit + + + tests/acceptance + + + tests/bug + + + tests/smoke + + + + + + + diff --git a/vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php b/vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php new file mode 100644 index 0000000..069d11a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/IdenticalBinaryConstraint.php @@ -0,0 +1,62 @@ +value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + public function matches($other) + { + $aHex = $this->asHexString($this->value); + $bHex = $this->asHexString($other); + + return $aHex === $bHex; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'indentical binary'; + } + + /** + * Get the given string of bytes as a stirng of Hexadecimal sequences. + * + * @param string $binary + * + * @return string + */ + private function asHexString($binary) + { + $hex = ''; + + $bytes = unpack('H*', $binary); + + foreach ($bytes as &$byte) { + $byte = strtoupper($byte); + } + + return implode('', $bytes); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/StreamCollector.php b/vendor/swiftmailer/swiftmailer/tests/StreamCollector.php new file mode 100644 index 0000000..7f079d9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/StreamCollector.php @@ -0,0 +1,11 @@ +content .= $arg; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php new file mode 100644 index 0000000..21d89e8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerSmokeTestCase.php @@ -0,0 +1,46 @@ +markTestSkipped( + 'Smoke tests are skipped if tests/smoke.conf.php is not edited' + ); + } + } + + protected function _getMailer() + { + switch (SWIFT_SMOKE_TRANSPORT_TYPE) { + case 'smtp': + $transport = Swift_DependencyContainer::getInstance()->lookup('transport.smtp') + ->setHost(SWIFT_SMOKE_SMTP_HOST) + ->setPort(SWIFT_SMOKE_SMTP_PORT) + ->setUsername(SWIFT_SMOKE_SMTP_USER) + ->setPassword(SWIFT_SMOKE_SMTP_PASS) + ->setEncryption(SWIFT_SMOKE_SMTP_ENCRYPTION) + ; + break; + case 'sendmail': + $transport = Swift_DependencyContainer::getInstance()->lookup('transport.sendmail') + ->setCommand(SWIFT_SMOKE_SENDMAIL_COMMAND) + ; + break; + case 'mail': + case 'nativemail': + $transport = Swift_DependencyContainer::getInstance()->lookup('transport.mail'); + break; + default: + throw new Exception('Undefined transport ['.SWIFT_SMOKE_TRANSPORT_TYPE.']'); + } + + return new Swift_Mailer($transport); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php new file mode 100644 index 0000000..f0e2736 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/SwiftMailerTestCase.php @@ -0,0 +1,34 @@ + \ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/files/swiftmailer.png b/vendor/swiftmailer/swiftmailer/tests/_samples/files/swiftmailer.png new file mode 100644 index 0000000000000000000000000000000000000000..1b95f619af1fcbed1fca382d22022bdf329849cf GIT binary patch literal 3194 zcmV-=42AQFP)MSx|GoLxC1$NK z_Ph69ufRtQ76+DFuvF-8orSRY^mzh{hyTC*`g`GJF2^N{QXxQY!g418;53Gy4`6Wt z@W4WCEiaf6;7tNLOGCi-2=LHCZ7mC!tMIWuL$7RVA;G=7=x|%8^OYGLShnYUDhm&{ zg*qRgG59-xrCOlVLap5bussX4wX{GtNp2L?&||kP)YeRa-p?TroQ0vc-dGelVxhJ` zbHgmj`XYe6V)0vOpab&4Cj>2rw?=>CC(n=L-$$^lSx;?E2D-=Qo8dr^dBcluc+ORJ zo@Ok)#uf8ieAG%L42Q#c`YOud`3-#p& zx0pwR}3v$0N&KqF{v0Ir|mG1_!JrIaAnQ_n_r40Af1%SNq$VGh?- zDXXWpP)~pJk68AdlR|>owIyz$&Kl?@%RbwIX}OKd*e}{@>J;|jNB^t|SqlG$KX_@< zYPOT{srCWpmc{fEdFtc$hkWQ00_xRYTk~Q-#%(ZN)nMakx2TCY4cs|+=~l+UpzgXH z3KFBCVsZn{{@|S|8)bT`V)?Z;Zx^N|a=FAncPT*Yk;goObw@Ek5$~cJb)e&uswQUhp?_8ZB}LDU1)1XVxzQBgVh!5bYS*aa$n|JikshH+4PM@px5US z5KI1hj^xpt1p^u_NV(oZoq@I4d3Q?gY1@Xf0y7?wX2swT7EcHglRVbe z0s_q_WwNWG>Pu4vf1~O-k3Mg0@!VhV^|zdz4O~YIj}5@ z)|!D$l=1lH%JrgXWP>a>qy`4 zfd_Xf*yB0msv=VnO421sgGW<)JvhBrJALv+<+Cq+cK6}C5v;E~_vYC~kG{Uo{Le>+ zz5eve?B>-s*bC2G&c>a(FWeJ{VvUPW=jQFDIEYEMIT|^L*`CApFW>5Gpxq`exeb89 zK7rhmuDQL!NoS)W{BchYPx)ZMZO}SZReVg^Oa`>3+8)*qpg=su`iAD=;tzrDdzX&m zBt-gYJVkUPuC3gN{#jlARs{76&#bZQS6)8f>I#dXe&yLW+0E5AlsiJm3G|b<*-N~2 zQ#XAApDW}JY&X_81Ta_V;cXaP170tBZ-i?d;8r~(n~HKdiE#iXUU^&Ld}NF6C2Whw z-vGGfa~qqI4Vd+ry8rqeEX!*GX zPzuPkInk36ZVgpFze_+8TWRTeWKV;P8V#8kOEY&5b$_oN=C zfVZ9!c1KeSWEKdT+UKXAXurGQxsz=>%?&57Zc?TJIUHR%WUjYKGjD6|O`mFWU-Qsy z8$ex&%YLW|W*0O|TXOeRszyS|bM{oy0VIWM2yYGh6Now-{;$MnFB9tv7p>Bn0mTPK z!QiJ~e83)pUOtEVUq&Z^zk2`=z%|g`FMi`kNt*|xpDPM&E!L90^pHD3t%<+)L}>x= zHZ-2OF^#M+sfjfAr7g+}fWNCK^DS7vtB@f&qSp+K@c*Z;;KpqHsmIns`02nB4 zKwSio^mHYm+UaW~&e@6U0#MmVdv+-Z>#CrJZMv%8!uDfOe*$nqU}t{`TMB zU-0PvdvY3n|LTu*WzdK7X$j|mWieEgSzQ4MV=|V}Au&KJv7|%P<1?LGi1~iu+Cz>4&0c?*B=<+n4#;eO3s47W{ zB(KnrY5~Xa!V72jAAhXU+^x4>?YbX+^mElu_Obwx(tOI-?@m=iD#6~&JN?i2qW>RG*IxzvC z%KL6Ag*@E5P1w+3EWTGe$UjqK^=XXgpk4UJ66dz_(44`X-J*wOdY zC3PE?sU85{Opeu46To)qgaW}McTAwBbX>sVJLeoLAdicbjaw0CU|8T4Yz>qR`*Zx{ z+j~*|8UM!5zy9YpMF>P?YXZdggn*yNM!9B0Ks$Q?rb>=BDOWvU=S|x+dKevHdv6+N zwxXyaOr8>5gMrK9JEM4tCpyjcS$XLuzi#aBtm>469-6Pme$)jpWxpG8LPf}kplnsm zF<8y|{FYhzd|-=GfF_<;r>D7h_Y#gj$N}i82*;<7K8QN|$63<%c^Ed4vvfnRa+O8k zt$hMJcR7Yox0kQ|+vLDli0e4wKui0mXP$0f{gmtdgjU3G+I6I(FJyO6e*FExA7i_h ze)i??Y}=IZgekTUlC}k~cMZTn$FZiU&cg{G2IFg>kt}O0x&1?~f!b4)+h{l!M*UB| zTCG3+$Ip%xItYL4#dzm&aXQ|L7~9OP>cyuT4WkKyD#A@x{zjTR4?7HbbltHp9Ks=4 zvJ^p}1>jph{m!3#)xI_7A$O?_sPhstId7(!_m4)vIEs7c(g0t@$jUKGD|Z1+@@7 z?vvLG$!qTxG$6#j9L))L;gN?9EglOFwWc~p0NO?IXhII!W;iU=sXd`Qp=6(c2<8Mc zA2&H^qfx*Vb46RbBv1<=b=i2o*xcbJ?VM(2P1$mL-?z1bLrp+Aq_e9ExCdEzKJv^6 z^VSqn!+K{6^+Z*FK%Q_g3*Ak@Du2_#q@MX~c7E%fEz~AnyTkNT_J$N#p3r9yCsXKQ z;jeeQ#bz`6&|I~@wUR+CY1~dqSs+S)Earl*U32337r@fWm`K g-$HCnw*Lz-0AR%yng8k!ga7~l07*qoM6N<$f>^s2o&W#< literal 0 HcmV?d00001 diff --git a/vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip b/vendor/swiftmailer/swiftmailer/tests/_samples/files/textfile.zip new file mode 100644 index 0000000000000000000000000000000000000000..5a580ecbf81ff98445bc5c6d0fce7a9a301f0e39 GIT binary patch literal 202 zcmWIWW@h1HU}9ikII%0tqR&rhgC39#!aNM33?-=*C25&Csd^<9C7~gl49w;G_PK*_ zXax(ySH`c5AsLy)3P4nlSX82rpQezg5L}*_R-)jW2-H=iP-db~oSUDWs!)markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_tmpDir = SWIFT_TMP_DIR; + $this->_testFile = $this->_tmpDir.'/swift-test-file'.__CLASS__; + file_put_contents($this->_testFile, 'abcdefghijklm'); + } + + public function tearDown() + { + unlink($this->_testFile); + } + + public function testFileDataCanBeRead() + { + $file = $this->_createFileStream($this->_testFile); + $str = ''; + while (false !== $bytes = $file->read(8192)) { + $str .= $bytes; + } + $this->assertEquals('abcdefghijklm', $str); + } + + public function testFileDataCanBeReadSequentially() + { + $file = $this->_createFileStream($this->_testFile); + $this->assertEquals('abcde', $file->read(5)); + $this->assertEquals('fghijklm', $file->read(8)); + $this->assertFalse($file->read(1)); + } + + public function testFilenameIsReturned() + { + $file = $this->_createFileStream($this->_testFile); + $this->assertEquals($this->_testFile, $file->getPath()); + } + + public function testFileCanBeWrittenTo() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $file->write('foobar'); + $this->assertEquals('foobar', $file->read(8192)); + } + + public function testReadingFromThenWritingToFile() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $file->write('foobar'); + $this->assertEquals('foobar', $file->read(8192)); + $file->write('zipbutton'); + $this->assertEquals('zipbutton', $file->read(8192)); + } + + public function testWritingToFileWithCanonicalization() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $file->addFilter($this->_createFilter(array("\r\n", "\r"), "\n"), 'allToLF'); + $file->write("foo\r\nbar\r"); + $file->write("\nzip\r\ntest\r"); + $file->flushBuffers(); + $this->assertEquals("foo\nbar\nzip\ntest\n", file_get_contents($this->_testFile)); + } + + public function testBindingOtherStreamsMirrorsWriteOperations() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->at(0)) + ->method('write') + ->with('x'); + $is2->expects($this->at(1)) + ->method('write') + ->with('y'); + + $file->bind($is1); + $file->bind($is2); + + $file->write('x'); + $file->write('y'); + } + + public function testBindingOtherStreamsMirrorsFlushOperations() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->once()) + ->method('flushBuffers'); + $is2->expects($this->once()) + ->method('flushBuffers'); + + $file->bind($is1); + $file->bind($is2); + + $file->flushBuffers(); + } + + public function testUnbindingStreamPreventsFurtherWrites() + { + $file = $this->_createFileStream( + $this->_testFile, true + ); + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->once()) + ->method('write') + ->with('x'); + + $file->bind($is1); + $file->bind($is2); + + $file->write('x'); + + $file->unbind($is2); + + $file->write('y'); + } + + // -- Creation methods + + private function _createFilter($search, $replace) + { + return new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } + + private function _createMockInputStream() + { + return $this->getMock('Swift_InputByteStream'); + } + + private function _createFileStream($file, $writable = false) + { + return new Swift_ByteStream_FileByteStream($file, $writable); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php new file mode 100644 index 0000000..8ce4a18 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/CharacterReaderFactory/SimpleCharacterReaderFactoryAcceptanceTest.php @@ -0,0 +1,179 @@ +_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testCreatingUtf8Reader() + { + foreach (array('utf8', 'utf-8', 'UTF-8', 'UTF8') as $utf8) { + $reader = $this->_factory->getReaderFor($utf8); + $this->assertInstanceof($this->_prefix.'Utf8Reader', $reader); + } + } + + public function testCreatingIso8859XReaders() + { + $charsets = array(); + foreach (range(1, 16) as $number) { + foreach (array('iso', 'iec') as $body) { + $charsets[] = $body.'-8859-'.$number; + $charsets[] = $body.'8859-'.$number; + $charsets[] = strtoupper($body).'-8859-'.$number; + $charsets[] = strtoupper($body).'8859-'.$number; + } + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingWindows125XReaders() + { + $charsets = array(); + foreach (range(0, 8) as $number) { + $charsets[] = 'windows-125'.$number; + $charsets[] = 'windows125'.$number; + $charsets[] = 'WINDOWS-125'.$number; + $charsets[] = 'WINDOWS125'.$number; + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingCodePageReaders() + { + $charsets = array(); + foreach (range(0, 8) as $number) { + $charsets[] = 'cp-125'.$number; + $charsets[] = 'cp125'.$number; + $charsets[] = 'CP-125'.$number; + $charsets[] = 'CP125'.$number; + } + + foreach (array(437, 737, 850, 855, 857, 858, 860, + 861, 863, 865, 866, 869, ) as $number) { + $charsets[] = 'cp-'.$number; + $charsets[] = 'cp'.$number; + $charsets[] = 'CP-'.$number; + $charsets[] = 'CP'.$number; + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingAnsiReader() + { + foreach (array('ansi', 'ANSI') as $ansi) { + $reader = $this->_factory->getReaderFor($ansi); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingMacintoshReader() + { + foreach (array('macintosh', 'MACINTOSH') as $mac) { + $reader = $this->_factory->getReaderFor($mac); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingKOIReaders() + { + $charsets = array(); + foreach (array('7', '8-r', '8-u', '8u', '8r') as $end) { + $charsets[] = 'koi-'.$end; + $charsets[] = 'koi'.$end; + $charsets[] = 'KOI-'.$end; + $charsets[] = 'KOI'.$end; + } + + foreach ($charsets as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingIsciiReaders() + { + foreach (array('iscii', 'ISCII', 'viscii', 'VISCII') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingMIKReader() + { + foreach (array('mik', 'MIK') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingCorkReader() + { + foreach (array('cork', 'CORK', 't1', 'T1') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(1, $reader->getInitialByteSize()); + } + } + + public function testCreatingUcs2Reader() + { + foreach (array('ucs-2', 'UCS-2', 'ucs2', 'UCS2') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(2, $reader->getInitialByteSize()); + } + } + + public function testCreatingUtf16Reader() + { + foreach (array('utf-16', 'UTF-16', 'utf16', 'UTF16') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(2, $reader->getInitialByteSize()); + } + } + + public function testCreatingUcs4Reader() + { + foreach (array('ucs-4', 'UCS-4', 'ucs4', 'UCS4') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(4, $reader->getInitialByteSize()); + } + } + + public function testCreatingUtf32Reader() + { + foreach (array('utf-32', 'UTF-32', 'utf32', 'UTF32') as $charset) { + $reader = $this->_factory->getReaderFor($charset); + $this->assertInstanceof($this->_prefix.'GenericFixedWidthReader', $reader); + $this->assertEquals(4, $reader->getInitialByteSize()); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php new file mode 100644 index 0000000..8caf6f5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/DependencyContainerAcceptanceTest.php @@ -0,0 +1,20 @@ +listItems() as $itemName) { + try { + $di->lookup($itemName); + } catch (Swift_DependencyException $e) { + $this->fail($e->getMessage()); + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php new file mode 100644 index 0000000..fc5a814 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EmbeddedFileAcceptanceTest.php @@ -0,0 +1,12 @@ +_samplesDir = realpath(__DIR__.'/../../../_samples/charsets'); + $this->_encoder = new Swift_Encoder_Base64Encoder(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $this->_encoder->encodeString($text); + + $this->assertEquals( + base64_decode($encodedText), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php new file mode 100644 index 0000000..736dccb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/QpEncoderAcceptanceTest.php @@ -0,0 +1,50 @@ +_samplesDir = realpath(__DIR__.'/../../../_samples/charsets'); + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $charStream = new Swift_CharacterStream_ArrayCharacterStream( + $this->_factory, $encoding); + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $encoder->encodeString($text); + + $this->assertEquals( + quoted_printable_decode($encodedText), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php new file mode 100644 index 0000000..043ddf8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Encoder/Rfc2231EncoderAcceptanceTest.php @@ -0,0 +1,50 @@ +_samplesDir = realpath(__DIR__.'/../../../_samples/charsets'); + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $charStream = new Swift_CharacterStream_ArrayCharacterStream( + $this->_factory, $encoding); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $encoder->encodeString($text); + + $this->assertEquals( + urldecode(implode('', explode("\r\n", $encodedText))), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php new file mode 100644 index 0000000..6a4d05d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/EncodingAcceptanceTest.php @@ -0,0 +1,30 @@ +assertEquals('7bit', $encoder->getName()); + } + + public function testGet8BitEncodingReturns8BitEncoder() + { + $encoder = Swift_Encoding::get8BitEncoding(); + $this->assertEquals('8bit', $encoder->getName()); + } + + public function testGetQpEncodingReturnsQpEncoder() + { + $encoder = Swift_Encoding::getQpEncoding(); + $this->assertEquals('quoted-printable', $encoder->getName()); + } + + public function testGetBase64EncodingReturnsBase64Encoder() + { + $encoder = Swift_Encoding::getBase64Encoding(); + $this->assertEquals('base64', $encoder->getName()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php new file mode 100644 index 0000000..6b06e2e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/ArrayKeyCacheAcceptanceTest.php @@ -0,0 +1,173 @@ +_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + } + + public function testStringDataCanBeSetAndFetched() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeOverwritten() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'whatever', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('whatever', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'ing', Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testing', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testHasKeyReturnValue() + { + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key2, 'foo', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key2, 'foo')); + } + + public function testItemKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key1, 'bar')); + } + + public function testByteStreamCanBeImported() + { + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('abcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamCanBeAppended() + { + $os1 = new Swift_ByteStream_ArrayByteStream(); + $os1->write('abcdef'); + + $os2 = new Swift_ByteStream_ArrayByteStream(); + $os2->write('xyzuvw'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os1, Swift_KeyCache::MODE_APPEND + ); + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os2, Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('abcdefxyzuvw', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamAndStringCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_APPEND + ); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testabcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testDataCanBeExportedToByteStream() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_cache->exportToByteStream($this->_key1, 'foo', $is); + + $string = ''; + while (false !== $bytes = $is->read(8192)) { + $string .= $bytes; + } + + $this->assertEquals('test', $string); + } + + public function testKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->clearKey($this->_key1, 'foo'); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'xyz', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'bar')); + $this->_cache->clearAll($this->_key1); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'bar')); + } + + public function testKeyCacheInputStream() + { + $is = $this->_cache->getInputByteStream($this->_key1, 'foo'); + $is->write('abc'); + $is->write('xyz'); + $this->assertEquals('abcxyz', $this->_cache->getString($this->_key1, 'foo')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php new file mode 100644 index 0000000..392edde --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/KeyCache/DiskKeyCacheAcceptanceTest.php @@ -0,0 +1,183 @@ +markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_key1 = uniqid(microtime(true), true); + $this->_key2 = uniqid(microtime(true), true); + $this->_cache = new Swift_KeyCache_DiskKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream(), + SWIFT_TMP_DIR + ); + } + + public function testStringDataCanBeSetAndFetched() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeOverwritten() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'whatever', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('whatever', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'foo', 'ing', Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testing', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testHasKeyReturnValue() + { + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key2, 'foo', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key2, 'foo')); + } + + public function testItemKeyIsWellPartitioned() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'ing', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $this->_cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $this->_cache->getString($this->_key1, 'bar')); + } + + public function testByteStreamCanBeImported() + { + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('abcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamCanBeAppended() + { + $os1 = new Swift_ByteStream_ArrayByteStream(); + $os1->write('abcdef'); + + $os2 = new Swift_ByteStream_ArrayByteStream(); + $os2->write('xyzuvw'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os1, Swift_KeyCache::MODE_APPEND + ); + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os2, Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('abcdefxyzuvw', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamAndStringCanBeAppended() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_APPEND + ); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write('abcdef'); + + $this->_cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testabcdef', $this->_cache->getString($this->_key1, 'foo')); + } + + public function testDataCanBeExportedToByteStream() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_cache->exportToByteStream($this->_key1, 'foo', $is); + + $string = ''; + while (false !== $bytes = $is->read(8192)) { + $string .= $bytes; + } + + $this->assertEquals('test', $string); + } + + public function testKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->_cache->clearKey($this->_key1, 'foo'); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyCanBeCleared() + { + $this->_cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->_cache->setString( + $this->_key1, 'bar', 'xyz', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertTrue($this->_cache->hasKey($this->_key1, 'bar')); + $this->_cache->clearAll($this->_key1); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'foo')); + $this->assertFalse($this->_cache->hasKey($this->_key1, 'bar')); + } + + public function testKeyCacheInputStream() + { + $is = $this->_cache->getInputByteStream($this->_key1, 'foo'); + $is->write('abc'); + $is->write('xyz'); + $this->assertEquals('abcxyz', $this->_cache->getString($this->_key1, 'foo')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php new file mode 100644 index 0000000..9372fbf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MessageAcceptanceTest.php @@ -0,0 +1,57 @@ +_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $message->addPart('foo', 'text/plain', 'iso-8859-1'); + $message->addPart('test foo', 'text/html', 'iso-8859-1'); + + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'test foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + // -- Private helpers + + protected function _createMessage() + { + Swift_DependencyContainer::getInstance() + ->register('properties.charset')->asValue(null); + + return Swift_Message::newInstance(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php new file mode 100644 index 0000000..e925367 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/AttachmentAcceptanceTest.php @@ -0,0 +1,125 @@ +_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $this->_contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $this->_grammar = new Swift_Mime_Grammar(); + $this->_headers = new Swift_Mime_SimpleHeaderSet( + new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $this->_grammar) + ); + } + + public function testDispositionIsSetInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setDisposition('inline'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline'."\r\n", + $attachment->toString() + ); + } + + public function testDispositionIsAttachmentByDefault() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment'."\r\n", + $attachment->toString() + ); + } + + public function testFilenameIsSetInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n", + $attachment->toString() + ); + } + + public function testSizeIsSetInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; size=12340'."\r\n", + $attachment->toString() + ); + } + + public function testMultipleParametersInHeader() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf; size=12340'."\r\n", + $attachment->toString() + ); + } + + public function testEndToEnd() + { + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setSize(12340); + $attachment->setBody('abcd'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf; size=12340'."\r\n". + "\r\n". + base64_encode('abcd'), + $attachment->toString() + ); + } + + // -- Private helpers + + protected function _createAttachment() + { + $entity = new Swift_Mime_Attachment( + $this->_headers, + $this->_contentEncoder, + $this->_cache, + $this->_grammar + ); + + return $entity; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php new file mode 100644 index 0000000..2a5c562 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/Base64ContentEncoderAcceptanceTest.php @@ -0,0 +1,56 @@ +_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_encoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + base64_decode($encoded), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php new file mode 100644 index 0000000..e8313da --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/NativeQpContentEncoderAcceptanceTest.php @@ -0,0 +1,86 @@ +_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_encoder = new Swift_Mime_ContentEncoder_NativeQpContentEncoder(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + $this->_encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + quoted_printable_decode($encoded), + // CR and LF are converted to CRLF + preg_replace('~\r(?!\n)|(?_createEncoderFromContainer(); + $this->assertSame('=C3=A4=C3=B6=C3=BC=C3=9F', $encoder->encodeString('äöüß')); + } + + /** + * @expectedException RuntimeException + */ + public function testCharsetChangeNotImplemented() + { + $this->_encoder->charsetChanged('utf-8'); + $this->_encoder->charsetChanged('charset'); + $this->_encoder->encodeString('foo'); + } + + public function testGetName() + { + $this->assertSame('quoted-printable', $this->_encoder->getName()); + } + + private function _createEncoderFromContainer() + { + return Swift_DependencyContainer::getInstance() + ->lookup('mime.nativeqpcontentencoder') + ; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php new file mode 100644 index 0000000..1541b7e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/PlainContentEncoderAcceptanceTest.php @@ -0,0 +1,88 @@ +_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_encoder = new Swift_Mime_ContentEncoder_PlainContentEncoder('8bit'); + } + + public function testEncodingAndDecodingSamplesString() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + $encodedText = $this->_encoder->encodeString($text); + + $this->assertEquals( + $encodedText, $text, + '%s: Encoded string should be identical to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingAndDecodingSamplesByteStream() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + + $this->_encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + $encoded, $text, + '%s: Encoded string should be identical to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php new file mode 100644 index 0000000..501dbfa --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/ContentEncoder/QpContentEncoderAcceptanceTest.php @@ -0,0 +1,157 @@ +_samplesDir = realpath(__DIR__.'/../../../../_samples/charsets'); + $this->_factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + } + + public function testEncodingAndDecodingSamples() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $charStream = new Swift_CharacterStream_NgCharacterStream( + $this->_factory, $encoding); + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + $encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + quoted_printable_decode($encoded), $text, + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingAndDecodingSamplesFromDiConfiguredInstance() + { + $sampleFp = opendir($this->_samplesDir); + while (false !== $encodingDir = readdir($sampleFp)) { + if (substr($encodingDir, 0, 1) == '.') { + continue; + } + + $encoding = $encodingDir; + $encoder = $this->_createEncoderFromContainer(); + + $sampleDir = $this->_samplesDir.'/'.$encodingDir; + + if (is_dir($sampleDir)) { + $fileFp = opendir($sampleDir); + while (false !== $sampleFile = readdir($fileFp)) { + if (substr($sampleFile, 0, 1) == '.') { + continue; + } + + $text = file_get_contents($sampleDir.'/'.$sampleFile); + + $os = new Swift_ByteStream_ArrayByteStream(); + $os->write($text); + + $is = new Swift_ByteStream_ArrayByteStream(); + $encoder->encodeByteStream($os, $is); + + $encoded = ''; + while (false !== $bytes = $is->read(8192)) { + $encoded .= $bytes; + } + + $this->assertEquals( + str_replace("\r\n", "\n", quoted_printable_decode($encoded)), str_replace("\r\n", "\n", $text), + '%s: Encoded string should decode back to original string for sample '. + $sampleDir.'/'.$sampleFile + ); + } + closedir($fileFp); + } + } + closedir($sampleFp); + } + + public function testEncodingLFTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\nb\r\nc", $encoder->encodeString("a\nb\nc")); + } + + public function testEncodingCRTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\nb\r\nc", $encoder->encodeString("a\rb\rc")); + } + + public function testEncodingLFCRTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\n\r\nb\r\n\r\nc", $encoder->encodeString("a\n\rb\n\rc")); + } + + public function testEncodingCRLFTextWithDiConfiguredInstance() + { + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a\r\nb\r\nc", $encoder->encodeString("a\r\nb\r\nc")); + } + + public function testEncodingDotStuffingWithDiConfiguredInstance() + { + // Enable DotEscaping + Swift_Preferences::getInstance()->setQPDotEscape(true); + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a=2E\r\n=2E\r\n=2Eb\r\nc", $encoder->encodeString("a.\r\n.\r\n.b\r\nc")); + // Return to default + Swift_Preferences::getInstance()->setQPDotEscape(false); + $encoder = $this->_createEncoderFromContainer(); + $this->assertEquals("a.\r\n.\r\n.b\r\nc", $encoder->encodeString("a.\r\n.\r\n.b\r\nc")); + } + + public function testDotStuffingEncodingAndDecodingSamplesFromDiConfiguredInstance() + { + // Enable DotEscaping + Swift_Preferences::getInstance()->setQPDotEscape(true); + $this->testEncodingAndDecodingSamplesFromDiConfiguredInstance(); + // Disable DotStuffing to continue + Swift_Preferences::getInstance()->setQPDotEscape(false); + } + + private function _createEncoderFromContainer() + { + return Swift_DependencyContainer::getInstance() + ->lookup('mime.qpcontentencoder') + ; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php new file mode 100644 index 0000000..75f3c84 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/EmbeddedFileAcceptanceTest.php @@ -0,0 +1,137 @@ +_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $this->_contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $this->_grammar = new Swift_Mime_Grammar(); + $this->_headers = new Swift_Mime_SimpleHeaderSet( + new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $this->_grammar) + ); + } + + public function testContentIdIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $file->setContentType('application/pdf'); + $file->setId('foo@bar'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline'."\r\n". + 'Content-ID: '."\r\n", + $file->toString() + ); + } + + public function testDispositionIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setDisposition('attachment'); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n", + $file->toString() + ); + } + + public function testFilenameIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setFilename('foo.pdf'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; filename=foo.pdf'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n", + $file->toString() + ); + } + + public function testSizeIsSetInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; size=12340'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n", + $file->toString() + ); + } + + public function testMultipleParametersInHeader() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setFilename('foo.pdf'); + $file->setSize(12340); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; filename=foo.pdf; size=12340'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n", + $file->toString() + ); + } + + public function testEndToEnd() + { + $file = $this->_createEmbeddedFile(); + $id = $file->getId(); + $file->setContentType('application/pdf'); + $file->setFilename('foo.pdf'); + $file->setSize(12340); + $file->setBody('abcd'); + $this->assertEquals( + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; filename=foo.pdf; size=12340'."\r\n". + 'Content-ID: <'.$id.'>'."\r\n". + "\r\n". + base64_encode('abcd'), + $file->toString() + ); + } + + // -- Private helpers + + protected function _createEmbeddedFile() + { + $entity = new Swift_Mime_EmbeddedFile( + $this->_headers, + $this->_contentEncoder, + $this->_cache, + $this->_grammar + ); + + return $entity; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php new file mode 100644 index 0000000..304867a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/HeaderEncoder/Base64HeaderEncoderAcceptanceTest.php @@ -0,0 +1,32 @@ +_encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); + } + + public function testEncodingJIS() + { + if (function_exists('mb_convert_encoding')) { + // base64_encode and split cannot handle long JIS text to fold + $subject = '長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い長い件名'; + + $encodedWrapperLength = strlen('=?iso-2022-jp?'.$this->_encoder->getName().'??='); + + $old = mb_internal_encoding(); + mb_internal_encoding('utf-8'); + $newstring = mb_encode_mimeheader($subject, 'iso-2022-jp', 'B', "\r\n"); + mb_internal_encoding($old); + + $encoded = $this->_encoder->encodeString($subject, 0, 75 - $encodedWrapperLength, 'iso-2022-jp'); + $this->assertEquals( + $encoded, $newstring, + 'Encoded string should decode back to original string for sample ' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php new file mode 100644 index 0000000..8232fe6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/MimePartAcceptanceTest.php @@ -0,0 +1,129 @@ +_cache = new Swift_KeyCache_ArrayKeyCache( + new Swift_KeyCache_SimpleKeyCacheInputStream() + ); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $this->_contentEncoder = new Swift_Mime_ContentEncoder_QpContentEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'), + new Swift_StreamFilters_ByteArrayReplacementFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ) + ); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder( + new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8') + ); + $this->_grammar = new Swift_Mime_Grammar(); + $this->_headers = new Swift_Mime_SimpleHeaderSet( + new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $this->_grammar) + ); + } + + public function testCharsetIsSetInHeader() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('utf-8'); + $part->setBody('foobar'); + $this->assertEquals( + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foobar', + $part->toString() + ); + } + + public function testFormatIsSetInHeaders() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setFormat('flowed'); + $part->setBody('> foobar'); + $this->assertEquals( + 'Content-Type: text/plain; format=flowed'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + '> foobar', + $part->toString() + ); + } + + public function testDelSpIsSetInHeaders() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setDelSp(true); + $part->setBody('foobar'); + $this->assertEquals( + 'Content-Type: text/plain; delsp=yes'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foobar', + $part->toString() + ); + } + + public function testAll3ParamsInHeaders() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('utf-8'); + $part->setFormat('fixed'); + $part->setDelSp(true); + $part->setBody('foobar'); + $this->assertEquals( + 'Content-Type: text/plain; charset=utf-8; format=fixed; delsp=yes'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foobar', + $part->toString() + ); + } + + public function testBodyIsCanonicalized() + { + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('utf-8'); + $part->setBody("foobar\r\rtest\ning\r"); + $this->assertEquals( + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + "foobar\r\n". + "\r\n". + "test\r\n". + "ing\r\n", + $part->toString() + ); + } + + // -- Private helpers + + protected function _createMimePart() + { + $entity = new Swift_Mime_MimePart( + $this->_headers, + $this->_contentEncoder, + $this->_cache, + $this->_grammar + ); + + return $entity; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php new file mode 100644 index 0000000..84f3443 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Mime/SimpleMessageAcceptanceTest.php @@ -0,0 +1,1251 @@ +setCharset(null); //TODO: Test with the charset defined + } + + public function testBasicHeaders() + { + /* -- RFC 2822, 3.6. + */ + + $message = $this->_createMessage(); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString(), + '%s: Only required headers, and non-empty headers should be displayed' + ); + } + + public function testSubjectIsDisplayedIfSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testDateCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $id = $message->getId(); + $message->setDate(1234); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', 1234)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMessageIdCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setId('foo@bar'); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: '."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testContentTypeCanBeChanged() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setContentType('text/html'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testCharsetCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setContentType('text/html'); + $message->setCharset('iso-8859-1'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testFormatCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFormat('flowed'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain; format=flowed'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testEncoderCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setContentType('text/html'); + $message->setEncoder( + new Swift_Mime_ContentEncoder_PlainContentEncoder('7bit') + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: 7bit'."\r\n", + $message->toString() + ); + } + + public function testFromAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom('chris.corbyn@swiftmailer.org'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: chris.corbyn@swiftmailer.org'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testFromAddressCanBeSetWithName() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris Corbyn')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleFromAddressesCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn , mark@swiftmailer.org'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testReturnPathAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testEmptyReturnPathHeaderCanBeUsed() + { + $message = $this->_createMessage(); + $message->setReturnPath(''); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: <>'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testSenderCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setSender('chris.corbyn@swiftmailer.org'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Sender: chris.corbyn@swiftmailer.org'."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testSenderCanBeSetWithName() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setSender(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Sender: Chris '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testReplyToCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array('chris@w3style.co.uk' => 'Myself')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleReplyAddressCanBeUsed() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testToAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo('mark@swiftmailer.org'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleToAddressesCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testCcAddressCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc('john@some-site.com'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'Cc: john@some-site.com'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleCcAddressesCanBeSet() + { + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc(array( + 'john@some-site.com' => 'John West', + 'fred@another-site.co.uk' => 'Big Fred', + )); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'Cc: John West , Big Fred '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testBccAddressCanBeSet() + { + //Obviously Transports need to setBcc(array()) and send to each Bcc recipient + // separately in accordance with RFC 2822/2821 + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc(array( + 'john@some-site.com' => 'John West', + 'fred@another-site.co.uk' => 'Big Fred', + )); + $message->setBcc('x@alphabet.tld'); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'Cc: John West , Big Fred '."\r\n". + 'Bcc: x@alphabet.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testMultipleBccAddressesCanBeSet() + { + //Obviously Transports need to setBcc(array()) and send to each Bcc recipient + // separately in accordance with RFC 2822/2821 + $message = $this->_createMessage(); + $message->setSubject('just a test subject'); + $message->setFrom(array('chris.corbyn@swiftmailer.org' => 'Chris')); + $message->setReplyTo(array( + 'chris@w3style.co.uk' => 'Myself', + 'my.other@address.com' => 'Me', + )); + $message->setTo(array( + 'mark@swiftmailer.org', 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $message->setCc(array( + 'john@some-site.com' => 'John West', + 'fred@another-site.co.uk' => 'Big Fred', + )); + $message->setBcc(array('x@alphabet.tld', 'a@alphabet.tld' => 'A')); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris '."\r\n". + 'Reply-To: Myself , Me '."\r\n". + 'To: mark@swiftmailer.org, Chris Corbyn '."\r\n". + 'Cc: John West , Big Fred '."\r\n". + 'Bcc: x@alphabet.tld, A '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString() + ); + } + + public function testStringBodyIsAppended() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setBody( + 'just a test body'."\r\n". + 'with a new line' + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'just a test body'."\r\n". + 'with a new line', + $message->toString() + ); + } + + public function testStringBodyIsEncoded() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setBody( + 'Just s'.pack('C*', 0xC2, 0x01, 0x01).'me multi-'."\r\n". + 'line message!' + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'Just s=C2=01=01me multi-'."\r\n". + 'line message!', + $message->toString() + ); + } + + public function testChildrenCanBeAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/plain'); + $part1->setCharset('iso-8859-1'); + $part1->setBody('foo'); + + $message->attach($part1); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/html'); + $part2->setCharset('iso-8859-1'); + $part2->setBody('test foo'); + + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'test foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testAttachmentsBeingAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo'); + + $message->attach($part); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $this->assertRegExp( + '~^'. + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } + + public function testAttachmentsAndEmbeddedFilesBeingAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo'); + + $message->attach($part); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $file = $this->_createEmbeddedFile(); + $file->setContentType('image/jpeg'); + $file->setFilename('myimage.jpg'); + $file->setBody(''); + + $message->attach($file); + + $cid = $file->getId(); + + $this->assertRegExp( + '~^'. + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\2'."\r\n". + 'Content-Type: image/jpeg; name=myimage.jpg'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; filename=myimage.jpg'."\r\n". + 'Content-ID: <'.$cid.'>'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--\\2--'."\r\n". + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } + + public function testComplexEmbeddingOfContent() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $file = $this->_createEmbeddedFile(); + $file->setContentType('image/jpeg'); + $file->setFilename('myimage.jpg'); + $file->setBody(''); + + $part = $this->_createMimePart(); + $part->setContentType('text/html'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo '); + + $message->attach($part); + + $cid = $file->getId(); + + $this->assertRegExp( + '~^'. + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo './/=3D is just = in QP + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: image/jpeg; name=myimage.jpg'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; filename=myimage.jpg'."\r\n". + 'Content-ID: <'.$cid.'>'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } + + public function testAttachingAndDetachingContent() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $part = $this->_createMimePart(); + $part->setContentType('text/plain'); + $part->setCharset('iso-8859-1'); + $part->setBody('foo'); + + $message->attach($part); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $file = $this->_createEmbeddedFile(); + $file->setContentType('image/jpeg'); + $file->setFilename('myimage.jpg'); + $file->setBody(''); + + $message->attach($file); + + $cid = $file->getId(); + + $message->detach($attachment); + + $this->assertRegExp( + '~^'. + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: image/jpeg; name=myimage.jpg'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; filename=myimage.jpg'."\r\n". + 'Content-ID: <'.$cid.'>'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString(), + '%s: Attachment should have been detached' + ); + } + + public function testBoundaryDoesNotAppearAfterAllPartsAreDetached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/plain'); + $part1->setCharset('iso-8859-1'); + $part1->setBody('foo'); + + $message->attach($part1); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/html'); + $part2->setCharset('iso-8859-1'); + $part2->setBody('test foo'); + + $message->attach($part2); + + $message->detach($part1); + $message->detach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n", + $message->toString(), + '%s: Message should be restored to orignal state after parts are detached' + ); + } + + public function testCharsetFormatOrDelSpAreNotShownWhenBoundaryIsSet() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setCharset('utf-8'); + $message->setFormat('flowed'); + $message->setDelSp(true); + + $id = $message->getId(); + $date = $message->getDate(); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/plain'); + $part1->setCharset('iso-8859-1'); + $part1->setBody('foo'); + + $message->attach($part1); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/html'); + $part2->setCharset('iso-8859-1'); + $part2->setBody('test foo'); + + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'test foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testBodyCanBeSetWithAttachments() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setContentType('text/html'); + $message->setCharset('iso-8859-1'); + $message->setBody('foo'); + + $id = $message->getId(); + $date = date('r', $message->getDate()); + $boundary = $message->getBoundary(); + + $attachment = $this->_createAttachment(); + $attachment->setContentType('application/pdf'); + $attachment->setFilename('foo.pdf'); + $attachment->setBody(''); + + $message->attach($attachment); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=iso-8859-1'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: application/pdf; name=foo.pdf'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=foo.pdf'."\r\n". + "\r\n". + base64_encode(''). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testHtmlPartAlwaysAppearsLast() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + + $id = $message->getId(); + $date = date('r', $message->getDate()); + $boundary = $message->getBoundary(); + + $part1 = $this->_createMimePart(); + $part1->setContentType('text/html'); + $part1->setBody('foo'); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/plain'); + $part2->setBody('bar'); + + $message->attach($part1); + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'bar'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testBodyBecomesPartIfOtherPartsAttached() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setContentType('text/html'); + $message->setBody('foo'); + + $id = $message->getId(); + $date = date('r', $message->getDate()); + $boundary = $message->getBoundary(); + + $part2 = $this->_createMimePart(); + $part2->setContentType('text/plain'); + $part2->setBody('bar'); + + $message->attach($part2); + + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'bar'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'foo'. + "\r\n\r\n". + '--'.$boundary.'--'."\r\n", + $message->toString() + ); + } + + public function testBodyIsCanonicalized() + { + $message = $this->_createMessage(); + $message->setReturnPath('chris@w3style.co.uk'); + $message->setSubject('just a test subject'); + $message->setFrom(array( + 'chris.corbyn@swiftmailer.org' => 'Chris Corbyn', )); + $message->setBody( + 'just a test body'."\n". + 'with a new line' + ); + $id = $message->getId(); + $date = $message->getDate(); + $this->assertEquals( + 'Return-Path: '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.date('r', $date)."\r\n". + 'Subject: just a test subject'."\r\n". + 'From: Chris Corbyn '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: text/plain'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'just a test body'."\r\n". + 'with a new line', + $message->toString() + ); + } + + // -- Private helpers + + protected function _createMessage() + { + return new Swift_Message(); + } + + protected function _createMimePart() + { + return new Swift_MimePart(); + } + + protected function _createAttachment() + { + return new Swift_Attachment(); + } + + protected function _createEmbeddedFile() + { + return new Swift_EmbeddedFile(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php new file mode 100644 index 0000000..f42405d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/MimePartAcceptanceTest.php @@ -0,0 +1,15 @@ +register('properties.charset')->asValue(null); + + return Swift_MimePart::newInstance(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php new file mode 100644 index 0000000..465f2c1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/AbstractStreamBufferAcceptanceTest.php @@ -0,0 +1,134 @@ +markTestSkipped( + 'Will fail on travis-ci if not skipped due to travis blocking '. + 'socket mailing tcp connections.' + ); + } + + $this->_buffer = new Swift_Transport_StreamBuffer( + $this->getMock('Swift_ReplacementFilterFactory') + ); + } + + public function testReadLine() + { + $this->_initializeBuffer(); + + $line = $this->_buffer->readLine(0); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + $seq = $this->_buffer->write("QUIT\r\n"); + $this->assertTrue((bool) $seq); + $line = $this->_buffer->readLine($seq); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + $this->_buffer->terminate(); + } + + public function testWrite() + { + $this->_initializeBuffer(); + + $line = $this->_buffer->readLine(0); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + + $seq = $this->_buffer->write("HELO foo\r\n"); + $this->assertTrue((bool) $seq); + $line = $this->_buffer->readLine($seq); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + + $seq = $this->_buffer->write("QUIT\r\n"); + $this->assertTrue((bool) $seq); + $line = $this->_buffer->readLine($seq); + $this->assertRegExp('/^[0-9]{3}.*?\r\n$/D', $line); + $this->_buffer->terminate(); + } + + public function testBindingOtherStreamsMirrorsWriteOperations() + { + $this->_initializeBuffer(); + + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->at(0)) + ->method('write') + ->with('x'); + $is2->expects($this->at(1)) + ->method('write') + ->with('y'); + + $this->_buffer->bind($is1); + $this->_buffer->bind($is2); + + $this->_buffer->write('x'); + $this->_buffer->write('y'); + } + + public function testBindingOtherStreamsMirrorsFlushOperations() + { + $this->_initializeBuffer(); + + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->once()) + ->method('flushBuffers'); + $is2->expects($this->once()) + ->method('flushBuffers'); + + $this->_buffer->bind($is1); + $this->_buffer->bind($is2); + + $this->_buffer->flushBuffers(); + } + + public function testUnbindingStreamPreventsFurtherWrites() + { + $this->_initializeBuffer(); + + $is1 = $this->_createMockInputStream(); + $is2 = $this->_createMockInputStream(); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->once()) + ->method('write') + ->with('x'); + + $this->_buffer->bind($is1); + $this->_buffer->bind($is2); + + $this->_buffer->write('x'); + + $this->_buffer->unbind($is2); + + $this->_buffer->write('y'); + } + + // -- Creation Methods + + private function _createMockInputStream() + { + return $this->getMock('Swift_InputByteStream'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php new file mode 100644 index 0000000..8f6e453 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/BasicSocketAcceptanceTest.php @@ -0,0 +1,34 @@ +markTestSkipped( + 'Cannot run test without an SMTP host to connect to (define '. + 'SWIFT_SMTP_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + parent::setUp(); + } + + protected function _initializeBuffer() + { + $parts = explode(':', SWIFT_SMTP_HOST); + $host = $parts[0]; + $port = isset($parts[1]) ? $parts[1] : 25; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'tcp', + 'blocking' => 1, + 'timeout' => 15, + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php new file mode 100644 index 0000000..b4e88a7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/ProcessAcceptanceTest.php @@ -0,0 +1,27 @@ +markTestSkipped( + 'Cannot run test without a path to sendmail (define '. + 'SWIFT_SENDMAIL_PATH in tests/acceptance.conf.php if you wish to run this test)' + ); + } + + parent::setUp(); + } + + protected function _initializeBuffer() + { + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_PROCESS, + 'command' => SWIFT_SENDMAIL_PATH.' -bs', + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php new file mode 100644 index 0000000..81db65e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SocketTimeoutTest.php @@ -0,0 +1,65 @@ +markTestSkipped( + 'Cannot run test without an SMTP host to connect to (define '. + 'SWIFT_SMTP_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + + $serverStarted = false; + for ($i = 0; $i < 5; ++$i) { + $this->_randomHighPort = rand(50000, 65000); + $this->_server = stream_socket_server('tcp://127.0.0.1:'.$this->_randomHighPort); + if ($this->_server) { + $serverStarted = true; + } + } + + $this->_buffer = new Swift_Transport_StreamBuffer( + $this->getMock('Swift_ReplacementFilterFactory') + ); + } + + protected function _initializeBuffer() + { + $host = '127.0.0.1'; + $port = $this->_randomHighPort; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'tcp', + 'blocking' => 1, + 'timeout' => 1, + )); + } + + public function testTimeoutException() + { + $this->_initializeBuffer(); + $e = null; + try { + $line = $this->_buffer->readLine(0); + } catch (Exception $e) { + } + $this->assertInstanceof('Swift_IoException', $e, 'IO Exception Not Thrown On Connection Timeout'); + $this->assertRegExp('/Connection to .* Timed Out/', $e->getMessage()); + } + + public function tearDown() + { + if ($this->_server) { + stream_socket_shutdown($this->_server, STREAM_SHUT_RDWR); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php new file mode 100644 index 0000000..d2d2a38 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/SslSocketAcceptanceTest.php @@ -0,0 +1,41 @@ +markTestSkipped( + 'SSL is not configured for your system. It is not possible to run this test' + ); + } + if (!defined('SWIFT_SSL_HOST')) { + $this->markTestSkipped( + 'Cannot run test without an SSL enabled SMTP host to connect to (define '. + 'SWIFT_SSL_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + + parent::setUp(); + } + + protected function _initializeBuffer() + { + $parts = explode(':', SWIFT_SSL_HOST); + $host = $parts[0]; + $port = isset($parts[1]) ? $parts[1] : 25; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'ssl', + 'blocking' => 1, + 'timeout' => 15, + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php new file mode 100644 index 0000000..314fffe --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/acceptance/Swift/Transport/StreamBuffer/TlsSocketAcceptanceTest.php @@ -0,0 +1,40 @@ +markTestSkipped( + 'TLS is not configured for your system. It is not possible to run this test' + ); + } + if (!defined('SWIFT_TLS_HOST')) { + $this->markTestSkipped( + 'Cannot run test without a TLS enabled SMTP host to connect to (define '. + 'SWIFT_TLS_HOST in tests/acceptance.conf.php if you wish to run this test)' + ); + } + parent::setUp(); + } + + protected function _initializeBuffer() + { + $parts = explode(':', SWIFT_TLS_HOST); + $host = $parts[0]; + $port = isset($parts[1]) ? $parts[1] : 25; + + $this->_buffer->initialize(array( + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + 'host' => $host, + 'port' => $port, + 'protocol' => 'tls', + 'blocking' => 1, + 'timeout' => 15, + )); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bootstrap.php b/vendor/swiftmailer/swiftmailer/tests/bootstrap.php new file mode 100644 index 0000000..9b169b3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bootstrap.php @@ -0,0 +1,19 @@ +add('Swift_', __DIR__.'/unit'); + +set_include_path(get_include_path().PATH_SEPARATOR.dirname(__DIR__).'/lib'); + +\Mockery::getConfiguration()->allowMockingNonExistentMethods(false); + +if (is_file(__DIR__.'/acceptance.conf.php')) { + require_once __DIR__.'/acceptance.conf.php'; +} +if (is_file(__DIR__.'/smoke.conf.php')) { + require_once __DIR__.'/smoke.conf.php'; +} +require_once __DIR__.'/StreamCollector.php'; +require_once __DIR__.'/IdenticalBinaryConstraint.php'; +require_once __DIR__.'/SwiftMailerTestCase.php'; +require_once __DIR__.'/SwiftMailerSmokeTestCase.php'; diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php new file mode 100644 index 0000000..ba29ba8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug111Test.php @@ -0,0 +1,42 @@ + array( + 'email1@example.com', + 'email2@example.com', + 'email3@example.com', + 'email4@example.com', + 'email5@example.com', + ), + 'sub' => array( + '-name-' => array( + 'email1', + '"email2"', + 'email3\\', + 'email4', + 'email5', + ), + '-url-' => array( + 'http://google.com', + 'http://yahoo.com', + 'http://hotmail.com', + 'http://aol.com', + 'http://facebook.com', + ), + ), + ); + $json = json_encode($complicated_header); + + $message = new Swift_Message(); + $headers = $message->getHeaders(); + $headers->addTextHeader('X-SMTPAPI', $json); + $header = $headers->get('X-SMTPAPI'); + + $this->assertEquals('Swift_Mime_Headers_UnstructuredHeader', get_class($header)); + $this->assertEquals($json, $header->getFieldBody()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php new file mode 100644 index 0000000..bd10c71 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug118Test.php @@ -0,0 +1,20 @@ +_message = new Swift_Message(); + } + + public function testCallingGenerateIdChangesTheMessageId() + { + $currentId = $this->_message->getId(); + $this->_message->generateId(); + $newId = $this->_message->getId(); + + $this->assertNotEquals($currentId, $newId); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php new file mode 100644 index 0000000..fdfa530 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug206Test.php @@ -0,0 +1,38 @@ +_factory = new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $grammar); + } + + public function testMailboxHeaderEncoding() + { + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Name, Name', ' "Family Name, Name" '); + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Namé, Name', ' Family =?utf-8?Q?Nam=C3=A9=2C?= Name'); + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Namé , Name', ' Family =?utf-8?Q?Nam=C3=A9_=2C?= Name'); + $this->_testHeaderIsFullyEncoded('email@example.org', 'Family Namé ;Name', ' Family =?utf-8?Q?Nam=C3=A9_=3BName?= '); + } + + private function _testHeaderIsFullyEncoded($email, $name, $expected) + { + $mailboxHeader = $this->_factory->createMailboxHeader('To', array( + $email => $name, + )); + + $headerBody = substr($mailboxHeader->toString(), 3, strlen($expected)); + + $this->assertEquals($expected, $headerBody); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php new file mode 100644 index 0000000..d305d02 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug274Test.php @@ -0,0 +1,21 @@ +setExpectedException('Swift_IoException', 'The path cannot be empty'); + $message->attach(Swift_Attachment::fromPath('')); + } + + public function testNonEmptyFileNameAsAttachement() + { + $message = new Swift_Message(); + try { + $message->attach(Swift_Attachment::fromPath(__FILE__)); + } catch (Exception $e) { + $this->fail('Path should not be empty'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php new file mode 100644 index 0000000..6a0f33d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug34Test.php @@ -0,0 +1,75 @@ +setCharset('utf-8'); + } + + public function testEmbeddedFilesWithMultipartDataCreateMultipartRelatedContentAsAnAlternative() + { + $message = Swift_Message::newInstance(); + $message->setCharset('utf-8'); + $message->setSubject('test subject'); + $message->addPart('plain part', 'text/plain'); + + $image = Swift_Image::newInstance('', 'image.gif', 'image/gif'); + $cid = $message->embed($image); + + $message->setBody('', 'text/html'); + + $message->setTo(array('user@domain.tld' => 'User')); + + $message->setFrom(array('other@domain.tld' => 'Other')); + $message->setSender(array('other@domain.tld' => 'Other')); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + $cidVal = $image->getId(); + + $this->assertRegExp( + '~^'. + 'Sender: Other '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: Other '."\r\n". + 'To: User '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'plain part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + ''. + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; filename=image.gif'."\r\n". + 'Content-ID: <'.$cidVal.'>'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php new file mode 100644 index 0000000..e07ee8f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug35Test.php @@ -0,0 +1,73 @@ +setCharset('utf-8'); + } + + public function testHTMLPartAppearsLastEvenWhenAttachmentsAdded() + { + $message = Swift_Message::newInstance(); + $message->setCharset('utf-8'); + $message->setSubject('test subject'); + $message->addPart('plain part', 'text/plain'); + + $attachment = Swift_Attachment::newInstance('', 'image.gif', 'image/gif'); + $message->attach($attachment); + + $message->setBody('HTML part', 'text/html'); + + $message->setTo(array('user@domain.tld' => 'User')); + + $message->setFrom(array('other@domain.tld' => 'Other')); + $message->setSender(array('other@domain.tld' => 'Other')); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $this->assertRegExp( + '~^'. + 'Sender: Other '."\r\n". + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: Other '."\r\n". + 'To: User '."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: multipart/alternative;'."\r\n". + ' boundary="(.*?)"'."\r\n". + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/plain; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'plain part'. + "\r\n\r\n". + '--\\1'."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--\\1--'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename=image.gif'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $message->toString() + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php new file mode 100644 index 0000000..e281516 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug38Test.php @@ -0,0 +1,194 @@ +_attFileName = 'data.txt'; + $this->_attFileType = 'text/plain'; + $this->_attFile = __DIR__.'/../../_samples/files/data.txt'; + Swift_Preferences::getInstance()->setCharset('utf-8'); + } + + public function testWritingMessageToByteStreamProducesCorrectStructure() + { + $message = new Swift_Message(); + $message->setSubject('test subject'); + $message->setTo('user@domain.tld'); + $message->setCc('other@domain.tld'); + $message->setFrom('user@domain.tld'); + + $image = new Swift_Image('', 'image.gif', 'image/gif'); + + $cid = $message->embed($image); + $message->setBody('HTML part', 'text/html'); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + $imgId = $image->getId(); + + $stream = new Swift_ByteStream_ArrayByteStream(); + + $message->toByteStream($stream); + + $this->assertPatternInStream( + '~^'. + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: user@domain.tld'."\r\n". + 'To: user@domain.tld'."\r\n". + 'Cc: other@domain.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; filename=image.gif'."\r\n". + 'Content-ID: <'.preg_quote($imgId, '~').'>'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D', + $stream + ); + } + + public function testWritingMessageToByteStreamTwiceProducesCorrectStructure() + { + $message = new Swift_Message(); + $message->setSubject('test subject'); + $message->setTo('user@domain.tld'); + $message->setCc('other@domain.tld'); + $message->setFrom('user@domain.tld'); + + $image = new Swift_Image('', 'image.gif', 'image/gif'); + + $cid = $message->embed($image); + $message->setBody('HTML part', 'text/html'); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + $imgId = $image->getId(); + + $pattern = '~^'. + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: user@domain.tld'."\r\n". + 'To: user@domain.tld'."\r\n". + 'Cc: other@domain.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/related;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: image/gif; name=image.gif'."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: inline; filename=image.gif'."\r\n". + 'Content-ID: <'.preg_quote($imgId, '~').'>'."\r\n". + "\r\n". + preg_quote(base64_encode(''), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D' + ; + + $streamA = new Swift_ByteStream_ArrayByteStream(); + $streamB = new Swift_ByteStream_ArrayByteStream(); + + $message->toByteStream($streamA); + $message->toByteStream($streamB); + + $this->assertPatternInStream($pattern, $streamA); + $this->assertPatternInStream($pattern, $streamB); + } + + public function testWritingMessageToByteStreamTwiceUsingAFileAttachment() + { + $message = new Swift_Message(); + $message->setSubject('test subject'); + $message->setTo('user@domain.tld'); + $message->setCc('other@domain.tld'); + $message->setFrom('user@domain.tld'); + + $attachment = Swift_Attachment::fromPath($this->_attFile); + + $message->attach($attachment); + + $message->setBody('HTML part', 'text/html'); + + $id = $message->getId(); + $date = preg_quote(date('r', $message->getDate()), '~'); + $boundary = $message->getBoundary(); + + $streamA = new Swift_ByteStream_ArrayByteStream(); + $streamB = new Swift_ByteStream_ArrayByteStream(); + + $pattern = '~^'. + 'Message-ID: <'.$id.'>'."\r\n". + 'Date: '.$date."\r\n". + 'Subject: test subject'."\r\n". + 'From: user@domain.tld'."\r\n". + 'To: user@domain.tld'."\r\n". + 'Cc: other@domain.tld'."\r\n". + 'MIME-Version: 1.0'."\r\n". + 'Content-Type: multipart/mixed;'."\r\n". + ' boundary="'.$boundary.'"'."\r\n". + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: text/html; charset=utf-8'."\r\n". + 'Content-Transfer-Encoding: quoted-printable'."\r\n". + "\r\n". + 'HTML part'. + "\r\n\r\n". + '--'.$boundary."\r\n". + 'Content-Type: '.$this->_attFileType.'; name='.$this->_attFileName."\r\n". + 'Content-Transfer-Encoding: base64'."\r\n". + 'Content-Disposition: attachment; filename='.$this->_attFileName."\r\n". + "\r\n". + preg_quote(base64_encode(file_get_contents($this->_attFile)), '~'). + "\r\n\r\n". + '--'.$boundary.'--'."\r\n". + '$~D' + ; + + $message->toByteStream($streamA); + $message->toByteStream($streamB); + + $this->assertPatternInStream($pattern, $streamA); + $this->assertPatternInStream($pattern, $streamB); + } + + // -- Helpers + + public function assertPatternInStream($pattern, $stream, $message = '%s') + { + $string = ''; + while (false !== $bytes = $stream->read(8192)) { + $string .= $bytes; + } + $this->assertRegExp($pattern, $string, $message); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php new file mode 100644 index 0000000..b83984f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug518Test.php @@ -0,0 +1,38 @@ +setTo('foo@bar.com'); + + $that = $this; + $messageValidation = function ($m) use ($that) { + //the getTo should return the same value as we put in + $that->assertEquals('foo@bar.com', key($m->getTo()), 'The message has changed after it was put to the memory queue'); + + return true; + }; + + $transport = m::mock('Swift_Transport'); + $transport->shouldReceive('isStarted')->andReturn(true); + $transport->shouldReceive('send') + ->with(m::on($messageValidation), $failedRecipients) + ->andReturn(1); + + $memorySpool = new Swift_MemorySpool(); + $memorySpool->queueMessage($message); + + /* + * The message is queued in memory. + * Lets change the message + */ + $message->setTo('other@value.com'); + + $memorySpool->flushQueue($transport, $failedRecipients); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php new file mode 100644 index 0000000..b9c33b0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug51Test.php @@ -0,0 +1,121 @@ +markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_attachmentFile = SWIFT_TMP_DIR.'/attach.rand.bin'; + file_put_contents($this->_attachmentFile, ''); + + $this->_outputFile = SWIFT_TMP_DIR.'/attach.out.bin'; + file_put_contents($this->_outputFile, ''); + } + + public function tearDown() + { + unlink($this->_attachmentFile); + unlink($this->_outputFile); + } + + public function testAttachmentsDoNotGetTruncatedUsingToByteStream() + { + //Run 100 times with 10KB attachments + for ($i = 0; $i < 10; ++$i) { + $message = $this->_createMessageWithRandomAttachment( + 10000, $this->_attachmentFile + ); + + file_put_contents($this->_outputFile, ''); + $message->toByteStream( + new Swift_ByteStream_FileByteStream($this->_outputFile, true) + ); + + $emailSource = file_get_contents($this->_outputFile); + + $this->assertAttachmentFromSourceMatches( + file_get_contents($this->_attachmentFile), + $emailSource + ); + } + } + + public function testAttachmentsDoNotGetTruncatedUsingToString() + { + //Run 100 times with 10KB attachments + for ($i = 0; $i < 10; ++$i) { + $message = $this->_createMessageWithRandomAttachment( + 10000, $this->_attachmentFile + ); + + $emailSource = $message->toString(); + + $this->assertAttachmentFromSourceMatches( + file_get_contents($this->_attachmentFile), + $emailSource + ); + } + } + + // -- Custom Assertions + + public function assertAttachmentFromSourceMatches($attachmentData, $source) + { + $encHeader = 'Content-Transfer-Encoding: base64'; + $base64declaration = strpos($source, $encHeader); + + $attachmentDataStart = strpos($source, "\r\n\r\n", $base64declaration); + $attachmentDataEnd = strpos($source, "\r\n--", $attachmentDataStart); + + if (false === $attachmentDataEnd) { + $attachmentBase64 = trim(substr($source, $attachmentDataStart)); + } else { + $attachmentBase64 = trim(substr( + $source, $attachmentDataStart, + $attachmentDataEnd - $attachmentDataStart + )); + } + + $this->assertIdenticalBinary($attachmentData, base64_decode($attachmentBase64)); + } + + // -- Creation Methods + + private function _fillFileWithRandomBytes($byteCount, $file) + { + // I was going to use dd with if=/dev/random but this way seems more + // cross platform even if a hella expensive!! + + file_put_contents($file, ''); + $fp = fopen($file, 'wb'); + for ($i = 0; $i < $byteCount; ++$i) { + $byteVal = rand(0, 255); + fwrite($fp, pack('i', $byteVal)); + } + fclose($fp); + } + + private function _createMessageWithRandomAttachment($size, $attachmentPath) + { + $this->_fillFileWithRandomBytes($size, $attachmentPath); + + $message = Swift_Message::newInstance() + ->setSubject('test') + ->setBody('test') + ->setFrom('a@b.c') + ->setTo('d@e.f') + ->attach(Swift_Attachment::fromPath($attachmentPath)) + ; + + return $message; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php new file mode 100644 index 0000000..263cae5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug534Test.php @@ -0,0 +1,38 @@ +setFrom('from@example.com') + ->setTo('to@example.com') + ->setSubject('test') + ; + $cid = $message->embed(Swift_Image::fromPath(__DIR__.'/../../_samples/files/swiftmailer.png')); + $message->setBody('', 'text/html'); + + $that = $this; + $messageValidation = function (Swift_Mime_Message $message) use ($that) { + preg_match('/cid:(.*)"/', $message->toString(), $matches); + $cid = $matches[1]; + preg_match('/Content-ID: <(.*)>/', $message->toString(), $matches); + $contentId = $matches[1]; + $that->assertEquals($cid, $contentId, 'cid in body and mime part Content-ID differ'); + + return true; + }; + + $failedRecipients = array(); + + $transport = m::mock('Swift_Transport'); + $transport->shouldReceive('isStarted')->andReturn(true); + $transport->shouldReceive('send')->with(m::on($messageValidation), $failedRecipients)->andReturn(1); + + $memorySpool = new Swift_MemorySpool(); + $memorySpool->queueMessage($message); + $memorySpool->flushQueue($transport, $failedRecipients); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php new file mode 100644 index 0000000..4b80453 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug71Test.php @@ -0,0 +1,20 @@ +_message = new Swift_Message('test'); + } + + public function testCallingToStringAfterSettingNewBodyReflectsChanges() + { + $this->_message->setBody('BODY1'); + $this->assertRegExp('/BODY1/', $this->_message->toString()); + + $this->_message->setBody('BODY2'); + $this->assertRegExp('/BODY2/', $this->_message->toString()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php new file mode 100644 index 0000000..c2b12cb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/bug/Swift/Bug76Test.php @@ -0,0 +1,82 @@ +markTestSkipped( + 'Cannot run test without a writable directory to use ('. + 'define SWIFT_TMP_DIR in tests/config.php if you wish to run this test)' + ); + } + + $this->_inputFile = SWIFT_TMP_DIR.'/in.bin'; + file_put_contents($this->_inputFile, ''); + + $this->_outputFile = SWIFT_TMP_DIR.'/out.bin'; + file_put_contents($this->_outputFile, ''); + + $this->_encoder = $this->_createEncoder(); + } + + public function tearDown() + { + unlink($this->_inputFile); + unlink($this->_outputFile); + } + + public function testBase64EncodedLineLengthNeverExceeds76CharactersEvenIfArgsDo() + { + $this->_fillFileWithRandomBytes(1000, $this->_inputFile); + + $os = $this->_createStream($this->_inputFile); + $is = $this->_createStream($this->_outputFile); + + $this->_encoder->encodeByteStream($os, $is, 0, 80); //Exceeds 76 + + $this->assertMaxLineLength(76, $this->_outputFile, + '%s: Line length should not exceed 76 characters' + ); + } + + // -- Custom Assertions + + public function assertMaxLineLength($length, $filePath, $message = '%s') + { + $lines = file($filePath); + foreach ($lines as $line) { + $this->assertTrue((strlen(trim($line)) <= 76), $message); + } + } + + // -- Creation Methods + + private function _fillFileWithRandomBytes($byteCount, $file) + { + // I was going to use dd with if=/dev/random but this way seems more + // cross platform even if a hella expensive!! + + file_put_contents($file, ''); + $fp = fopen($file, 'wb'); + for ($i = 0; $i < $byteCount; ++$i) { + $byteVal = rand(0, 255); + fwrite($fp, pack('i', $byteVal)); + } + fclose($fp); + } + + private function _createEncoder() + { + return new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + } + + private function _createStream($file) + { + return new Swift_ByteStream_FileByteStream($file, true); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/fixtures/EsmtpTransportFixture.php b/vendor/swiftmailer/swiftmailer/tests/fixtures/EsmtpTransportFixture.php new file mode 100644 index 0000000..d56d88d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/fixtures/EsmtpTransportFixture.php @@ -0,0 +1,10 @@ +level = $level; + $this->string = $string; + $this->contentType = $contentType; + } + + public function getNestingLevel() + { + return $this->level; + } + + public function toString() + { + return $this->string; + } + + public function getContentType() + { + return $this->contentType; + } + + // These methods are here to account for the implemented interfaces + public function getId() + { + } + public function getHeaders() + { + } + public function getBody() + { + } + public function setBody($body, $contentType = null) + { + } + public function toByteStream(Swift_InputByteStream $is) + { + } + public function charsetChanged($charset) + { + } + public function encoderChanged(Swift_Mime_ContentEncoder $encoder) + { + } + public function getChildren() + { + } + public function setChildren(array $children) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default b/vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default new file mode 100644 index 0000000..0de2763 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke.conf.php.default @@ -0,0 +1,63 @@ +_attFile = __DIR__.'/../../../_samples/files/textfile.zip'; + } + + public function testAttachmentSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance() + ->setSubject('[Swift Mailer] AttachmentSmokeTest') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Swift Mailer')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->setBody('This message should contain an attached ZIP file (named "textfile.zip").'.PHP_EOL. + 'When unzipped, the archive should produce a text file which reads:'.PHP_EOL. + '"This is part of a Swift Mailer v4 smoke test."' + ) + ->attach(Swift_Attachment::fromPath($this->_attFile)) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php new file mode 100644 index 0000000..c7501d4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/BasicSmokeTest.php @@ -0,0 +1,23 @@ +_getMailer(); + $message = Swift_Message::newInstance() + ->setSubject('[Swift Mailer] BasicSmokeTest') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Swift Mailer')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->setBody('One, two, three, four, five...'.PHP_EOL. + 'six, seven, eight...' + ) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php new file mode 100644 index 0000000..a589752 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/HtmlWithAttachmentSmokeTest.php @@ -0,0 +1,29 @@ +_attFile = __DIR__.'/../../../_samples/files/textfile.zip'; + } + + public function testAttachmentSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance('[Swift Mailer] HtmlWithAttachmentSmokeTest') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Swift Mailer')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->attach(Swift_Attachment::fromPath($this->_attFile)) + ->setBody('

This HTML-formatted message should contain an attached ZIP file (named "textfile.zip").'.PHP_EOL. + 'When unzipped, the archive should produce a text file which reads:

'.PHP_EOL. + '

This is part of a Swift Mailer v4 smoke test.

', 'text/html' + ) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php new file mode 100644 index 0000000..e7c695e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/smoke/Swift/Smoke/InternationalSmokeTest.php @@ -0,0 +1,37 @@ +_attFile = __DIR__.'/../../../_samples/files/textfile.zip'; + } + + public function testAttachmentSending() + { + $mailer = $this->_getMailer(); + $message = Swift_Message::newInstance() + ->setCharset('utf-8') + ->setSubject('[Swift Mailer] InternationalSmokeTest (διεθνής)') + ->setFrom(array(SWIFT_SMOKE_EMAIL_ADDRESS => 'Χριστοφορου (Swift Mailer)')) + ->setTo(SWIFT_SMOKE_EMAIL_ADDRESS) + ->setBody('This message should contain an attached ZIP file (named "κείμενο, εδάφιο, θέμα.zip").'.PHP_EOL. + 'When unzipped, the archive should produce a text file which reads:'.PHP_EOL. + '"This is part of a Swift Mailer v4 smoke test."'.PHP_EOL. + PHP_EOL. + 'Following is some arbitrary Greek text:'.PHP_EOL. + 'Δεν βρέθηκαν λέξεις.' + ) + ->attach(Swift_Attachment::fromPath($this->_attFile) + ->setContentType('application/zip') + ->setFilename('κείμενο, εδάφιο, θέμα.zip') + ) + ; + $this->assertEquals(1, $mailer->send($message), + '%s: The smoke test should send a single message' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php new file mode 100644 index 0000000..0193484 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/ByteStream/ArrayByteStreamTest.php @@ -0,0 +1,204 @@ +_createArrayStream($input); + $output = array(); + while (false !== $bytes = $bs->read(1)) { + $output[] = $bytes; + } + $this->assertEquals($input, $output, + '%s: Bytes read from stream should be the same as bytes in constructor' + ); + } + + public function testReadingMultipleBytesFromBaseInput() + { + $input = array('a', 'b', 'c', 'd'); + $bs = $this->_createArrayStream($input); + $output = array(); + while (false !== $bytes = $bs->read(2)) { + $output[] = $bytes; + } + $this->assertEquals(array('ab', 'cd'), $output, + '%s: Bytes read from stream should be in pairs' + ); + } + + public function testReadingOddOffsetOnLastByte() + { + $input = array('a', 'b', 'c', 'd', 'e'); + $bs = $this->_createArrayStream($input); + $output = array(); + while (false !== $bytes = $bs->read(2)) { + $output[] = $bytes; + } + $this->assertEquals(array('ab', 'cd', 'e'), $output, + '%s: Bytes read from stream should be in pairs except final read' + ); + } + + public function testSettingPointerPartway() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + $bs->setReadPointer(1); + $this->assertEquals('b', $bs->read(1), + '%s: Byte should be second byte since pointer as at offset 1' + ); + } + + public function testResettingPointerAfterExhaustion() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + while (false !== $bs->read(1)); + + $bs->setReadPointer(0); + $this->assertEquals('a', $bs->read(1), + '%s: Byte should be first byte since pointer as at offset 0' + ); + } + + public function testPointerNeverSetsBelowZero() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->setReadPointer(-1); + $this->assertEquals('a', $bs->read(1), + '%s: Byte should be first byte since pointer should be at offset 0' + ); + } + + public function testPointerNeverSetsAboveStackSize() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->setReadPointer(3); + $this->assertSame(false, $bs->read(1), + '%s: Stream should be at end and thus return false' + ); + } + + public function testBytesCanBeWrittenToStream() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->write('de'); + + $output = array(); + while (false !== $bytes = $bs->read(1)) { + $output[] = $bytes; + } + $this->assertEquals(array('a', 'b', 'c', 'd', 'e'), $output, + '%s: Bytes read from stream should be from initial stack + written' + ); + } + + public function testContentsCanBeFlushed() + { + $input = array('a', 'b', 'c'); + $bs = $this->_createArrayStream($input); + + $bs->flushBuffers(); + + $this->assertSame(false, $bs->read(1), + '%s: Contents have been flushed so read() should return false' + ); + } + + public function testConstructorCanTakeStringArgument() + { + $bs = $this->_createArrayStream('abc'); + $output = array(); + while (false !== $bytes = $bs->read(1)) { + $output[] = $bytes; + } + $this->assertEquals(array('a', 'b', 'c'), $output, + '%s: Bytes read from stream should be the same as bytes in constructor' + ); + } + + public function testBindingOtherStreamsMirrorsWriteOperations() + { + $bs = $this->_createArrayStream(''); + $is1 = $this->getMock('Swift_InputByteStream'); + $is2 = $this->getMock('Swift_InputByteStream'); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->at(0)) + ->method('write') + ->with('x'); + $is2->expects($this->at(1)) + ->method('write') + ->with('y'); + + $bs->bind($is1); + $bs->bind($is2); + + $bs->write('x'); + $bs->write('y'); + } + + public function testBindingOtherStreamsMirrorsFlushOperations() + { + $bs = $this->_createArrayStream(''); + $is1 = $this->getMock('Swift_InputByteStream'); + $is2 = $this->getMock('Swift_InputByteStream'); + + $is1->expects($this->once()) + ->method('flushBuffers'); + $is2->expects($this->once()) + ->method('flushBuffers'); + + $bs->bind($is1); + $bs->bind($is2); + + $bs->flushBuffers(); + } + + public function testUnbindingStreamPreventsFurtherWrites() + { + $bs = $this->_createArrayStream(''); + $is1 = $this->getMock('Swift_InputByteStream'); + $is2 = $this->getMock('Swift_InputByteStream'); + + $is1->expects($this->at(0)) + ->method('write') + ->with('x'); + $is1->expects($this->at(1)) + ->method('write') + ->with('y'); + $is2->expects($this->once()) + ->method('write') + ->with('x'); + + $bs->bind($is1); + $bs->bind($is2); + + $bs->write('x'); + + $bs->unbind($is2); + + $bs->write('y'); + } + + // -- Creation Methods + + private function _createArrayStream($input) + { + return new Swift_ByteStream_ArrayByteStream($input); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php new file mode 100644 index 0000000..3f7a46c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/GenericFixedWidthReaderTest.php @@ -0,0 +1,43 @@ +assertSame(1, $reader->getInitialByteSize()); + + $reader = new Swift_CharacterReader_GenericFixedWidthReader(4); + $this->assertSame(4, $reader->getInitialByteSize()); + } + + public function testValidationValueIsBasedOnOctetCount() + { + $reader = new Swift_CharacterReader_GenericFixedWidthReader(4); + + $this->assertSame( + 1, $reader->validateByteSequence(array(0x01, 0x02, 0x03), 3) + ); //3 octets + + $this->assertSame( + 2, $reader->validateByteSequence(array(0x01, 0x0A), 2) + ); //2 octets + + $this->assertSame( + 3, $reader->validateByteSequence(array(0xFE), 1) + ); //1 octet + + $this->assertSame( + 0, $reader->validateByteSequence(array(0xFE, 0x03, 0x67, 0x9A), 4) + ); //All 4 octets + } + + public function testValidationFailsIfTooManyOctets() + { + $reader = new Swift_CharacterReader_GenericFixedWidthReader(6); + + $this->assertSame(-1, $reader->validateByteSequence( + array(0xFE, 0x03, 0x67, 0x9A, 0x10, 0x09, 0x85), 7 + )); //7 octets + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php new file mode 100644 index 0000000..41f8f70 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/UsAsciiReaderTest.php @@ -0,0 +1,52 @@ +read($size); ) { + $c .= $bytes; + $size = $v->validateCharacter($c); + if (-1 == $size) { + throw new Exception( ... invalid char .. ); + } elseif (0 == $size) { + return $c; //next character in $os + } + } + + */ + + private $_reader; + + public function setUp() + { + $this->_reader = new Swift_CharacterReader_UsAsciiReader(); + } + + public function testAllValidAsciiCharactersReturnZero() + { + for ($ordinal = 0x00; $ordinal <= 0x7F; ++$ordinal) { + $this->assertSame( + 0, $this->_reader->validateByteSequence(array($ordinal), 1) + ); + } + } + + public function testMultipleBytesAreInvalid() + { + for ($ordinal = 0x00; $ordinal <= 0x7F; $ordinal += 2) { + $this->assertSame( + -1, $this->_reader->validateByteSequence(array($ordinal, $ordinal + 1), 2) + ); + } + } + + public function testBytesAboveAsciiRangeAreInvalid() + { + for ($ordinal = 0x80; $ordinal <= 0xFF; ++$ordinal) { + $this->assertSame( + -1, $this->_reader->validateByteSequence(array($ordinal), 1) + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php new file mode 100644 index 0000000..34e9c91 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterReader/Utf8ReaderTest.php @@ -0,0 +1,65 @@ +_reader = new Swift_CharacterReader_Utf8Reader(); + } + + public function testLeading7BitOctetCausesReturnZero() + { + for ($ordinal = 0x00; $ordinal <= 0x7F; ++$ordinal) { + $this->assertSame( + 0, $this->_reader->validateByteSequence(array($ordinal), 1) + ); + } + } + + public function testLeadingByteOf2OctetCharCausesReturn1() + { + for ($octet = 0xC0; $octet <= 0xDF; ++$octet) { + $this->assertSame( + 1, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf3OctetCharCausesReturn2() + { + for ($octet = 0xE0; $octet <= 0xEF; ++$octet) { + $this->assertSame( + 2, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf4OctetCharCausesReturn3() + { + for ($octet = 0xF0; $octet <= 0xF7; ++$octet) { + $this->assertSame( + 3, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf5OctetCharCausesReturn4() + { + for ($octet = 0xF8; $octet <= 0xFB; ++$octet) { + $this->assertSame( + 4, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } + + public function testLeadingByteOf6OctetCharCausesReturn5() + { + for ($octet = 0xFC; $octet <= 0xFD; ++$octet) { + $this->assertSame( + 5, $this->_reader->validateByteSequence(array($octet), 1) + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php new file mode 100644 index 0000000..e22e496 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/CharacterStream/ArrayCharacterStreamTest.php @@ -0,0 +1,360 @@ +_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', + 0xD0, 0x94, + 0xD0, 0xB6, + 0xD0, 0xBE, + 0xD1, 0x8D, + 0xD0, 0xBB, + 0xD0, 0xB0 + ) + ); + } + + public function testCharactersWrittenUseValidator() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->write(pack('C*', + 0xD0, 0xBB, + 0xD1, 0x8E, + 0xD0, 0xB1, + 0xD1, 0x8B, + 0xD1, 0x85 + ) + ); + } + + public function testReadCharactersAreInTact() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + //String + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + //Stream + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->write(pack('C*', + 0xD0, 0xBB, + 0xD1, 0x8E, + 0xD0, 0xB1, + 0xD1, 0x8B, + 0xD1, 0x85 + ) + ); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + $this->assertIdenticalBinary( + pack('C*', 0xD0, 0xB6, 0xD0, 0xBE), $stream->read(2) + ); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBB), $stream->read(1)); + $this->assertIdenticalBinary( + pack('C*', 0xD1, 0x8E, 0xD0, 0xB1, 0xD1, 0x8B), $stream->read(3) + ); + $this->assertIdenticalBinary(pack('C*', 0xD1, 0x85), $stream->read(1)); + + $this->assertSame(false, $stream->read(1)); + } + + public function testCharactersCanBeReadAsByteArrays() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + //String + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + //Stream + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->write(pack('C*', + 0xD0, 0xBB, + 0xD1, 0x8E, + 0xD0, 0xB1, + 0xD1, 0x8B, + 0xD1, 0x85 + ) + ); + + $this->assertEquals(array(0xD0, 0x94), $stream->readBytes(1)); + $this->assertEquals(array(0xD0, 0xB6, 0xD0, 0xBE), $stream->readBytes(2)); + $this->assertEquals(array(0xD0, 0xBB), $stream->readBytes(1)); + $this->assertEquals( + array(0xD1, 0x8E, 0xD0, 0xB1, 0xD1, 0x8B), $stream->readBytes(3) + ); + $this->assertEquals(array(0xD1, 0x85), $stream->readBytes(1)); + + $this->assertSame(false, $stream->readBytes(1)); + } + + public function testRequestingLargeCharCountPastEndOfStream() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE), + $stream->read(100) + ); + + $this->assertSame(false, $stream->read(1)); + } + + public function testRequestingByteArrayCountPastEndOfStream() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $this->assertEquals(array(0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE), + $stream->readBytes(100) + ); + + $this->assertSame(false, $stream->readBytes(1)); + } + + public function testPointerOffsetCanBeSet() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + + $stream->setPointer(0); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + + $stream->setPointer(2); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBE), $stream->read(1)); + } + + public function testContentsCanBeFlushed() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importString(pack('C*', 0xD0, 0x94, 0xD0, 0xB6, 0xD0, 0xBE)); + + $stream->flushContents(); + + $this->assertSame(false, $stream->read(1)); + } + + public function testByteStreamCanBeImportingUsesValidator() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + $os = $this->_getByteStream(); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $os->shouldReceive('setReadPointer') + ->between(0, 1) + ->with(0); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0x94)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xB6)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xBE)); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importByteStream($os); + } + + public function testImportingStreamProducesCorrectCharArray() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + $os = $this->_getByteStream(); + + $stream = new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8'); + + $os->shouldReceive('setReadPointer') + ->between(0, 1) + ->with(0); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0x94)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xB6)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xD0)); + $os->shouldReceive('read')->once()->andReturn(pack('C*', 0xBE)); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0), 1)->andReturn(1); + + $stream->importByteStream($os); + + $this->assertIdenticalBinary(pack('C*', 0xD0, 0x94), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xB6), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBE), $stream->read(1)); + + $this->assertSame(false, $stream->read(1)); + } + + public function testAlgorithmWithFixedWidthCharsets() + { + $reader = $this->_getReader(); + $factory = $this->_getFactory($reader); + + $reader->shouldReceive('getInitialByteSize') + ->zeroOrMoreTimes() + ->andReturn(2); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD1, 0x8D), 2); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0, 0xBB), 2); + $reader->shouldReceive('validateByteSequence')->once()->with(array(0xD0, 0xB0), 2); + + $stream = new Swift_CharacterStream_ArrayCharacterStream( + $factory, 'utf-8' + ); + $stream->importString(pack('C*', 0xD1, 0x8D, 0xD0, 0xBB, 0xD0, 0xB0)); + + $this->assertIdenticalBinary(pack('C*', 0xD1, 0x8D), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xBB), $stream->read(1)); + $this->assertIdenticalBinary(pack('C*', 0xD0, 0xB0), $stream->read(1)); + + $this->assertSame(false, $stream->read(1)); + } + + // -- Creation methods + + private function _getReader() + { + return $this->getMockery('Swift_CharacterReader'); + } + + private function _getFactory($reader) + { + $factory = $this->getMockery('Swift_CharacterReaderFactory'); + $factory->shouldReceive('getReaderFor') + ->zeroOrMoreTimes() + ->with('utf-8') + ->andReturn($reader); + + return $factory; + } + + private function _getByteStream() + { + return $this->getMockery('Swift_OutputByteStream'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php new file mode 100644 index 0000000..bc4a79b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/DependencyContainerTest.php @@ -0,0 +1,174 @@ +arg1 = $arg1; + $this->arg2 = $arg2; + } +} + +class Swift_DependencyContainerTest extends \PHPUnit_Framework_TestCase +{ + private $_container; + + public function setUp() + { + $this->_container = new Swift_DependencyContainer(); + } + + public function testRegisterAndLookupValue() + { + $this->_container->register('foo')->asValue('bar'); + $this->assertEquals('bar', $this->_container->lookup('foo')); + } + + public function testHasReturnsTrueForRegisteredValue() + { + $this->_container->register('foo')->asValue('bar'); + $this->assertTrue($this->_container->has('foo')); + } + + public function testHasReturnsFalseForUnregisteredValue() + { + $this->assertFalse($this->_container->has('foo')); + } + + public function testRegisterAndLookupNewInstance() + { + $this->_container->register('one')->asNewInstanceOf('One'); + $this->assertInstanceof('One', $this->_container->lookup('one')); + } + + public function testHasReturnsTrueForRegisteredInstance() + { + $this->_container->register('one')->asNewInstanceOf('One'); + $this->assertTrue($this->_container->has('one')); + } + + public function testNewInstanceIsAlwaysNew() + { + $this->_container->register('one')->asNewInstanceOf('One'); + $a = $this->_container->lookup('one'); + $b = $this->_container->lookup('one'); + $this->assertEquals($a, $b); + } + + public function testRegisterAndLookupSharedInstance() + { + $this->_container->register('one')->asSharedInstanceOf('One'); + $this->assertInstanceof('One', $this->_container->lookup('one')); + } + + public function testHasReturnsTrueForSharedInstance() + { + $this->_container->register('one')->asSharedInstanceOf('One'); + $this->assertTrue($this->_container->has('one')); + } + + public function testMultipleSharedInstancesAreSameInstance() + { + $this->_container->register('one')->asSharedInstanceOf('One'); + $a = $this->_container->lookup('one'); + $b = $this->_container->lookup('one'); + $this->assertEquals($a, $b); + } + + public function testNewInstanceWithDependencies() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One') + ->withDependencies(array('foo')); + $obj = $this->_container->lookup('one'); + $this->assertSame('FOO', $obj->arg1); + } + + public function testNewInstanceWithMultipleDependencies() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asValue(42); + $this->_container->register('one')->asNewInstanceOf('One') + ->withDependencies(array('foo', 'bar')); + $obj = $this->_container->lookup('one'); + $this->assertSame('FOO', $obj->arg1); + $this->assertSame(42, $obj->arg2); + } + + public function testNewInstanceWithInjectedObjects() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One'); + $this->_container->register('two')->asNewInstanceOf('One') + ->withDependencies(array('one', 'foo')); + $obj = $this->_container->lookup('two'); + $this->assertEquals($this->_container->lookup('one'), $obj->arg1); + $this->assertSame('FOO', $obj->arg2); + } + + public function testNewInstanceWithAddConstructorValue() + { + $this->_container->register('one')->asNewInstanceOf('One') + ->addConstructorValue('x') + ->addConstructorValue(99); + $obj = $this->_container->lookup('one'); + $this->assertSame('x', $obj->arg1); + $this->assertSame(99, $obj->arg2); + } + + public function testNewInstanceWithAddConstructorLookup() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asValue(42); + $this->_container->register('one')->asNewInstanceOf('One') + ->addConstructorLookup('foo') + ->addConstructorLookup('bar'); + + $obj = $this->_container->lookup('one'); + $this->assertSame('FOO', $obj->arg1); + $this->assertSame(42, $obj->arg2); + } + + public function testResolvedDependenciesCanBeLookedUp() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One'); + $this->_container->register('two')->asNewInstanceOf('One') + ->withDependencies(array('one', 'foo')); + $deps = $this->_container->createDependenciesFor('two'); + $this->assertEquals( + array($this->_container->lookup('one'), 'FOO'), $deps + ); + } + + public function testArrayOfDependenciesCanBeSpecified() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('one')->asNewInstanceOf('One'); + $this->_container->register('two')->asNewInstanceOf('One') + ->withDependencies(array(array('one', 'foo'), 'foo')); + + $obj = $this->_container->lookup('two'); + $this->assertEquals(array($this->_container->lookup('one'), 'FOO'), $obj->arg1); + $this->assertSame('FOO', $obj->arg2); + } + + public function testAliasCanBeSet() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asAliasOf('foo'); + + $this->assertSame('FOO', $this->_container->lookup('bar')); + } + + public function testAliasOfAliasCanBeSet() + { + $this->_container->register('foo')->asValue('FOO'); + $this->_container->register('bar')->asAliasOf('foo'); + $this->_container->register('zip')->asAliasOf('bar'); + $this->_container->register('button')->asAliasOf('zip'); + + $this->assertSame('FOO', $this->_container->lookup('button')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php new file mode 100644 index 0000000..1e712fe --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Base64EncoderTest.php @@ -0,0 +1,173 @@ +_encoder = new Swift_Encoder_Base64Encoder(); + } + + /* + There's really no point in testing the entire base64 encoding to the + level QP encoding has been tested. base64_encode() has been in PHP for + years. + */ + + public function testInputOutputRatioIs3to4Bytes() + { + /* + RFC 2045, 6.8 + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + */ + + $this->assertEquals( + 'MTIz', $this->_encoder->encodeString('123'), + '%s: 3 bytes of input should yield 4 bytes of output' + ); + $this->assertEquals( + 'MTIzNDU2', $this->_encoder->encodeString('123456'), + '%s: 6 bytes in input should yield 8 bytes of output' + ); + $this->assertEquals( + 'MTIzNDU2Nzg5', $this->_encoder->encodeString('123456789'), + '%s: 9 bytes in input should yield 12 bytes of output' + ); + } + + public function testPadLength() + { + /* + RFC 2045, 6.8 + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a body. When fewer than 24 input bits + are available in an input group, zero bits are added (on the right) + to form an integral number of 6-bit groups. Padding at the end of + the data is performed using the "=" character. Since all base64 + input is an integral number of octets, only the following cases can + arise: (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded output will be + an integral multiple of 4 characters with no "=" padding, (2) the + final quantum of encoding input is exactly 8 bits; here, the final + unit of encoded output will be two characters followed by two "=" + padding characters, or (3) the final quantum of encoding input is + exactly 16 bits; here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + + for ($i = 0; $i < 30; ++$i) { + $input = pack('C', rand(0, 255)); + $this->assertRegExp( + '~^[a-zA-Z0-9/\+]{2}==$~', $this->_encoder->encodeString($input), + '%s: A single byte should have 2 bytes of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $input = pack('C*', rand(0, 255), rand(0, 255)); + $this->assertRegExp( + '~^[a-zA-Z0-9/\+]{3}=$~', $this->_encoder->encodeString($input), + '%s: Two bytes should have 1 byte of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $input = pack('C*', rand(0, 255), rand(0, 255), rand(0, 255)); + $this->assertRegExp( + '~^[a-zA-Z0-9/\+]{4}$~', $this->_encoder->encodeString($input), + '%s: Three bytes should have no padding' + ); + } + } + + public function testMaximumLineLengthIs76Characters() + { + /* + The encoded output stream must be represented in lines of no more + than 76 characters each. All line breaks or other characters not + found in Table 1 must be ignored by decoding software. + */ + + $input = + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $output = + 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38 + 'NERUZHSElKS0xNTk9QUVJTVFVWV1hZWjEyMzQ1'."\r\n".//76 * + 'Njc4OTBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3'.//38 + 'h5ekFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFla'."\r\n".//76 * + 'MTIzNDU2Nzg5MEFCQ0RFRkdISUpLTE1OT1BRUl'.//38 + 'NUVVZXWFla'; //48 + + $this->assertEquals( + $output, $this->_encoder->encodeString($input), + '%s: Lines should be no more than 76 characters' + ); + } + + public function testMaximumLineLengthCanBeSpecified() + { + $input = + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $output = + 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38 + 'NERUZHSElKS0'."\r\n".//50 * + 'xNTk9QUVJTVFVWV1hZWjEyMzQ1Njc4OTBhYmNk'.//38 + 'ZWZnaGlqa2xt'."\r\n".//50 * + 'bm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLTE1OT1'.//38 + 'BRUlNUVVZXWF'."\r\n".//50 * + 'laMTIzNDU2Nzg5MEFCQ0RFRkdISUpLTE1OT1BR'.//38 + 'UlNUVVZXWFla'; //50 * + + $this->assertEquals( + $output, $this->_encoder->encodeString($input, 0, 50), + '%s: Lines should be no more than 100 characters' + ); + } + + public function testFirstLineLengthCanBeDifferent() + { + $input = + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + '1234567890'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $output = + 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQk'.//38 + 'NERUZHSElKS0xNTk9QU'."\r\n".//57 * + 'VJTVFVWV1hZWjEyMzQ1Njc4OTBhYmNkZWZnaGl'.//38 + 'qa2xtbm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLT'."\r\n".//76 * + 'E1OT1BRUlNUVVZXWFlaMTIzNDU2Nzg5MEFCQ0R'.//38 + 'FRkdISUpLTE1OT1BRUlNUVVZXWFla'; //67 + + $this->assertEquals( + $output, $this->_encoder->encodeString($input, 19), + '%s: First line offset is 19 so first line should be 57 chars long' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php new file mode 100644 index 0000000..adf485d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/QpEncoderTest.php @@ -0,0 +1,381 @@ +_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertIdenticalBinary($char, $encoder->encodeString($char)); + } + } + + public function testWhiteSpaceAtLineEndingIsEncoded() + { + /* -- RFC 2045, 6.7 -- + (3) (White Space) Octets with values of 9 and 32 MAY be + represented as US-ASCII TAB (HT) and SPACE characters, + respectively, but MUST NOT be so represented at the end + of an encoded line. Any TAB (HT) or SPACE characters + on an encoded line MUST thus be followed on that line + by a printable character. In particular, an "=" at the + end of an encoded line, indicating a soft line break + (see rule #5) may follow one or more TAB (HT) or SPACE + characters. It follows that an octet with decimal + value 9 or 32 appearing at the end of an encoded line + must be represented according to Rule #1. This rule is + necessary because some MTAs (Message Transport Agents, + programs which transport messages from one user to + another, or perform a portion of such transfers) are + known to pad lines of text with SPACEs, and others are + known to remove "white space" characters from the end + of a line. Therefore, when decoding a Quoted-Printable + body, any trailing white space on a line must be + deleted, as it will necessarily have been added by + intermediate transport agents. + */ + + $HT = chr(0x09); //9 + $SPACE = chr(0x20); //32 + + //HT + $string = 'a'.$HT.$HT."\r\n".'b'; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals( + 'a'.$HT.'=09'."\r\n".'b', + $encoder->encodeString($string) + ); + + //SPACE + $string = 'a'.$SPACE.$SPACE."\r\n".'b'; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals( + 'a'.$SPACE.'=20'."\r\n".'b', + $encoder->encodeString($string) + ); + } + + public function testCRLFIsLeftAlone() + { + /* + (4) (Line Breaks) A line break in a text body, represented + as a CRLF sequence in the text canonical form, must be + represented by a (RFC 822) line break, which is also a + CRLF sequence, in the Quoted-Printable encoding. Since + the canonical representation of media types other than + text do not generally include the representation of + line breaks as CRLF sequences, no hard line breaks + (i.e. line breaks that are intended to be meaningful + and to be displayed to the user) can occur in the + quoted-printable encoding of such types. Sequences + like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely + appear in non-text data represented in quoted- + printable, of course. + + Note that many implementations may elect to encode the + local representation of various content types directly + rather than converting to canonical form first, + encoding, and then converting back to local + representation. In particular, this may apply to plain + text material on systems that use newline conventions + other than a CRLF terminator sequence. Such an + implementation optimization is permissible, but only + when the combined canonicalization-encoding step is + equivalent to performing the three steps separately. + */ + + $string = 'a'."\r\n".'b'."\r\n".'c'."\r\n"; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(ord('c'))); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes')->once()->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes')->once()->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals($string, $encoder->encodeString($string)); + } + + public function testLinesLongerThan76CharactersAreSoftBroken() + { + /* + (5) (Soft Line Breaks) The Quoted-Printable encoding + REQUIRES that encoded lines be no more than 76 + characters long. If longer lines are to be encoded + with the Quoted-Printable encoding, "soft" line breaks + must be used. An equal sign as the last character on a + encoded line indicates such a non-significant ("soft") + line break in the encoded text. + */ + + $input = str_repeat('a', 140); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($input); + + $output = ''; + for ($i = 0; $i < 140; ++$i) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (75 == $i) { + $output .= "=\r\n"; + } + $output .= 'a'; + } + + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals($output, $encoder->encodeString($input)); + } + + public function testMaxLineLengthCanBeSpecified() + { + $input = str_repeat('a', 100); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($input); + + $output = ''; + for ($i = 0; $i < 100; ++$i) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (53 == $i) { + $output .= "=\r\n"; + } + $output .= 'a'; + } + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals($output, $encoder->encodeString($input, 0, 54)); + } + + public function testBytesBelowPermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(0, 32) as $ordinal) { + $char = chr($ordinal); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertEquals( + sprintf('=%02X', $ordinal), $encoder->encodeString($char) + ); + } + } + + public function testDecimalByte61IsEncoded() + { + /* + According to Rule (1 & 2) + */ + + $char = '='; + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(61)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertEquals('=3D', $encoder->encodeString('=')); + } + + public function testBytesAbovePermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(127, 255) as $ordinal) { + $char = chr($ordinal); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($char); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + + $this->assertEquals( + sprintf('=%02X', $ordinal), $encoder->encodeString($char) + ); + } + } + + public function testFirstLineLengthCanBeDifferent() + { + $input = str_repeat('a', 140); + + $charStream = $this->_createCharStream(); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($input); + + $output = ''; + for ($i = 0; $i < 140; ++$i) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (53 == $i || 53 + 75 == $i) { + $output .= "=\r\n"; + } + $output .= 'a'; + } + + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(false); + + $encoder = new Swift_Encoder_QpEncoder($charStream); + $this->assertEquals( + $output, $encoder->encodeString($input, 22), + '%s: First line should start at offset 22 so can only have max length 54' + ); + } + + // -- Creation methods + + private function _createCharStream() + { + return $this->getMockery('Swift_CharacterStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php new file mode 100644 index 0000000..28eae6f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Encoder/Rfc2231EncoderTest.php @@ -0,0 +1,141 @@ +getMockery('Swift_CharacterStream'); + + $string = ''; + foreach (range(0x00, 0x7F) as $octet) { + $char = pack('C', $octet); + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + $encoded = $encoder->encodeString($string); + + foreach (explode("\r\n", $encoded) as $line) { + $this->assertRegExp($this->_rfc2045Token, $line, + '%s: Encoder should always return a valid RFC 2045 token.'); + } + } + + public function testEncodingNonAsciiCharactersProducesValidToken() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + foreach (range(0x80, 0xFF) as $octet) { + $char = pack('C', $octet); + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + + $encoded = $encoder->encodeString($string); + + foreach (explode("\r\n", $encoded) as $line) { + $this->assertRegExp($this->_rfc2045Token, $line, + '%s: Encoder should always return a valid RFC 2045 token.'); + } + } + + public function testMaximumLineLengthCanBeSet() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + for ($x = 0; $x < 200; ++$x) { + $char = 'a'; + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + + $encoded = $encoder->encodeString($string, 0, 75); + + $this->assertEquals( + str_repeat('a', 75)."\r\n". + str_repeat('a', 75)."\r\n". + str_repeat('a', 50), + $encoded, + '%s: Lines should be wrapped at each 75 characters' + ); + } + + public function testFirstLineCanHaveShorterLength() + { + $charStream = $this->getMockery('Swift_CharacterStream'); + + $string = ''; + for ($x = 0; $x < 200; ++$x) { + $char = 'a'; + $string .= $char; + $charStream->shouldReceive('read') + ->once() + ->andReturn($char); + } + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importString') + ->once() + ->with($string); + $charStream->shouldReceive('read') + ->atLeast()->times(1) + ->andReturn(false); + $encoder = new Swift_Encoder_Rfc2231Encoder($charStream); + $encoded = $encoder->encodeString($string, 25, 75); + + $this->assertEquals( + str_repeat('a', 50)."\r\n". + str_repeat('a', 75)."\r\n". + str_repeat('a', 75), + $encoded, + '%s: First line should be 25 bytes shorter than the others.' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php new file mode 100644 index 0000000..7e59802 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/CommandEventTest.php @@ -0,0 +1,36 @@ +_createEvent($this->_createTransport(), "FOO\r\n"); + $this->assertEquals("FOO\r\n", $evt->getCommand()); + } + + public function testSuccessCodesCanBeFetchedViaGetter() + { + $evt = $this->_createEvent($this->_createTransport(), "FOO\r\n", array(250)); + $this->assertEquals(array(250), $evt->getSuccessCodes()); + } + + public function testSourceIsBuffer() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, "FOO\r\n"); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source, $command, $successCodes = array()) + { + return new Swift_Events_CommandEvent($source, $command, $successCodes); + } + + private function _createTransport() + { + return $this->getMock('Swift_Transport'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php new file mode 100644 index 0000000..62a91be --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/EventObjectTest.php @@ -0,0 +1,34 @@ +_createEvent($source); + $ref = $evt->getSource(); + $this->assertEquals($source, $ref); + } + + public function testEventDoesNotHaveCancelledBubbleWhenNew() + { + $source = new stdClass(); + $evt = $this->_createEvent($source); + $this->assertFalse($evt->bubbleCancelled()); + } + + public function testBubbleCanBeCancelledInEvent() + { + $source = new stdClass(); + $evt = $this->_createEvent($source); + $evt->cancelBubble(); + $this->assertTrue($evt->bubbleCancelled()); + } + + // -- Creation Methods + + private function _createEvent($source) + { + return new Swift_Events_EventObject($source); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php new file mode 100644 index 0000000..e0f3e36 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/ResponseEventTest.php @@ -0,0 +1,40 @@ +_createEvent($this->_createTransport(), "250 Ok\r\n", true); + $this->assertEquals("250 Ok\r\n", $evt->getResponse(), + '%s: Response should be available via getResponse()' + ); + } + + public function testResultCanBeFetchedViaGetter() + { + $evt = $this->_createEvent($this->_createTransport(), "250 Ok\r\n", false); + $this->assertFalse($evt->isValid(), + '%s: Result should be checkable via isValid()' + ); + } + + public function testSourceIsBuffer() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, "250 Ok\r\n", true); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source, $response, $result) + { + return new Swift_Events_ResponseEvent($source, $response, $result); + } + + private function _createTransport() + { + return $this->getMock('Swift_Transport'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php new file mode 100644 index 0000000..8003870 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SendEventTest.php @@ -0,0 +1,99 @@ +_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $ref = $evt->getMessage(); + $this->assertEquals($message, $ref, + '%s: Message should be returned from getMessage()' + ); + } + + public function testTransportCanBeFetchViaGetter() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $ref = $evt->getTransport(); + $this->assertEquals($transport, $ref, + '%s: Transport should be returned from getTransport()' + ); + } + + public function testTransportCanBeFetchViaGetSource() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref, + '%s: Transport should be returned from getSource()' + ); + } + + public function testResultCanBeSetAndGet() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $evt->setResult( + Swift_Events_SendEvent::RESULT_SUCCESS | Swift_Events_SendEvent::RESULT_TENTATIVE + ); + + $this->assertTrue((bool) ($evt->getResult() & Swift_Events_SendEvent::RESULT_SUCCESS)); + $this->assertTrue((bool) ($evt->getResult() & Swift_Events_SendEvent::RESULT_TENTATIVE)); + } + + public function testFailedRecipientsCanBeSetAndGet() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + + $evt->setFailedRecipients(array('foo@bar', 'zip@button')); + + $this->assertEquals(array('foo@bar', 'zip@button'), $evt->getFailedRecipients(), + '%s: FailedRecipients should be returned from getter' + ); + } + + public function testFailedRecipientsGetsPickedUpCorrectly() + { + $message = $this->_createMessage(); + $transport = $this->_createTransport(); + + $evt = $this->_createEvent($transport, $message); + $this->assertEquals(array(), $evt->getFailedRecipients()); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source, + Swift_Mime_Message $message) + { + return new Swift_Events_SendEvent($source, $message); + } + + private function _createTransport() + { + return $this->getMock('Swift_Transport'); + } + + private function _createMessage() + { + return $this->getMock('Swift_Mime_Message'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php new file mode 100644 index 0000000..52ae07c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/SimpleEventDispatcherTest.php @@ -0,0 +1,135 @@ +_dispatcher = new Swift_Events_SimpleEventDispatcher(); + } + + public function testSendEventCanBeCreated() + { + $transport = $this->getMock('Swift_Transport'); + $message = $this->getMock('Swift_Mime_Message'); + $evt = $this->_dispatcher->createSendEvent($transport, $message); + $this->assertInstanceof('Swift_Events_SendEvent', $evt); + $this->assertSame($message, $evt->getMessage()); + $this->assertSame($transport, $evt->getTransport()); + } + + public function testCommandEventCanBeCreated() + { + $buf = $this->getMock('Swift_Transport'); + $evt = $this->_dispatcher->createCommandEvent($buf, "FOO\r\n", array(250)); + $this->assertInstanceof('Swift_Events_CommandEvent', $evt); + $this->assertSame($buf, $evt->getSource()); + $this->assertEquals("FOO\r\n", $evt->getCommand()); + $this->assertEquals(array(250), $evt->getSuccessCodes()); + } + + public function testResponseEventCanBeCreated() + { + $buf = $this->getMock('Swift_Transport'); + $evt = $this->_dispatcher->createResponseEvent($buf, "250 Ok\r\n", true); + $this->assertInstanceof('Swift_Events_ResponseEvent', $evt); + $this->assertSame($buf, $evt->getSource()); + $this->assertEquals("250 Ok\r\n", $evt->getResponse()); + $this->assertTrue($evt->isValid()); + } + + public function testTransportChangeEventCanBeCreated() + { + $transport = $this->getMock('Swift_Transport'); + $evt = $this->_dispatcher->createTransportChangeEvent($transport); + $this->assertInstanceof('Swift_Events_TransportChangeEvent', $evt); + $this->assertSame($transport, $evt->getSource()); + } + + public function testTransportExceptionEventCanBeCreated() + { + $transport = $this->getMock('Swift_Transport'); + $ex = new Swift_TransportException(''); + $evt = $this->_dispatcher->createTransportExceptionEvent($transport, $ex); + $this->assertInstanceof('Swift_Events_TransportExceptionEvent', $evt); + $this->assertSame($transport, $evt->getSource()); + $this->assertSame($ex, $evt->getException()); + } + + public function testListenersAreNotifiedOfDispatchedEvent() + { + $transport = $this->getMock('Swift_Transport'); + + $evt = $this->_dispatcher->createTransportChangeEvent($transport); + + $listenerA = $this->getMock('Swift_Events_TransportChangeListener'); + $listenerB = $this->getMock('Swift_Events_TransportChangeListener'); + + $this->_dispatcher->bindEventListener($listenerA); + $this->_dispatcher->bindEventListener($listenerB); + + $listenerA->expects($this->once()) + ->method('transportStarted') + ->with($evt); + $listenerB->expects($this->once()) + ->method('transportStarted') + ->with($evt); + + $this->_dispatcher->dispatchEvent($evt, 'transportStarted'); + } + + public function testListenersAreOnlyCalledIfImplementingCorrectInterface() + { + $transport = $this->getMock('Swift_Transport'); + $message = $this->getMock('Swift_Mime_Message'); + + $evt = $this->_dispatcher->createSendEvent($transport, $message); + + $targetListener = $this->getMock('Swift_Events_SendListener'); + $otherListener = $this->getMock('Swift_Events_TransportChangeListener'); + + $this->_dispatcher->bindEventListener($targetListener); + $this->_dispatcher->bindEventListener($otherListener); + + $targetListener->expects($this->once()) + ->method('sendPerformed') + ->with($evt); + $otherListener->expects($this->never()) + ->method('sendPerformed'); + + $this->_dispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + public function testListenersCanCancelBubblingOfEvent() + { + $transport = $this->getMock('Swift_Transport'); + $message = $this->getMock('Swift_Mime_Message'); + + $evt = $this->_dispatcher->createSendEvent($transport, $message); + + $listenerA = $this->getMock('Swift_Events_SendListener'); + $listenerB = $this->getMock('Swift_Events_SendListener'); + + $this->_dispatcher->bindEventListener($listenerA); + $this->_dispatcher->bindEventListener($listenerB); + + $listenerA->expects($this->once()) + ->method('sendPerformed') + ->with($evt) + ->will($this->returnCallback(function ($object) { + $object->cancelBubble(true); + })); + $listenerB->expects($this->never()) + ->method('sendPerformed'); + + $this->_dispatcher->dispatchEvent($evt, 'sendPerformed'); + + $this->assertTrue($evt->bubbleCancelled()); + } + + private function _createDispatcher(array $map) + { + return new Swift_Events_SimpleEventDispatcher($map); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php new file mode 100644 index 0000000..40bec83 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportChangeEventTest.php @@ -0,0 +1,32 @@ +_createTransport(); + $evt = $this->_createEvent($transport); + $ref = $evt->getTransport(); + $this->assertEquals($transport, $ref); + } + + public function testSourceIsTransport() + { + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $source) + { + return new Swift_Events_TransportChangeEvent($source); + } + + private function _createTransport() + { + return $this->getMock('Swift_Transport'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php new file mode 100644 index 0000000..86c636b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Events/TransportExceptionEventTest.php @@ -0,0 +1,43 @@ +_createException(); + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, $ex); + $ref = $evt->getException(); + $this->assertEquals($ex, $ref, + '%s: Exception should be available via getException()' + ); + } + + public function testSourceIsTransport() + { + $ex = $this->_createException(); + $transport = $this->_createTransport(); + $evt = $this->_createEvent($transport, $ex); + $ref = $evt->getSource(); + $this->assertEquals($transport, $ref, + '%s: Transport should be available via getSource()' + ); + } + + // -- Creation Methods + + private function _createEvent(Swift_Transport $transport, Swift_TransportException $ex) + { + return new Swift_Events_TransportExceptionEvent($transport, $ex); + } + + private function _createTransport() + { + return $this->getMock('Swift_Transport'); + } + + private function _createException() + { + return new Swift_TransportException(''); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php new file mode 100644 index 0000000..36512cf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/ArrayKeyCacheTest.php @@ -0,0 +1,242 @@ +_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('test', $cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeOverwritten() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'foo', 'whatever', Swift_KeyCache::MODE_WRITE + ); + + $this->assertEquals('whatever', $cache->getString($this->_key1, 'foo')); + } + + public function testStringDataCanBeAppended() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'foo', 'ing', Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('testing', $cache->getString($this->_key1, 'foo')); + } + + public function testHasKeyReturnValue() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $this->assertTrue($cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyIsWellPartitioned() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key2, 'foo', 'ing', Swift_KeyCache::MODE_WRITE + ); + + $this->assertEquals('test', $cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $cache->getString($this->_key2, 'foo')); + } + + public function testItemKeyIsWellPartitioned() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'bar', 'ing', Swift_KeyCache::MODE_WRITE + ); + + $this->assertEquals('test', $cache->getString($this->_key1, 'foo')); + $this->assertEquals('ing', $cache->getString($this->_key1, 'bar')); + } + + public function testByteStreamCanBeImported() + { + $os = $this->_createOutputStream(); + $os->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('abc')); + $os->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('def')); + $os->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + $cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_WRITE + ); + $this->assertEquals('abcdef', $cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamCanBeAppended() + { + $os1 = $this->_createOutputStream(); + $os1->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('abc')); + $os1->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('def')); + $os1->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $os2 = $this->_createOutputStream(); + $os2->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('xyz')); + $os2->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('uvw')); + $os2->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $is = $this->_createKeyCacheInputStream(true); + + $cache = $this->_createCache($is); + + $cache->importFromByteStream( + $this->_key1, 'foo', $os1, Swift_KeyCache::MODE_APPEND + ); + $cache->importFromByteStream( + $this->_key1, 'foo', $os2, Swift_KeyCache::MODE_APPEND + ); + + $this->assertEquals('abcdefxyzuvw', $cache->getString($this->_key1, 'foo')); + } + + public function testByteStreamAndStringCanBeAppended() + { + $os = $this->_createOutputStream(); + $os->expects($this->at(0)) + ->method('read') + ->will($this->returnValue('abc')); + $os->expects($this->at(1)) + ->method('read') + ->will($this->returnValue('def')); + $os->expects($this->at(2)) + ->method('read') + ->will($this->returnValue(false)); + + $is = $this->_createKeyCacheInputStream(true); + + $cache = $this->_createCache($is); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_APPEND + ); + $cache->importFromByteStream( + $this->_key1, 'foo', $os, Swift_KeyCache::MODE_APPEND + ); + $this->assertEquals('testabcdef', $cache->getString($this->_key1, 'foo')); + } + + public function testDataCanBeExportedToByteStream() + { + //See acceptance test for more detail + $is = $this->_createInputStream(); + $is->expects($this->atLeastOnce()) + ->method('write'); + + $kcis = $this->_createKeyCacheInputStream(true); + + $cache = $this->_createCache($kcis); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + + $cache->exportToByteStream($this->_key1, 'foo', $is); + } + + public function testKeyCanBeCleared() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($cache->hasKey($this->_key1, 'foo')); + $cache->clearKey($this->_key1, 'foo'); + $this->assertFalse($cache->hasKey($this->_key1, 'foo')); + } + + public function testNsKeyCanBeCleared() + { + $is = $this->_createKeyCacheInputStream(); + $cache = $this->_createCache($is); + + $cache->setString( + $this->_key1, 'foo', 'test', Swift_KeyCache::MODE_WRITE + ); + $cache->setString( + $this->_key1, 'bar', 'xyz', Swift_KeyCache::MODE_WRITE + ); + $this->assertTrue($cache->hasKey($this->_key1, 'foo')); + $this->assertTrue($cache->hasKey($this->_key1, 'bar')); + $cache->clearAll($this->_key1); + $this->assertFalse($cache->hasKey($this->_key1, 'foo')); + $this->assertFalse($cache->hasKey($this->_key1, 'bar')); + } + + // -- Creation methods + + private function _createCache($is) + { + return new Swift_KeyCache_ArrayKeyCache($is); + } + + private function _createKeyCacheInputStream() + { + return $this->getMock('Swift_KeyCache_KeyCacheInputStream'); + } + + private function _createOutputStream() + { + return $this->getMock('Swift_OutputByteStream'); + } + + private function _createInputStream() + { + return $this->getMock('Swift_InputByteStream'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php new file mode 100644 index 0000000..691c1e7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/KeyCache/SimpleKeyCacheInputStreamTest.php @@ -0,0 +1,73 @@ +getMock('Swift_KeyCache'); + $cache->expects($this->at(0)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'a', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(1)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'b', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(2)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'c', Swift_KeyCache::MODE_APPEND); + + $stream = new Swift_KeyCache_SimpleKeyCacheInputStream(); + $stream->setKeyCache($cache); + $stream->setNsKey($this->_nsKey); + $stream->setItemKey('foo'); + + $stream->write('a'); + $stream->write('b'); + $stream->write('c'); + } + + public function testFlushContentClearsKey() + { + $cache = $this->getMock('Swift_KeyCache'); + $cache->expects($this->once()) + ->method('clearKey') + ->with($this->_nsKey, 'foo'); + + $stream = new Swift_KeyCache_SimpleKeyCacheInputStream(); + $stream->setKeyCache($cache); + $stream->setNsKey($this->_nsKey); + $stream->setItemKey('foo'); + + $stream->flushBuffers(); + } + + public function testClonedStreamStillReferencesSameCache() + { + $cache = $this->getMock('Swift_KeyCache'); + $cache->expects($this->at(0)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'a', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(1)) + ->method('setString') + ->with($this->_nsKey, 'foo', 'b', Swift_KeyCache::MODE_APPEND); + $cache->expects($this->at(2)) + ->method('setString') + ->with('test', 'bar', 'x', Swift_KeyCache::MODE_APPEND); + + $stream = new Swift_KeyCache_SimpleKeyCacheInputStream(); + $stream->setKeyCache($cache); + $stream->setNsKey($this->_nsKey); + $stream->setItemKey('foo'); + + $stream->write('a'); + $stream->write('b'); + + $newStream = clone $stream; + $newStream->setKeyCache($cache); + $newStream->setNsKey('test'); + $newStream->setItemKey('bar'); + + $newStream->write('x'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php new file mode 100644 index 0000000..ff0bce4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mailer/ArrayRecipientIteratorTest.php @@ -0,0 +1,42 @@ +assertFalse($it->hasNext()); + } + + public function testHasNextReturnsTrueIfItemsLeft() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array('foo@bar' => 'Foo')); + $this->assertTrue($it->hasNext()); + } + + public function testReadingToEndOfListCausesHasNextToReturnFalse() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array('foo@bar' => 'Foo')); + $this->assertTrue($it->hasNext()); + $it->nextRecipient(); + $this->assertFalse($it->hasNext()); + } + + public function testReturnedValueHasPreservedKeyValuePair() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array('foo@bar' => 'Foo')); + $this->assertEquals(array('foo@bar' => 'Foo'), $it->nextRecipient()); + } + + public function testIteratorMovesNextAfterEachIteration() + { + $it = new Swift_Mailer_ArrayRecipientIterator(array( + 'foo@bar' => 'Foo', + 'zip@button' => 'Zip thing', + 'test@test' => null, + )); + $this->assertEquals(array('foo@bar' => 'Foo'), $it->nextRecipient()); + $this->assertEquals(array('zip@button' => 'Zip thing'), $it->nextRecipient()); + $this->assertEquals(array('test@test' => null), $it->nextRecipient()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php new file mode 100644 index 0000000..db0b35a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MailerTest.php @@ -0,0 +1,152 @@ +_createTransport(); + $message = $this->_createMessage(); + + $started = false; + $transport->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$started) { + return $started; + }); + $transport->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$started) { + $started = true; + + return; + }); + + $mailer = $this->_createMailer($transport); + $mailer->send($message); + } + + public function testTransportIsOnlyStartedOnce() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + + $started = false; + $transport->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$started) { + return $started; + }); + $transport->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$started) { + $started = true; + + return; + }); + + $mailer = $this->_createMailer($transport); + for ($i = 0; $i < 10; ++$i) { + $mailer->send($message); + } + } + + public function testMessageIsPassedToTransport() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $transport->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()); + + $mailer = $this->_createMailer($transport); + $mailer->send($message); + } + + public function testSendReturnsCountFromTransport() + { + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $transport->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturn(57); + + $mailer = $this->_createMailer($transport); + $this->assertEquals(57, $mailer->send($message)); + } + + public function testFailedRecipientReferenceIsPassedToTransport() + { + $failures = array(); + + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $transport->shouldReceive('send') + ->once() + ->with($message, $failures) + ->andReturn(57); + + $mailer = $this->_createMailer($transport); + $mailer->send($message, $failures); + } + + public function testSendRecordsRfcComplianceExceptionAsEntireSendFailure() + { + $failures = array(); + + $rfcException = new Swift_RfcComplianceException('test'); + $transport = $this->_createTransport(); + $message = $this->_createMessage(); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo&invalid' => 'Foo', 'bar@valid.tld' => 'Bar')); + $transport->shouldReceive('send') + ->once() + ->with($message, $failures) + ->andThrow($rfcException); + + $mailer = $this->_createMailer($transport); + $this->assertEquals(0, $mailer->send($message, $failures), '%s: Should return 0'); + $this->assertEquals(array('foo&invalid', 'bar@valid.tld'), $failures, '%s: Failures should contain all addresses since the entire message failed to compile'); + } + + public function testRegisterPluginDelegatesToTransport() + { + $plugin = $this->_createPlugin(); + $transport = $this->_createTransport(); + $mailer = $this->_createMailer($transport); + + $transport->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + + $mailer->registerPlugin($plugin); + } + + // -- Creation methods + + private function _createPlugin() + { + return $this->getMockery('Swift_Events_EventListener')->shouldIgnoreMissing(); + } + + private function _createTransport() + { + return $this->getMockery('Swift_Transport')->shouldIgnoreMissing(); + } + + private function _createMessage() + { + return $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + } + + private function _createIterator() + { + return $this->getMockery('Swift_Mailer_RecipientIterator')->shouldIgnoreMissing(); + } + + private function _createMailer(Swift_Transport $transport) + { + return new Swift_Mailer($transport); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php new file mode 100644 index 0000000..c9e7383 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/MessageTest.php @@ -0,0 +1,130 @@ +_recursiveObjectCloningCheck($message1, $message2, $message1_clone); + } + + public function testCloningWithSigners() + { + $message1 = new Swift_Message('subj', 'body', 'ctype'); + $signer = new Swift_Signers_DKIMSigner(dirname(dirname(__DIR__)).'/_samples/dkim/dkim.test.priv', 'test.example', 'example'); + $message1->attachSigner($signer); + $message2 = new Swift_Message('subj', 'body', 'ctype'); + $signer = new Swift_Signers_DKIMSigner(dirname(dirname(__DIR__)).'/_samples/dkim/dkim.test.priv', 'test.example', 'example'); + $message2->attachSigner($signer); + $message1_clone = clone $message1; + + $this->_recursiveObjectCloningCheck($message1, $message2, $message1_clone); + } + + public function testBodySwap() + { + $message1 = new Swift_Message('Test'); + $html = Swift_MimePart::newInstance('', 'text/html'); + $html->getHeaders()->addTextHeader('X-Test-Remove', 'Test-Value'); + $html->getHeaders()->addTextHeader('X-Test-Alter', 'Test-Value'); + $message1->attach($html); + $source = $message1->toString(); + $message2 = clone $message1; + $message2->setSubject('Message2'); + foreach ($message2->getChildren() as $child) { + $child->setBody('Test'); + $child->getHeaders()->removeAll('X-Test-Remove'); + $child->getHeaders()->get('X-Test-Alter')->setValue('Altered'); + } + $final = $message1->toString(); + if ($source != $final) { + $this->fail("Difference although object cloned \n [".$source."]\n[".$final."]\n"); + } + $final = $message2->toString(); + if ($final == $source) { + $this->fail('Two body matches although they should differ'."\n [".$source."]\n[".$final."]\n"); + } + $id_1 = $message1->getId(); + $id_2 = $message2->getId(); + $this->assertEquals($id_1, $id_2, 'Message Ids differ'); + $id_2 = $message2->generateId(); + $this->assertNotEquals($id_1, $id_2, 'Message Ids are the same'); + } + + // -- Private helpers + protected function _recursiveObjectCloningCheck($obj1, $obj2, $obj1_clone) + { + $obj1_properties = (array) $obj1; + $obj2_properties = (array) $obj2; + $obj1_clone_properties = (array) $obj1_clone; + + foreach ($obj1_properties as $property => $value) { + if (is_object($value)) { + $obj1_value = $obj1_properties[$property]; + $obj2_value = $obj2_properties[$property]; + $obj1_clone_value = $obj1_clone_properties[$property]; + + if ($obj1_value !== $obj2_value) { + // two separetely instanciated objects property not referencing same object + $this->assertFalse( + // but object's clone does - not everything copied + $obj1_value === $obj1_clone_value, + "Property `$property` cloning error: source and cloned objects property is referencing same object" + ); + } else { + // two separetely instanciated objects have same reference + $this->assertFalse( + // but object's clone doesn't - overdone making copies + $obj1_value !== $obj1_clone_value, + "Property `$property` not properly cloned: it should reference same object as cloning source (overdone copping)" + ); + } + // recurse + $this->_recursiveObjectCloningCheck($obj1_value, $obj2_value, $obj1_clone_value); + } elseif (is_array($value)) { + $obj1_value = $obj1_properties[$property]; + $obj2_value = $obj2_properties[$property]; + $obj1_clone_value = $obj1_clone_properties[$property]; + + return $this->_recursiveArrayCloningCheck($obj1_value, $obj2_value, $obj1_clone_value); + } + } + } + + protected function _recursiveArrayCloningCheck($array1, $array2, $array1_clone) + { + foreach ($array1 as $key => $value) { + if (is_object($value)) { + $arr1_value = $array1[$key]; + $arr2_value = $array2[$key]; + $arr1_clone_value = $array1_clone[$key]; + if ($arr1_value !== $arr2_value) { + // two separetely instanciated objects property not referencing same object + $this->assertFalse( + // but object's clone does - not everything copied + $arr1_value === $arr1_clone_value, + "Key `$key` cloning error: source and cloned objects property is referencing same object" + ); + } else { + // two separetely instanciated objects have same reference + $this->assertFalse( + // but object's clone doesn't - overdone making copies + $arr1_value !== $arr1_clone_value, + "Key `$key` not properly cloned: it should reference same object as cloning source (overdone copping)" + ); + } + // recurse + $this->_recursiveObjectCloningCheck($arr1_value, $arr2_value, $arr1_clone_value); + } elseif (is_array($value)) { + $arr1_value = $array1[$key]; + $arr2_value = $array2[$key]; + $arr1_clone_value = $array1_clone[$key]; + + return $this->_recursiveArrayCloningCheck($obj1_value, $obj2_value, $obj1_clone_value); + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php new file mode 100644 index 0000000..dc8ea09 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AbstractMimeEntityTest.php @@ -0,0 +1,1054 @@ +_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertSame($headers, $entity->getHeaders()); + } + + public function testContentTypeIsReturnedFromHeader() + { + $ctype = $this->_createHeader('Content-Type', 'image/jpeg-test'); + $headers = $this->_createHeaderSet(array('Content-Type' => $ctype)); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals('image/jpeg-test', $entity->getContentType()); + } + + public function testContentTypeIsSetInHeader() + { + $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $headers = $this->_createHeaderSet(array('Content-Type' => $ctype)); + + $ctype->shouldReceive('setFieldBodyModel') + ->once() + ->with('image/jpeg'); + $ctype->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes() + ->with(\Mockery::not('image/jpeg')); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setContentType('image/jpeg'); + } + + public function testContentTypeHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Type', 'image/jpeg'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setContentType('image/jpeg'); + } + + public function testContentTypeCanBeSetViaSetBody() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Type', 'text/html'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBody('foo', 'text/html'); + } + + public function testGetEncoderFromConstructor() + { + $encoder = $this->_createEncoder('base64'); + $entity = $this->_createEntity($this->_createHeaderSet(), $encoder, + $this->_createCache() + ); + $this->assertSame($encoder, $entity->getEncoder()); + } + + public function testSetAndGetEncoder() + { + $encoder = $this->_createEncoder('base64'); + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setEncoder($encoder); + $this->assertSame($encoder, $entity->getEncoder()); + } + + public function testSettingEncoderUpdatesTransferEncoding() + { + $encoder = $this->_createEncoder('base64'); + $encoding = $this->_createHeader( + 'Content-Transfer-Encoding', '8bit', array(), false + ); + $headers = $this->_createHeaderSet(array( + 'Content-Transfer-Encoding' => $encoding, + )); + $encoding->shouldReceive('setFieldBodyModel') + ->once() + ->with('base64'); + $encoding->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setEncoder($encoder); + } + + public function testSettingEncoderAddsEncodingHeaderIfNonePresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('Content-Transfer-Encoding', 'something'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setEncoder($this->_createEncoder('something')); + } + + public function testIdIsReturnedFromHeader() + { + /* -- RFC 2045, 7. + In constructing a high-level user agent, it may be desirable to allow + one body to make reference to another. Accordingly, bodies may be + labelled using the "Content-ID" header field, which is syntactically + identical to the "Message-ID" header field + */ + + $cid = $this->_createHeader('Content-ID', 'zip@button'); + $headers = $this->_createHeaderSet(array('Content-ID' => $cid)); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals('zip@button', $entity->getId()); + } + + public function testIdIsSetInHeader() + { + $cid = $this->_createHeader('Content-ID', 'zip@button', array(), false); + $headers = $this->_createHeaderSet(array('Content-ID' => $cid)); + + $cid->shouldReceive('setFieldBodyModel') + ->once() + ->with('foo@bar'); + $cid->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setId('foo@bar'); + } + + public function testIdIsAutoGenerated() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertRegExp('/^.*?@.*?$/D', $entity->getId()); + } + + public function testGenerateIdCreatesNewId() + { + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $id1 = $entity->generateId(); + $id2 = $entity->generateId(); + $this->assertNotEquals($id1, $id2); + } + + public function testGenerateIdSetsNewId() + { + $headers = $this->_createHeaderSet(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $id = $entity->generateId(); + $this->assertEquals($id, $entity->getId()); + } + + public function testDescriptionIsReadFromHeader() + { + /* -- RFC 2045, 8. + The ability to associate some descriptive information with a given + body is often desirable. For example, it may be useful to mark an + "image" body as "a picture of the Space Shuttle Endeavor." Such text + may be placed in the Content-Description header field. This header + field is always optional. + */ + + $desc = $this->_createHeader('Content-Description', 'something'); + $headers = $this->_createHeaderSet(array('Content-Description' => $desc)); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals('something', $entity->getDescription()); + } + + public function testDescriptionIsSetInHeader() + { + $desc = $this->_createHeader('Content-Description', '', array(), false); + $desc->shouldReceive('setFieldBodyModel')->once()->with('whatever'); + + $headers = $this->_createHeaderSet(array('Content-Description' => $desc)); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setDescription('whatever'); + } + + public function testDescriptionHeaderIsAddedIfNotPresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('Content-Description', 'whatever'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setDescription('whatever'); + } + + public function testSetAndGetMaxLineLength() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setMaxLineLength(60); + $this->assertEquals(60, $entity->getMaxLineLength()); + } + + public function testEncoderIsUsedForStringGeneration() + { + $encoder = $this->_createEncoder('base64', false); + $encoder->expects($this->once()) + ->method('encodeString') + ->with('blah'); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $encoder, $this->_createCache() + ); + $entity->setBody('blah'); + $entity->toString(); + } + + public function testMaxLineLengthIsProvidedWhenEncoding() + { + $encoder = $this->_createEncoder('base64', false); + $encoder->expects($this->once()) + ->method('encodeString') + ->with('blah', 0, 65); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $encoder, $this->_createCache() + ); + $entity->setBody('blah'); + $entity->setMaxLineLength(65); + $entity->toString(); + } + + public function testHeadersAppearInString() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->once() + ->andReturn( + "Content-Type: text/plain; charset=utf-8\r\n". + "X-MyHeader: foobar\r\n" + ); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "X-MyHeader: foobar\r\n", + $entity->toString() + ); + } + + public function testSetAndGetBody() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setBody("blah\r\nblah!"); + $this->assertEquals("blah\r\nblah!", $entity->getBody()); + } + + public function testBodyIsAppended() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->once() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBody("blah\r\nblah!"); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "\r\n". + "blah\r\nblah!", + $entity->toString() + ); + } + + public function testGetBodyReturnsStringFromByteStream() + { + $os = $this->_createOutputStream('byte stream string'); + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setBody($os); + $this->assertEquals('byte stream string', $entity->getBody()); + } + + public function testByteStreamBodyIsAppended() + { + $headers = $this->_createHeaderSet(array(), false); + $os = $this->_createOutputStream('streamed'); + $headers->shouldReceive('toString') + ->once() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBody($os); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "\r\n". + 'streamed', + $entity->toString() + ); + } + + public function testBoundaryCanBeRetrieved() + { + /* -- RFC 2046, 5.1.1. + boundary := 0*69 bcharsnospace + + bchars := bcharsnospace / " " + + bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / + "+" / "_" / "," / "-" / "." / + "/" / ":" / "=" / "?" + */ + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertRegExp( + '/^[a-zA-Z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-zA-Z0-9\'\(\)\+_\-,\.\/:=\?]$/D', + $entity->getBoundary() + ); + } + + public function testBoundaryNeverChanges() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $firstBoundary = $entity->getBoundary(); + for ($i = 0; $i < 10; $i++) { + $this->assertEquals($firstBoundary, $entity->getBoundary()); + } + } + + public function testBoundaryCanBeSet() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setBoundary('foobar'); + $this->assertEquals('foobar', $entity->getBoundary()); + } + + public function testAddingChildrenGeneratesBoundaryInHeaders() + { + $child = $this->_createChild(); + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $cType->shouldReceive('setParameter') + ->once() + ->with('boundary', \Mockery::any()); + $cType->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, + )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + + public function testChildrenOfLevelAttachmentAndLessCauseMultipartMixed() + { + for ($level = Swift_Mime_MimeEntity::LEVEL_MIXED; + $level > Swift_Mime_MimeEntity::LEVEL_TOP; $level /= 2) { + $child = $this->_createChild($level); + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/mixed'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + } + + public function testChildrenOfLevelAlternativeAndLessCauseMultipartAlternative() + { + for ($level = Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE; + $level > Swift_Mime_MimeEntity::LEVEL_MIXED; $level /= 2) { + $child = $this->_createChild($level); + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/alternative'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + } + + public function testChildrenOfLevelRelatedAndLessCauseMultipartRelated() + { + for ($level = Swift_Mime_MimeEntity::LEVEL_RELATED; + $level > Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE; $level /= 2) { + $child = $this->_createChild($level); + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/related'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + } + } + + public function testHighestLevelChildDeterminesContentType() + { + $combinations = array( + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED, + Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + Swift_Mime_MimeEntity::LEVEL_RELATED, + ), + 'type' => 'multipart/mixed', + ), + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED, + Swift_Mime_MimeEntity::LEVEL_RELATED, + ), + 'type' => 'multipart/mixed', + ), + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED, + Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + ), + 'type' => 'multipart/mixed', + ), + array('levels' => array(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + Swift_Mime_MimeEntity::LEVEL_RELATED, + ), + 'type' => 'multipart/alternative', + ), + ); + + foreach ($combinations as $combination) { + $children = array(); + foreach ($combination['levels'] as $level) { + $children[] = $this->_createChild($level); + } + + $cType = $this->_createHeader( + 'Content-Type', 'text/plain', array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with($combination['type']); + + $headerSet = $this->_createHeaderSet(array('Content-Type' => $cType)); + $headerSet->shouldReceive('newInstance') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($headerSet) { + return $headerSet; + }); + $entity = $this->_createEntity($headerSet, + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren($children); + } + } + + public function testChildrenAppearNestedInString() + { + /* -- RFC 2046, 5.1.1. + (excerpt too verbose to paste here) + */ + + $headers = $this->_createHeaderSet(array(), false); + + $child1 = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/plain\r\n". + "\r\n". + 'foobar' + ); + + $child2 = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/html\r\n". + "\r\n". + 'foobar' + ); + + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/alternative; boundary=\"xxx\"\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBoundary('xxx'); + $entity->setChildren(array($child1, $child2)); + + $this->assertEquals( + "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n". + "\r\n". + "\r\n--xxx\r\n". + "Content-Type: text/plain\r\n". + "\r\n". + "foobar\r\n". + "\r\n--xxx\r\n". + "Content-Type: text/html\r\n". + "\r\n". + "foobar\r\n". + "\r\n--xxx--\r\n", + $entity->toString() + ); + } + + public function testMixingLevelsIsHierarchical() + { + $headers = $this->_createHeaderSet(array(), false); + $newHeaders = $this->_createHeaderSet(array(), false); + + $part = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/plain\r\n". + "\r\n". + 'foobar' + ); + + $attachment = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_MIXED, + "Content-Type: application/octet-stream\r\n". + "\r\n". + 'data' + ); + + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/mixed; boundary=\"xxx\"\r\n"); + $headers->shouldReceive('newInstance') + ->zeroOrMoreTimes() + ->andReturn($newHeaders); + $newHeaders->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/alternative; boundary=\"yyy\"\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBoundary('xxx'); + $entity->setChildren(array($part, $attachment)); + + $this->assertRegExp( + '~^'. + "Content-Type: multipart/mixed; boundary=\"xxx\"\r\n". + "\r\n\r\n--xxx\r\n". + "Content-Type: multipart/alternative; boundary=\"yyy\"\r\n". + "\r\n\r\n--(.*?)\r\n". + "Content-Type: text/plain\r\n". + "\r\n". + 'foobar'. + "\r\n\r\n--\\1--\r\n". + "\r\n\r\n--xxx\r\n". + "Content-Type: application/octet-stream\r\n". + "\r\n". + 'data'. + "\r\n\r\n--xxx--\r\n". + "\$~", + $entity->toString() + ); + } + + public function testSettingEncoderNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $encoder = $this->_createEncoder('base64'); + + $child->shouldReceive('encoderChanged') + ->once() + ->with($encoder); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + $entity->setEncoder($encoder); + } + + public function testReceiptOfEncoderChangeNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $encoder = $this->_createEncoder('base64'); + + $child->shouldReceive('encoderChanged') + ->once() + ->with($encoder); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + $entity->encoderChanged($encoder); + } + + public function testReceiptOfCharsetChangeNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $child->shouldReceive('charsetChanged') + ->once() + ->with('windows-874'); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $entity->setChildren(array($child)); + $entity->charsetChanged('windows-874'); + } + + public function testEntityIsWrittenToByteStream() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $is = $this->_createInputStream(false); + $is->expects($this->atLeastOnce()) + ->method('write'); + + $entity->toByteStream($is); + } + + public function testEntityHeadersAreComittedToByteStream() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $is = $this->_createInputStream(false); + $is->expects($this->atLeastOnce()) + ->method('write'); + $is->expects($this->atLeastOnce()) + ->method('commit'); + + $entity->toByteStream($is); + } + + public function testOrderingTextBeforeHtml() + { + $htmlChild = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/html\r\n". + "\r\n". + 'HTML PART', + 'text/html' + ); + $textChild = new MimeEntityFixture(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, + "Content-Type: text/plain\r\n". + "\r\n". + 'TEXT PART', + 'text/plain' + ); + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: multipart/alternative; boundary=\"xxx\"\r\n"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $this->_createCache() + ); + $entity->setBoundary('xxx'); + $entity->setChildren(array($htmlChild, $textChild)); + + $this->assertEquals( + "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n". + "\r\n\r\n--xxx\r\n". + "Content-Type: text/plain\r\n". + "\r\n". + 'TEXT PART'. + "\r\n\r\n--xxx\r\n". + "Content-Type: text/html\r\n". + "\r\n". + 'HTML PART'. + "\r\n\r\n--xxx--\r\n", + $entity->toString() + ); + } + + public function testUnsettingChildrenRestoresContentType() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $child = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE); + + $cType->shouldReceive('setFieldBodyModel') + ->twice() + ->with('image/jpeg'); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('multipart/alternative'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes() + ->with(\Mockery::not('multipart/alternative', 'image/jpeg')); + + $entity = $this->_createEntity($this->_createHeaderSet(array( + 'Content-Type' => $cType, + )), + $this->_createEncoder(), $this->_createCache() + ); + + $entity->setContentType('image/jpeg'); + $entity->setChildren(array($child)); + $entity->setChildren(array()); + } + + public function testBodyIsReadFromCacheWhenUsingToStringIfPresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(true); + $cache->shouldReceive('getString') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn("\r\ncache\r\ncache!"); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $this->assertEquals( + "Content-Type: text/plain; charset=utf-8\r\n". + "\r\n". + "cache\r\ncache!", + $entity->toString() + ); + } + + public function testBodyIsAddedToCacheWhenUsingToString() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(false); + $cache->shouldReceive('setString') + ->once() + ->with(\Mockery::any(), 'body', "\r\nblah\r\nblah!", Swift_KeyCache::MODE_WRITE); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + } + + public function testBodyIsClearedFromCacheIfNewBodySet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // We set the expectation at this point because we only care what happens when calling setBody() + $cache->shouldReceive('clearKey') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity->setBody("new\r\nnew!"); + } + + public function testBodyIsNotClearedFromCacheIfSameBodySet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // We set the expectation at this point because we only care what happens when calling setBody() + $cache->shouldReceive('clearKey') + ->never(); + + $entity->setBody("blah\r\nblah!"); + } + + public function testBodyIsClearedFromCacheIfNewEncoderSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + $otherEncoder = $this->_createEncoder(); + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // We set the expectation at this point because we only care what happens when calling setEncoder() + $cache->shouldReceive('clearKey') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity->setEncoder($otherEncoder); + } + + public function testBodyIsReadFromCacheWhenUsingToByteStreamIfPresent() + { + $is = $this->_createInputStream(); + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(true); + $cache->shouldReceive('exportToByteStream') + ->once() + ->with(\Mockery::any(), 'body', $is); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $cache + ); + $entity->setBody('foo'); + + $entity->toByteStream($is); + } + + public function testBodyIsAddedToCacheWhenUsingToByteStream() + { + $is = $this->_createInputStream(); + $cache = $this->_createCache(false); + $cache->shouldReceive('hasKey') + ->once() + ->with(\Mockery::any(), 'body') + ->andReturn(false); + $cache->shouldReceive('getInputByteStream') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $cache + ); + $entity->setBody('foo'); + + $entity->toByteStream($is); + } + + public function testFluidInterface() + { + $entity = $this->_createEntity($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $this->assertSame($entity, + $entity + ->setContentType('text/plain') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my description') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ); + } + + // -- Private helpers + + abstract protected function _createEntity($headers, $encoder, $cache); + + protected function _createChild($level = null, $string = '', $stub = true) + { + $child = $this->getMockery('Swift_Mime_MimeEntity')->shouldIgnoreMissing(); + if (isset($level)) { + $child->shouldReceive('getNestingLevel') + ->zeroOrMoreTimes() + ->andReturn($level); + } + $child->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn($string); + + return $child; + } + + protected function _createEncoder($name = 'quoted-printable', $stub = true) + { + $encoder = $this->getMock('Swift_Mime_ContentEncoder'); + $encoder->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $encoder->expects($this->any()) + ->method('encodeString') + ->will($this->returnCallback(function () { + $args = func_get_args(); + + return array_shift($args); + })); + + return $encoder; + } + + protected function _createCache($stub = true) + { + return $this->getMockery('Swift_KeyCache')->shouldIgnoreMissing(); + } + + protected function _createHeaderSet($headers = array(), $stub = true) + { + $set = $this->getMockery('Swift_Mime_HeaderSet')->shouldIgnoreMissing(); + $set->shouldReceive('get') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($key) use ($headers) { + return $headers[$key]; + }); + $set->shouldReceive('has') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($key) use ($headers) { + return array_key_exists($key, $headers); + }); + + return $set; + } + + protected function _createHeader($name, $model = null, $params = array(), $stub = true) + { + $header = $this->getMockery('Swift_Mime_ParameterizedHeader')->shouldIgnoreMissing(); + $header->shouldReceive('getFieldName') + ->zeroOrMoreTimes() + ->andReturn($name); + $header->shouldReceive('getFieldBodyModel') + ->zeroOrMoreTimes() + ->andReturn($model); + $header->shouldReceive('getParameter') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($key) use ($params) { + return $params[$key]; + }); + + return $header; + } + + protected function _createOutputStream($data = null, $stub = true) + { + $os = $this->getMockery('Swift_OutputByteStream'); + if (isset($data)) { + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($data) { + static $first = true; + if (!$first) { + return false; + } + + $first = false; + + return $data; + }); + $os->shouldReceive('setReadPointer') + ->zeroOrMoreTimes(); + } + + return $os; + } + + protected function _createInputStream($stub = true) + { + return $this->getMock('Swift_InputByteStream'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php new file mode 100644 index 0000000..bd2499c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/AttachmentTest.php @@ -0,0 +1,320 @@ +_createAttachment($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_MIXED, $attachment->getNestingLevel() + ); + } + + public function testDispositionIsReturnedFromHeader() + { + /* -- RFC 2183, 2.1, 2.2. + */ + + $disposition = $this->_createHeader('Content-Disposition', 'attachment'); + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('attachment', $attachment->getDisposition()); + } + + public function testDispositionIsSetInHeader() + { + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array(), false + ); + $disposition->shouldReceive('setFieldBodyModel') + ->once() + ->with('inline'); + $disposition->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setDisposition('inline'); + } + + public function testDispositionIsAddedIfNonePresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Disposition', 'inline'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache() + ); + $attachment->setDisposition('inline'); + } + + public function testDispositionIsAutoDefaultedToAttachment() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Disposition', 'attachment'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testDefaultContentTypeInitializedToOctetStream() + { + $cType = $this->_createHeader('Content-Type', '', + array(), false + ); + $cType->shouldReceive('setFieldBodyModel') + ->once() + ->with('application/octet-stream'); + $cType->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + } + + public function testFilenameIsReturnedFromHeader() + { + /* -- RFC 2183, 2.3. + */ + + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt') + ); + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('foo.txt', $attachment->getFilename()); + } + + public function testFilenameIsSetInHeader() + { + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'bar.txt'); + $disposition->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFilename('bar.txt'); + } + + public function testSettingFilenameSetsNameInContentType() + { + /* + This is a legacy requirement which isn't covered by up-to-date RFCs. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array(), false + ); + $cType->shouldReceive('setParameter') + ->once() + ->with('name', 'bar.txt'); + $cType->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFilename('bar.txt'); + } + + public function testSizeIsReturnedFromHeader() + { + /* -- RFC 2183, 2.7. + */ + + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('size' => 1234) + ); + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(1234, $attachment->getSize()); + } + + public function testSizeIsSetInHeader() + { + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array(), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('size', 12345); + $disposition->shouldReceive('setParameter') + ->zeroOrMoreTimes(); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setSize(12345); + } + + public function testFilnameCanBeReadFromFileStream() + { + $file = $this->_createFileStream('/bar/file.ext', ''); + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'file.ext'); + + $attachment = $this->_createAttachment($this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, )), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFile($file); + } + + public function testContentTypeCanBeSetViaSetFile() + { + $file = $this->_createFileStream('/bar/file.ext', ''); + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.txt'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'file.ext'); + + $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $ctype->shouldReceive('setFieldBodyModel') + ->once() + ->with('text/html'); + $ctype->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $headers = $this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, + 'Content-Type' => $ctype, + )); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache() + ); + $attachment->setFile($file, 'text/html'); + } + + public function XtestContentTypeCanBeLookedUpFromCommonListIfNotProvided() + { + $file = $this->_createFileStream('/bar/file.zip', ''); + $disposition = $this->_createHeader('Content-Disposition', 'attachment', + array('filename' => 'foo.zip'), false + ); + $disposition->shouldReceive('setParameter') + ->once() + ->with('filename', 'file.zip'); + + $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $ctype->shouldReceive('setFieldBodyModel') + ->once() + ->with('application/zip'); + $ctype->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $headers = $this->_createHeaderSet(array( + 'Content-Disposition' => $disposition, + 'Content-Type' => $ctype, + )); + + $attachment = $this->_createAttachment($headers, $this->_createEncoder(), + $this->_createCache(), array('zip' => 'application/zip', 'txt' => 'text/plain') + ); + $attachment->setFile($file); + } + + public function testDataCanBeReadFromFile() + { + $file = $this->_createFileStream('/foo/file.ext', ''); + $attachment = $this->_createAttachment($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $attachment->setFile($file); + $this->assertEquals('', $attachment->getBody()); + } + + public function testFluidInterface() + { + $attachment = $this->_createAttachment($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertSame($attachment, + $attachment + ->setContentType('application/pdf') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my pdf') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ->setDisposition('inline') + ->setFilename('afile.txt') + ->setSize(123) + ->setFile($this->_createFileStream('foo.txt', '')) + ); + } + + // -- Private helpers + + protected function _createEntity($headers, $encoder, $cache) + { + return $this->_createAttachment($headers, $encoder, $cache); + } + + protected function _createAttachment($headers, $encoder, $cache, $mimeTypes = array()) + { + return new Swift_Mime_Attachment($headers, $encoder, $cache, new Swift_Mime_Grammar(), $mimeTypes); + } + + protected function _createFileStream($path, $data, $stub = true) + { + $file = $this->getMockery('Swift_FileStream'); + $file->shouldReceive('getPath') + ->zeroOrMoreTimes() + ->andReturn($path); + $file->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($data) { + static $first = true; + if (!$first) { + return false; + } + + $first = false; + + return $data; + }); + $file->shouldReceive('setReadPointer') + ->zeroOrMoreTimes(); + + return $file; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php new file mode 100644 index 0000000..0442af3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/Base64ContentEncoderTest.php @@ -0,0 +1,323 @@ +_encoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + } + + public function testNameIsBase64() + { + $this->assertEquals('base64', $this->_encoder->getName()); + } + + /* + There's really no point in testing the entire base64 encoding to the + level QP encoding has been tested. base64_encode() has been in PHP for + years. + */ + + public function testInputOutputRatioIs3to4Bytes() + { + /* + RFC 2045, 6.8 + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + */ + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('123'); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertEquals('MTIz', $collection->content); + } + + public function testPadLength() + { + /* + RFC 2045, 6.8 + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a body. When fewer than 24 input bits + are available in an input group, zero bits are added (on the right) + to form an integral number of 6-bit groups. Padding at the end of + the data is performed using the "=" character. Since all base64 + input is an integral number of octets, only the following cases can + arise: (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded output will be + an integral multiple of 4 characters with no "=" padding, (2) the + final quantum of encoding input is exactly 8 bits; here, the final + unit of encoded output will be two characters followed by two "=" + padding characters, or (3) the final quantum of encoding input is + exactly 16 bits; here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + + for ($i = 0; $i < 30; ++$i) { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn(pack('C', rand(0, 255))); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertRegExp('~^[a-zA-Z0-9/\+]{2}==$~', $collection->content, + '%s: A single byte should have 2 bytes of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn(pack('C*', rand(0, 255), rand(0, 255))); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertRegExp('~^[a-zA-Z0-9/\+]{3}=$~', $collection->content, + '%s: Two bytes should have 1 byte of padding' + ); + } + + for ($i = 0; $i < 30; ++$i) { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn(pack('C*', rand(0, 255), rand(0, 255), rand(0, 255))); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertRegExp('~^[a-zA-Z0-9/\+]{4}$~', $collection->content, + '%s: Three bytes should have no padding' + ); + } + } + + public function testMaximumLineLengthIs76Characters() + { + /* + The encoded output stream must be represented in lines of no more + than 76 characters each. All line breaks or other characters not + found in Table 1 must be ignored by decoding software. + */ + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3ODkwQUJDREVGR0hJSktMTU5PUFFS\r\n". + 'U1RVVldYWVoxMjM0NTY3YWJjZGVmZ2hpamts', + $collection->content + ); + } + + public function testMaximumLineLengthCanBeDifferent() + { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is, 0, 50); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3OD\r\n". + "kwQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVoxMjM0NTY3YWJj\r\n". + 'ZGVmZ2hpamts', + $collection->content + ); + } + + public function testMaximumLineLengthIsNeverMoreThan76Chars() + { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is, 0, 100); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3ODkwQUJDREVGR0hJSktMTU5PUFFS\r\n". + 'U1RVVldYWVoxMjM0NTY3YWJjZGVmZ2hpamts', + $collection->content + ); + } + + public function testFirstLineLengthCanBeDifferent() + { + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //12 + $os->shouldReceive('read') + ->once() + ->andReturn('mnopqrstuvwx'); //24 + $os->shouldReceive('read') + ->once() + ->andReturn('yzabc1234567'); //36 + $os->shouldReceive('read') + ->once() + ->andReturn('890ABCDEFGHI'); //48 + $os->shouldReceive('read') + ->once() + ->andReturn('JKLMNOPQRSTU'); //60 + $os->shouldReceive('read') + ->once() + ->andReturn('VWXYZ1234567'); //72 + $os->shouldReceive('read') + ->once() + ->andReturn('abcdefghijkl'); //84 + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_encoder->encodeByteStream($os, $is, 19); + $this->assertEquals( + "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmMxMjM0NTY3ODkwQUJDR\r\n". + 'EVGR0hJSktMTU5PUFFSU1RVVldYWVoxMjM0NTY3YWJjZGVmZ2hpamts', + $collection->content + ); + } + + private function _createOutputByteStream($stub = false) + { + return $this->getMockery('Swift_OutputByteStream')->shouldIgnoreMissing(); + } + + private function _createInputByteStream($stub = false) + { + return $this->getMockery('Swift_InputByteStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php new file mode 100644 index 0000000..526cedc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/PlainContentEncoderTest.php @@ -0,0 +1,173 @@ +_getEncoder('7bit'); + $this->assertEquals('7bit', $encoder->getName()); + + $encoder = $this->_getEncoder('8bit'); + $this->assertEquals('8bit', $encoder->getName()); + } + + public function testNoOctetsAreModifiedInString() + { + $encoder = $this->_getEncoder('7bit'); + foreach (range(0x00, 0xFF) as $octet) { + $byte = pack('C', $octet); + $this->assertIdenticalBinary($byte, $encoder->encodeString($byte)); + } + } + + public function testNoOctetsAreModifiedInByteStream() + { + $encoder = $this->_getEncoder('7bit'); + foreach (range(0x00, 0xFF) as $octet) { + $byte = pack('C', $octet); + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn($byte); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder->encodeByteStream($os, $is); + $this->assertIdenticalBinary($byte, $collection->content); + } + } + + public function testLineLengthCanBeSpecified() + { + $encoder = $this->_getEncoder('7bit'); + + $chars = array(); + for ($i = 0; $i < 50; $i++) { + $chars[] = 'a'; + } + $input = implode(' ', $chars); //99 chars long + + $this->assertEquals( + 'a a a a a a a a a a a a a a a a a a a a a a a a a '."\r\n".//50 * + 'a a a a a a a a a a a a a a a a a a a a a a a a a', //99 + $encoder->encodeString($input, 0, 50), + '%s: Lines should be wrapped at 50 chars' + ); + } + + public function testLineLengthCanBeSpecifiedInByteStream() + { + $encoder = $this->_getEncoder('7bit'); + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + + for ($i = 0; $i < 50; $i++) { + $os->shouldReceive('read') + ->once() + ->andReturn('a '); + } + + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder->encodeByteStream($os, $is, 0, 50); + $this->assertEquals( + str_repeat('a ', 25)."\r\n".str_repeat('a ', 25), + $collection->content + ); + } + + public function testencodeStringGeneratesCorrectCrlf() + { + $encoder = $this->_getEncoder('7bit', true); + $this->assertEquals("a\r\nb", $encoder->encodeString("a\rb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\nb", $encoder->encodeString("a\nb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\n\r\nb", $encoder->encodeString("a\n\rb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\n\r\nb", $encoder->encodeString("a\r\rb"), + '%s: Line endings should be standardized' + ); + $this->assertEquals("a\r\n\r\nb", $encoder->encodeString("a\n\nb"), + '%s: Line endings should be standardized' + ); + } + + public function crlfProvider() + { + return array( + array("\r", "a\r\nb"), + array("\n", "a\r\nb"), + array("\n\r", "a\r\n\r\nb"), + array("\n\n", "a\r\n\r\nb"), + array("\r\r", "a\r\n\r\nb"), + ); + } + + /** + * @dataProvider crlfProvider + */ + public function testCanonicEncodeByteStreamGeneratesCorrectCrlf($test, $expected) + { + $encoder = $this->_getEncoder('7bit', true); + + $os = $this->_createOutputByteStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $os->shouldReceive('read') + ->once() + ->andReturn('a'); + $os->shouldReceive('read') + ->once() + ->andReturn($test); + $os->shouldReceive('read') + ->once() + ->andReturn('b'); + $os->shouldReceive('read') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder->encodeByteStream($os, $is); + $this->assertEquals($expected, $collection->content); + } + + // -- Private helpers + + private function _getEncoder($name, $canonical = false) + { + return new Swift_Mime_ContentEncoder_PlainContentEncoder($name, $canonical); + } + + private function _createOutputByteStream($stub = false) + { + return $this->getMockery('Swift_OutputByteStream')->shouldIgnoreMissing(); + } + + private function _createInputByteStream($stub = false) + { + return $this->getMockery('Swift_InputByteStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php new file mode 100644 index 0000000..66fa8f3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/ContentEncoder/QpContentEncoderTest.php @@ -0,0 +1,496 @@ +_createCharacterStream(true) + ); + $this->assertEquals('quoted-printable', $encoder->getName()); + } + + /* -- RFC 2045, 6.7 -- + (1) (General 8bit representation) Any octet, except a CR or + LF that is part of a CRLF line break of the canonical + (standard) form of the data being encoded, may be + represented by an "=" followed by a two digit + hexadecimal representation of the octet's value. The + digits of the hexadecimal alphabet, for this purpose, + are "0123456789ABCDEF". Uppercase letters must be + used; lowercase letters are not allowed. Thus, for + example, the decimal value 12 (US-ASCII form feed) can + be represented by "=0C", and the decimal value 61 (US- + ASCII EQUAL SIGN) can be represented by "=3D". This + rule must be followed except when the following rules + allow an alternative encoding. + */ + + public function testPermittedCharactersAreNotEncoded() + { + /* -- RFC 2045, 6.7 -- + (2) (Literal representation) Octets with decimal values of + 33 through 60 inclusive, and 62 through 126, inclusive, + MAY be represented as the US-ASCII characters which + correspond to those octets (EXCLAMATION POINT through + LESS THAN, and GREATER THAN through TILDE, + respectively). + */ + + foreach (array_merge(range(33, 60), range(62, 126)) as $ordinal) { + $char = chr($ordinal); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertIdenticalBinary($char, $collection->content); + } + } + + public function testLinearWhiteSpaceAtLineEndingIsEncoded() + { + /* -- RFC 2045, 6.7 -- + (3) (White Space) Octets with values of 9 and 32 MAY be + represented as US-ASCII TAB (HT) and SPACE characters, + respectively, but MUST NOT be so represented at the end + of an encoded line. Any TAB (HT) or SPACE characters + on an encoded line MUST thus be followed on that line + by a printable character. In particular, an "=" at the + end of an encoded line, indicating a soft line break + (see rule #5) may follow one or more TAB (HT) or SPACE + characters. It follows that an octet with decimal + value 9 or 32 appearing at the end of an encoded line + must be represented according to Rule #1. This rule is + necessary because some MTAs (Message Transport Agents, + programs which transport messages from one user to + another, or perform a portion of such transfers) are + known to pad lines of text with SPACEs, and others are + known to remove "white space" characters from the end + of a line. Therefore, when decoding a Quoted-Printable + body, any trailing white space on a line must be + deleted, as it will necessarily have been added by + intermediate transport agents. + */ + + $HT = chr(0x09); //9 + $SPACE = chr(0x20); //32 + + //HT + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x09)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + + $this->assertEquals("a\t=09\r\nb", $collection->content); + + //SPACE + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + + $this->assertEquals("a =20\r\nb", $collection->content); + } + + public function testCRLFIsLeftAlone() + { + /* + (4) (Line Breaks) A line break in a text body, represented + as a CRLF sequence in the text canonical form, must be + represented by a (RFC 822) line break, which is also a + CRLF sequence, in the Quoted-Printable encoding. Since + the canonical representation of media types other than + text do not generally include the representation of + line breaks as CRLF sequences, no hard line breaks + (i.e. line breaks that are intended to be meaningful + and to be displayed to the user) can occur in the + quoted-printable encoding of such types. Sequences + like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely + appear in non-text data represented in quoted- + printable, of course. + + Note that many implementations may elect to encode the + local representation of various content types directly + rather than converting to canonical form first, + encoding, and then converting back to local + representation. In particular, this may apply to plain + text material on systems that use newline conventions + other than a CRLF terminator sequence. Such an + implementation optimization is permissible, but only + when the combined canonicalization-encoding step is + equivalent to performing the three steps separately. + */ + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('c'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0D)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x0A)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals("a\r\nb\r\nc\r\n", $collection->content); + } + + public function testLinesLongerThan76CharactersAreSoftBroken() + { + /* + (5) (Soft Line Breaks) The Quoted-Printable encoding + REQUIRES that encoded lines be no more than 76 + characters long. If longer lines are to be encoded + with the Quoted-Printable encoding, "soft" line breaks + must be used. An equal sign as the last character on a + encoded line indicates such a non-significant ("soft") + line break in the encoded text. + */ + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + + for ($seq = 0; $seq <= 140; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + } + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(str_repeat('a', 75)."=\r\n".str_repeat('a', 66), $collection->content); + } + + public function testMaxLineLengthCanBeSpecified() + { + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + + for ($seq = 0; $seq <= 100; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + } + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is, 0, 54); + $this->assertEquals(str_repeat('a', 53)."=\r\n".str_repeat('a', 48), $collection->content); + } + + public function testBytesBelowPermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(0, 32) as $ordinal) { + $char = chr($ordinal); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(sprintf('=%02X', $ordinal), $collection->content); + } + } + + public function testDecimalByte61IsEncoded() + { + /* + According to Rule (1 & 2) + */ + + $char = chr(61); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(61)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(sprintf('=%02X', 61), $collection->content); + } + + public function testBytesAbovePermittedRangeAreEncoded() + { + /* + According to Rule (1 & 2) + */ + + foreach (range(127, 255) as $ordinal) { + $char = chr($ordinal); + + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($ordinal)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is); + $this->assertEquals(sprintf('=%02X', $ordinal), $collection->content); + } + } + + public function testFirstLineLengthCanBeDifferent() + { + $os = $this->_createOutputByteStream(true); + $charStream = $this->_createCharacterStream(); + $is = $this->_createInputByteStream(); + $collection = new Swift_StreamCollector(); + + $is->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturnUsing($collection); + $charStream->shouldReceive('flushContents') + ->once(); + $charStream->shouldReceive('importByteStream') + ->once() + ->with($os); + + for ($seq = 0; $seq <= 140; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + } + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($charStream); + $encoder->encodeByteStream($os, $is, 22); + $this->assertEquals( + str_repeat('a', 53)."=\r\n".str_repeat('a', 75)."=\r\n".str_repeat('a', 13), + $collection->content + ); + } + + public function testObserverInterfaceCanChangeCharset() + { + $stream = $this->_createCharacterStream(); + $stream->shouldReceive('setCharacterSet') + ->once() + ->with('windows-1252'); + + $encoder = new Swift_Mime_ContentEncoder_QpContentEncoder($stream); + $encoder->charsetChanged('windows-1252'); + } + + // -- Creation Methods + + private function _createCharacterStream($stub = false) + { + return $this->getMockery('Swift_CharacterStream')->shouldIgnoreMissing(); + } + + private function _createEncoder($charStream) + { + return new Swift_Mime_HeaderEncoder_QpHeaderEncoder($charStream); + } + + private function _createOutputByteStream($stub = false) + { + return $this->getMockery('Swift_OutputByteStream')->shouldIgnoreMissing(); + } + + private function _createInputByteStream($stub = false) + { + return $this->getMockery('Swift_InputByteStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php new file mode 100644 index 0000000..f4c3ac8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/EmbeddedFileTest.php @@ -0,0 +1,57 @@ +_createEmbeddedFile($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_RELATED, $file->getNestingLevel() + ); + } + + public function testIdIsAutoGenerated() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addIdHeader') + ->once() + ->with('Content-ID', '/^.*?@.*?$/D'); + + $file = $this->_createEmbeddedFile($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testDefaultDispositionIsInline() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addParameterizedHeader') + ->once() + ->with('Content-Disposition', 'inline'); + $headers->shouldReceive('addParameterizedHeader') + ->zeroOrMoreTimes(); + + $file = $this->_createEmbeddedFile($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + // -- Private helpers + + protected function _createAttachment($headers, $encoder, $cache, $mimeTypes = array()) + { + return $this->_createEmbeddedFile($headers, $encoder, $cache, $mimeTypes); + } + + private function _createEmbeddedFile($headers, $encoder, $cache) + { + return new Swift_Mime_EmbeddedFile($headers, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php new file mode 100644 index 0000000..3580155 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/Base64HeaderEncoderTest.php @@ -0,0 +1,13 @@ +assertEquals('B', $encoder->getName()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php new file mode 100644 index 0000000..54a792a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/HeaderEncoder/QpHeaderEncoderTest.php @@ -0,0 +1,223 @@ +_createEncoder( + $this->_createCharacterStream(true) + ); + $this->assertEquals('Q', $encoder->getName()); + } + + public function testSpaceAndTabNeverAppear() + { + /* -- RFC 2047, 4. + Only a subset of the printable ASCII characters may be used in + 'encoded-text'. Space and tab characters are not allowed, so that + the beginning and end of an 'encoded-word' are obvious. + */ + + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->atLeast()->times(6) + ->andReturn(array(ord('a')), array(0x20), array(0x09), array(0x20), array(ord('b')), false); + + $encoder = $this->_createEncoder($charStream); + $this->assertNotRegExp('~[ \t]~', $encoder->encodeString("a \t b"), + '%s: encoded-words in headers cannot contain LWSP as per RFC 2047.' + ); + } + + public function testSpaceIsRepresentedByUnderscore() + { + /* -- RFC 2047, 4.2. + (2) The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be + represented as "_" (underscore, ASCII 95.). (This character may + not pass through some internetwork mail gateways, but its use + will greatly enhance readability of "Q" encoded data with mail + readers that do not support this encoding.) Note that the "_" + always represents hexadecimal 20, even if the SPACE character + occupies a different code position in the character set in use. + */ + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(0x20)); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('b'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals('a_b', $encoder->encodeString('a b'), + '%s: Spaces can be represented by more readable underscores as per RFC 2047.' + ); + } + + public function testEqualsAndQuestionAndUnderscoreAreEncoded() + { + /* -- RFC 2047, 4.2. + (3) 8-bit values which correspond to printable ASCII characters other + than "=", "?", and "_" (underscore), MAY be represented as those + characters. (But see section 5 for restrictions.) In + particular, SPACE and TAB MUST NOT be represented as themselves + within encoded words. + */ + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('='))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('?'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('_'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals('=3D=3F=5F', $encoder->encodeString('=?_'), + '%s: Chars =, ? and _ (underscore) may not appear as per RFC 2047.' + ); + } + + public function testParensAndQuotesAreEncoded() + { + /* -- RFC 2047, 5 (2). + A "Q"-encoded 'encoded-word' which appears in a 'comment' MUST NOT + contain the characters "(", ")" or " + */ + + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('('))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('"'))); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord(')'))); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals('=28=22=29', $encoder->encodeString('(")'), + '%s: Chars (, " (DQUOTE) and ) may not appear as per RFC 2047.' + ); + } + + public function testOnlyCharactersAllowedInPhrasesAreUsed() + { + /* -- RFC 2047, 5. + (3) As a replacement for a 'word' entity within a 'phrase', for example, + one that precedes an address in a From, To, or Cc header. The ABNF + definition for 'phrase' from RFC 822 thus becomes: + + phrase = 1*( encoded-word / word ) + + In this case the set of characters that may be used in a "Q"-encoded + 'encoded-word' is restricted to: . An 'encoded-word' that appears within a + 'phrase' MUST be separated from any adjacent 'word', 'text' or + 'special' by 'linear-white-space'. + */ + + $allowedBytes = array_merge( + range(ord('a'), ord('z')), range(ord('A'), ord('Z')), + range(ord('0'), ord('9')), + array(ord('!'), ord('*'), ord('+'), ord('-'), ord('/')) + ); + + foreach (range(0x00, 0xFF) as $byte) { + $char = pack('C', $byte); + + $charStream = $this->_createCharacterStream(); + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array($byte)); + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $encodedChar = $encoder->encodeString($char); + + if (in_array($byte, $allowedBytes)) { + $this->assertEquals($char, $encodedChar, + '%s: Character '.$char.' should not be encoded.' + ); + } elseif (0x20 == $byte) { + //Special case + $this->assertEquals('_', $encodedChar, + '%s: Space character should be replaced.' + ); + } else { + $this->assertEquals(sprintf('=%02X', $byte), $encodedChar, + '%s: Byte '.$byte.' should be encoded.' + ); + } + } + } + + public function testEqualsNeverAppearsAtEndOfLine() + { + /* -- RFC 2047, 5 (3). + The 'encoded-text' in an 'encoded-word' must be self-contained; + 'encoded-text' MUST NOT be continued from one 'encoded-word' to + another. This implies that the 'encoded-text' portion of a "B" + 'encoded-word' will be a multiple of 4 characters long; for a "Q" + 'encoded-word', any "=" character that appears in the 'encoded-text' + portion will be followed by two hexadecimal characters. + */ + + $input = str_repeat('a', 140); + + $charStream = $this->_createCharacterStream(); + + $output = ''; + $seq = 0; + for (; $seq < 140; ++$seq) { + $charStream->shouldReceive('readBytes') + ->once() + ->andReturn(array(ord('a'))); + + if (75 == $seq) { + $output .= "\r\n"; // =\r\n + } + $output .= 'a'; + } + + $charStream->shouldReceive('readBytes') + ->zeroOrMoreTimes() + ->andReturn(false); + + $encoder = $this->_createEncoder($charStream); + $this->assertEquals($output, $encoder->encodeString($input)); + } + + // -- Creation Methods + + private function _createEncoder($charStream) + { + return new Swift_Mime_HeaderEncoder_QpHeaderEncoder($charStream); + } + + private function _createCharacterStream($stub = false) + { + return $this->getMockery('Swift_CharacterStream')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php new file mode 100644 index 0000000..1822ea6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/DateHeaderTest.php @@ -0,0 +1,69 @@ +_getHeader('Date'); + $this->assertEquals(Swift_Mime_Header::TYPE_DATE, $header->getFieldType()); + } + + public function testGetTimestamp() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertSame($timestamp, $header->getTimestamp()); + } + + public function testTimestampCanBeSetBySetter() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertSame($timestamp, $header->getTimestamp()); + } + + public function testIntegerTimestampIsConvertedToRfc2822Date() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertEquals(date('r', $timestamp), $header->getFieldBody()); + } + + public function testSetBodyModel() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setFieldBodyModel($timestamp); + $this->assertEquals(date('r', $timestamp), $header->getFieldBody()); + } + + public function testGetBodyModel() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertEquals($timestamp, $header->getFieldBodyModel()); + } + + public function testToString() + { + $timestamp = time(); + $header = $this->_getHeader('Date'); + $header->setTimestamp($timestamp); + $this->assertEquals('Date: '.date('r', $timestamp)."\r\n", + $header->toString() + ); + } + + private function _getHeader($name) + { + return new Swift_Mime_Headers_DateHeader($name, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php new file mode 100644 index 0000000..93b3f60 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/IdentificationHeaderTest.php @@ -0,0 +1,189 @@ +_getHeader('Message-ID'); + $this->assertEquals(Swift_Mime_Header::TYPE_ID, $header->getFieldType()); + } + + public function testValueMatchesMsgIdSpec() + { + /* -- RFC 2822, 3.6.4. + message-id = "Message-ID:" msg-id CRLF + + in-reply-to = "In-Reply-To:" 1*msg-id CRLF + + references = "References:" 1*msg-id CRLF + + msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] + + id-left = dot-atom-text / no-fold-quote / obs-id-left + + id-right = dot-atom-text / no-fold-literal / obs-id-right + + no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE + + no-fold-literal = "[" *(dtext / quoted-pair) "]" + */ + + $header = $this->_getHeader('Message-ID'); + $header->setId('id-left@id-right'); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testIdCanBeRetrievedVerbatim() + { + $header = $this->_getHeader('Message-ID'); + $header->setId('id-left@id-right'); + $this->assertEquals('id-left@id-right', $header->getId()); + } + + public function testMultipleIdsCanBeSet() + { + $header = $this->_getHeader('References'); + $header->setIds(array('a@b', 'x@y')); + $this->assertEquals(array('a@b', 'x@y'), $header->getIds()); + } + + public function testSettingMultipleIdsProducesAListValue() + { + /* -- RFC 2822, 3.6.4. + The "References:" and "In-Reply-To:" field each contain one or more + unique message identifiers, optionally separated by CFWS. + + .. SNIP .. + + in-reply-to = "In-Reply-To:" 1*msg-id CRLF + + references = "References:" 1*msg-id CRLF + */ + + $header = $this->_getHeader('References'); + $header->setIds(array('a@b', 'x@y')); + $this->assertEquals(' ', $header->getFieldBody()); + } + + public function testIdLeftCanBeQuoted() + { + /* -- RFC 2822, 3.6.4. + id-left = dot-atom-text / no-fold-quote / obs-id-left + */ + + $header = $this->_getHeader('References'); + $header->setId('"ab"@c'); + $this->assertEquals('"ab"@c', $header->getId()); + $this->assertEquals('<"ab"@c>', $header->getFieldBody()); + } + + public function testIdLeftCanContainAnglesAsQuotedPairs() + { + /* -- RFC 2822, 3.6.4. + no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE + */ + + $header = $this->_getHeader('References'); + $header->setId('"a\\<\\>b"@c'); + $this->assertEquals('"a\\<\\>b"@c', $header->getId()); + $this->assertEquals('<"a\\<\\>b"@c>', $header->getFieldBody()); + } + + public function testIdLeftCanBeDotAtom() + { + $header = $this->_getHeader('References'); + $header->setId('a.b+&%$.c@d'); + $this->assertEquals('a.b+&%$.c@d', $header->getId()); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testInvalidIdLeftThrowsException() + { + try { + $header = $this->_getHeader('References'); + $header->setId('a b c@d'); + $this->fail( + 'Exception should be thrown since "a b c" is not valid id-left.' + ); + } catch (Exception $e) { + } + } + + public function testIdRightCanBeDotAtom() + { + /* -- RFC 2822, 3.6.4. + id-right = dot-atom-text / no-fold-literal / obs-id-right + */ + + $header = $this->_getHeader('References'); + $header->setId('a@b.c+&%$.d'); + $this->assertEquals('a@b.c+&%$.d', $header->getId()); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testIdRightCanBeLiteral() + { + /* -- RFC 2822, 3.6.4. + no-fold-literal = "[" *(dtext / quoted-pair) "]" + */ + + $header = $this->_getHeader('References'); + $header->setId('a@[1.2.3.4]'); + $this->assertEquals('a@[1.2.3.4]', $header->getId()); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testInvalidIdRightThrowsException() + { + try { + $header = $this->_getHeader('References'); + $header->setId('a@b c d'); + $this->fail( + 'Exception should be thrown since "b c d" is not valid id-right.' + ); + } catch (Exception $e) { + } + } + + public function testMissingAtSignThrowsException() + { + /* -- RFC 2822, 3.6.4. + msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] + */ + + try { + $header = $this->_getHeader('References'); + $header->setId('abc'); + $this->fail( + 'Exception should be thrown since "abc" is does not contain @.' + ); + } catch (Exception $e) { + } + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Message-ID'); + $header->setFieldBodyModel('a@b'); + $this->assertEquals(array('a@b'), $header->getIds()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Message-ID'); + $header->setId('a@b'); + $this->assertEquals(array('a@b'), $header->getFieldBodyModel()); + } + + public function testStringValue() + { + $header = $this->_getHeader('References'); + $header->setIds(array('a@b', 'x@y')); + $this->assertEquals('References: '."\r\n", $header->toString()); + } + + private function _getHeader($name) + { + return new Swift_Mime_Headers_IdentificationHeader($name, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php new file mode 100644 index 0000000..0713ff4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/MailboxHeaderTest.php @@ -0,0 +1,327 @@ +_getHeader('To', $this->_getEncoder('Q', true)); + $this->assertEquals(Swift_Mime_Header::TYPE_MAILBOX, $header->getFieldType()); + } + + public function testMailboxIsSetForAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses('chris@swiftmailer.org'); + $this->assertEquals(array('chris@swiftmailer.org'), + $header->getNameAddressStrings() + ); + } + + public function testMailboxIsRenderedForNameAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris Corbyn')); + $this->assertEquals( + array('Chris Corbyn '), $header->getNameAddressStrings() + ); + } + + public function testAddressCanBeReturnedForAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses('chris@swiftmailer.org'); + $this->assertEquals(array('chris@swiftmailer.org'), $header->getAddresses()); + } + + public function testAddressCanBeReturnedForNameAddress() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris Corbyn')); + $this->assertEquals(array('chris@swiftmailer.org'), $header->getAddresses()); + } + + public function testQuotesInNameAreQuoted() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn, "DHE"', + )); + $this->assertEquals( + array('"Chris Corbyn, \"DHE\"" '), + $header->getNameAddressStrings() + ); + } + + public function testEscapeCharsInNameAreQuoted() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn, \\escaped\\', + )); + $this->assertEquals( + array('"Chris Corbyn, \\\\escaped\\\\" '), + $header->getNameAddressStrings() + ); + } + + public function testGetMailboxesReturnsNameValuePairs() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn, DHE', + )); + $this->assertEquals( + array('chris@swiftmailer.org' => 'Chris Corbyn, DHE'), $header->getNameAddresses() + ); + } + + public function testMultipleAddressesCanBeSetAndFetched() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array( + 'chris@swiftmailer.org', 'mark@swiftmailer.org', + )); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testMultipleAddressesAsMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array( + 'chris@swiftmailer.org', 'mark@swiftmailer.org', + )); + $this->assertEquals( + array('chris@swiftmailer.org' => null, 'mark@swiftmailer.org' => null), + $header->getNameAddresses() + ); + } + + public function testMultipleAddressesAsMailboxStrings() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array( + 'chris@swiftmailer.org', 'mark@swiftmailer.org', + )); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getNameAddressStrings() + ); + } + + public function testMultipleNamedMailboxesReturnsMultipleAddresses() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testMultipleNamedMailboxesReturnsMultipleMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + ), + $header->getNameAddresses() + ); + } + + public function testMultipleMailboxesProducesMultipleMailboxStrings() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals(array( + 'Chris Corbyn ', + 'Mark Corbyn ', + ), + $header->getNameAddressStrings() + ); + } + + public function testSetAddressesOverwritesAnyMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + array('chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', ), + $header->getNameAddresses() + ); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + + $header->setAddresses(array('chris@swiftmailer.org', 'mark@swiftmailer.org')); + + $this->assertEquals( + array('chris@swiftmailer.org' => null, 'mark@swiftmailer.org' => null), + $header->getNameAddresses() + ); + $this->assertEquals( + array('chris@swiftmailer.org', 'mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testNameIsEncodedIfNonAscii() + { + $name = 'C'.pack('C', 0x8F).'rbyn'; + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($name, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('C=8Frbyn'); + + $header = $this->_getHeader('From', $encoder); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris '.$name)); + + $addresses = $header->getNameAddressStrings(); + $this->assertEquals( + 'Chris =?'.$this->_charset.'?Q?C=8Frbyn?= ', + array_shift($addresses) + ); + } + + public function testEncodingLineLengthCalculations() + { + /* -- RFC 2047, 2. + An 'encoded-word' may not be more than 75 characters long, including + 'charset', 'encoding', 'encoded-text', and delimiters. + */ + + $name = 'C'.pack('C', 0x8F).'rbyn'; + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($name, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('C=8Frbyn'); + + $header = $this->_getHeader('From', $encoder); + $header->setNameAddresses(array('chris@swiftmailer.org' => 'Chris '.$name)); + + $header->getNameAddressStrings(); + } + + public function testGetValueReturnsMailboxStringValue() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + )); + $this->assertEquals( + 'Chris Corbyn ', $header->getFieldBody() + ); + } + + public function testGetValueReturnsMailboxStringValueForMultipleMailboxes() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + 'Chris Corbyn , Mark Corbyn ', + $header->getFieldBody() + ); + } + + public function testRemoveAddressesWithSingleValue() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $header->removeAddresses('chris@swiftmailer.org'); + $this->assertEquals(array('mark@swiftmailer.org'), + $header->getAddresses() + ); + } + + public function testRemoveAddressesWithList() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $header->removeAddresses( + array('chris@swiftmailer.org', 'mark@swiftmailer.org') + ); + $this->assertEquals(array(), $header->getAddresses()); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setFieldBodyModel('chris@swiftmailer.org'); + $this->assertEquals(array('chris@swiftmailer.org' => null), $header->getNameAddresses()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setAddresses(array('chris@swiftmailer.org')); + $this->assertEquals(array('chris@swiftmailer.org' => null), $header->getFieldBodyModel()); + } + + public function testToString() + { + $header = $this->_getHeader('From', $this->_getEncoder('Q', true)); + $header->setNameAddresses(array( + 'chris@swiftmailer.org' => 'Chris Corbyn', + 'mark@swiftmailer.org' => 'Mark Corbyn', + )); + $this->assertEquals( + 'From: Chris Corbyn , '. + 'Mark Corbyn '."\r\n", + $header->toString() + ); + } + + private function _getHeader($name, $encoder) + { + $header = new Swift_Mime_Headers_MailboxHeader($name, $encoder, new Swift_Mime_Grammar()); + $header->setCharset($this->_charset); + + return $header; + } + + private function _getEncoder($type, $stub = false) + { + $encoder = $this->getMockery('Swift_Mime_HeaderEncoder')->shouldIgnoreMissing(); + $encoder->shouldReceive('getName') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $encoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php new file mode 100644 index 0000000..0f3fe14 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/ParameterizedHeaderTest.php @@ -0,0 +1,400 @@ +_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $this->assertEquals(Swift_Mime_Header::TYPE_PARAMETERIZED, $header->getFieldType()); + } + + public function testValueIsReturnedVerbatim() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/plain'); + $this->assertEquals('text/plain', $header->getValue()); + } + + public function testParametersAreAppended() + { + /* -- RFC 2045, 5.1 + parameter := attribute "=" value + + attribute := token + ; Matching of attributes + ; is ALWAYS case-insensitive. + + value := token / quoted-string + + token := 1* + + tspecials := "(" / ")" / "<" / ">" / "@" / + "," / ";" / ":" / "\" / <"> + "/" / "[" / "]" / "?" / "=" + ; Must be in quoted-string, + ; to use within parameter values + */ + + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/plain'); + $header->setParameters(array('charset' => 'utf-8')); + $this->assertEquals('text/plain; charset=utf-8', $header->getFieldBody()); + } + + public function testSpaceInParamResultsInQuotedString() + { + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => 'my file.txt')); + $this->assertEquals('attachment; filename="my file.txt"', + $header->getFieldBody() + ); + } + + public function testLongParamsAreBrokenIntoMultipleAttributeStrings() + { + /* -- RFC 2231, 3. + The asterisk character ("*") followed + by a decimal count is employed to indicate that multiple parameters + are being used to encapsulate a single parameter value. The count + starts at 0 and increments by 1 for each subsequent section of the + parameter value. Decimal values are used and neither leading zeroes + nor gaps in the sequence are allowed. + + The original parameter value is recovered by concatenating the + various sections of the parameter, in order. For example, the + content-type field + + Content-Type: message/external-body; access-type=URL; + URL*0="ftp://"; + URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar" + + is semantically identical to + + Content-Type: message/external-body; access-type=URL; + URL="ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar" + + Note that quotes around parameter values are part of the value + syntax; they are NOT part of the value itself. Furthermore, it is + explicitly permitted to have a mixture of quoted and unquoted + continuation fields. + */ + + $value = str_repeat('a', 180); + + $encoder = $this->_getParameterEncoder(); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), 63, \Mockery::any()) + ->andReturn(str_repeat('a', 63)."\r\n". + str_repeat('a', 63)."\r\n".str_repeat('a', 54)); + + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $encoder + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => $value)); + $header->setMaxLineLength(78); + $this->assertEquals( + 'attachment; '. + 'filename*0*=utf-8\'\''.str_repeat('a', 63).";\r\n ". + 'filename*1*='.str_repeat('a', 63).";\r\n ". + 'filename*2*='.str_repeat('a', 54), + $header->getFieldBody() + ); + } + + public function testEncodedParamDataIncludesCharsetAndLanguage() + { + /* -- RFC 2231, 4. + Asterisks ("*") are reused to provide the indicator that language and + character set information is present and encoding is being used. A + single quote ("'") is used to delimit the character set and language + information at the beginning of the parameter value. Percent signs + ("%") are used as the encoding flag, which agrees with RFC 2047. + + Specifically, an asterisk at the end of a parameter name acts as an + indicator that character set and language information may appear at + the beginning of the parameter value. A single quote is used to + separate the character set, language, and actual value information in + the parameter value string, and an percent sign is used to flag + octets encoded in hexadecimal. For example: + + Content-Type: application/x-stuff; + title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A + + Note that it is perfectly permissible to leave either the character + set or language field blank. Note also that the single quote + delimiters MUST be present even when one of the field values is + omitted. + */ + + $value = str_repeat('a', 20).pack('C', 0x8F).str_repeat('a', 10); + + $encoder = $this->_getParameterEncoder(); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, 12, 62, \Mockery::any()) + ->andReturn(str_repeat('a', 20).'%8F'.str_repeat('a', 10)); + + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $encoder + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => $value)); + $header->setMaxLineLength(78); + $header->setLanguage($this->_lang); + $this->assertEquals( + 'attachment; filename*='.$this->_charset."'".$this->_lang."'". + str_repeat('a', 20).'%8F'.str_repeat('a', 10), + $header->getFieldBody() + ); + } + + public function testMultipleEncodedParamLinesAreFormattedCorrectly() + { + /* -- RFC 2231, 4.1. + Character set and language information may be combined with the + parameter continuation mechanism. For example: + + Content-Type: application/x-stuff + title*0*=us-ascii'en'This%20is%20even%20more%20 + title*1*=%2A%2A%2Afun%2A%2A%2A%20 + title*2="isn't it!" + + Note that: + + (1) Language and character set information only appear at + the beginning of a given parameter value. + + (2) Continuations do not provide a facility for using more + than one character set or language in the same + parameter value. + + (3) A value presented using multiple continuations may + contain a mixture of encoded and unencoded segments. + + (4) The first segment of a continuation MUST be encoded if + language and character set information are given. + + (5) If the first segment of a continued parameter value is + encoded the language and character set field delimiters + MUST be present even when the fields are left blank. + */ + + $value = str_repeat('a', 20).pack('C', 0x8F).str_repeat('a', 60); + + $encoder = $this->_getParameterEncoder(); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, 12, 62, \Mockery::any()) + ->andReturn(str_repeat('a', 20).'%8F'.str_repeat('a', 28)."\r\n". + str_repeat('a', 32)); + + $header = $this->_getHeader('Content-Disposition', + $this->_getHeaderEncoder('Q', true), $encoder + ); + $header->setValue('attachment'); + $header->setParameters(array('filename' => $value)); + $header->setMaxLineLength(78); + $header->setLanguage($this->_lang); + $this->assertEquals( + 'attachment; filename*0*='.$this->_charset."'".$this->_lang."'". + str_repeat('a', 20).'%8F'.str_repeat('a', 28).";\r\n ". + 'filename*1*='.str_repeat('a', 32), + $header->getFieldBody() + ); + } + + public function testToString() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/html'); + $header->setParameters(array('charset' => 'utf-8')); + $this->assertEquals('Content-Type: text/html; charset=utf-8'."\r\n", + $header->toString() + ); + } + + public function testValueCanBeEncodedIfNonAscii() + { + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, $this->_getParameterEncoder(true)); + $header->setValue($value); + $header->setParameters(array('lookslike' => 'foobar')); + $this->assertEquals('X-Foo: =?utf-8?Q?fo=8Fbar?=; lookslike=foobar'."\r\n", + $header->toString() + ); + } + + public function testValueAndParamCanBeEncodedIfNonAscii() + { + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $paramEncoder = $this->_getParameterEncoder(); + $paramEncoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo%8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, $paramEncoder); + $header->setValue($value); + $header->setParameters(array('says' => $value)); + $this->assertEquals("X-Foo: =?utf-8?Q?fo=8Fbar?=; says*=utf-8''fo%8Fbar\r\n", + $header->toString() + ); + } + + public function testParamsAreEncodedWithEncodedWordsIfNoParamEncoderSet() + { + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, null); + $header->setValue('bar'); + $header->setParameters(array('says' => $value)); + $this->assertEquals("X-Foo: bar; says=\"=?utf-8?Q?fo=8Fbar?=\"\r\n", + $header->toString() + ); + } + + public function testLanguageInformationAppearsInEncodedWords() + { + /* -- RFC 2231, 5. + 5. Language specification in Encoded Words + + RFC 2047 provides support for non-US-ASCII character sets in RFC 822 + message header comments, phrases, and any unstructured text field. + This is done by defining an encoded word construct which can appear + in any of these places. Given that these are fields intended for + display, it is sometimes necessary to associate language information + with encoded words as well as just the character set. This + specification extends the definition of an encoded word to allow the + inclusion of such information. This is simply done by suffixing the + character set specification with an asterisk followed by the language + tag. For example: + + From: =?US-ASCII*EN?Q?Keith_Moore?= + */ + + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getHeaderEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $paramEncoder = $this->_getParameterEncoder(); + $paramEncoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo%8Fbar'); + + $header = $this->_getHeader('X-Foo', $encoder, $paramEncoder); + $header->setLanguage('en'); + $header->setValue($value); + $header->setParameters(array('says' => $value)); + $this->assertEquals("X-Foo: =?utf-8*en?Q?fo=8Fbar?=; says*=utf-8'en'fo%8Fbar\r\n", + $header->toString() + ); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setFieldBodyModel('text/html'); + $this->assertEquals('text/html', $header->getValue()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setValue('text/plain'); + $this->assertEquals('text/plain', $header->getFieldBodyModel()); + } + + public function testSetParameter() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setParameters(array('charset' => 'utf-8', 'delsp' => 'yes')); + $header->setParameter('delsp', 'no'); + $this->assertEquals(array('charset' => 'utf-8', 'delsp' => 'no'), + $header->getParameters() + ); + } + + public function testGetParameter() + { + $header = $this->_getHeader('Content-Type', + $this->_getHeaderEncoder('Q', true), $this->_getParameterEncoder(true) + ); + $header->setParameters(array('charset' => 'utf-8', 'delsp' => 'yes')); + $this->assertEquals('utf-8', $header->getParameter('charset')); + } + + // -- Private helper + + private function _getHeader($name, $encoder, $paramEncoder) + { + $header = new Swift_Mime_Headers_ParameterizedHeader($name, $encoder, + $paramEncoder, new Swift_Mime_Grammar() + ); + $header->setCharset($this->_charset); + + return $header; + } + + private function _getHeaderEncoder($type, $stub = false) + { + $encoder = $this->getMockery('Swift_Mime_HeaderEncoder')->shouldIgnoreMissing(); + $encoder->shouldReceive('getName') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $encoder; + } + + private function _getParameterEncoder($stub = false) + { + return $this->getMockery('Swift_Encoder')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php new file mode 100644 index 0000000..a9f35e9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/PathHeaderTest.php @@ -0,0 +1,77 @@ +_getHeader('Return-Path'); + $this->assertEquals(Swift_Mime_Header::TYPE_PATH, $header->getFieldType()); + } + + public function testSingleAddressCanBeSetAndFetched() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chris@swiftmailer.org'); + $this->assertEquals('chris@swiftmailer.org', $header->getAddress()); + } + + public function testAddressMustComplyWithRfc2822() + { + try { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chr is@swiftmailer.org'); + $this->fail('Addresses not valid according to RFC 2822 addr-spec grammar must be rejected.'); + } catch (Exception $e) { + } + } + + public function testValueIsAngleAddrWithValidAddress() + { + /* -- RFC 2822, 3.6.7. + + return = "Return-Path:" path CRLF + + path = ([CFWS] "<" ([CFWS] / addr-spec) ">" [CFWS]) / + obs-path + */ + + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chris@swiftmailer.org'); + $this->assertEquals('', $header->getFieldBody()); + } + + public function testValueIsEmptyAngleBracketsIfEmptyAddressSet() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress(''); + $this->assertEquals('<>', $header->getFieldBody()); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Return-Path'); + $header->setFieldBodyModel('foo@bar.tld'); + $this->assertEquals('foo@bar.tld', $header->getAddress()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('foo@bar.tld'); + $this->assertEquals('foo@bar.tld', $header->getFieldBodyModel()); + } + + public function testToString() + { + $header = $this->_getHeader('Return-Path'); + $header->setAddress('chris@swiftmailer.org'); + $this->assertEquals('Return-Path: '."\r\n", + $header->toString() + ); + } + + private function _getHeader($name) + { + return new Swift_Mime_Headers_PathHeader($name, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php new file mode 100644 index 0000000..2e1dc8c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/Headers/UnstructuredHeaderTest.php @@ -0,0 +1,355 @@ +_getHeader('Subject', $this->_getEncoder('Q', true)); + $this->assertEquals(Swift_Mime_Header::TYPE_TEXT, $header->getFieldType()); + } + + public function testGetNameReturnsNameVerbatim() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $this->assertEquals('Subject', $header->getFieldName()); + } + + public function testGetValueReturnsValueVerbatim() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setValue('Test'); + $this->assertEquals('Test', $header->getValue()); + } + + public function testBasicStructureIsKeyValuePair() + { + /* -- RFC 2822, 2.2 + Header fields are lines composed of a field name, followed by a colon + (":"), followed by a field body, and terminated by CRLF. + */ + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setValue('Test'); + $this->assertEquals('Subject: Test'."\r\n", $header->toString()); + } + + public function testLongHeadersAreFoldedAtWordBoundary() + { + /* -- RFC 2822, 2.2.3 + Each header field is logically a single line of characters comprising + the field name, the colon, and the field body. For convenience + however, and to deal with the 998/78 character limitations per line, + the field body portion of a header field can be split into a multiple + line representation; this is called "folding". The general rule is + that wherever this standard allows for folding white space (not + simply WSP characters), a CRLF may be inserted before any WSP. + */ + + $value = 'The quick brown fox jumped over the fence, he was a very very '. + 'scary brown fox with a bushy tail'; + $header = $this->_getHeader('X-Custom-Header', + $this->_getEncoder('Q', true) + ); + $header->setValue($value); + $header->setMaxLineLength(78); //A safe [RFC 2822, 2.2.3] default + /* + X-Custom-Header: The quick brown fox jumped over the fence, he was a very very + scary brown fox with a bushy tail + */ + $this->assertEquals( + 'X-Custom-Header: The quick brown fox jumped over the fence, he was a'. + ' very very'."\r\n".//Folding + ' scary brown fox with a bushy tail'."\r\n", + $header->toString(), '%s: The header should have been folded at 78th char' + ); + } + + public function testPrintableAsciiOnlyAppearsInHeaders() + { + /* -- RFC 2822, 2.2. + A field name MUST be composed of printable US-ASCII characters (i.e., + characters that have values between 33 and 126, inclusive), except + colon. A field body may be composed of any US-ASCII characters, + except for CR and LF. + */ + + $nonAsciiChar = pack('C', 0x8F); + $header = $this->_getHeader('X-Test', $this->_getEncoder('Q', true)); + $header->setValue($nonAsciiChar); + $this->assertRegExp( + '~^[^:\x00-\x20\x80-\xFF]+: [^\x80-\xFF\r\n]+\r\n$~s', + $header->toString() + ); + } + + public function testEncodedWordsFollowGeneralStructure() + { + /* -- RFC 2047, 1. + Generally, an "encoded-word" is a sequence of printable ASCII + characters that begins with "=?", ends with "?=", and has two "?"s in + between. + */ + + $nonAsciiChar = pack('C', 0x8F); + $header = $this->_getHeader('X-Test', $this->_getEncoder('Q', true)); + $header->setValue($nonAsciiChar); + $this->assertRegExp( + '~^X-Test: \=?.*?\?.*?\?.*?\?=\r\n$~s', + $header->toString() + ); + } + + public function testEncodedWordIncludesCharsetAndEncodingMethodAndText() + { + /* -- RFC 2047, 2. + An 'encoded-word' is defined by the following ABNF grammar. The + notation of RFC 822 is used, with the exception that white space + characters MUST NOT appear between components of an 'encoded-word'. + + encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" + */ + + $nonAsciiChar = pack('C', 0x8F); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($nonAsciiChar, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('=8F'); + + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($nonAsciiChar); + $this->assertEquals( + 'X-Test: =?'.$this->_charset.'?Q?=8F?='."\r\n", + $header->toString() + ); + } + + public function testEncodedWordsAreUsedToEncodedNonPrintableAscii() + { + //SPACE and TAB permitted + $nonPrintableBytes = array_merge( + range(0x00, 0x08), range(0x10, 0x19), array(0x7F) + ); + + foreach ($nonPrintableBytes as $byte) { + $char = pack('C', $byte); + $encodedChar = sprintf('=%02X', $byte); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($char, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn($encodedChar); + + $header = $this->_getHeader('X-A', $encoder); + $header->setValue($char); + + $this->assertEquals( + 'X-A: =?'.$this->_charset.'?Q?'.$encodedChar.'?='."\r\n", + $header->toString(), '%s: Non-printable ascii should be encoded' + ); + } + } + + public function testEncodedWordsAreUsedToEncode8BitOctets() + { + $_8BitBytes = range(0x80, 0xFF); + + foreach ($_8BitBytes as $byte) { + $char = pack('C', $byte); + $encodedChar = sprintf('=%02X', $byte); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($char, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn($encodedChar); + + $header = $this->_getHeader('X-A', $encoder); + $header->setValue($char); + + $this->assertEquals( + 'X-A: =?'.$this->_charset.'?Q?'.$encodedChar.'?='."\r\n", + $header->toString(), '%s: 8-bit octets should be encoded' + ); + } + } + + public function testEncodedWordsAreNoMoreThan75CharsPerLine() + { + /* -- RFC 2047, 2. + An 'encoded-word' may not be more than 75 characters long, including + 'charset', 'encoding', 'encoded-text', and delimiters. + + ... SNIP ... + + While there is no limit to the length of a multiple-line header + field, each line of a header field that contains one or more + 'encoded-word's is limited to 76 characters. + */ + + $nonAsciiChar = pack('C', 0x8F); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($nonAsciiChar, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('=8F'); + //Note that multi-line headers begin with LWSP which makes 75 + 1 = 76 + //Note also that =?utf-8?q??= is 12 chars which makes 75 - 12 = 63 + + //* X-Test: is 8 chars + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($nonAsciiChar); + + $this->assertEquals( + 'X-Test: =?'.$this->_charset.'?Q?=8F?='."\r\n", + $header->toString() + ); + } + + public function testFWSPIsUsedWhenEncoderReturnsMultipleLines() + { + /* --RFC 2047, 2. + If it is desirable to encode more text than will fit in an 'encoded-word' of + 75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may + be used. + */ + + //Note the Mock does NOT return 8F encoded, the 8F merely triggers + // encoding for the sake of testing + $nonAsciiChar = pack('C', 0x8F); + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($nonAsciiChar, 8, 63, \Mockery::any()) + ->andReturn('line_one_here'."\r\n".'line_two_here'); + + //Note that multi-line headers begin with LWSP which makes 75 + 1 = 76 + //Note also that =?utf-8?q??= is 12 chars which makes 75 - 12 = 63 + + //* X-Test: is 8 chars + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($nonAsciiChar); + + $this->assertEquals( + 'X-Test: =?'.$this->_charset.'?Q?line_one_here?='."\r\n". + ' =?'.$this->_charset.'?Q?line_two_here?='."\r\n", + $header->toString() + ); + } + + public function testAdjacentWordsAreEncodedTogether() + { + /* -- RFC 2047, 5 (1) + Ordinary ASCII text and 'encoded-word's may appear together in the + same header field. However, an 'encoded-word' that appears in a + header field defined as '*text' MUST be separated from any adjacent + 'encoded-word' or 'text' by 'linear-white-space'. + + -- RFC 2047, 2. + IMPORTANT: 'encoded-word's are designed to be recognized as 'atom's + by an RFC 822 parser. As a consequence, unencoded white space + characters (such as SPACE and HTAB) are FORBIDDEN within an + 'encoded-word'. + */ + + //It would be valid to encode all words needed, however it's probably + // easiest to encode the longest amount required at a time + + $word = 'w'.pack('C', 0x8F).'rd'; + $text = 'start '.$word.' '.$word.' then end '.$word; + // 'start', ' word word', ' and end', ' word' + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($word.' '.$word, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('w=8Frd_w=8Frd'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($word, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('w=8Frd'); + + $header = $this->_getHeader('X-Test', $encoder); + $header->setValue($text); + + $headerString = $header->toString(); + + $this->assertEquals('X-Test: start =?'.$this->_charset.'?Q?'. + 'w=8Frd_w=8Frd?= then end =?'.$this->_charset.'?Q?'. + 'w=8Frd?='."\r\n", $headerString, + '%s: Adjacent encoded words should appear grouped with WSP encoded' + ); + } + + public function testLanguageInformationAppearsInEncodedWords() + { + /* -- RFC 2231, 5. + 5. Language specification in Encoded Words + + RFC 2047 provides support for non-US-ASCII character sets in RFC 822 + message header comments, phrases, and any unstructured text field. + This is done by defining an encoded word construct which can appear + in any of these places. Given that these are fields intended for + display, it is sometimes necessary to associate language information + with encoded words as well as just the character set. This + specification extends the definition of an encoded word to allow the + inclusion of such information. This is simply done by suffixing the + character set specification with an asterisk followed by the language + tag. For example: + + From: =?US-ASCII*EN?Q?Keith_Moore?= + */ + + $value = 'fo'.pack('C', 0x8F).'bar'; + + $encoder = $this->_getEncoder('Q'); + $encoder->shouldReceive('encodeString') + ->once() + ->with($value, \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn('fo=8Fbar'); + + $header = $this->_getHeader('Subject', $encoder); + $header->setLanguage('en'); + $header->setValue($value); + $this->assertEquals("Subject: =?utf-8*en?Q?fo=8Fbar?=\r\n", + $header->toString() + ); + } + + public function testSetBodyModel() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setFieldBodyModel('test'); + $this->assertEquals('test', $header->getValue()); + } + + public function testGetBodyModel() + { + $header = $this->_getHeader('Subject', $this->_getEncoder('Q', true)); + $header->setValue('test'); + $this->assertEquals('test', $header->getFieldBodyModel()); + } + + private function _getHeader($name, $encoder) + { + $header = new Swift_Mime_Headers_UnstructuredHeader($name, $encoder, new Swift_Mime_Grammar()); + $header->setCharset($this->_charset); + + return $header; + } + + private function _getEncoder($type, $stub = false) + { + $encoder = $this->getMockery('Swift_Mime_HeaderEncoder')->shouldIgnoreMissing(); + $encoder->shouldReceive('getName') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $encoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php new file mode 100644 index 0000000..af69088 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/MimePartTest.php @@ -0,0 +1,233 @@ +_createMimePart($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE, $part->getNestingLevel() + ); + } + + public function testCharsetIsReturnedFromHeader() + { + /* -- RFC 2046, 4.1.2. + A critical parameter that may be specified in the Content-Type field + for "text/plain" data is the character set. This is specified with a + "charset" parameter, as in: + + Content-type: text/plain; charset=iso-8859-1 + + Unlike some other parameter values, the values of the charset + parameter are NOT case sensitive. The default character set, which + must be assumed in the absence of a charset parameter, is US-ASCII. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1') + ); + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('iso-8859-1', $part->getCharset()); + } + + public function testCharsetIsSetInHeader() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1'), false + ); + $cType->shouldReceive('setParameter')->once()->with('charset', 'utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setCharset('utf-8'); + } + + public function testCharsetIsSetInHeaderIfPassedToSetBody() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1'), false + ); + $cType->shouldReceive('setParameter')->once()->with('charset', 'utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setBody('', 'text/plian', 'utf-8'); + } + + public function testSettingCharsetNotifiesEncoder() + { + $encoder = $this->_createEncoder('quoted-printable', false); + $encoder->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(), + $encoder, $this->_createCache() + ); + $part->setCharset('utf-8'); + } + + public function testSettingCharsetNotifiesHeaders() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('charsetChanged') + ->zeroOrMoreTimes() + ->with('utf-8'); + + $part = $this->_createMimePart($headers, $this->_createEncoder(), + $this->_createCache() + ); + $part->setCharset('utf-8'); + } + + public function testSettingCharsetNotifiesChildren() + { + $child = $this->_createChild(0, '', false); + $child->shouldReceive('charsetChanged') + ->once() + ->with('windows-874'); + + $part = $this->_createMimePart($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $part->setChildren(array($child)); + $part->setCharset('windows-874'); + } + + public function testCharsetChangeUpdatesCharset() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('charset' => 'iso-8859-1'), false + ); + $cType->shouldReceive('setParameter')->once()->with('charset', 'utf-8'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->charsetChanged('utf-8'); + } + + public function testSettingCharsetClearsCache() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn("Content-Type: text/plain; charset=utf-8\r\n"); + + $cache = $this->_createCache(false); + + $entity = $this->_createEntity($headers, $this->_createEncoder(), + $cache + ); + + $entity->setBody("blah\r\nblah!"); + $entity->toString(); + + // Initialize the expectation here because we only care about what happens in setCharset() + $cache->shouldReceive('clearKey') + ->once() + ->with(\Mockery::any(), 'body'); + + $entity->setCharset('iso-2022'); + } + + public function testFormatIsReturnedFromHeader() + { + /* -- RFC 3676. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('format' => 'flowed') + ); + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('flowed', $part->getFormat()); + } + + public function testFormatIsSetInHeader() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $cType->shouldReceive('setParameter')->once()->with('format', 'fixed'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setFormat('fixed'); + } + + public function testDelSpIsReturnedFromHeader() + { + /* -- RFC 3676. + */ + + $cType = $this->_createHeader('Content-Type', 'text/plain', + array('delsp' => 'no') + ); + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertSame(false, $part->getDelSp()); + } + + public function testDelSpIsSetInHeader() + { + $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false); + $cType->shouldReceive('setParameter')->once()->with('delsp', 'yes'); + + $part = $this->_createMimePart($this->_createHeaderSet(array( + 'Content-Type' => $cType, )), + $this->_createEncoder(), $this->_createCache() + ); + $part->setDelSp(true); + } + + public function testFluidInterface() + { + $part = $this->_createMimePart($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $this->assertSame($part, + $part + ->setContentType('text/plain') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my description') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ->setCharset('utf-8') + ->setFormat('flowed') + ->setDelSp(true) + ); + } + + // -- Private helpers + + //abstract + protected function _createEntity($headers, $encoder, $cache) + { + return $this->_createMimePart($headers, $encoder, $cache); + } + + protected function _createMimePart($headers, $encoder, $cache) + { + return new Swift_Mime_MimePart($headers, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php new file mode 100644 index 0000000..0d5573f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderFactoryTest.php @@ -0,0 +1,168 @@ +_factory = $this->_createFactory(); + } + + public function testMailboxHeaderIsCorrectType() + { + $header = $this->_factory->createMailboxHeader('X-Foo'); + $this->assertInstanceof('Swift_Mime_Headers_MailboxHeader', $header); + } + + public function testMailboxHeaderHasCorrectName() + { + $header = $this->_factory->createMailboxHeader('X-Foo'); + $this->assertEquals('X-Foo', $header->getFieldName()); + } + + public function testMailboxHeaderHasCorrectModel() + { + $header = $this->_factory->createMailboxHeader('X-Foo', + array('foo@bar' => 'FooBar') + ); + $this->assertEquals(array('foo@bar' => 'FooBar'), $header->getFieldBodyModel()); + } + + public function testDateHeaderHasCorrectType() + { + $header = $this->_factory->createDateHeader('X-Date'); + $this->assertInstanceof('Swift_Mime_Headers_DateHeader', $header); + } + + public function testDateHeaderHasCorrectName() + { + $header = $this->_factory->createDateHeader('X-Date'); + $this->assertEquals('X-Date', $header->getFieldName()); + } + + public function testDateHeaderHasCorrectModel() + { + $header = $this->_factory->createDateHeader('X-Date', 123); + $this->assertEquals(123, $header->getFieldBodyModel()); + } + + public function testTextHeaderHasCorrectType() + { + $header = $this->_factory->createTextHeader('X-Foo'); + $this->assertInstanceof('Swift_Mime_Headers_UnstructuredHeader', $header); + } + + public function testTextHeaderHasCorrectName() + { + $header = $this->_factory->createTextHeader('X-Foo'); + $this->assertEquals('X-Foo', $header->getFieldName()); + } + + public function testTextHeaderHasCorrectModel() + { + $header = $this->_factory->createTextHeader('X-Foo', 'bar'); + $this->assertEquals('bar', $header->getFieldBodyModel()); + } + + public function testParameterizedHeaderHasCorrectType() + { + $header = $this->_factory->createParameterizedHeader('X-Foo'); + $this->assertInstanceof('Swift_Mime_Headers_ParameterizedHeader', $header); + } + + public function testParameterizedHeaderHasCorrectName() + { + $header = $this->_factory->createParameterizedHeader('X-Foo'); + $this->assertEquals('X-Foo', $header->getFieldName()); + } + + public function testParameterizedHeaderHasCorrectModel() + { + $header = $this->_factory->createParameterizedHeader('X-Foo', 'bar'); + $this->assertEquals('bar', $header->getFieldBodyModel()); + } + + public function testParameterizedHeaderHasCorrectParams() + { + $header = $this->_factory->createParameterizedHeader('X-Foo', 'bar', + array('zip' => 'button') + ); + $this->assertEquals(array('zip' => 'button'), $header->getParameters()); + } + + public function testIdHeaderHasCorrectType() + { + $header = $this->_factory->createIdHeader('X-ID'); + $this->assertInstanceof('Swift_Mime_Headers_IdentificationHeader', $header); + } + + public function testIdHeaderHasCorrectName() + { + $header = $this->_factory->createIdHeader('X-ID'); + $this->assertEquals('X-ID', $header->getFieldName()); + } + + public function testIdHeaderHasCorrectModel() + { + $header = $this->_factory->createIdHeader('X-ID', 'xyz@abc'); + $this->assertEquals(array('xyz@abc'), $header->getFieldBodyModel()); + } + + public function testPathHeaderHasCorrectType() + { + $header = $this->_factory->createPathHeader('X-Path'); + $this->assertInstanceof('Swift_Mime_Headers_PathHeader', $header); + } + + public function testPathHeaderHasCorrectName() + { + $header = $this->_factory->createPathHeader('X-Path'); + $this->assertEquals('X-Path', $header->getFieldName()); + } + + public function testPathHeaderHasCorrectModel() + { + $header = $this->_factory->createPathHeader('X-Path', 'foo@bar'); + $this->assertEquals('foo@bar', $header->getFieldBodyModel()); + } + + public function testCharsetChangeNotificationNotifiesEncoders() + { + $encoder = $this->_createHeaderEncoder(); + $encoder->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + $paramEncoder = $this->_createParamEncoder(); + $paramEncoder->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + + $factory = $this->_createFactory($encoder, $paramEncoder); + + $factory->charsetChanged('utf-8'); + } + + // -- Creation methods + + private function _createFactory($encoder = null, $paramEncoder = null) + { + return new Swift_Mime_SimpleHeaderFactory( + $encoder + ? $encoder : $this->_createHeaderEncoder(), + $paramEncoder + ? $paramEncoder : $this->_createParamEncoder(), + new Swift_Mime_Grammar() + ); + } + + private function _createHeaderEncoder() + { + return $this->getMock('Swift_Mime_HeaderEncoder'); + } + + private function _createParamEncoder() + { + return $this->getMock('Swift_Encoder'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php new file mode 100644 index 0000000..a781a08 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleHeaderSetTest.php @@ -0,0 +1,734 @@ +_createFactory(); + $factory->expects($this->once()) + ->method('createMailboxHeader') + ->with('From', array('person@domain' => 'Person')) + ->will($this->returnValue($this->_createHeader('From'))); + + $set = $this->_createSet($factory); + $set->addMailboxHeader('From', array('person@domain' => 'Person')); + } + + public function testAddDateHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createDateHeader') + ->with('Date', 1234) + ->will($this->returnValue($this->_createHeader('Date'))); + + $set = $this->_createSet($factory); + $set->addDateHeader('Date', 1234); + } + + public function testAddTextHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($this->_createHeader('Subject'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + } + + public function testAddParameterizedHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createParameterizedHeader') + ->with('Content-Type', 'text/plain', array('charset' => 'utf-8')) + ->will($this->returnValue($this->_createHeader('Content-Type'))); + + $set = $this->_createSet($factory); + $set->addParameterizedHeader('Content-Type', 'text/plain', + array('charset' => 'utf-8') + ); + } + + public function testAddIdHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + } + + public function testAddPathHeaderDelegatesToFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createPathHeader') + ->with('Return-Path', 'some@path') + ->will($this->returnValue($this->_createHeader('Return-Path'))); + + $set = $this->_createSet($factory); + $set->addPathHeader('Return-Path', 'some@path'); + } + + public function testHasReturnsFalseWhenNoHeaders() + { + $set = $this->_createSet($this->_createFactory()); + $this->assertFalse($set->has('Some-Header')); + } + + public function testAddedMailboxHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createMailboxHeader') + ->with('From', array('person@domain' => 'Person')) + ->will($this->returnValue($this->_createHeader('From'))); + + $set = $this->_createSet($factory); + $set->addMailboxHeader('From', array('person@domain' => 'Person')); + $this->assertTrue($set->has('From')); + } + + public function testAddedDateHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createDateHeader') + ->with('Date', 1234) + ->will($this->returnValue($this->_createHeader('Date'))); + + $set = $this->_createSet($factory); + $set->addDateHeader('Date', 1234); + $this->assertTrue($set->has('Date')); + } + + public function testAddedTextHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($this->_createHeader('Subject'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + $this->assertTrue($set->has('Subject')); + } + + public function testAddedParameterizedHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createParameterizedHeader') + ->with('Content-Type', 'text/plain', array('charset' => 'utf-8')) + ->will($this->returnValue($this->_createHeader('Content-Type'))); + + $set = $this->_createSet($factory); + $set->addParameterizedHeader('Content-Type', 'text/plain', + array('charset' => 'utf-8') + ); + $this->assertTrue($set->has('Content-Type')); + } + + public function testAddedIdHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertTrue($set->has('Message-ID')); + } + + public function testAddedPathHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createPathHeader') + ->with('Return-Path', 'some@path') + ->will($this->returnValue($this->_createHeader('Return-Path'))); + + $set = $this->_createSet($factory); + $set->addPathHeader('Return-Path', 'some@path'); + $this->assertTrue($set->has('Return-Path')); + } + + public function testNewlySetHeaderIsSeenByHas() + { + $factory = $this->_createFactory(); + $header = $this->_createHeader('X-Foo', 'bar'); + $set = $this->_createSet($factory); + $set->set($header); + $this->assertTrue($set->has('X-Foo')); + } + + public function testHasCanAcceptOffset() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertTrue($set->has('Message-ID', 0)); + } + + public function testHasWithIllegalOffsetReturnsFalse() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertFalse($set->has('Message-ID', 1)); + } + + public function testHasCanDistinguishMultipleHeaders() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($this->_createHeader('Message-ID'))); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $this->assertTrue($set->has('Message-ID', 1)); + } + + public function testGetWithUnspecifiedOffset() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertSame($header, $set->get('Message-ID')); + } + + public function testGetWithSpeiciedOffset() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $header2 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + $factory->expects($this->at(2)) + ->method('createIdHeader') + ->with('Message-ID', 'more@id') + ->will($this->returnValue($header2)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->addIdHeader('Message-ID', 'more@id'); + $this->assertSame($header1, $set->get('Message-ID', 1)); + } + + public function testGetReturnsNullIfHeaderNotSet() + { + $set = $this->_createSet($this->_createFactory()); + $this->assertNull($set->get('Message-ID', 99)); + } + + public function testGetAllReturnsAllHeadersMatchingName() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $header2 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + $factory->expects($this->at(2)) + ->method('createIdHeader') + ->with('Message-ID', 'more@id') + ->will($this->returnValue($header2)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->addIdHeader('Message-ID', 'more@id'); + + $this->assertEquals(array($header0, $header1, $header2), + $set->getAll('Message-ID') + ); + } + + public function testGetAllReturnsAllHeadersIfNoArguments() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Subject'); + $header2 = $this->_createHeader('To'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Subject', 'thing') + ->will($this->returnValue($header1)); + $factory->expects($this->at(2)) + ->method('createIdHeader') + ->with('To', 'person@example.org') + ->will($this->returnValue($header2)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Subject', 'thing'); + $set->addIdHeader('To', 'person@example.org'); + + $this->assertEquals(array($header0, $header1, $header2), + $set->getAll() + ); + } + + public function testGetAllReturnsEmptyArrayIfNoneSet() + { + $set = $this->_createSet($this->_createFactory()); + $this->assertEquals(array(), $set->getAll('Received')); + } + + public function testRemoveWithUnspecifiedOffset() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->remove('Message-ID'); + $this->assertFalse($set->has('Message-ID')); + } + + public function testRemoveWithSpecifiedIndexRemovesHeader() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->remove('Message-ID', 1); + $this->assertFalse($set->has('Message-ID', 1)); + } + + public function testRemoveWithSpecifiedIndexLeavesOtherHeaders() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->remove('Message-ID', 1); + $this->assertTrue($set->has('Message-ID', 0)); + } + + public function testRemoveWithInvalidOffsetDoesNothing() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->remove('Message-ID', 50); + $this->assertTrue($set->has('Message-ID')); + } + + public function testRemoveAllRemovesAllHeadersWithName() + { + $header0 = $this->_createHeader('Message-ID'); + $header1 = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header0)); + $factory->expects($this->at(1)) + ->method('createIdHeader') + ->with('Message-ID', 'other@id') + ->will($this->returnValue($header1)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->addIdHeader('Message-ID', 'other@id'); + $set->removeAll('Message-ID'); + $this->assertFalse($set->has('Message-ID', 0)); + $this->assertFalse($set->has('Message-ID', 1)); + } + + public function testHasIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertTrue($set->has('message-id')); + } + + public function testGetIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertSame($header, $set->get('message-id')); + } + + public function testGetAllIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $this->assertEquals(array($header), $set->getAll('message-id')); + } + + public function testRemoveIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->remove('message-id'); + $this->assertFalse($set->has('Message-ID')); + } + + public function testRemoveAllIsNotCaseSensitive() + { + $header = $this->_createHeader('Message-ID'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createIdHeader') + ->with('Message-ID', 'some@id') + ->will($this->returnValue($header)); + + $set = $this->_createSet($factory); + $set->addIdHeader('Message-ID', 'some@id'); + $set->removeAll('message-id'); + $this->assertFalse($set->has('Message-ID')); + } + + public function testNewInstance() + { + $set = $this->_createSet($this->_createFactory()); + $instance = $set->newInstance(); + $this->assertInstanceof('Swift_Mime_HeaderSet', $instance); + } + + public function testToStringJoinsHeadersTogether() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Foo', 'bar') + ->will($this->returnValue($this->_createHeader('Foo', 'bar'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Zip', 'buttons') + ->will($this->returnValue($this->_createHeader('Zip', 'buttons'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Foo', 'bar'); + $set->addTextHeader('Zip', 'buttons'); + $this->assertEquals( + "Foo: bar\r\n". + "Zip: buttons\r\n", + $set->toString() + ); + } + + public function testHeadersWithoutBodiesAreNotDisplayed() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Foo', 'bar') + ->will($this->returnValue($this->_createHeader('Foo', 'bar'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Zip', '') + ->will($this->returnValue($this->_createHeader('Zip', ''))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Foo', 'bar'); + $set->addTextHeader('Zip', ''); + $this->assertEquals( + "Foo: bar\r\n", + $set->toString() + ); + } + + public function testHeadersWithoutBodiesCanBeForcedToDisplay() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Foo', '') + ->will($this->returnValue($this->_createHeader('Foo', ''))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Zip', '') + ->will($this->returnValue($this->_createHeader('Zip', ''))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Foo', ''); + $set->addTextHeader('Zip', ''); + $set->setAlwaysDisplayed(array('Foo', 'Zip')); + $this->assertEquals( + "Foo: \r\n". + "Zip: \r\n", + $set->toString() + ); + } + + public function testHeaderSequencesCanBeSpecified() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Third', 'three') + ->will($this->returnValue($this->_createHeader('Third', 'three'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('First', 'one') + ->will($this->returnValue($this->_createHeader('First', 'one'))); + $factory->expects($this->at(2)) + ->method('createTextHeader') + ->with('Second', 'two') + ->will($this->returnValue($this->_createHeader('Second', 'two'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Third', 'three'); + $set->addTextHeader('First', 'one'); + $set->addTextHeader('Second', 'two'); + + $set->defineOrdering(array('First', 'Second', 'Third')); + + $this->assertEquals( + "First: one\r\n". + "Second: two\r\n". + "Third: three\r\n", + $set->toString() + ); + } + + public function testUnsortedHeadersAppearAtEnd() + { + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Fourth', 'four') + ->will($this->returnValue($this->_createHeader('Fourth', 'four'))); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('Fifth', 'five') + ->will($this->returnValue($this->_createHeader('Fifth', 'five'))); + $factory->expects($this->at(2)) + ->method('createTextHeader') + ->with('Third', 'three') + ->will($this->returnValue($this->_createHeader('Third', 'three'))); + $factory->expects($this->at(3)) + ->method('createTextHeader') + ->with('First', 'one') + ->will($this->returnValue($this->_createHeader('First', 'one'))); + $factory->expects($this->at(4)) + ->method('createTextHeader') + ->with('Second', 'two') + ->will($this->returnValue($this->_createHeader('Second', 'two'))); + + $set = $this->_createSet($factory); + $set->addTextHeader('Fourth', 'four'); + $set->addTextHeader('Fifth', 'five'); + $set->addTextHeader('Third', 'three'); + $set->addTextHeader('First', 'one'); + $set->addTextHeader('Second', 'two'); + + $set->defineOrdering(array('First', 'Second', 'Third')); + + $this->assertEquals( + "First: one\r\n". + "Second: two\r\n". + "Third: three\r\n". + "Fourth: four\r\n". + "Fifth: five\r\n", + $set->toString() + ); + } + + public function testSettingCharsetNotifiesAlreadyExistingHeaders() + { + $subject = $this->_createHeader('Subject', 'some text'); + $xHeader = $this->_createHeader('X-Header', 'some text'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($subject)); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('X-Header', 'some text') + ->will($this->returnValue($xHeader)); + $subject->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + $xHeader->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + $set->addTextHeader('X-Header', 'some text'); + + $set->setCharset('utf-8'); + } + + public function testCharsetChangeNotifiesAlreadyExistingHeaders() + { + $subject = $this->_createHeader('Subject', 'some text'); + $xHeader = $this->_createHeader('X-Header', 'some text'); + $factory = $this->_createFactory(); + $factory->expects($this->at(0)) + ->method('createTextHeader') + ->with('Subject', 'some text') + ->will($this->returnValue($subject)); + $factory->expects($this->at(1)) + ->method('createTextHeader') + ->with('X-Header', 'some text') + ->will($this->returnValue($xHeader)); + $subject->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + $xHeader->expects($this->once()) + ->method('setCharset') + ->with('utf-8'); + + $set = $this->_createSet($factory); + $set->addTextHeader('Subject', 'some text'); + $set->addTextHeader('X-Header', 'some text'); + + $set->charsetChanged('utf-8'); + } + + public function testCharsetChangeNotifiesFactory() + { + $factory = $this->_createFactory(); + $factory->expects($this->once()) + ->method('charsetChanged') + ->with('utf-8'); + + $set = $this->_createSet($factory); + + $set->setCharset('utf-8'); + } + + // -- Creation methods + + private function _createSet($factory) + { + return new Swift_Mime_SimpleHeaderSet($factory); + } + + private function _createFactory() + { + return $this->getMock('Swift_Mime_HeaderFactory'); + } + + private function _createHeader($name, $body = '') + { + $header = $this->getMock('Swift_Mime_Header'); + $header->expects($this->any()) + ->method('getFieldName') + ->will($this->returnValue($name)); + $header->expects($this->any()) + ->method('toString') + ->will($this->returnValue(sprintf("%s: %s\r\n", $name, $body))); + $header->expects($this->any()) + ->method('getFieldBody') + ->will($this->returnValue($body)); + + return $header; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php new file mode 100644 index 0000000..a2cbbca --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMessageTest.php @@ -0,0 +1,829 @@ +_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals( + Swift_Mime_MimeEntity::LEVEL_TOP, $message->getNestingLevel() + ); + } + + public function testDateIsReturnedFromHeader() + { + $date = $this->_createHeader('Date', 123); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Date' => $date)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(123, $message->getDate()); + } + + public function testDateIsSetInHeader() + { + $date = $this->_createHeader('Date', 123, array(), false); + $date->shouldReceive('setFieldBodyModel') + ->once() + ->with(1234); + $date->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Date' => $date)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setDate(1234); + } + + public function testDateHeaderIsCreatedIfNonePresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addDateHeader') + ->once() + ->with('Date', 1234); + $headers->shouldReceive('addDateHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setDate(1234); + } + + public function testDateHeaderIsAddedDuringConstruction() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addDateHeader') + ->once() + ->with('Date', '/^[0-9]+$/D'); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testIdIsReturnedFromHeader() + { + /* -- RFC 2045, 7. + In constructing a high-level user agent, it may be desirable to allow + one body to make reference to another. Accordingly, bodies may be + labelled using the "Content-ID" header field, which is syntactically + identical to the "Message-ID" header field + */ + + $messageId = $this->_createHeader('Message-ID', 'a@b'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Message-ID' => $messageId)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('a@b', $message->getId()); + } + + public function testIdIsSetInHeader() + { + $messageId = $this->_createHeader('Message-ID', 'a@b', array(), false); + $messageId->shouldReceive('setFieldBodyModel') + ->once() + ->with('x@y'); + $messageId->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Message-ID' => $messageId)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setId('x@y'); + } + + public function testIdIsAutoGenerated() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addIdHeader') + ->once() + ->with('Message-ID', '/^.*?@.*?$/D'); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + } + + public function testSubjectIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.5. + */ + + $subject = $this->_createHeader('Subject', 'example subject'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Subject' => $subject)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('example subject', $message->getSubject()); + } + + public function testSubjectIsSetInHeader() + { + $subject = $this->_createHeader('Subject', '', array(), false); + $subject->shouldReceive('setFieldBodyModel') + ->once() + ->with('foo'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Subject' => $subject)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setSubject('foo'); + } + + public function testSubjectHeaderIsCreatedIfNotPresent() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('Subject', 'example subject'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setSubject('example subject'); + } + + public function testReturnPathIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.7. + */ + + $path = $this->_createHeader('Return-Path', 'bounces@domain'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Return-Path' => $path)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals('bounces@domain', $message->getReturnPath()); + } + + public function testReturnPathIsSetInHeader() + { + $path = $this->_createHeader('Return-Path', '', array(), false); + $path->shouldReceive('setFieldBodyModel') + ->once() + ->with('bounces@domain'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Return-Path' => $path)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setReturnPath('bounces@domain'); + } + + public function testReturnPathHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addPathHeader') + ->once() + ->with('Return-Path', 'bounces@domain'); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReturnPath('bounces@domain'); + } + + public function testSenderIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.2. + */ + + $sender = $this->_createHeader('Sender', array('sender@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Sender' => $sender)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('sender@domain' => 'Name'), $message->getSender()); + } + + public function testSenderIsSetInHeader() + { + $sender = $this->_createHeader('Sender', array('sender@domain' => 'Name'), + array(), false + ); + $sender->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Sender' => $sender)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setSender(array('other@domain' => 'Other')); + } + + public function testSenderHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Sender', (array) 'sender@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setSender('sender@domain'); + } + + public function testNameCanBeUsedInSenderHeader() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Sender', array('sender@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setSender('sender@domain', 'Name'); + } + + public function testFromIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.2. + */ + + $from = $this->_createHeader('From', array('from@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('From' => $from)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('from@domain' => 'Name'), $message->getFrom()); + } + + public function testFromIsSetInHeader() + { + $from = $this->_createHeader('From', array('from@domain' => 'Name'), + array(), false + ); + $from->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('From' => $from)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setFrom(array('other@domain' => 'Other')); + } + + public function testFromIsAddedToHeadersDuringAddFrom() + { + $from = $this->_createHeader('From', array('from@domain' => 'Name'), + array(), false + ); + $from->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('From' => $from)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addFrom('other@domain', 'Other'); + } + + public function testFromHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('From', (array) 'from@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setFrom('from@domain'); + } + + public function testPersonalNameCanBeUsedInFromAddress() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('From', array('from@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setFrom('from@domain', 'Name'); + } + + public function testReplyToIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.2. + */ + + $reply = $this->_createHeader('Reply-To', array('reply@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Reply-To' => $reply)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('reply@domain' => 'Name'), $message->getReplyTo()); + } + + public function testReplyToIsSetInHeader() + { + $reply = $this->_createHeader('Reply-To', array('reply@domain' => 'Name'), + array(), false + ); + $reply->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Reply-To' => $reply)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setReplyTo(array('other@domain' => 'Other')); + } + + public function testReplyToIsAddedToHeadersDuringAddReplyTo() + { + $replyTo = $this->_createHeader('Reply-To', array('from@domain' => 'Name'), + array(), false + ); + $replyTo->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Reply-To' => $replyTo)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addReplyTo('other@domain', 'Other'); + } + + public function testReplyToHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Reply-To', (array) 'reply@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReplyTo('reply@domain'); + } + + public function testNameCanBeUsedInReplyTo() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Reply-To', array('reply@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReplyTo('reply@domain', 'Name'); + } + + public function testToIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.3. + */ + + $to = $this->_createHeader('To', array('to@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('To' => $to)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('to@domain' => 'Name'), $message->getTo()); + } + + public function testToIsSetInHeader() + { + $to = $this->_createHeader('To', array('to@domain' => 'Name'), + array(), false + ); + $to->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('To' => $to)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setTo(array('other@domain' => 'Other')); + } + + public function testToIsAddedToHeadersDuringAddTo() + { + $to = $this->_createHeader('To', array('from@domain' => 'Name'), + array(), false + ); + $to->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('To' => $to)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addTo('other@domain', 'Other'); + } + + public function testToHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('To', (array) 'to@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setTo('to@domain'); + } + + public function testNameCanBeUsedInToHeader() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('To', array('to@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setTo('to@domain', 'Name'); + } + + public function testCcIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.3. + */ + + $cc = $this->_createHeader('Cc', array('cc@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Cc' => $cc)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('cc@domain' => 'Name'), $message->getCc()); + } + + public function testCcIsSetInHeader() + { + $cc = $this->_createHeader('Cc', array('cc@domain' => 'Name'), + array(), false + ); + $cc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Cc' => $cc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setCc(array('other@domain' => 'Other')); + } + + public function testCcIsAddedToHeadersDuringAddCc() + { + $cc = $this->_createHeader('Cc', array('from@domain' => 'Name'), + array(), false + ); + $cc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Cc' => $cc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addCc('other@domain', 'Other'); + } + + public function testCcHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Cc', (array) 'cc@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setCc('cc@domain'); + } + + public function testNameCanBeUsedInCcHeader() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Cc', array('cc@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setCc('cc@domain', 'Name'); + } + + public function testBccIsReturnedFromHeader() + { + /* -- RFC 2822, 3.6.3. + */ + + $bcc = $this->_createHeader('Bcc', array('bcc@domain' => 'Name')); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Bcc' => $bcc)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('bcc@domain' => 'Name'), $message->getBcc()); + } + + public function testBccIsSetInHeader() + { + $bcc = $this->_createHeader('Bcc', array('bcc@domain' => 'Name'), + array(), false + ); + $bcc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Bcc' => $bcc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setBcc(array('other@domain' => 'Other')); + } + + public function testBccIsAddedToHeadersDuringAddBcc() + { + $bcc = $this->_createHeader('Bcc', array('from@domain' => 'Name'), + array(), false + ); + $bcc->shouldReceive('setFieldBodyModel') + ->once() + ->with(array('from@domain' => 'Name', 'other@domain' => 'Other')); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Bcc' => $bcc)), + $this->_createEncoder(), $this->_createCache() + ); + $message->addBcc('other@domain', 'Other'); + } + + public function testBccHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Bcc', (array) 'bcc@domain'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setBcc('bcc@domain'); + } + + public function testNameCanBeUsedInBcc() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Bcc', array('bcc@domain' => 'Name')); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setBcc('bcc@domain', 'Name'); + } + + public function testPriorityIsReadFromHeader() + { + $prio = $this->_createHeader('X-Priority', '2 (High)'); + $message = $this->_createMessage( + $this->_createHeaderSet(array('X-Priority' => $prio)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(2, $message->getPriority()); + } + + public function testPriorityIsSetInHeader() + { + $prio = $this->_createHeader('X-Priority', '2 (High)', array(), false); + $prio->shouldReceive('setFieldBodyModel') + ->once() + ->with('5 (Lowest)'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('X-Priority' => $prio)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setPriority(5); + } + + public function testPriorityHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addTextHeader') + ->once() + ->with('X-Priority', '4 (Low)'); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setPriority(4); + } + + public function testReadReceiptAddressReadFromHeader() + { + $rcpt = $this->_createHeader('Disposition-Notification-To', + array('chris@swiftmailer.org' => 'Chris') + ); + $message = $this->_createMessage( + $this->_createHeaderSet(array('Disposition-Notification-To' => $rcpt)), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertEquals(array('chris@swiftmailer.org' => 'Chris'), + $message->getReadReceiptTo() + ); + } + + public function testReadReceiptIsSetInHeader() + { + $rcpt = $this->_createHeader('Disposition-Notification-To', array(), array(), false); + $rcpt->shouldReceive('setFieldBodyModel') + ->once() + ->with('mark@swiftmailer.org'); + + $message = $this->_createMessage( + $this->_createHeaderSet(array('Disposition-Notification-To' => $rcpt)), + $this->_createEncoder(), $this->_createCache() + ); + $message->setReadReceiptTo('mark@swiftmailer.org'); + } + + public function testReadReceiptHeaderIsAddedIfNoneSet() + { + $headers = $this->_createHeaderSet(array(), false); + $headers->shouldReceive('addMailboxHeader') + ->once() + ->with('Disposition-Notification-To', 'mark@swiftmailer.org'); + $headers->shouldReceive('addMailboxHeader') + ->zeroOrMoreTimes(); + + $message = $this->_createMessage($headers, $this->_createEncoder(), + $this->_createCache() + ); + $message->setReadReceiptTo('mark@swiftmailer.org'); + } + + public function testChildrenCanBeAttached() + { + $child1 = $this->_createChild(); + $child2 = $this->_createChild(); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $message->attach($child1); + $message->attach($child2); + + $this->assertEquals(array($child1, $child2), $message->getChildren()); + } + + public function testChildrenCanBeDetached() + { + $child1 = $this->_createChild(); + $child2 = $this->_createChild(); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $message->attach($child1); + $message->attach($child2); + + $message->detach($child1); + + $this->assertEquals(array($child2), $message->getChildren()); + } + + public function testEmbedAttachesChild() + { + $child = $this->_createChild(); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $message->embed($child); + + $this->assertEquals(array($child), $message->getChildren()); + } + + public function testEmbedReturnsValidCid() + { + $child = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_RELATED, '', + false + ); + $child->shouldReceive('getId') + ->zeroOrMoreTimes() + ->andReturn('foo@bar'); + + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + + $this->assertEquals('cid:foo@bar', $message->embed($child)); + } + + public function testFluidInterface() + { + $child = $this->_createChild(); + $message = $this->_createMessage($this->_createHeaderSet(), + $this->_createEncoder(), $this->_createCache() + ); + $this->assertSame($message, + $message + ->setContentType('text/plain') + ->setEncoder($this->_createEncoder()) + ->setId('foo@bar') + ->setDescription('my description') + ->setMaxLineLength(998) + ->setBody('xx') + ->setBoundary('xyz') + ->setChildren(array()) + ->setCharset('iso-8859-1') + ->setFormat('flowed') + ->setDelSp(false) + ->setSubject('subj') + ->setDate(123) + ->setReturnPath('foo@bar') + ->setSender('foo@bar') + ->setFrom(array('x@y' => 'XY')) + ->setReplyTo(array('ab@cd' => 'ABCD')) + ->setTo(array('chris@site.tld', 'mark@site.tld')) + ->setCc('john@somewhere.tld') + ->setBcc(array('one@site', 'two@site' => 'Two')) + ->setPriority(4) + ->setReadReceiptTo('a@b') + ->attach($child) + ->detach($child) + ); + } + + // -- Private helpers + + //abstract + protected function _createEntity($headers, $encoder, $cache) + { + return $this->_createMessage($headers, $encoder, $cache); + } + + protected function _createMimePart($headers, $encoder, $cache) + { + return $this->_createMessage($headers, $encoder, $cache); + } + + private function _createMessage($headers, $encoder, $cache) + { + return new Swift_Mime_SimpleMessage($headers, $encoder, $cache, new Swift_Mime_Grammar()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php new file mode 100644 index 0000000..b54d7f8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Mime/SimpleMimeEntityTest.php @@ -0,0 +1,11 @@ +assertEquals(10, $plugin->getThreshold()); + $plugin->setThreshold(100); + $this->assertEquals(100, $plugin->getThreshold()); + } + + public function testSleepTimeCanBeSetAndFetched() + { + $plugin = new Swift_Plugins_AntiFloodPlugin(10, 5); + $this->assertEquals(5, $plugin->getSleepTime()); + $plugin->setSleepTime(1); + $this->assertEquals(1, $plugin->getSleepTime()); + } + + public function testPluginStopsConnectionAfterThreshold() + { + $transport = $this->_createTransport(); + $transport->expects($this->once()) + ->method('start'); + $transport->expects($this->once()) + ->method('stop'); + + $evt = $this->_createSendEvent($transport); + + $plugin = new Swift_Plugins_AntiFloodPlugin(10); + for ($i = 0; $i < 12; $i++) { + $plugin->sendPerformed($evt); + } + } + + public function testPluginCanStopAndStartMultipleTimes() + { + $transport = $this->_createTransport(); + $transport->expects($this->exactly(5)) + ->method('start'); + $transport->expects($this->exactly(5)) + ->method('stop'); + + $evt = $this->_createSendEvent($transport); + + $plugin = new Swift_Plugins_AntiFloodPlugin(2); + for ($i = 0; $i < 11; $i++) { + $plugin->sendPerformed($evt); + } + } + + public function testPluginCanSleepDuringRestart() + { + $sleeper = $this->getMock('Swift_Plugins_Sleeper'); + $sleeper->expects($this->once()) + ->method('sleep') + ->with(10); + + $transport = $this->_createTransport(); + $transport->expects($this->once()) + ->method('start'); + $transport->expects($this->once()) + ->method('stop'); + + $evt = $this->_createSendEvent($transport); + + $plugin = new Swift_Plugins_AntiFloodPlugin(99, 10, $sleeper); + for ($i = 0; $i < 101; $i++) { + $plugin->sendPerformed($evt); + } + } + + // -- Creation Methods + + private function _createTransport() + { + return $this->getMock('Swift_Transport'); + } + + private function _createSendEvent($transport) + { + $evt = $this->getMockBuilder('Swift_Events_SendEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getSource') + ->will($this->returnValue($transport)); + $evt->expects($this->any()) + ->method('getTransport') + ->will($this->returnValue($transport)); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php new file mode 100644 index 0000000..f140993 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/BandwidthMonitorPluginTest.php @@ -0,0 +1,127 @@ +_monitor = new Swift_Plugins_BandwidthMonitorPlugin(); + } + + public function testBytesOutIncreasesWhenCommandsSent() + { + $evt = $this->_createCommandEvent("RCPT TO:\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(23, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(46, $this->_monitor->getBytesOut()); + } + + public function testBytesInIncreasesWhenResponsesReceived() + { + $evt = $this->_createResponseEvent("250 Ok\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(8, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(16, $this->_monitor->getBytesIn()); + } + + public function testCountersCanBeReset() + { + $evt = $this->_createResponseEvent("250 Ok\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(8, $this->_monitor->getBytesIn()); + $this->_monitor->responseReceived($evt); + $this->assertEquals(16, $this->_monitor->getBytesIn()); + + $evt = $this->_createCommandEvent("RCPT TO:\r\n"); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(23, $this->_monitor->getBytesOut()); + $this->_monitor->commandSent($evt); + $this->assertEquals(46, $this->_monitor->getBytesOut()); + + $this->_monitor->reset(); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->assertEquals(0, $this->_monitor->getBytesIn()); + } + + public function testBytesOutIncreasesAccordingToMessageLength() + { + $message = $this->_createMessageWithByteCount(6); + $evt = $this->_createSendEvent($message); + + $this->assertEquals(0, $this->_monitor->getBytesOut()); + $this->_monitor->sendPerformed($evt); + $this->assertEquals(6, $this->_monitor->getBytesOut()); + $this->_monitor->sendPerformed($evt); + $this->assertEquals(12, $this->_monitor->getBytesOut()); + } + + // -- Creation Methods + + private function _createSendEvent($message) + { + $evt = $this->getMockBuilder('Swift_Events_SendEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getMessage') + ->will($this->returnValue($message)); + + return $evt; + } + + private function _createCommandEvent($command) + { + $evt = $this->getMockBuilder('Swift_Events_CommandEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getCommand') + ->will($this->returnValue($command)); + + return $evt; + } + + private function _createResponseEvent($response) + { + $evt = $this->getMockBuilder('Swift_Events_ResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getResponse') + ->will($this->returnValue($response)); + + return $evt; + } + + private function _createMessageWithByteCount($bytes) + { + $this->_bytes = $bytes; + $msg = $this->getMock('Swift_Mime_Message'); + $msg->expects($this->any()) + ->method('toByteStream') + ->will($this->returnCallback(array($this, '_write'))); + /* $this->_checking(Expectations::create() + -> ignoring($msg)->toByteStream(any()) -> calls(array($this, '_write')) + ); */ + + return $msg; + } + + private $_bytes = 0; + public function _write($is) + { + for ($i = 0; $i < $this->_bytes; ++$i) { + $is->write('x'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php new file mode 100644 index 0000000..7f4cdef --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/DecoratorPluginTest.php @@ -0,0 +1,269 @@ +_createMessage( + $this->_createHeaders(), + array('zip@button.tld' => 'Zipathon'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'Subject', + 'Hello {name}, you are customer #{id}' + ); + $message->shouldReceive('setBody') + ->once() + ->with('Hello Zip, you are customer #456'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array('zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456')) + ); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsCanBeAppliedToSameMessageMultipleTimes() + { + $message = $this->_createMessage( + $this->_createHeaders(), + array('zip@button.tld' => 'Zipathon', 'foo@bar.tld' => 'Foo'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'Subject', + 'Hello {name}, you are customer #{id}' + ); + $message->shouldReceive('setBody') + ->once() + ->with('Hello Zip, you are customer #456'); + $message->shouldReceive('setBody') + ->once() + ->with('Hello {name}, you are customer #{id}'); + $message->shouldReceive('setBody') + ->once() + ->with('Hello Foo, you are customer #123'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array( + 'foo@bar.tld' => array('{name}' => 'Foo', '{id}' => '123'), + 'zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456'), + ) + ); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsCanBeMadeInHeaders() + { + $headers = $this->_createHeaders(array( + $returnPathHeader = $this->_createHeader('Return-Path', 'foo-{id}@swiftmailer.org'), + $toHeader = $this->_createHeader('Subject', 'A message for {name}!'), + )); + + $message = $this->_createMessage( + $headers, + array('zip@button.tld' => 'Zipathon'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'A message for {name}!', + 'Hello {name}, you are customer #{id}' + ); + + $message->shouldReceive('setBody') + ->once() + ->with('Hello Zip, you are customer #456'); + $toHeader->shouldReceive('setFieldBodyModel') + ->once() + ->with('A message for Zip!'); + $returnPathHeader->shouldReceive('setFieldBodyModel') + ->once() + ->with('foo-456@swiftmailer.org'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + $toHeader->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + $returnPathHeader->shouldReceive('setFieldBodyModel') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array('zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456')) + ); + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsAreMadeOnSubparts() + { + $part1 = $this->_createPart('text/plain', 'Your name is {name}?', '1@x'); + $part2 = $this->_createPart('text/html', 'Your name is {name}?', '2@x'); + $message = $this->_createMessage( + $this->_createHeaders(), + array('zip@button.tld' => 'Zipathon'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'A message for {name}!', + 'Subject' + ); + $message->shouldReceive('getChildren') + ->zeroOrMoreTimes() + ->andReturn(array($part1, $part2)); + $part1->shouldReceive('setBody') + ->once() + ->with('Your name is Zip?'); + $part2->shouldReceive('setBody') + ->once() + ->with('Your name is Zip?'); + $part1->shouldReceive('setBody') + ->zeroOrMoreTimes(); + $part2->shouldReceive('setBody') + ->zeroOrMoreTimes(); + + $plugin = $this->_createPlugin( + array('zip@button.tld' => array('{name}' => 'Zip', '{id}' => '456')) + ); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + public function testReplacementsCanBeTakenFromCustomReplacementsObject() + { + $message = $this->_createMessage( + $this->_createHeaders(), + array('foo@bar' => 'Foobar', 'zip@zap' => 'Zip zap'), + array('chris.corbyn@swiftmailer.org' => 'Chris'), + 'Subject', + 'Something {a}' + ); + + $replacements = $this->_createReplacements(); + + $message->shouldReceive('setBody') + ->once() + ->with('Something b'); + $message->shouldReceive('setBody') + ->once() + ->with('Something c'); + $message->shouldReceive('setBody') + ->zeroOrMoreTimes(); + $replacements->shouldReceive('getReplacementsFor') + ->once() + ->with('foo@bar') + ->andReturn(array('{a}' => 'b')); + $replacements->shouldReceive('getReplacementsFor') + ->once() + ->with('zip@zap') + ->andReturn(array('{a}' => 'c')); + + $plugin = $this->_createPlugin($replacements); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + + // -- Creation methods + + private function _createMessage($headers, $to = array(), $from = null, $subject = null, + $body = null) + { + $message = $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + foreach ($to as $addr => $name) { + $message->shouldReceive('getTo') + ->once() + ->andReturn(array($addr => $name)); + } + $message->shouldReceive('getHeaders') + ->zeroOrMoreTimes() + ->andReturn($headers); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn($from); + $message->shouldReceive('getSubject') + ->zeroOrMoreTimes() + ->andReturn($subject); + $message->shouldReceive('getBody') + ->zeroOrMoreTimes() + ->andReturn($body); + + return $message; + } + + private function _createPlugin($replacements) + { + return new Swift_Plugins_DecoratorPlugin($replacements); + } + + private function _createReplacements() + { + return $this->getMockery('Swift_Plugins_Decorator_Replacements')->shouldIgnoreMissing(); + } + + private function _createSendEvent(Swift_Mime_Message $message) + { + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $evt->shouldReceive('getMessage') + ->zeroOrMoreTimes() + ->andReturn($message); + + return $evt; + } + + private function _createPart($type, $body, $id) + { + $part = $this->getMockery('Swift_Mime_MimeEntity')->shouldIgnoreMissing(); + $part->shouldReceive('getContentType') + ->zeroOrMoreTimes() + ->andReturn($type); + $part->shouldReceive('getBody') + ->zeroOrMoreTimes() + ->andReturn($body); + $part->shouldReceive('getId') + ->zeroOrMoreTimes() + ->andReturn($id); + + return $part; + } + + private function _createHeaders($headers = array()) + { + $set = $this->getMockery('Swift_Mime_HeaderSet')->shouldIgnoreMissing(); + $set->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->andReturn($headers); + + foreach ($headers as $header) { + $set->set($header); + } + + return $set; + } + + private function _createHeader($name, $body = '') + { + $header = $this->getMockery('Swift_Mime_Header')->shouldIgnoreMissing(); + $header->shouldReceive('getFieldName') + ->zeroOrMoreTimes() + ->andReturn($name); + $header->shouldReceive('getFieldBodyModel') + ->zeroOrMoreTimes() + ->andReturn($body); + + return $header; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php new file mode 100644 index 0000000..bd1fd97 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/LoggerPluginTest.php @@ -0,0 +1,190 @@ +_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with('foo'); + + $plugin = $this->_createPlugin($logger); + $plugin->add('foo'); + } + + public function testLoggerDelegatesDumpingEntries() + { + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('dump') + ->will($this->returnValue('foobar')); + + $plugin = $this->_createPlugin($logger); + $this->assertEquals('foobar', $plugin->dump()); + } + + public function testLoggerDelegatesClearingEntries() + { + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('clear'); + + $plugin = $this->_createPlugin($logger); + $plugin->clear(); + } + + public function testCommandIsSentToLogger() + { + $evt = $this->_createCommandEvent("foo\r\n"); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->regExp('~foo\r\n~')); + + $plugin = $this->_createPlugin($logger); + $plugin->commandSent($evt); + } + + public function testResponseIsSentToLogger() + { + $evt = $this->_createResponseEvent("354 Go ahead\r\n"); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->regExp('~354 Go ahead\r\n~')); + + $plugin = $this->_createPlugin($logger); + $plugin->responseReceived($evt); + } + + public function testTransportBeforeStartChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->beforeTransportStarted($evt); + } + + public function testTransportStartChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->transportStarted($evt); + } + + public function testTransportStopChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->transportStopped($evt); + } + + public function testTransportBeforeStopChangeIsSentToLogger() + { + $evt = $this->_createTransportChangeEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + $plugin->beforeTransportStopped($evt); + } + + public function testExceptionsArePassedToDelegateAndLeftToBubbleUp() + { + $transport = $this->_createTransport(); + $evt = $this->_createTransportExceptionEvent(); + $logger = $this->_createLogger(); + $logger->expects($this->once()) + ->method('add') + ->with($this->anything()); + + $plugin = $this->_createPlugin($logger); + try { + $plugin->exceptionThrown($evt); + $this->fail('Exception should bubble up.'); + } catch (Swift_TransportException $ex) { + } + } + + // -- Creation Methods + + private function _createLogger() + { + return $this->getMock('Swift_Plugins_Logger'); + } + + private function _createPlugin($logger) + { + return new Swift_Plugins_LoggerPlugin($logger); + } + + private function _createCommandEvent($command) + { + $evt = $this->getMockBuilder('Swift_Events_CommandEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getCommand') + ->will($this->returnValue($command)); + + return $evt; + } + + private function _createResponseEvent($response) + { + $evt = $this->getMockBuilder('Swift_Events_ResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getResponse') + ->will($this->returnValue($response)); + + return $evt; + } + + private function _createTransport() + { + return $this->getMock('Swift_Transport'); + } + + private function _createTransportChangeEvent() + { + $evt = $this->getMockBuilder('Swift_Events_TransportChangeEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getSource') + ->will($this->returnValue($this->_createTransport())); + + return $evt; + } + + public function _createTransportExceptionEvent() + { + $evt = $this->getMockBuilder('Swift_Events_TransportExceptionEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getException') + ->will($this->returnValue(new Swift_TransportException(''))); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php new file mode 100644 index 0000000..880bb32 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/ArrayLoggerTest.php @@ -0,0 +1,65 @@ +add(">> Foo\r\n"); + $this->assertEquals(">> Foo\r\n", $logger->dump()); + } + + public function testAddingMultipleEntriesDumpsMultipleLines() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $logger->add(">> FOO\r\n"); + $logger->add("<< 502 That makes no sense\r\n"); + $logger->add(">> RSET\r\n"); + $logger->add("<< 250 OK\r\n"); + + $this->assertEquals( + ">> FOO\r\n".PHP_EOL. + "<< 502 That makes no sense\r\n".PHP_EOL. + ">> RSET\r\n".PHP_EOL. + "<< 250 OK\r\n", + $logger->dump() + ); + } + + public function testLogCanBeCleared() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $logger->add(">> FOO\r\n"); + $logger->add("<< 502 That makes no sense\r\n"); + $logger->add(">> RSET\r\n"); + $logger->add("<< 250 OK\r\n"); + + $this->assertEquals( + ">> FOO\r\n".PHP_EOL. + "<< 502 That makes no sense\r\n".PHP_EOL. + ">> RSET\r\n".PHP_EOL. + "<< 250 OK\r\n", + $logger->dump() + ); + + $logger->clear(); + + $this->assertEquals('', $logger->dump()); + } + + public function testLengthCanBeTruncated() + { + $logger = new Swift_Plugins_Loggers_ArrayLogger(2); + $logger->add(">> FOO\r\n"); + $logger->add("<< 502 That makes no sense\r\n"); + $logger->add(">> RSET\r\n"); + $logger->add("<< 250 OK\r\n"); + + $this->assertEquals( + ">> RSET\r\n".PHP_EOL. + "<< 250 OK\r\n", + $logger->dump(), + '%s: Log should be truncated to last 2 entries' + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php new file mode 100644 index 0000000..6134fe6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Loggers/EchoLoggerTest.php @@ -0,0 +1,24 @@ +add('>> Foo'); + $data = ob_get_clean(); + + $this->assertEquals('>> Foo'.PHP_EOL, $data); + } + + public function testAddingEntryDumpsEscapedLineWithHtml() + { + $logger = new Swift_Plugins_Loggers_EchoLogger(true); + ob_start(); + $logger->add('>> Foo'); + $data = ob_get_clean(); + + $this->assertEquals('>> Foo
'.PHP_EOL, $data); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php new file mode 100644 index 0000000..1227c24 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/PopBeforeSmtpPluginTest.php @@ -0,0 +1,103 @@ +_createConnection(); + $connection->expects($this->once()) + ->method('connect'); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + + $transport = $this->_createTransport(); + $evt = $this->_createTransportChangeEvent($transport); + + $plugin->beforeTransportStarted($evt); + } + + public function testPluginDisconnectsFromPop3HostBeforeTransportStarts() + { + $connection = $this->_createConnection(); + $connection->expects($this->once()) + ->method('disconnect'); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + + $transport = $this->_createTransport(); + $evt = $this->_createTransportChangeEvent($transport); + + $plugin->beforeTransportStarted($evt); + } + + public function testPluginDoesNotConnectToSmtpIfBoundToDifferentTransport() + { + $connection = $this->_createConnection(); + $connection->expects($this->never()) + ->method('disconnect'); + $connection->expects($this->never()) + ->method('connect'); + + $smtp = $this->_createTransport(); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + $plugin->bindSmtp($smtp); + + $transport = $this->_createTransport(); + $evt = $this->_createTransportChangeEvent($transport); + + $plugin->beforeTransportStarted($evt); + } + + public function testPluginCanBindToSpecificTransport() + { + $connection = $this->_createConnection(); + $connection->expects($this->once()) + ->method('connect'); + + $smtp = $this->_createTransport(); + + $plugin = $this->_createPlugin('pop.host.tld', 110); + $plugin->setConnection($connection); + $plugin->bindSmtp($smtp); + + $evt = $this->_createTransportChangeEvent($smtp); + + $plugin->beforeTransportStarted($evt); + } + + // -- Creation Methods + + private function _createTransport() + { + return $this->getMock('Swift_Transport'); + } + + private function _createTransportChangeEvent($transport) + { + $evt = $this->getMockBuilder('Swift_Events_TransportChangeEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getSource') + ->will($this->returnValue($transport)); + $evt->expects($this->any()) + ->method('getTransport') + ->will($this->returnValue($transport)); + + return $evt; + } + + public function _createConnection() + { + return $this->getMock('Swift_Plugins_Pop_Pop3Connection'); + } + + public function _createPlugin($host, $port, $crypto = null) + { + return new Swift_Plugins_PopBeforeSmtpPlugin($host, $port, $crypto); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php new file mode 100644 index 0000000..4cc7e89 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/RedirectingPluginTest.php @@ -0,0 +1,185 @@ +assertEquals('fabien@example.com', $plugin->getRecipient()); + $plugin->setRecipient('chris@example.com'); + $this->assertEquals('chris@example.com', $plugin->getRecipient()); + } + + public function testPluginChangesRecipients() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setTo($to = array( + 'fabien-to@example.com' => 'Fabien (To)', + 'chris-to@example.com' => 'Chris (To)', + )) + ->setCc($cc = array( + 'fabien-cc@example.com' => 'Fabien (Cc)', + 'chris-cc@example.com' => 'Chris (Cc)', + )) + ->setBcc($bcc = array( + 'fabien-bcc@example.com' => 'Fabien (Bcc)', + 'chris-bcc@example.com' => 'Chris (Bcc)', + )) + ->setBody('...') + ; + + $plugin = new Swift_Plugins_RedirectingPlugin('god@example.com'); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals($message->getTo(), array('god@example.com' => '')); + $this->assertEquals($message->getCc(), array()); + $this->assertEquals($message->getBcc(), array()); + + $plugin->sendPerformed($evt); + + $this->assertEquals($message->getTo(), $to); + $this->assertEquals($message->getCc(), $cc); + $this->assertEquals($message->getBcc(), $bcc); + } + + public function testPluginRespectsUnsetToList() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setCc($cc = array( + 'fabien-cc@example.com' => 'Fabien (Cc)', + 'chris-cc@example.com' => 'Chris (Cc)', + )) + ->setBcc($bcc = array( + 'fabien-bcc@example.com' => 'Fabien (Bcc)', + 'chris-bcc@example.com' => 'Chris (Bcc)', + )) + ->setBody('...') + ; + + $plugin = new Swift_Plugins_RedirectingPlugin('god@example.com'); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals($message->getTo(), array('god@example.com' => '')); + $this->assertEquals($message->getCc(), array()); + $this->assertEquals($message->getBcc(), array()); + + $plugin->sendPerformed($evt); + + $this->assertEquals($message->getTo(), array()); + $this->assertEquals($message->getCc(), $cc); + $this->assertEquals($message->getBcc(), $bcc); + } + + public function testPluginRespectsAWhitelistOfPatterns() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setTo($to = array( + 'fabien-to@example.com' => 'Fabien (To)', + 'chris-to@example.com' => 'Chris (To)', + 'lars-to@internal.com' => 'Lars (To)', + )) + ->setCc($cc = array( + 'fabien-cc@example.com' => 'Fabien (Cc)', + 'chris-cc@example.com' => 'Chris (Cc)', + 'lars-cc@internal.org' => 'Lars (Cc)', + )) + ->setBcc($bcc = array( + 'fabien-bcc@example.com' => 'Fabien (Bcc)', + 'chris-bcc@example.com' => 'Chris (Bcc)', + 'john-bcc@example.org' => 'John (Bcc)', + )) + ->setBody('...') + ; + + $recipient = 'god@example.com'; + $patterns = array('/^.*@internal.[a-z]+$/', '/^john-.*$/'); + + $plugin = new Swift_Plugins_RedirectingPlugin($recipient, $patterns); + + $this->assertEquals($recipient, $plugin->getRecipient()); + $this->assertEquals($plugin->getWhitelist(), $patterns); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals($message->getTo(), array('lars-to@internal.com' => 'Lars (To)', 'god@example.com' => null)); + $this->assertEquals($message->getCc(), array('lars-cc@internal.org' => 'Lars (Cc)')); + $this->assertEquals($message->getBcc(), array('john-bcc@example.org' => 'John (Bcc)')); + + $plugin->sendPerformed($evt); + + $this->assertEquals($message->getTo(), $to); + $this->assertEquals($message->getCc(), $cc); + $this->assertEquals($message->getBcc(), $bcc); + } + + public function testArrayOfRecipientsCanBeExplicitlyDefined() + { + $message = Swift_Message::newInstance() + ->setSubject('...') + ->setFrom(array('john@example.com' => 'John Doe')) + ->setTo(array( + 'fabien@example.com' => 'Fabien', + 'chris@example.com' => 'Chris (To)', + 'lars-to@internal.com' => 'Lars (To)', + )) + ->setCc(array( + 'fabien@example.com' => 'Fabien', + 'chris-cc@example.com' => 'Chris (Cc)', + 'lars-cc@internal.org' => 'Lars (Cc)', + )) + ->setBcc(array( + 'fabien@example.com' => 'Fabien', + 'chris-bcc@example.com' => 'Chris (Bcc)', + 'john-bcc@example.org' => 'John (Bcc)', + )) + ->setBody('...') + ; + + $recipients = array('god@example.com', 'fabien@example.com'); + $patterns = array('/^.*@internal.[a-z]+$/'); + + $plugin = new Swift_Plugins_RedirectingPlugin($recipients, $patterns); + + $evt = $this->_createSendEvent($message); + + $plugin->beforeSendPerformed($evt); + + $this->assertEquals( + $message->getTo(), + array('fabien@example.com' => 'Fabien', 'lars-to@internal.com' => 'Lars (To)', 'god@example.com' => null) + ); + $this->assertEquals( + $message->getCc(), + array('fabien@example.com' => 'Fabien', 'lars-cc@internal.org' => 'Lars (Cc)') + ); + $this->assertEquals($message->getBcc(), array('fabien@example.com' => 'Fabien')); + } + + // -- Creation Methods + + private function _createSendEvent(Swift_Mime_Message $message) + { + $evt = $this->getMockBuilder('Swift_Events_SendEvent') + ->disableOriginalConstructor() + ->getMock(); + $evt->expects($this->any()) + ->method('getMessage') + ->will($this->returnValue($message)); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php new file mode 100644 index 0000000..8101883 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ReporterPluginTest.php @@ -0,0 +1,88 @@ +_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array()); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + public function testReportingFailedTo() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo', 'zip@button' => 'Zip')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array('zip@button')); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + $reporter->shouldReceive('notify')->once()->with($message, 'zip@button', Swift_Plugins_Reporter::RESULT_FAIL); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + public function testReportingFailedCc() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo')); + $message->shouldReceive('getCc')->zeroOrMoreTimes()->andReturn(array('zip@button' => 'Zip', 'test@test.com' => 'Test')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array('zip@button')); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + $reporter->shouldReceive('notify')->once()->with($message, 'zip@button', Swift_Plugins_Reporter::RESULT_FAIL); + $reporter->shouldReceive('notify')->once()->with($message, 'test@test.com', Swift_Plugins_Reporter::RESULT_PASS); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + public function testReportingFailedBcc() + { + $message = $this->_createMessage(); + $evt = $this->_createSendEvent(); + $reporter = $this->_createReporter(); + + $message->shouldReceive('getTo')->zeroOrMoreTimes()->andReturn(array('foo@bar.tld' => 'Foo')); + $message->shouldReceive('getBcc')->zeroOrMoreTimes()->andReturn(array('zip@button' => 'Zip', 'test@test.com' => 'Test')); + $evt->shouldReceive('getMessage')->zeroOrMoreTimes()->andReturn($message); + $evt->shouldReceive('getFailedRecipients')->zeroOrMoreTimes()->andReturn(array('zip@button')); + $reporter->shouldReceive('notify')->once()->with($message, 'foo@bar.tld', Swift_Plugins_Reporter::RESULT_PASS); + $reporter->shouldReceive('notify')->once()->with($message, 'zip@button', Swift_Plugins_Reporter::RESULT_FAIL); + $reporter->shouldReceive('notify')->once()->with($message, 'test@test.com', Swift_Plugins_Reporter::RESULT_PASS); + + $plugin = new Swift_Plugins_ReporterPlugin($reporter); + $plugin->sendPerformed($evt); + } + + // -- Creation Methods + + private function _createMessage() + { + return $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + } + + private function _createSendEvent() + { + return $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + } + + private function _createReporter() + { + return $this->getMockery('Swift_Plugins_Reporter')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php new file mode 100644 index 0000000..4d31c3c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HitReporterTest.php @@ -0,0 +1,64 @@ +_hitReporter = new Swift_Plugins_Reporters_HitReporter(); + $this->_message = $this->getMock('Swift_Mime_Message'); + } + + public function testReportingFail() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->assertEquals(array('foo@bar.tld'), + $this->_hitReporter->getFailedRecipients() + ); + } + + public function testMultipleReports() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->_hitReporter->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->assertEquals(array('foo@bar.tld', 'zip@button'), + $this->_hitReporter->getFailedRecipients() + ); + } + + public function testReportingPassIsIgnored() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->_hitReporter->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_PASS + ); + $this->assertEquals(array('foo@bar.tld'), + $this->_hitReporter->getFailedRecipients() + ); + } + + public function testBufferCanBeCleared() + { + $this->_hitReporter->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->_hitReporter->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $this->assertEquals(array('foo@bar.tld', 'zip@button'), + $this->_hitReporter->getFailedRecipients() + ); + $this->_hitReporter->clear(); + $this->assertEquals(array(), $this->_hitReporter->getFailedRecipients()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php new file mode 100644 index 0000000..3e78fca --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/Reporters/HtmlReporterTest.php @@ -0,0 +1,54 @@ +_html = new Swift_Plugins_Reporters_HtmlReporter(); + $this->_message = $this->getMock('Swift_Mime_Message'); + } + + public function testReportingPass() + { + ob_start(); + $this->_html->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_PASS + ); + $html = ob_get_clean(); + + $this->assertRegExp('~ok|pass~i', $html, '%s: Reporter should indicate pass'); + $this->assertRegExp('~foo@bar\.tld~', $html, '%s: Reporter should show address'); + } + + public function testReportingFail() + { + ob_start(); + $this->_html->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $html = ob_get_clean(); + + $this->assertRegExp('~fail~i', $html, '%s: Reporter should indicate fail'); + $this->assertRegExp('~zip@button~', $html, '%s: Reporter should show address'); + } + + public function testMultipleReports() + { + ob_start(); + $this->_html->notify($this->_message, 'foo@bar.tld', + Swift_Plugins_Reporter::RESULT_PASS + ); + $this->_html->notify($this->_message, 'zip@button', + Swift_Plugins_Reporter::RESULT_FAIL + ); + $html = ob_get_clean(); + + $this->assertRegExp('~ok|pass~i', $html, '%s: Reporter should indicate pass'); + $this->assertRegExp('~foo@bar\.tld~', $html, '%s: Reporter should show address'); + $this->assertRegExp('~fail~i', $html, '%s: Reporter should indicate fail'); + $this->assertRegExp('~zip@button~', $html, '%s: Reporter should show address'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php new file mode 100644 index 0000000..a50f14f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Plugins/ThrottlerPluginTest.php @@ -0,0 +1,104 @@ +_createSleeper(); + $timer = $this->_createTimer(); + + //10MB/min + $plugin = new Swift_Plugins_ThrottlerPlugin( + 10000000, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE, + $sleeper, $timer + ); + + $timer->shouldReceive('getTimestamp')->once()->andReturn(0); + $timer->shouldReceive('getTimestamp')->once()->andReturn(1); //expected 0.6 + $timer->shouldReceive('getTimestamp')->once()->andReturn(1); //expected 1.2 (sleep 1) + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 1.8 + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 2.4 (sleep 1) + $sleeper->shouldReceive('sleep')->twice()->with(1); + + //10,000,000 bytes per minute + //100,000 bytes per email + + // .: (10,000,000/100,000)/60 emails per second = 1.667 emais/sec + + $message = $this->_createMessageWithByteCount(100000); //100KB + + $evt = $this->_createSendEvent($message); + + for ($i = 0; $i < 5; ++$i) { + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + } + + public function testMessagesPerMinuteThrottling() + { + $sleeper = $this->_createSleeper(); + $timer = $this->_createTimer(); + + //60/min + $plugin = new Swift_Plugins_ThrottlerPlugin( + 60, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE, + $sleeper, $timer + ); + + $timer->shouldReceive('getTimestamp')->once()->andReturn(0); + $timer->shouldReceive('getTimestamp')->once()->andReturn(0); //expected 1 (sleep 1) + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 2 + $timer->shouldReceive('getTimestamp')->once()->andReturn(2); //expected 3 (sleep 1) + $timer->shouldReceive('getTimestamp')->once()->andReturn(4); //expected 4 + $sleeper->shouldReceive('sleep')->twice()->with(1); + + //60 messages per minute + //1 message per second + + $message = $this->_createMessageWithByteCount(10); + + $evt = $this->_createSendEvent($message); + + for ($i = 0; $i < 5; ++$i) { + $plugin->beforeSendPerformed($evt); + $plugin->sendPerformed($evt); + } + } + + // -- Creation Methods + + private function _createSleeper() + { + return $this->getMockery('Swift_Plugins_Sleeper'); + } + + private function _createTimer() + { + return $this->getMockery('Swift_Plugins_Timer'); + } + + private function _createMessageWithByteCount($bytes) + { + $msg = $this->getMockery('Swift_Mime_Message'); + $msg->shouldReceive('toByteStream') + ->zeroOrMoreTimes() + ->andReturnUsing(function ($is) use ($bytes) { + for ($i = 0; $i < $bytes; ++$i) { + $is->write('x'); + } + }); + + return $msg; + } + + private function _createSendEvent($message) + { + $evt = $this->getMockery('Swift_Events_SendEvent'); + $evt->shouldReceive('getMessage') + ->zeroOrMoreTimes() + ->andReturn($message); + + return $evt; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php new file mode 100644 index 0000000..13c1b4c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/DKIMSignerTest.php @@ -0,0 +1,227 @@ +markTestSkipped( + 'skipping because of https://bugs.php.net/bug.php?id=61421' + ); + } + } + + public function testBasicSigningHeaderManipulation() + { + $headers = $this->_createHeaders(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + /* @var $signer Swift_Signers_HeaderSigner */ + $altered = $signer->getAlteredHeaders(); + $signer->reset(); + // Headers + $signer->setHeaders($headers); + // Body + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + // Signing + $signer->addSignature($headers); + } + + // Default Signing + public function testSigningDefaults() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setSignatureTimestamp('1299879181'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha1; bh=wlbYcY9O9OPInGJ4D0E/rGsvMLE=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; t=1299879181; b=RMSNelzM2O5MAAnMjT3G3/VF36S3DGJXoPCXR001F1WDReu0prGphWjuzK/m6V1pwqQL8cCNg Hi74mTx2bvyAvmkjvQtJf1VMUOCc9WHGcm1Yec66I3ZWoNMGSWZ1EKAm2CtTzyG0IFw4ml9DI wSkyAFxlgicckDD6FibhqwX4w='); + } + + // SHA256 Signing + public function testSigning256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; t=1299879181; b=jqPmieHzF5vR9F4mXCAkowuphpO4iJ8IAVuioh1BFZ3VITXZj5jlOFxULJMBiiApm2keJirnh u4mzogj444QkpT3lJg8/TBGAYQPdcvkG3KC0jdyN6QpSgpITBJG2BwWa+keXsv2bkQgLRAzNx qRhP45vpHCKun0Tg9LrwW/KCg='); + } + + // Relaxed/Relaxed Hash Signing + public function testSigningRelaxedRelaxed256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $signer->setBodyCanon('relaxed'); + $signer->setHeaderCanon('relaxed'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; c=relaxed/relaxed; t=1299879181; b=gzOI+PX6HpZKQFzwwmxzcVJsyirdLXOS+4pgfCpVHQIdqYusKLrhlLeFBTNoz75HrhNvGH6T0 Rt3w5aTqkrWfUuAEYt0Ns14GowLM7JojaFN+pZ4eYnRB3CBBgW6fee4NEMD5WPca3uS09tr1E 10RYh9ILlRtl+84sovhx5id3Y='); + } + + // Relaxed/Simple Hash Signing + public function testSigningRelaxedSimple256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $signer->setHeaderCanon('relaxed'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; c=relaxed; t=1299879181; b=dLPJNec5v81oelyzGOY0qPqTlGnQeNfUNBOrV/JKbStr3NqWGI9jH4JAe2YvO2V32lfPNoby1 4MMzZ6EPkaZkZDDSPa+53YbCPQAlqiD9QZZIUe2UNM33HN8yAMgiWEF5aP7MbQnxeVZMfVLEl 9S8qOImu+K5JZqhQQTL0dgLwA='); + } + + // Simple/Relaxed Hash Signing + public function testSigningSimpleRelaxed256() + { + $headerSet = $this->_createHeaderSet(); + $messageContent = 'Hello World'; + $signer = new Swift_Signers_DKIMSigner(file_get_contents(dirname(dirname(dirname(__DIR__))).'/_samples/dkim/dkim.test.priv'), 'dummy.nxdomain.be', 'dummySelector'); + $signer->setHashAlgorithm('rsa-sha256'); + $signer->setSignatureTimestamp('1299879181'); + $signer->setBodyCanon('relaxed'); + $altered = $signer->getAlteredHeaders(); + $this->assertEquals(array('DKIM-Signature'), $altered); + $signer->reset(); + $signer->setHeaders($headerSet); + $this->assertFalse($headerSet->has('DKIM-Signature')); + $signer->startBody(); + $signer->write($messageContent); + $signer->endBody(); + $signer->addSignature($headerSet); + $this->assertTrue($headerSet->has('DKIM-Signature')); + $dkim = $headerSet->getAll('DKIM-Signature'); + $sig = reset($dkim); + $this->assertEquals($sig->getValue(), 'v=1; a=rsa-sha256; bh=f+W+hu8dIhf2VAni89o8lF6WKTXi7nViA4RrMdpD5/U=; d=dummy.nxdomain.be; h=; i=@dummy.nxdomain.be; s=dummySelector; c=simple/relaxed; t=1299879181; b=M5eomH/zamyzix9kOes+6YLzQZxuJdBP4x3nP9zF2N26eMLG2/cBKbnNyqiOTDhJdYfWPbLIa 1CWnjST0j5p4CpeOkGYuiE+M4TWEZwhRmRWootlPO3Ii6XpbBJKFk1o9zviS7OmXblUUE4aqb yRSIMDhtLdCK5GlaCneFLN7RQ='); + } + + // -- Creation Methods + private function _createHeaderSet() + { + $cache = new Swift_KeyCache_ArrayKeyCache(new Swift_KeyCache_SimpleKeyCacheInputStream()); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $grammar = new Swift_Mime_Grammar(); + $headers = new Swift_Mime_SimpleHeaderSet(new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $grammar)); + + return $headers; + } + + /** + * @return Swift_Mime_Headers + */ + private function _createHeaders() + { + $x = 0; + $cache = new Swift_KeyCache_ArrayKeyCache(new Swift_KeyCache_SimpleKeyCacheInputStream()); + $factory = new Swift_CharacterReaderFactory_SimpleCharacterReaderFactory(); + $contentEncoder = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); + + $headerEncoder = new Swift_Mime_HeaderEncoder_QpHeaderEncoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $paramEncoder = new Swift_Encoder_Rfc2231Encoder(new Swift_CharacterStream_ArrayCharacterStream($factory, 'utf-8')); + $grammar = new Swift_Mime_Grammar(); + $headerFactory = new Swift_Mime_SimpleHeaderFactory($headerEncoder, $paramEncoder, $grammar); + $headers = $this->getMockery('Swift_Mime_HeaderSet'); + + $headers->shouldReceive('listAll') + ->zeroOrMoreTimes() + ->andReturn(array('From', 'To', 'Date', 'Subject')); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('From') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('From') + ->andReturn(array($headerFactory->createMailboxHeader('From', 'test@test.test'))); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('To') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('To') + ->andReturn(array($headerFactory->createMailboxHeader('To', 'test@test.test'))); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('Date') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('Date') + ->andReturn(array($headerFactory->createTextHeader('Date', 'Fri, 11 Mar 2011 20:56:12 +0000 (GMT)'))); + $headers->shouldReceive('has') + ->zeroOrMoreTimes() + ->with('Subject') + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('Subject') + ->andReturn(array($headerFactory->createTextHeader('Subject', 'Foo Bar Text Message'))); + $headers->shouldReceive('addTextHeader') + ->zeroOrMoreTimes() + ->with('DKIM-Signature', \Mockery::any()) + ->andReturn(true); + $headers->shouldReceive('getAll') + ->zeroOrMoreTimes() + ->with('DKIM-Signature') + ->andReturn(array($headerFactory->createTextHeader('DKIM-Signature', 'Foo Bar Text Message'))); + + return $headers; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php new file mode 100644 index 0000000..00e48c1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/OpenDKIMSignerTest.php @@ -0,0 +1,45 @@ +markTestSkipped( + 'Need OpenDKIM extension run these tests.' + ); + } + } + + public function testBasicSigningHeaderManipulation() + { + } + + // Default Signing + public function testSigningDefaults() + { + } + + // SHA256 Signing + public function testSigning256() + { + } + + // Relaxed/Relaxed Hash Signing + public function testSigningRelaxedRelaxed256() + { + } + + // Relaxed/Simple Hash Signing + public function testSigningRelaxedSimple256() + { + } + + // Simple/Relaxed Hash Signing + public function testSigningSimpleRelaxed256() + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php new file mode 100644 index 0000000..df745ef --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php @@ -0,0 +1,554 @@ +replacementFactory = Swift_DependencyContainer::getInstance() + ->lookup('transport.replacementfactory'); + + $this->samplesDir = str_replace('\\', '/', realpath(__DIR__.'/../../../_samples/')).'/'; + } + + public function testUnSingedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $this->assertEquals('Here is the message itself', $message->getBody()); + } + + public function testSingedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<assertValidVerify($expectedBody, $messageStream); + unset($messageStream); + } + + public function testSingedMessageExtraCerts() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign2.crt', $this->samplesDir.'smime/sign2.key', PKCS7_DETACHED, $this->samplesDir.'smime/intermediate.crt'); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<assertValidVerify($expectedBody, $messageStream); + unset($messageStream); + } + + public function testSingedMessageBinary() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key', PKCS7_BINARY); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=signed\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $this->assertEquals($headers['content-transfer-encoding'], 'base64'); + $this->assertEquals($headers['content-disposition'], 'attachment; filename="smime.p7m"'); + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $messageStreamClean = $this->newFilteredStream(); + + $this->assertValidVerify($expectedBody, $messageStream); + unset($messageStreamClean, $messageStream); + } + + public function testSingedMessageWithAttachments() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $message->attach(Swift_Attachment::fromPath($this->samplesDir.'/files/textfile.zip')); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<assertValidVerify($expectedBody, $messageStream); + unset($messageStream); + } + + public function testEncryptedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $originalMessage = $this->cleanMessage($message->toString()); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt'); + $message->attachSigner($signer); + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($decryptedMessageStream, $messageStream); + } + + public function testEncryptedMessageWithMultipleCerts() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $originalMessage = $this->cleanMessage($message->toString()); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setEncryptCertificate(array($this->samplesDir.'smime/encrypt.crt', $this->samplesDir.'smime/encrypt2.crt')); + $message->attachSigner($signer); + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($decryptedMessageStream); + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt2.crt', array('file://'.$this->samplesDir.'smime/encrypt2.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($decryptedMessageStream, $messageStream); + } + + public function testSignThenEncryptedMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $signer = new Swift_Signers_SMimeSigner(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt'); + $message->attachSigner($signer); + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) { + $this->fail('Content-type does not match.'); + + return false; + } + + $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})'; + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $entityString = $decryptedMessageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<assertValidVerify($expectedBody, $decryptedMessageStream)) { + return false; + } + + unset($decryptedMessageStream, $messageStream); + } + + public function testEncryptThenSignMessage() + { + $message = Swift_SignedMessage::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself'); + + $originalMessage = $this->cleanMessage($message->toString()); + + $signer = Swift_Signers_SMimeSigner::newInstance(); + $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key'); + $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt'); + $signer->setSignThenEncrypt(false); + $message->attachSigner($signer); + + $messageStream = $this->newFilteredStream(); + $message->toByteStream($messageStream); + $messageStream->commit(); + + $entityString = $messageStream->getContent(); + $headers = self::getHeadersOfMessage($entityString); + + if (!($boundary = $this->getBoundary($headers['content-type']))) { + return false; + } + + $expectedBody = <<MIME-Version: 1\.0 +Content-Disposition: attachment; filename="smime\.p7m" +Content-Type: application/(x\-)?pkcs7-mime; smime-type=enveloped-data; name="smime\.p7m" +Content-Transfer-Encoding: base64 + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + + +)--$boundary +Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="smime\.p7s" + +(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2}) + +--$boundary-- +OEL; + + if (!$this->assertValidVerify($expectedBody, $messageStream)) { + return false; + } + + $expectedBody = str_replace("\n", "\r\n", $expectedBody); + if (!preg_match('%'.$expectedBody.'*%m', $entityString, $entities)) { + $this->fail('Failed regex match.'); + + return false; + } + + $messageStreamClean = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStreamClean->write($entities['encrypted_message']); + + $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_decrypt($messageStreamClean->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) { + $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string())); + } + + $this->assertEquals($originalMessage, $decryptedMessageStream->getContent()); + unset($messageStreamClean, $messageStream, $decryptedMessageStream); + } + + protected function assertValidVerify($expected, Swift_ByteStream_TemporaryFileByteStream $messageStream) + { + $actual = $messageStream->getContent(); + + // File is UNIX encoded so convert them to correct line ending + $expected = str_replace("\n", "\r\n", $expected); + + $actual = trim(self::getBodyOfMessage($actual)); + if (!$this->assertRegExp('%^'.$expected.'$\s*%m', $actual)) { + return false; + } + + $opensslOutput = new Swift_ByteStream_TemporaryFileByteStream(); + $verify = openssl_pkcs7_verify($messageStream->getPath(), null, $opensslOutput->getPath(), array($this->samplesDir.'smime/ca.crt')); + + if (false === $verify) { + $this->fail('Verification of the message failed.'); + + return false; + } elseif (-1 === $verify) { + $this->fail(sprintf('Verification of the message failed. Internal error "%s".', openssl_error_string())); + + return false; + } + + return true; + } + + protected function getBoundary($contentType) + { + if (!preg_match('/boundary=("[^"]+"|(?:[^\s]+|$))/is', $contentType, $contentTypeData)) { + $this->fail('Failed to find Boundary parameter'); + + return false; + } + + return trim($contentTypeData[1], '"'); + } + + protected function newFilteredStream() + { + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $messageStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + return $messageStream; + } + + protected static function getBodyOfMessage($message) + { + return substr($message, strpos($message, "\r\n\r\n")); + } + + /** + * Strips of the sender headers and Mime-Version. + * + * @param Swift_ByteStream_TemporaryFileByteStream $messageStream + * @param Swift_ByteStream_TemporaryFileByteStream $inputStream + */ + protected function cleanMessage($content) + { + $newContent = ''; + + $headers = self::getHeadersOfMessage($content); + foreach ($headers as $headerName => $value) { + if (!in_array($headerName, array('content-type', 'content-transfer-encoding', 'content-disposition'))) { + continue; + } + + $headerName = explode('-', $headerName); + $headerName = array_map('ucfirst', $headerName); + $headerName = implode('-', $headerName); + + if (strlen($value) > 62) { + $value = wordwrap($value, 62, "\n "); + } + + $newContent .= "$headerName: $value\r\n"; + } + + return $newContent."\r\n".ltrim(self::getBodyOfMessage($content)); + } + + /** + * Returns the headers of the message. + * + * Header-names are lowercase. + * + * @param string $message + * + * @return array + */ + protected static function getHeadersOfMessage($message) + { + $headersPosEnd = strpos($message, "\r\n\r\n"); + $headerData = substr($message, 0, $headersPosEnd); + $headerLines = explode("\r\n", $headerData); + + if (empty($headerLines)) { + return array(); + } + + $headers = array(); + + foreach ($headerLines as $headerLine) { + if (ctype_space($headerLines[0]) || false === strpos($headerLine, ':')) { + $headers[$currentHeaderName] .= ' '.trim($headerLine); + continue; + } + + $header = explode(':', $headerLine, 2); + $currentHeaderName = strtolower($header[0]); + $headers[$currentHeaderName] = trim($header[1]); + } + + return $headers; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php new file mode 100644 index 0000000..e4d4f48 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/ByteArrayReplacementFilterTest.php @@ -0,0 +1,131 @@ +_createFilter(array(0x61, 0x62), array(0x63, 0x64)); + $this->assertEquals( + array(0x59, 0x60, 0x63, 0x64, 0x65), + $filter->filter(array(0x59, 0x60, 0x61, 0x62, 0x65)) + ); + } + + public function testShouldBufferReturnsTrueIfPartialMatchAtEndOfBuffer() + { + $filter = $this->_createFilter(array(0x61, 0x62), array(0x63, 0x64)); + $this->assertTrue($filter->shouldBuffer(array(0x59, 0x60, 0x61)), + '%s: Filter should buffer since 0x61 0x62 is the needle and the ending '. + '0x61 could be from 0x61 0x62' + ); + } + + public function testFilterCanMakeMultipleReplacements() + { + $filter = $this->_createFilter(array(array(0x61), array(0x62)), array(0x63)); + $this->assertEquals( + array(0x60, 0x63, 0x60, 0x63, 0x60), + $filter->filter(array(0x60, 0x61, 0x60, 0x62, 0x60)) + ); + } + + public function testMultipleReplacementsCanBeDifferent() + { + $filter = $this->_createFilter(array(array(0x61), array(0x62)), array(array(0x63), array(0x64))); + $this->assertEquals( + array(0x60, 0x63, 0x60, 0x64, 0x60), + $filter->filter(array(0x60, 0x61, 0x60, 0x62, 0x60)) + ); + } + + public function testShouldBufferReturnsFalseIfPartialMatchNotAtEndOfString() + { + $filter = $this->_createFilter(array(0x0D, 0x0A), array(0x0A)); + $this->assertFalse($filter->shouldBuffer(array(0x61, 0x62, 0x0D, 0x0A, 0x63)), + '%s: Filter should not buffer since x0Dx0A is the needle and is not at EOF' + ); + } + + public function testShouldBufferReturnsTrueIfAnyOfMultipleMatchesAtEndOfString() + { + $filter = $this->_createFilter(array(array(0x61, 0x62), array(0x63)), array(0x64)); + $this->assertTrue($filter->shouldBuffer(array(0x59, 0x60, 0x61)), + '%s: Filter should buffer since 0x61 0x62 is a needle and the ending '. + '0x61 could be from 0x61 0x62' + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsLF() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0A, 0x61, 0x0A, 0x62, 0x0A, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsCR() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0D, 0x61, 0x0D, 0x62, 0x0D, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsCRLF() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputIsLFCR() + { + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0A, 0x0D, 0x61, 0x0A, 0x0D, 0x62, 0x0A, 0x0D, 0x63)) + ); + } + + public function testConvertingAllLineEndingsToCRLFWhenInputContainsLFLF() + { + //Lighthouse Bug #23 + + $filter = $this->_createFilter( + array(array(0x0D, 0x0A), array(0x0D), array(0x0A)), + array(array(0x0A), array(0x0A), array(0x0D, 0x0A)) + ); + + $this->assertEquals( + array(0x60, 0x0D, 0x0A, 0x0D, 0x0A, 0x61, 0x0D, 0x0A, 0x0D, 0x0A, 0x62, 0x0D, 0x0A, 0x0D, 0x0A, 0x63), + $filter->filter(array(0x60, 0x0A, 0x0A, 0x61, 0x0A, 0x0A, 0x62, 0x0A, 0x0A, 0x63)) + ); + } + + // -- Creation methods + + private function _createFilter($search, $replace) + { + return new Swift_StreamFilters_ByteArrayReplacementFilter($search, $replace); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php new file mode 100644 index 0000000..bd44f5c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterFactoryTest.php @@ -0,0 +1,38 @@ +_createFactory(); + $this->assertInstanceof( + 'Swift_StreamFilters_StringReplacementFilter', + $factory->createFilter('a', 'b') + ); + } + + public function testSameInstancesAreCached() + { + $factory = $this->_createFactory(); + $filter1 = $factory->createFilter('a', 'b'); + $filter2 = $factory->createFilter('a', 'b'); + $this->assertSame($filter1, $filter2, '%s: Instances should be cached'); + } + + public function testDifferingInstancesAreNotCached() + { + $factory = $this->_createFactory(); + $filter1 = $factory->createFilter('a', 'b'); + $filter2 = $factory->createFilter('a', 'c'); + $this->assertNotEquals($filter1, $filter2, + '%s: Differing instances should not be cached' + ); + } + + // -- Creation methods + + private function _createFactory() + { + return new Swift_StreamFilters_StringReplacementFilterFactory(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php new file mode 100644 index 0000000..7a98a2f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/StreamFilters/StringReplacementFilterTest.php @@ -0,0 +1,55 @@ +_createFilter('foo', 'bar'); + $this->assertEquals('XbarYbarZ', $filter->filter('XfooYfooZ')); + } + + public function testShouldBufferReturnsTrueIfPartialMatchAtEndOfBuffer() + { + $filter = $this->_createFilter('foo', 'bar'); + $this->assertTrue($filter->shouldBuffer('XfooYf'), + '%s: Filter should buffer since "foo" is the needle and the ending '. + '"f" could be from "foo"' + ); + } + + public function testFilterCanMakeMultipleReplacements() + { + $filter = $this->_createFilter(array('a', 'b'), 'foo'); + $this->assertEquals('XfooYfooZ', $filter->filter('XaYbZ')); + } + + public function testMultipleReplacementsCanBeDifferent() + { + $filter = $this->_createFilter(array('a', 'b'), array('foo', 'zip')); + $this->assertEquals('XfooYzipZ', $filter->filter('XaYbZ')); + } + + public function testShouldBufferReturnsFalseIfPartialMatchNotAtEndOfString() + { + $filter = $this->_createFilter("\r\n", "\n"); + $this->assertFalse($filter->shouldBuffer("foo\r\nbar"), + '%s: Filter should not buffer since x0Dx0A is the needle and is not at EOF' + ); + } + + public function testShouldBufferReturnsTrueIfAnyOfMultipleMatchesAtEndOfString() + { + $filter = $this->_createFilter(array('foo', 'zip'), 'bar'); + $this->assertTrue($filter->shouldBuffer('XfooYzi'), + '%s: Filter should buffer since "zip" is a needle and the ending '. + '"zi" could be from "zip"' + ); + } + + // -- Creation methods + + private function _createFilter($search, $replace) + { + return new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php new file mode 100644 index 0000000..121aaba --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpEventSupportTest.php @@ -0,0 +1,560 @@ +_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $listener = $this->getMockery('Swift_Events_EventListener'); + $smtp = $this->_getTransport($buf, $dispatcher); + $dispatcher->shouldReceive('bindEventListener') + ->once() + ->with($listener); + + $smtp->registerPlugin($listener); + } + + public function testSendingDispatchesBeforeSendEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $message = $this->_createMessage(); + $smtp = $this->_getTransport($buf, $dispatcher); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $dispatcher->shouldReceive('createSendEvent') + ->once() + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeSendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message)); + } + + public function testSendingDispatchesSendEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $message = $this->_createMessage(); + $smtp = $this->_getTransport($buf, $dispatcher); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $dispatcher->shouldReceive('createSendEvent') + ->once() + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message)); + } + + public function testSendEventCapturesFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("500 Not now\r\n"); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setFailedRecipients') + ->once() + ->with(array('mark@swiftmailer.org')); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message)); + } + + public function testSendEventHasResultFailedIfAllFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("500 Not now\r\n"); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setResult') + ->once() + ->with(Swift_Events_SendEvent::RESULT_FAILED); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message)); + } + + public function testSendEventHasResultTentativeIfSomeFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array( + 'mark@swiftmailer.org' => 'Mark', + 'chris@site.tld' => 'Chris', + )); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("500 Not now\r\n"); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setResult') + ->once() + ->with(Swift_Events_SendEvent::RESULT_TENTATIVE); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message)); + } + + public function testSendEventHasResultSuccessIfNoFailures() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array( + 'mark@swiftmailer.org' => 'Mark', + 'chris@site.tld' => 'Chris', + )); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'sendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturn(false); + $evt->shouldReceive('setResult') + ->once() + ->with(Swift_Events_SendEvent::RESULT_SUCCESS); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(2, $smtp->send($message)); + } + + public function testCancellingEventBubbleBeforeSendStopsEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_SendEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('chris@swiftmailer.org' => null)); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('mark@swiftmailer.org' => 'Mark')); + $dispatcher->shouldReceive('createSendEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeSendPerformed'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(true); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message)); + } + + public function testStartingTransportDispatchesTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'transportStarted'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testStartingTransportDispatchesBeforeTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStarted'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(false); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testCancellingBubbleBeforeTransportStartStopsEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStarted'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(true); + + $this->_finishBuffer($buf); + $smtp->start(); + + $this->assertFalse($smtp->isStarted(), + '%s: Transport should not be started since event bubble was cancelled' + ); + } + + public function testStoppingTransportDispatchesTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'transportStopped'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->stop(); + } + + public function testStoppingTransportDispatchesBeforeTransportChangeEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent')->shouldIgnoreMissing(); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStopped'); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->stop(); + } + + public function testCancellingBubbleBeforeTransportStoppedStopsEvent() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportChangeEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $hasRun = false; + $dispatcher->shouldReceive('createTransportChangeEvent') + ->atLeast()->once() + ->with($smtp) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'beforeTransportStopped') + ->andReturnUsing(function () use (&$hasRun) { + $hasRun = true; + }); + $dispatcher->shouldReceive('dispatchEvent') + ->zeroOrMoreTimes(); + $evt->shouldReceive('bubbleCancelled') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$hasRun) { + return $hasRun; + }); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->stop(); + + $this->assertTrue($smtp->isStarted(), + '%s: Transport should not be stopped since event bubble was cancelled' + ); + } + + public function testResponseEventsAreGenerated() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_ResponseEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createResponseEvent') + ->atLeast()->once() + ->with($smtp, \Mockery::any(), \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->atLeast()->once() + ->with($evt, 'responseReceived'); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testCommandEventsAreGenerated() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_CommandEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $dispatcher->shouldReceive('createCommandEvent') + ->once() + ->with($smtp, \Mockery::any(), \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'commandSent'); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + public function testExceptionsCauseExceptionEvents() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportExceptionEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $buf->shouldReceive('readLine') + ->atLeast()->once() + ->andReturn("503 I'm sleepy, go away!\r\n"); + $dispatcher->shouldReceive('createTransportExceptionEvent') + ->zeroOrMoreTimes() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->once() + ->with($evt, 'exceptionThrown'); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(false); + + try { + $smtp->start(); + $this->fail('TransportException should be thrown on invalid response'); + } catch (Swift_TransportException $e) { + } + } + + public function testExceptionBubblesCanBeCancelled() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(false); + $evt = $this->getMockery('Swift_Events_TransportExceptionEvent'); + $smtp = $this->_getTransport($buf, $dispatcher); + + $buf->shouldReceive('readLine') + ->atLeast()->once() + ->andReturn("503 I'm sleepy, go away!\r\n"); + $dispatcher->shouldReceive('createTransportExceptionEvent') + ->twice() + ->with($smtp, \Mockery::any()) + ->andReturn($evt); + $dispatcher->shouldReceive('dispatchEvent') + ->twice() + ->with($evt, 'exceptionThrown'); + $evt->shouldReceive('bubbleCancelled') + ->atLeast()->once() + ->andReturn(true); + + $this->_finishBuffer($buf); + $smtp->start(); + } + + // -- Creation Methods + + protected function _createEventDispatcher($stub = true) + { + return $this->getMockery('Swift_Events_EventDispatcher')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php new file mode 100644 index 0000000..f49b489 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/AbstractSmtpTest.php @@ -0,0 +1,1249 @@ +_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + + $this->_finishBuffer($buf); + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->assertTrue($smtp->isStarted(), '%s: start() should have started connection'); + } catch (Exception $e) { + $this->fail('220 is a valid SMTP greeting and should be accepted'); + } + } + + public function testBadGreetingCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("554 I'm busy\r\n"); + $this->_finishBuffer($buf); + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->fail('554 greeting indicates an error and should cause an exception'); + } catch (Exception $e) { + $this->assertFalse($smtp->isStarted(), '%s: start() should have failed'); + } + } + + public function testStartSendsHeloToInitiate() + { + /* -- RFC 2821, 3.2. + + 3.2 Client Initiation + + Once the server has sent the welcoming message and the client has + received it, the client normally sends the EHLO command to the + server, indicating the client's identity. In addition to opening the + session, use of EHLO indicates that the client is able to process + service extensions and requests that the server provide a list of the + extensions it supports. Older SMTP systems which are unable to + support service extensions and contemporary clients which do not + require service extensions in the mail session being initiated, MAY + use HELO instead of EHLO. Servers MUST NOT return the extended + EHLO-style response to a HELO command. For a particular connection + attempt, if the server returns a "command not recognized" response to + EHLO, the client SHOULD be able to fall back and send HELO. + + In the EHLO command the host sending the command identifies itself; + the command may be interpreted as saying "Hello, I am " (and, + in the case of EHLO, "and I support service extension requests"). + + -- RFC 2281, 4.1.1.1. + + ehlo = "EHLO" SP Domain CRLF + helo = "HELO" SP Domain CRLF + + -- RFC 2821, 4.3.2. + + EHLO or HELO + S: 250 + E: 504, 550 + + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + } catch (Exception $e) { + $this->fail('Starting SMTP should send HELO and accept 250 response'); + } + } + + public function testInvalidHeloResponseCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('504 WTF'."\r\n"); + + $this->_finishBuffer($buf); + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->fail('Non 250 HELO response should raise Exception'); + } catch (Exception $e) { + $this->assertFalse($smtp->isStarted(), '%s: SMTP start() should have failed'); + } + } + + public function testDomainNameIsPlacedInHelo() + { + /* -- RFC 2821, 4.1.4. + + The SMTP client MUST, if possible, ensure that the domain parameter + to the EHLO command is a valid principal host name (not a CNAME or MX + name) for its host. If this is not possible (e.g., when the client's + address is dynamically assigned and the client does not have an + obvious name), an address literal SHOULD be substituted for the + domain name and supplemental information provided that will assist in + identifying the client. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("HELO mydomain.com\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->setLocalDomain('mydomain.com'); + $smtp->start(); + } + + public function testSuccessfulMailCommand() + { + /* -- RFC 2821, 3.3. + + There are three steps to SMTP mail transactions. The transaction + starts with a MAIL command which gives the sender identification. + + ..... + + The first step in the procedure is the MAIL command. + + MAIL FROM: [SP ] + + -- RFC 2821, 4.1.1.2. + + Syntax: + + "MAIL FROM:" ("<>" / Reverse-Path) + [SP Mail-parameters] CRLF + -- RFC 2821, 4.1.2. + + Reverse-path = Path + Forward-path = Path + Path = "<" [ A-d-l ":" ] Mailbox ">" + A-d-l = At-domain *( "," A-d-l ) + ; Note that this form, the so-called "source route", + ; MUST BE accepted, SHOULD NOT be generated, and SHOULD be + ; ignored. + At-domain = "@" domain + + -- RFC 2821, 4.3.2. + + MAIL + S: 250 + E: 552, 451, 452, 550, 553, 503 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + } catch (Exception $e) { + $this->fail('MAIL FROM should accept a 250 response'); + } + } + + public function testInvalidResponseCodeFromMailCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('553 Bad'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + $this->fail('MAIL FROM should accept a 250 response'); + } catch (Exception $e) { + } + } + + public function testSenderIsPreferredOverFrom() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getSender') + ->once() + ->andReturn(array('another@domain.com' => 'Someone')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testReturnPathIsPreferredOverSender() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getSender') + ->once() + ->andReturn(array('another@domain.com' => 'Someone')); + $message->shouldReceive('getReturnPath') + ->once() + ->andReturn('more@domain.com'); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testSuccessfulRcptCommandWith250Response() + { + /* -- RFC 2821, 3.3. + + The second step in the procedure is the RCPT command. + + RCPT TO: [ SP ] + + The first or only argument to this command includes a forward-path + (normally a mailbox and domain, always surrounded by "<" and ">" + brackets) identifying one recipient. If accepted, the SMTP server + returns a 250 OK reply and stores the forward-path. If the recipient + is known not to be a deliverable address, the SMTP server returns a + 550 reply, typically with a string such as "no such user - " and the + mailbox name (other circumstances and reply codes are possible). + This step of the procedure can be repeated any number of times. + + -- RFC 2821, 4.1.1.3. + + This command is used to identify an individual recipient of the mail + data; multiple recipients are specified by multiple use of this + command. The argument field contains a forward-path and may contain + optional parameters. + + The forward-path normally consists of the required destination + mailbox. Sending systems SHOULD not generate the optional list of + hosts known as a source route. + + ....... + + "RCPT TO:" ("" / "" / Forward-Path) + [SP Rcpt-parameters] CRLF + + -- RFC 2821, 4.2.2. + + 250 Requested mail action okay, completed + 251 User not local; will forward to + (See section 3.4) + 252 Cannot VRFY user, but will accept message and attempt + delivery + + -- RFC 2821, 4.3.2. + + RCPT + S: 250, 251 (but see section 3.4 for discussion of 251 and 551) + E: 550, 551, 552, 553, 450, 451, 452, 503, 550 + */ + + //We'll treat 252 as accepted since it isn't really a failure + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + } catch (Exception $e) { + $this->fail('RCPT TO should accept a 250 response'); + } + } + + public function testMailFromCommandIsOnlySentOncePerMessage() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->never() + ->with("MAIL FROM:\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testMultipleRecipientsSendsMultipleRcpt() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array( + 'foo@bar' => null, + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testCcRecipientsSendsMultipleRcpt() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getCc') + ->once() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testSendReturnsNumberOfSuccessfulRecipients() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getCc') + ->once() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('501 Nobody here'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(2, $smtp->send($message), + '%s: 1 of 3 recipients failed so 2 should be returned' + ); + } + + public function testRsetIsSentIfNoSuccessfulRecipients() + { + /* --RFC 2821, 4.1.1.5. + + This command specifies that the current mail transaction will be + aborted. Any stored sender, recipients, and mail data MUST be + discarded, and all buffers and state tables cleared. The receiver + MUST send a "250 OK" reply to a RSET command with no arguments. A + reset command may be issued by the client at any time. + + -- RFC 2821, 4.3.2. + + RSET + S: 250 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('503 Bad'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RSET\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(0, $smtp->send($message), + '%s: 1 of 1 recipients failed so 0 should be returned' + ); + } + + public function testSuccessfulDataCommand() + { + /* -- RFC 2821, 3.3. + + The third step in the procedure is the DATA command (or some + alternative specified in a service extension). + + DATA + + If accepted, the SMTP server returns a 354 Intermediate reply and + considers all succeeding lines up to but not including the end of + mail data indicator to be the message text. + + -- RFC 2821, 4.1.1.4. + + The receiver normally sends a 354 response to DATA, and then treats + the lines (strings ending in sequences, as described in + section 2.3.7) following the command as mail data from the sender. + This command causes the mail data to be appended to the mail data + buffer. The mail data may contain any of the 128 ASCII character + codes, although experience has indicated that use of control + characters other than SP, HT, CR, and LF may cause problems and + SHOULD be avoided when possible. + + -- RFC 2821, 4.3.2. + + DATA + I: 354 -> data -> S: 250 + E: 552, 554, 451, 452 + E: 451, 554, 503 + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('354 Go ahead'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + } catch (Exception $e) { + $this->fail('354 is the expected response to DATA'); + } + } + + public function testBadDataResponseCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('451 Bad'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + $this->fail('354 is the expected response to DATA (not observed)'); + } catch (Exception $e) { + } + } + + public function testMessageIsStreamedToBufferForData() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('354 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("\r\n.\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 OK'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testBadResponseAfterDataTransmissionCausesException() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->once() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->once() + ->andReturn(array('foo@bar' => null)); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('354 OK'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("\r\n.\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('554 Error'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + $smtp->send($message); + $this->fail('250 is the expected response after a DATA transmission (not observed)'); + } catch (Exception $e) { + } + } + + public function testBccRecipientsAreRemovedFromHeaders() + { + /* -- RFC 2821, 7.2. + + Addresses that do not appear in the message headers may appear in the + RCPT commands to an SMTP server for a number of reasons. The two + most common involve the use of a mailing address as a "list exploder" + (a single address that resolves into multiple addresses) and the + appearance of "blind copies". Especially when more than one RCPT + command is present, and in order to avoid defeating some of the + purpose of these mechanisms, SMTP clients and servers SHOULD NOT copy + the full set of RCPT command arguments into the headers, either as + part of trace headers or as informational or private-extension + headers. Since this rule is often violated in practice, and cannot + be enforced, sending SMTP systems that are aware of "bcc" use MAY + find it helpful to send each blind copy as a separate message + transaction containing only a single RCPT command. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->zeroOrMoreTimes(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + public function testEachBccRecipientIsSentASeparateMessage() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->once() + ->with(array('zip@button' => 'Zip Button')); + $message->shouldReceive('setBcc') + ->once() + ->with(array('test@domain' => 'Test user')); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(1); + $buf->shouldReceive('readLine')->once()->with(1)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(2); + $buf->shouldReceive('readLine')->once()->with(2)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(3); + $buf->shouldReceive('readLine')->once()->with(3)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(4); + $buf->shouldReceive('readLine')->once()->with(4)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(5); + $buf->shouldReceive('readLine')->once()->with(5)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(6); + $buf->shouldReceive('readLine')->once()->with(6)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(7); + $buf->shouldReceive('readLine')->once()->with(7)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(8); + $buf->shouldReceive('readLine')->once()->with(8)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(9); + $buf->shouldReceive('readLine')->once()->with(9)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(10); + $buf->shouldReceive('readLine')->once()->with(10)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(11); + $buf->shouldReceive('readLine')->once()->with(11)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(12); + $buf->shouldReceive('readLine')->once()->with(12)->andReturn("250 OK\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(3, $smtp->send($message)); + } + + public function testMessageStateIsRestoredOnFailure() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->once() + ->with(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("DATA\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn("451 No\r\n"); + + $this->_finishBuffer($buf); + + $smtp->start(); + try { + $smtp->send($message); + $this->fail('A bad response was given so exception is expected'); + } catch (Exception $e) { + } + } + + public function testStopSendsQuitCommand() + { + /* -- RFC 2821, 4.1.1.10. + + This command specifies that the receiver MUST send an OK reply, and + then close the transmission channel. + + The receiver MUST NOT intentionally close the transmission channel + until it receives and replies to a QUIT command (even if there was an + error). The sender MUST NOT intentionally close the transmission + channel until it sends a QUIT command and SHOULD wait until it + receives the reply (even if there was an error response to a previous + command). If the connection is closed prematurely due to violations + of the above or system or network failure, the server MUST cancel any + pending transaction, but not undo any previously completed + transaction, and generally MUST act as if the command or transaction + in progress had received a temporary error (i.e., a 4yz response). + + The QUIT command may be issued at any time. + + Syntax: + "QUIT" CRLF + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('write') + ->once() + ->with("QUIT\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("221 Bye\r\n"); + $buf->shouldReceive('terminate') + ->once(); + + $this->_finishBuffer($buf); + + $this->assertFalse($smtp->isStarted()); + $smtp->start(); + $this->assertTrue($smtp->isStarted()); + $smtp->stop(); + $this->assertFalse($smtp->isStarted()); + } + + public function testBufferCanBeFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ref = $smtp->getBuffer(); + $this->assertEquals($buf, $ref); + } + + public function testBufferCanBeWrittenToUsingExecuteCommand() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("FOO\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with(1) + ->andReturn("250 OK\r\n"); + + $res = $smtp->executeCommand("FOO\r\n"); + $this->assertEquals("250 OK\r\n", $res); + } + + public function testResponseCodesAreValidated() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("FOO\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with(1) + ->andReturn("551 Not ok\r\n"); + + try { + $smtp->executeCommand("FOO\r\n", array(250, 251)); + $this->fail('A 250 or 251 response was needed but 551 was returned.'); + } catch (Exception $e) { + } + } + + public function testFailedRecipientsCanBeCollectedByReference() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('getBcc') + ->zeroOrMoreTimes() + ->andReturn(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array()); + $message->shouldReceive('setBcc') + ->once() + ->with(array('zip@button' => 'Zip Button')); + $message->shouldReceive('setBcc') + ->once() + ->with(array('test@domain' => 'Test user')); + $message->shouldReceive('setBcc') + ->atLeast()->once() + ->with(array( + 'zip@button' => 'Zip Button', + 'test@domain' => 'Test user', + )); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(1); + $buf->shouldReceive('readLine')->once()->with(1)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(2); + $buf->shouldReceive('readLine')->once()->with(2)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("DATA\r\n")->andReturn(3); + $buf->shouldReceive('readLine')->once()->with(3)->andReturn("354 OK\r\n"); + $buf->shouldReceive('write')->once()->with("\r\n.\r\n")->andReturn(4); + $buf->shouldReceive('readLine')->once()->with(4)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(5); + $buf->shouldReceive('readLine')->once()->with(5)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(6); + $buf->shouldReceive('readLine')->once()->with(6)->andReturn("500 Bad\r\n"); + $buf->shouldReceive('write')->once()->with("RSET\r\n")->andReturn(7); + $buf->shouldReceive('readLine')->once()->with(7)->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write')->once()->with("MAIL FROM:\r\n")->andReturn(9); + $buf->shouldReceive('readLine')->once()->with(9)->andReturn("250 OK\r\n"); + $buf->shouldReceive('write')->once()->with("RCPT TO:\r\n")->andReturn(10); + $buf->shouldReceive('readLine')->once()->with(10)->andReturn("500 Bad\r\n"); + $buf->shouldReceive('write')->once()->with("RSET\r\n")->andReturn(11); + $buf->shouldReceive('readLine')->once()->with(11)->andReturn("250 OK\r\n"); + + $this->_finishBuffer($buf); + $smtp->start(); + $this->assertEquals(1, $smtp->send($message, $failures)); + $this->assertEquals(array('zip@button', 'test@domain'), $failures, + '%s: Failures should be caught in an array' + ); + } + + public function testSendingRegeneratesMessageId() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $message = $this->_createMessage(); + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain.com' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + $message->shouldReceive('generateId') + ->once(); + + $this->_finishBuffer($buf); + $smtp->start(); + $smtp->send($message); + } + + protected function _getBuffer() + { + return $this->getMockery('Swift_Transport_IoBuffer')->shouldIgnoreMissing(); + } + + protected function _createMessage() + { + return $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + } + + protected function _finishBuffer($buf) + { + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with(0) + ->andReturn('220 server.com foo'."\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with('~^(EH|HE)LO .*?\r\n$~D') + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn('250 ServerName'."\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with('~^MAIL FROM:<.*?>\r\n$~D') + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with('~^RCPT TO:<.*?>\r\n$~D') + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("DATA\r\n") + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("354 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("\r\n.\r\n") + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->with("RSET\r\n") + ->andReturn($x = uniqid()); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->with($x) + ->andReturn("250 OK\r\n"); + + $buf->shouldReceive('write') + ->zeroOrMoreTimes() + ->andReturn(false); + $buf->shouldReceive('readLine') + ->zeroOrMoreTimes() + ->andReturn(false); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php new file mode 100644 index 0000000..f64b071 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/CramMd5AuthenticatorTest.php @@ -0,0 +1,66 @@ +_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsCramMd5() + { + /* -- RFC 2195, 2. + The authentication type associated with CRAM is "CRAM-MD5". + */ + + $cram = $this->_getAuthenticator(); + $this->assertEquals('CRAM-MD5', $cram->getAuthKeyword()); + } + + public function testSuccessfulAuthentication() + { + $cram = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH CRAM-MD5\r\n", array(334)) + ->andReturn('334 '.base64_encode('')."\r\n"); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(\Mockery::any(), array(235)); + + $this->assertTrue($cram->authenticate($this->_agent, 'jack', 'pass'), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $cram = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH CRAM-MD5\r\n", array(334)) + ->andReturn('334 '.base64_encode('')."\r\n"); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(\Mockery::any(), array(235)) + ->andThrow(new Swift_TransportException('')); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($cram->authenticate($this->_agent, 'jack', 'pass'), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_CramMd5Authenticator(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php new file mode 100644 index 0000000..fc6e806 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/LoginAuthenticatorTest.php @@ -0,0 +1,66 @@ +_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsLogin() + { + $login = $this->_getAuthenticator(); + $this->assertEquals('LOGIN', $login->getAuthKeyword()); + } + + public function testSuccessfulAuthentication() + { + $login = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH LOGIN\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('jack')."\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('pass')."\r\n", array(235)); + + $this->assertTrue($login->authenticate($this->_agent, 'jack', 'pass'), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $login = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("AUTH LOGIN\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('jack')."\r\n", array(334)); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode('pass')."\r\n", array(235)) + ->andThrow(new Swift_TransportException('')); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($login->authenticate($this->_agent, 'jack', 'pass'), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_LoginAuthenticator(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php new file mode 100644 index 0000000..dd26294 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/NTLMAuthenticatorTest.php @@ -0,0 +1,235 @@ +markTestSkipped( + 'One of the required functions is not available.' + ); + } + } + + public function testKeywordIsNtlm() + { + $login = $this->_getAuthenticator(); + $this->assertEquals('NTLM', $login->getAuthKeyword()); + } + + public function testMessage1Generator() + { + $login = $this->_getAuthenticator(); + $message1 = $this->_invokePrivateMethod('createMessage1', $login); + + $this->assertEquals($this->_message1, bin2hex($message1), + '%s: We send the smallest ntlm message which should never fail.' + ); + } + + public function testLMv1Generator() + { + $password = 'test1234'; + $challenge = 'b019d38bad875c9d'; + $lmv1 = '1879f60127f8a877022132ec221bcbf3ca016a9f76095606'; + + $login = $this->_getAuthenticator(); + $lmv1Result = $this->_invokePrivateMethod('createLMPassword', $login, array($password, $this->hex2bin($challenge))); + + $this->assertEquals($lmv1, bin2hex($lmv1Result), + '%s: The keys should be the same cause we use the same values to generate them.' + ); + } + + public function testLMv2Generator() + { + $username = 'user'; + $password = 'SecREt01'; + $domain = 'DOMAIN'; + $challenge = '0123456789abcdef'; + $lmv2 = 'd6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344'; + + $login = $this->_getAuthenticator(); + $lmv2Result = $this->_invokePrivateMethod('createLMv2Password', $login, array($password, $username, $domain, $this->hex2bin($challenge), $this->hex2bin('ffffff0011223344'))); + + $this->assertEquals($lmv2, bin2hex($lmv2Result), + '%s: The keys should be the same cause we use the same values to generate them.' + ); + } + + public function testMessage3v1Generator() + { + $username = 'test'; + $domain = 'TESTNT'; + $workstation = 'MEMBER'; + $lmResponse = '1879f60127f8a877022132ec221bcbf3ca016a9f76095606'; + $ntlmResponse = 'e6285df3287c5d194f84df1a94817c7282d09754b6f9e02a'; + $message3T = '4e544c4d5353500003000000180018006000000018001800780000000c000c0040000000080008004c0000000c000c0054000000000000009a0000000102000054004500530054004e00540074006500730074004d0045004d004200450052001879f60127f8a877022132ec221bcbf3ca016a9f76095606e6285df3287c5d194f84df1a94817c7282d09754b6f9e02a'; + + $login = $this->_getAuthenticator(); + $message3 = $this->_invokePrivateMethod('createMessage3', $login, array($domain, $username, $workstation, $this->hex2bin($lmResponse), $this->hex2bin($ntlmResponse))); + + $this->assertEquals($message3T, bin2hex($message3), + '%s: We send the same information as the example is created with so this should be the same' + ); + } + + public function testMessage3v2Generator() + { + $username = 'test'; + $domain = 'TESTNT'; + $workstation = 'MEMBER'; + $lmResponse = 'bf2e015119f6bdb3f6fdb768aa12d478f5ce3d2401c8f6e9'; + $ntlmResponse = 'caa4da8f25d5e840974ed8976d3ada46010100000000000030fa7e3c677bc301f5ce3d2401c8f6e90000000002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d000000000000000000'; + + $login = $this->_getAuthenticator(); + $message3 = $this->_invokePrivateMethod('createMessage3', $login, array($domain, $username, $workstation, $this->hex2bin($lmResponse), $this->hex2bin($ntlmResponse))); + + $this->assertEquals($this->_message3, bin2hex($message3), + '%s: We send the same information as the example is created with so this should be the same' + ); + } + + public function testGetDomainAndUsername() + { + $username = "DOMAIN\user"; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('DOMAIN', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testGetDomainAndUsernameWithExtension() + { + $username = "domain.com\user"; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('domain.com', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testGetDomainAndUsernameWithAtSymbol() + { + $username = 'user@DOMAIN'; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('DOMAIN', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testGetDomainAndUsernameWithAtSymbolAndExtension() + { + $username = 'user@domain.com'; + + $login = $this->_getAuthenticator(); + list($domain, $user) = $this->_invokePrivateMethod('getDomainAndUsername', $login, array($username)); + + $this->assertEquals('domain.com', $domain, + '%s: the fetched domain did not match' + ); + $this->assertEquals('user', $user, + '%s: the fetched user did not match' + ); + } + + public function testSuccessfulAuthentication() + { + $domain = 'TESTNT'; + $username = 'test'; + $secret = 'test1234'; + + $ntlm = $this->_getAuthenticator(); + $agent = $this->_getAgent(); + $agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH NTLM '.base64_encode( + $this->_invokePrivateMethod('createMessage1', $ntlm) + )."\r\n", array(334)) + ->andReturn('334 '.base64_encode($this->hex2bin('4e544c4d53535000020000000c000c003000000035828980514246973ea892c10000000000000000460046003c00000054004500530054004e00540002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d0000000000'))); + $agent->shouldReceive('executeCommand') + ->once() + ->with(base64_encode( + $this->_invokePrivateMethod('createMessage3', $ntlm, array($domain, $username, $this->hex2bin('4d0045004d00420045005200'), $this->hex2bin('bf2e015119f6bdb3f6fdb768aa12d478f5ce3d2401c8f6e9'), $this->hex2bin('caa4da8f25d5e840974ed8976d3ada46010100000000000030fa7e3c677bc301f5ce3d2401c8f6e90000000002000c0054004500530054004e00540001000c004d0045004d0042004500520003001e006d0065006d006200650072002e0074006500730074002e0063006f006d000000000000000000')) + ))."\r\n", array(235)); + + $this->assertTrue($ntlm->authenticate($agent, $username.'@'.$domain, $secret, $this->hex2bin('30fa7e3c677bc301'), $this->hex2bin('f5ce3d2401c8f6e9')), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $domain = 'TESTNT'; + $username = 'test'; + $secret = 'test1234'; + + $ntlm = $this->_getAuthenticator(); + $agent = $this->_getAgent(); + $agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH NTLM '.base64_encode( + $this->_invokePrivateMethod('createMessage1', $ntlm) + )."\r\n", array(334)) + ->andThrow(new Swift_TransportException('')); + $agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($ntlm->authenticate($agent, $username.'@'.$domain, $secret, $this->hex2bin('30fa7e3c677bc301'), $this->hex2bin('f5ce3d2401c8f6e9')), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_NTLMAuthenticator(); + } + + private function _getAgent() + { + return $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + private function _invokePrivateMethod($method, $instance, array $args = array()) + { + $methodC = new ReflectionMethod($instance, trim($method)); + $methodC->setAccessible(true); + + return $methodC->invokeArgs($instance, $args); + } + + /** + * Hex2bin replacement for < PHP 5.4. + * + * @param string $hex + * + * @return string Binary + */ + protected function hex2bin($hex) + { + return function_exists('hex2bin') ? hex2bin($hex) : pack('H*', $hex); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php new file mode 100644 index 0000000..4fe9db8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/Auth/PlainAuthenticatorTest.php @@ -0,0 +1,69 @@ +_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsPlain() + { + /* -- RFC 4616, 1. + The name associated with this mechanism is "PLAIN". + */ + + $login = $this->_getAuthenticator(); + $this->assertEquals('PLAIN', $login->getAuthKeyword()); + } + + public function testSuccessfulAuthentication() + { + /* -- RFC 4616, 2. + The client presents the authorization identity (identity to act as), + followed by a NUL (U+0000) character, followed by the authentication + identity (identity whose password will be used), followed by a NUL + (U+0000) character, followed by the clear-text password. + */ + + $plain = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH PLAIN '.base64_encode( + 'jack'.chr(0).'jack'.chr(0).'pass' + )."\r\n", array(235)); + + $this->assertTrue($plain->authenticate($this->_agent, 'jack', 'pass'), + '%s: The buffer accepted all commands authentication should succeed' + ); + } + + public function testAuthenticationFailureSendRsetAndReturnFalse() + { + $plain = $this->_getAuthenticator(); + + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with('AUTH PLAIN '.base64_encode( + 'jack'.chr(0).'jack'.chr(0).'pass' + )."\r\n", array(235)) + ->andThrow(new Swift_TransportException('')); + $this->_agent->shouldReceive('executeCommand') + ->once() + ->with("RSET\r\n", array(250)); + + $this->assertFalse($plain->authenticate($this->_agent, 'jack', 'pass'), + '%s: Authentication fails, so RSET should be sent' + ); + } + + // -- Private helpers + + private function _getAuthenticator() + { + return new Swift_Transport_Esmtp_Auth_PlainAuthenticator(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php new file mode 100644 index 0000000..64327d4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/Esmtp/AuthHandlerTest.php @@ -0,0 +1,167 @@ +_agent = $this->getMockery('Swift_Transport_SmtpAgent')->shouldIgnoreMissing(); + } + + public function testKeywordIsAuth() + { + $auth = $this->_createHandler(array()); + $this->assertEquals('AUTH', $auth->getHandledKeyword()); + } + + public function testUsernameCanBeSetAndFetched() + { + $auth = $this->_createHandler(array()); + $auth->setUsername('jack'); + $this->assertEquals('jack', $auth->getUsername()); + } + + public function testPasswordCanBeSetAndFetched() + { + $auth = $this->_createHandler(array()); + $auth->setPassword('pass'); + $this->assertEquals('pass', $auth->getPassword()); + } + + public function testAuthModeCanBeSetAndFetched() + { + $auth = $this->_createHandler(array()); + $auth->setAuthMode('PLAIN'); + $this->assertEquals('PLAIN', $auth->getAuthMode()); + } + + public function testMixinMethods() + { + $auth = $this->_createHandler(array()); + $mixins = $auth->exposeMixinMethods(); + $this->assertTrue(in_array('getUsername', $mixins), + '%s: getUsername() should be accessible via mixin' + ); + $this->assertTrue(in_array('setUsername', $mixins), + '%s: setUsername() should be accessible via mixin' + ); + $this->assertTrue(in_array('getPassword', $mixins), + '%s: getPassword() should be accessible via mixin' + ); + $this->assertTrue(in_array('setPassword', $mixins), + '%s: setPassword() should be accessible via mixin' + ); + $this->assertTrue(in_array('setAuthMode', $mixins), + '%s: setAuthMode() should be accessible via mixin' + ); + $this->assertTrue(in_array('getAuthMode', $mixins), + '%s: getAuthMode() should be accessible via mixin' + ); + } + + public function testAuthenticatorsAreCalledAccordingToParamsAfterEhlo() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + + $a1->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass'); + $a2->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + + $auth = $this->_createHandler(array($a1, $a2)); + $auth->setUsername('jack'); + $auth->setPassword('pass'); + + $auth->setKeywordParams(array('CRAM-MD5', 'LOGIN')); + $auth->afterEhlo($this->_agent); + } + + public function testAuthenticatorsAreNotUsedIfNoUsernameSet() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + + $a1->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass'); + $a2->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + + $auth = $this->_createHandler(array($a1, $a2)); + + $auth->setKeywordParams(array('CRAM-MD5', 'LOGIN')); + $auth->afterEhlo($this->_agent); + } + + public function testSeveralAuthenticatorsAreTriedIfNeeded() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + + $a1->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(false); + $a2->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + + $auth = $this->_createHandler(array($a1, $a2)); + $auth->setUsername('jack'); + $auth->setPassword('pass'); + + $auth->setKeywordParams(array('PLAIN', 'LOGIN')); + $auth->afterEhlo($this->_agent); + } + + public function testFirstAuthenticatorToPassBreaksChain() + { + $a1 = $this->_createMockAuthenticator('PLAIN'); + $a2 = $this->_createMockAuthenticator('LOGIN'); + $a3 = $this->_createMockAuthenticator('CRAM-MD5'); + + $a1->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(false); + $a2->shouldReceive('authenticate') + ->once() + ->with($this->_agent, 'jack', 'pass') + ->andReturn(true); + $a3->shouldReceive('authenticate') + ->never() + ->with($this->_agent, 'jack', 'pass'); + + $auth = $this->_createHandler(array($a1, $a2)); + $auth->setUsername('jack'); + $auth->setPassword('pass'); + + $auth->setKeywordParams(array('PLAIN', 'LOGIN', 'CRAM-MD5')); + $auth->afterEhlo($this->_agent); + } + + // -- Private helpers + + private function _createHandler($authenticators) + { + return new Swift_Transport_Esmtp_AuthHandler($authenticators); + } + + private function _createMockAuthenticator($type) + { + $authenticator = $this->getMockery('Swift_Transport_Esmtp_Authenticator')->shouldIgnoreMissing(); + $authenticator->shouldReceive('getAuthKeyword') + ->zeroOrMoreTimes() + ->andReturn($type); + + return $authenticator; + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php new file mode 100644 index 0000000..44e5413 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransport/ExtensionSupportTest.php @@ -0,0 +1,530 @@ +_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('STARTTLS') + ->andReturn(0); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext2->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(-1); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $this->assertEquals(array($ext2, $ext1), $smtp->getExtensionHandlers()); + } + + public function testHandlersAreNotifiedOfParams() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('setKeywordParams') + ->once() + ->with(array('PLAIN', 'LOGIN')); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('setKeywordParams') + ->zeroOrMoreTimes() + ->with(array('123456')); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $smtp->start(); + } + + public function testSupportedExtensionHandlersAreRunAfterEhlo() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('afterEhlo') + ->once() + ->with($smtp); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('afterEhlo') + ->zeroOrMoreTimes() + ->with($smtp); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('afterEhlo') + ->never() + ->with($smtp); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + } + + public function testExtensionsCanModifyMailFromParams() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(); + $smtp = new EsmtpTransportFixture($buf, array(), $dispatcher); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .*?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM: FOO ZIP\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO:\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn("250 OK\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('getMailParams') + ->once() + ->andReturn('FOO'); + $ext1->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(-1); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('getMailParams') + ->once() + ->andReturn('ZIP'); + $ext2->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(1); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('getMailParams') + ->never(); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->send($message); + } + + public function testExtensionsCanModifyRcptParams() + { + $buf = $this->_getBuffer(); + $dispatcher = $this->_createEventDispatcher(); + $smtp = new EsmtpTransportFixture($buf, array(), $dispatcher); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $message = $this->_createMessage(); + + $message->shouldReceive('getFrom') + ->zeroOrMoreTimes() + ->andReturn(array('me@domain' => 'Me')); + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null)); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("MAIL FROM:\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 OK\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("RCPT TO: FOO ZIP\r\n") + ->andReturn(3); + $buf->shouldReceive('readLine') + ->once() + ->with(3) + ->andReturn("250 OK\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('getRcptParams') + ->once() + ->andReturn('FOO'); + $ext1->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(-1); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('getRcptParams') + ->once() + ->andReturn('ZIP'); + $ext2->shouldReceive('getPriorityOver') + ->zeroOrMoreTimes() + ->with('AUTH') + ->andReturn(1); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('getRcptParams') + ->never(); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->send($message); + } + + public function testExtensionsAreNotifiedOnCommand() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("FOO\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn("250 Cool\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('onCommand') + ->once() + ->with($smtp, "FOO\r\n", array(250, 251), \Mockery::any(), \Mockery::any()); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('onCommand') + ->once() + ->with($smtp, "FOO\r\n", array(250, 251), \Mockery::any(), \Mockery::any()); + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('onCommand') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->executeCommand("FOO\r\n", array(250, 251)); + } + + public function testChainOfCommandAlgorithmWhenNotifyingExtensions() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + $ext3 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 server.com foo\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-ServerName.tld\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250-AUTH PLAIN LOGIN\r\n"); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn("250 SIZE=123456\r\n"); + $buf->shouldReceive('write') + ->never() + ->with("FOO\r\n"); + $this->_finishBuffer($buf); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('onCommand') + ->once() + ->with($smtp, "FOO\r\n", array(250, 251), \Mockery::any(), \Mockery::any()) + ->andReturnUsing(function ($a, $b, $c, $d, &$e) { + $e = true; + + return '250 ok'; + }); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('SIZE'); + $ext2->shouldReceive('onCommand') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $ext3->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $ext3->shouldReceive('onCommand') + ->never() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $smtp->setExtensionHandlers(array($ext1, $ext2, $ext3)); + $smtp->start(); + $smtp->executeCommand("FOO\r\n", array(250, 251)); + } + + public function testExtensionsCanExposeMixinMethods() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandlerMixin')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('exposeMixinMethods') + ->zeroOrMoreTimes() + ->andReturn(array('setUsername', 'setPassword')); + $ext1->shouldReceive('setUsername') + ->once() + ->with('mick'); + $ext1->shouldReceive('setPassword') + ->once() + ->with('pass'); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $smtp->setUsername('mick'); + $smtp->setPassword('pass'); + } + + public function testMixinMethodsBeginningWithSetAndNullReturnAreFluid() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandlerMixin')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('exposeMixinMethods') + ->zeroOrMoreTimes() + ->andReturn(array('setUsername', 'setPassword')); + $ext1->shouldReceive('setUsername') + ->once() + ->with('mick') + ->andReturn(null); + $ext1->shouldReceive('setPassword') + ->once() + ->with('pass') + ->andReturn(null); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $ret = $smtp->setUsername('mick'); + $this->assertEquals($smtp, $ret); + $ret = $smtp->setPassword('pass'); + $this->assertEquals($smtp, $ret); + } + + public function testMixinSetterWhichReturnValuesAreNotFluid() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $ext1 = $this->getMockery('Swift_Transport_EsmtpHandlerMixin')->shouldIgnoreMissing(); + $ext2 = $this->getMockery('Swift_Transport_EsmtpHandler')->shouldIgnoreMissing(); + + $ext1->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('AUTH'); + $ext1->shouldReceive('exposeMixinMethods') + ->zeroOrMoreTimes() + ->andReturn(array('setUsername', 'setPassword')); + $ext1->shouldReceive('setUsername') + ->once() + ->with('mick') + ->andReturn('x'); + $ext1->shouldReceive('setPassword') + ->once() + ->with('pass') + ->andReturn('x'); + $ext2->shouldReceive('getHandledKeyword') + ->zeroOrMoreTimes() + ->andReturn('STARTTLS'); + $this->_finishBuffer($buf); + + $smtp->setExtensionHandlers(array($ext1, $ext2)); + $this->assertEquals('x', $smtp->setUsername('mick')); + $this->assertEquals('x', $smtp->setPassword('pass')); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php new file mode 100644 index 0000000..95a9ce2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/EsmtpTransportTest.php @@ -0,0 +1,298 @@ +_createEventDispatcher(); + } + + return new Swift_Transport_EsmtpTransport($buf, array(), $dispatcher); + } + + public function testHostCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $smtp->setHost('foo'); + $this->assertEquals('foo', $smtp->getHost(), '%s: Host should be returned'); + } + + public function testPortCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $smtp->setPort(25); + $this->assertEquals(25, $smtp->getPort(), '%s: Port should be returned'); + } + + public function testTimeoutCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $buf->shouldReceive('setParam') + ->once() + ->with('timeout', 10); + + $smtp = $this->_getTransport($buf); + $smtp->setTimeout(10); + $this->assertEquals(10, $smtp->getTimeout(), '%s: Timeout should be returned'); + } + + public function testEncryptionCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $smtp->setEncryption('tls'); + $this->assertEquals('tls', $smtp->getEncryption(), '%s: Crypto should be returned'); + } + + public function testStartSendsHeloToInitiate() + { + //Overridden for EHLO instead + } + + public function testStartSendsEhloToInitiate() + { + /* -- RFC 2821, 3.2. + + 3.2 Client Initiation + + Once the server has sent the welcoming message and the client has + received it, the client normally sends the EHLO command to the + server, indicating the client's identity. In addition to opening the + session, use of EHLO indicates that the client is able to process + service extensions and requests that the server provide a list of the + extensions it supports. Older SMTP systems which are unable to + support service extensions and contemporary clients which do not + require service extensions in the mail session being initiated, MAY + use HELO instead of EHLO. Servers MUST NOT return the extended + EHLO-style response to a HELO command. For a particular connection + attempt, if the server returns a "command not recognized" response to + EHLO, the client SHOULD be able to fall back and send HELO. + + In the EHLO command the host sending the command identifies itself; + the command may be interpreted as saying "Hello, I am " (and, + in the case of EHLO, "and I support service extension requests"). + + -- RFC 2281, 4.1.1.1. + + ehlo = "EHLO" SP Domain CRLF + helo = "HELO" SP Domain CRLF + + -- RFC 2821, 4.3.2. + + EHLO or HELO + S: 250 + E: 504, 550 + + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + } catch (Exception $e) { + $this->fail('Starting Esmtp should send EHLO and accept 250 response'); + } + } + + public function testHeloIsUsedAsFallback() + { + /* -- RFC 2821, 4.1.4. + + If the EHLO command is not acceptable to the SMTP server, 501, 500, + or 502 failure replies MUST be returned as appropriate. The SMTP + server MUST stay in the same state after transmitting these replies + that it was in before the EHLO was received. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('501 WTF'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .+?\r\n$~D') + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 HELO'."\r\n"); + + $this->_finishBuffer($buf); + try { + $smtp->start(); + } catch (Exception $e) { + $this->fail( + 'Starting Esmtp should fallback to HELO if needed and accept 250 response' + ); + } + } + + public function testInvalidHeloResponseCausesException() + { + //Overridden to first try EHLO + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('501 WTF'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^HELO .+?\r\n$~D') + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('504 WTF'."\r\n"); + $this->_finishBuffer($buf); + + try { + $this->assertFalse($smtp->isStarted(), '%s: SMTP should begin non-started'); + $smtp->start(); + $this->fail('Non 250 HELO response should raise Exception'); + } catch (Exception $e) { + $this->assertFalse($smtp->isStarted(), '%s: SMTP start() should have failed'); + } + } + + public function testDomainNameIsPlacedInEhlo() + { + /* -- RFC 2821, 4.1.4. + + The SMTP client MUST, if possible, ensure that the domain parameter + to the EHLO command is a valid principal host name (not a CNAME or MX + name) for its host. If this is not possible (e.g., when the client's + address is dynamically assigned and the client does not have an + obvious name), an address literal SHOULD be substituted for the + domain name and supplemental information provided that will assist in + identifying the client. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("EHLO mydomain.com\r\n") + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->setLocalDomain('mydomain.com'); + $smtp->start(); + } + + public function testDomainNameIsPlacedInHelo() + { + //Overridden to include ESMTP + /* -- RFC 2821, 4.1.4. + + The SMTP client MUST, if possible, ensure that the domain parameter + to the EHLO command is a valid principal host name (not a CNAME or MX + name) for its host. If this is not possible (e.g., when the client's + address is dynamically assigned and the client does not have an + obvious name), an address literal SHOULD be substituted for the + domain name and supplemental information provided that will assist in + identifying the client. + */ + + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('readLine') + ->once() + ->with(0) + ->andReturn("220 some.server.tld bleh\r\n"); + $buf->shouldReceive('write') + ->once() + ->with('~^EHLO .+?\r\n$~D') + ->andReturn(1); + $buf->shouldReceive('readLine') + ->once() + ->with(1) + ->andReturn('501 WTF'."\r\n"); + $buf->shouldReceive('write') + ->once() + ->with("HELO mydomain.com\r\n") + ->andReturn(2); + $buf->shouldReceive('readLine') + ->once() + ->with(2) + ->andReturn('250 ServerName'."\r\n"); + + $this->_finishBuffer($buf); + $smtp->setLocalDomain('mydomain.com'); + $smtp->start(); + } + + public function testFluidInterface() + { + $buf = $this->_getBuffer(); + $smtp = $this->_getTransport($buf); + $buf->shouldReceive('setParam') + ->once() + ->with('timeout', 30); + + $ref = $smtp + ->setHost('foo') + ->setPort(25) + ->setEncryption('tls') + ->setTimeout(30) + ; + $this->assertEquals($ref, $smtp); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php new file mode 100644 index 0000000..8d80f35 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/FailoverTransportTest.php @@ -0,0 +1,520 @@ +getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $t1->shouldReceive('send') + ->twice() + ->with(\Mockery::anyOf($message1, $message2), \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState) { + if ($connectionState) { + return 1; + } + }); + $t2->shouldReceive('start')->never(); + $t2->shouldReceive('send')->never(); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + } + + public function testMessageCanBeTriedOnNextTransportIfExceptionThrown() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message)); + } + + public function testZeroIsReturnedIfTransportReturnsZero() + { + $message = $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + $t1 = $this->getMockery('Swift_Transport')->shouldIgnoreMissing(); + + $connectionState = false; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $testCase = $this; + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState, $testCase) { + if (!$connectionState) { + $testCase->fail(); + } + + return 0; + }); + + $transport = $this->_getTransport(array($t1)); + $transport->start(); + $this->assertEquals(0, $transport->send($message)); + } + + public function testTransportsWhichThrowExceptionsAreNotRetried() + { + $e = new Swift_TransportException('maur b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $message3 = $this->getMockery('Swift_Mime_Message'); + $message4 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message3, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message4, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->times(4) + ->with(\Mockery::anyOf($message1, $message2, $message3, $message4), \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + $this->assertEquals(1, $transport->send($message3)); + $this->assertEquals(1, $transport->send($message4)); + } + + public function testExceptionIsThrownIfAllTransportsDie() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + } + } + + public function testStoppingTransportStopsAllDelegates() + { + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $connectionState1 = true; + $connectionState2 = true; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + $connectionState1 = false; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + $connectionState2 = false; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $transport->stop(); + } + + public function testTransportShowsAsNotStartedIfAllDelegatesDead() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + $connectionState1 = false; + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + $connectionState2 = false; + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + } + + public function testRestartingTransportRestartsDeadDelegates() + { + $e = new Swift_TransportException('b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->twice() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + $connectionState1 = false; + throw $e; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + return 10; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + $connectionState2 = false; + throw $e; + } + }); + $t2->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message1); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + //Restart and re-try + $transport->start(); + $this->assertTrue($transport->isStarted()); + $this->assertEquals(10, $transport->send($message2)); + } + + public function testFailureReferenceIsPassedToDelegates() + { + $failures = array(); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + + $connectionState = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use ($connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use ($connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, $failures) + ->andReturnUsing(function () use ($connectionState) { + if ($connectionState) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1)); + $transport->start(); + $transport->send($message, $failures); + } + + public function testRegisterPluginDelegatesToLoadedTransports() + { + $plugin = $this->_createPlugin(); + + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $t1->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + $t2->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->registerPlugin($plugin); + } + + // -- Private helpers + + private function _getTransport(array $transports) + { + $transport = new Swift_Transport_FailoverTransport(); + $transport->setTransports($transports); + + return $transport; + } + + private function _createPlugin() + { + return $this->getMockery('Swift_Events_EventListener'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php new file mode 100644 index 0000000..273f135 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/LoadBalancedTransportTest.php @@ -0,0 +1,751 @@ +getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $testCase) { + if ($connectionState1) { + return 1; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + $t2->shouldReceive('send') + ->never() + ->with($message1, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + } + + public function testTransportsAreReusedInRotatingFashion() + { + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $message3 = $this->getMockery('Swift_Mime_Message'); + $message4 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $testCase) { + if ($connectionState1) { + return 1; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + $t1->shouldReceive('send') + ->once() + ->with($message3, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $testCase) { + if ($connectionState1) { + return 1; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message4, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + $t2->shouldReceive('send') + ->never() + ->with($message1, \Mockery::any()); + $t2->shouldReceive('send') + ->once() + ->with($message4, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + $t2->shouldReceive('send') + ->never() + ->with($message3, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + $this->assertEquals(1, $transport->send($message3)); + $this->assertEquals(1, $transport->send($message4)); + } + + public function testMessageCanBeTriedOnNextTransportIfExceptionThrown() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e, $testCase) { + if ($connectionState1) { + throw $e; + } + $testCase->fail(); + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message)); + } + + public function testMessageIsTriedOnNextTransportIfZeroReturned() + { + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + return 0; + } + + return 1; + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + return 1; + } + + return 0; + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message)); + } + + public function testZeroIsReturnedIfAllTransportsReturnZero() + { + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + return 0; + } + + return 1; + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + return 0; + } + + return 1; + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(0, $transport->send($message)); + } + + public function testTransportsWhichThrowExceptionsAreNotRetried() + { + $e = new Swift_TransportException('maur b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $message3 = $this->getMockery('Swift_Mime_Message'); + $message4 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $testCase = $this; + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e, $testCase) { + if ($connectionState1) { + throw $e; + } + $testCase->fail(); + }); + $t1->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message3, \Mockery::any()); + $t1->shouldReceive('send') + ->never() + ->with($message4, \Mockery::any()); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->times(4) + ->with(\Mockery::anyOf($message1, $message3, $message3, $message4), \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $testCase) { + if ($connectionState2) { + return 1; + } + $testCase->fail(); + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertEquals(1, $transport->send($message1)); + $this->assertEquals(1, $transport->send($message2)); + $this->assertEquals(1, $transport->send($message3)); + $this->assertEquals(1, $transport->send($message4)); + } + + public function testExceptionIsThrownIfAllTransportsDie() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + } + } + + public function testStoppingTransportStopsAllDelegates() + { + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = true; + $connectionState2 = true; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if ($connectionState1) { + $connectionState1 = false; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('stop') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if ($connectionState2) { + $connectionState2 = false; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $transport->stop(); + } + + public function testTransportShowsAsNotStartedIfAllDelegatesDead() + { + $e = new Swift_TransportException('b0rken'); + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + throw $e; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + } + + public function testRestartingTransportRestartsDeadDelegates() + { + $e = new Swift_TransportException('b0rken'); + + $message1 = $this->getMockery('Swift_Mime_Message'); + $message2 = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + $connectionState1 = false; + $connectionState2 = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState1) { + return $connectionState1; + }); + $t1->shouldReceive('start') + ->twice() + ->andReturnUsing(function () use (&$connectionState1) { + if (!$connectionState1) { + $connectionState1 = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + $connectionState1 = false; + throw $e; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message2, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState1, $e) { + if ($connectionState1) { + return 10; + } + }); + + $t2->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState2) { + return $connectionState2; + }); + $t2->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState2) { + if (!$connectionState2) { + $connectionState2 = true; + } + }); + $t2->shouldReceive('send') + ->once() + ->with($message1, \Mockery::any()) + ->andReturnUsing(function () use (&$connectionState2, $e) { + if ($connectionState2) { + throw $e; + } + }); + $t2->shouldReceive('send') + ->never() + ->with($message2, \Mockery::any()); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->start(); + $this->assertTrue($transport->isStarted()); + try { + $transport->send($message1); + $this->fail('All transports failed so Exception should be thrown'); + } catch (Exception $e) { + $this->assertFalse($transport->isStarted()); + } + //Restart and re-try + $transport->start(); + $this->assertTrue($transport->isStarted()); + $this->assertEquals(10, $transport->send($message2)); + } + + public function testFailureReferenceIsPassedToDelegates() + { + $failures = array(); + $testCase = $this; + + $message = $this->getMockery('Swift_Mime_Message'); + $t1 = $this->getMockery('Swift_Transport'); + $connectionState = false; + + $t1->shouldReceive('isStarted') + ->zeroOrMoreTimes() + ->andReturnUsing(function () use (&$connectionState) { + return $connectionState; + }); + $t1->shouldReceive('start') + ->once() + ->andReturnUsing(function () use (&$connectionState) { + if (!$connectionState) { + $connectionState = true; + } + }); + $t1->shouldReceive('send') + ->once() + ->with($message, \Mockery::on(function (&$var) use (&$failures, $testCase) { + return $testCase->varsAreReferences($var, $failures); + })) + ->andReturnUsing(function () use (&$connectionState) { + if ($connectionState) { + return 1; + } + }); + + $transport = $this->_getTransport(array($t1)); + $transport->start(); + $transport->send($message, $failures); + } + + public function testRegisterPluginDelegatesToLoadedTransports() + { + $plugin = $this->_createPlugin(); + + $t1 = $this->getMockery('Swift_Transport'); + $t2 = $this->getMockery('Swift_Transport'); + + $t1->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + $t2->shouldReceive('registerPlugin') + ->once() + ->with($plugin); + + $transport = $this->_getTransport(array($t1, $t2)); + $transport->registerPlugin($plugin); + } + + /** + * Adapted from Yay_Matchers_ReferenceMatcher. + */ + public function varsAreReferences(&$ref1, &$ref2) + { + if (is_object($ref2)) { + return ($ref1 === $ref2); + } + if ($ref1 !== $ref2) { + return false; + } + + $copy = $ref2; + $randomString = uniqid('yay'); + $ref2 = $randomString; + $isRef = ($ref1 === $ref2); + $ref2 = $copy; + + return $isRef; + } + + // -- Private helpers + + private function _getTransport(array $transports) + { + $transport = new Swift_Transport_LoadBalancedTransport(); + $transport->setTransports($transports); + + return $transport; + } + + private function _createPlugin() + { + return $this->getMockery('Swift_Events_EventListener'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php new file mode 100644 index 0000000..d86d11a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/MailTransportTest.php @@ -0,0 +1,311 @@ +_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $invoker->shouldReceive('mail') + ->once(); + + $transport->send($message); + } + + public function testTransportUsesToFieldBodyInSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $to = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'To' => $to, + )); + $message = $this->_createMessage($headers); + + $to->shouldReceive('getFieldBody') + ->zeroOrMoreTimes() + ->andReturn('Foo '); + $invoker->shouldReceive('mail') + ->once() + ->with('Foo ', \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testTransportUsesSubjectFieldBodyInSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subj = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'Subject' => $subj, + )); + $message = $this->_createMessage($headers); + + $subj->shouldReceive('getFieldBody') + ->zeroOrMoreTimes() + ->andReturn('Thing'); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), 'Thing', \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testTransportUsesBodyOfMessage() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $message->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn( + "To: Foo \r\n". + "\r\n". + 'This body' + ); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), 'This body', \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testTransportUsesHeadersFromMessage() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $message->shouldReceive('toString') + ->zeroOrMoreTimes() + ->andReturn( + "Subject: Stuff\r\n". + "\r\n". + 'This body' + ); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), 'Subject: Stuff'.PHP_EOL, \Mockery::any()); + + $transport->send($message); + } + + public function testTransportReturnsCountOfAllRecipientsIfInvokerReturnsTrue() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null, 'zip@button' => null)); + $message->shouldReceive('getCc') + ->zeroOrMoreTimes() + ->andReturn(array('test@test' => null)); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn(true); + + $this->assertEquals(3, $transport->send($message)); + } + + public function testTransportReturnsZeroIfInvokerReturnsFalse() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $headers = $this->_createHeaders(); + $message = $this->_createMessage($headers); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => null, 'zip@button' => null)); + $message->shouldReceive('getCc') + ->zeroOrMoreTimes() + ->andReturn(array('test@test' => null)); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()) + ->andReturn(false); + + $this->assertEquals(0, $transport->send($message)); + } + + public function testToHeaderIsRemovedFromHeaderSetDuringSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $to = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'To' => $to, + )); + $message = $this->_createMessage($headers); + + $headers->shouldReceive('remove') + ->once() + ->with('To'); + $headers->shouldReceive('remove') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testSubjectHeaderIsRemovedFromHeaderSetDuringSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subject = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'Subject' => $subject, + )); + $message = $this->_createMessage($headers); + + $headers->shouldReceive('remove') + ->once() + ->with('Subject'); + $headers->shouldReceive('remove') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testToHeaderIsPutBackAfterSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $to = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'To' => $to, + )); + $message = $this->_createMessage($headers); + + $headers->shouldReceive('set') + ->once() + ->with($to); + $headers->shouldReceive('set') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + public function testSubjectHeaderIsPutBackAfterSending() + { + $invoker = $this->_createInvoker(); + $dispatcher = $this->_createEventDispatcher(); + $transport = $this->_createTransport($invoker, $dispatcher); + + $subject = $this->_createHeader(); + $headers = $this->_createHeaders(array( + 'Subject' => $subject, + )); + $message = $this->_createMessage($headers); + + $headers->shouldReceive('set') + ->once() + ->with($subject); + $headers->shouldReceive('set') + ->zeroOrMoreTimes(); + $invoker->shouldReceive('mail') + ->once() + ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any()); + + $transport->send($message); + } + + // -- Creation Methods + + private function _createTransport($invoker, $dispatcher) + { + return new Swift_Transport_MailTransport($invoker, $dispatcher); + } + + private function _createEventDispatcher() + { + return $this->getMockery('Swift_Events_EventDispatcher')->shouldIgnoreMissing(); + } + + private function _createInvoker() + { + return $this->getMockery('Swift_Transport_MailInvoker'); + } + + private function _createMessage($headers) + { + $message = $this->getMockery('Swift_Mime_Message')->shouldIgnoreMissing(); + $message->shouldReceive('getHeaders') + ->zeroOrMoreTimes() + ->andReturn($headers); + + return $message; + } + + private function _createHeaders($headers = array()) + { + $set = $this->getMockery('Swift_Mime_HeaderSet')->shouldIgnoreMissing(); + + if (count($headers) > 0) { + foreach ($headers as $name => $header) { + $set->shouldReceive('get') + ->zeroOrMoreTimes() + ->with($name) + ->andReturn($header); + $set->shouldReceive('has') + ->zeroOrMoreTimes() + ->with($name) + ->andReturn(true); + } + } + + $header = $this->_createHeader(); + $set->shouldReceive('get') + ->zeroOrMoreTimes() + ->andReturn($header); + $set->shouldReceive('has') + ->zeroOrMoreTimes() + ->andReturn(true); + + return $set; + } + + private function _createHeader() + { + return $this->getMockery('Swift_Mime_Header')->shouldIgnoreMissing(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php new file mode 100644 index 0000000..c704c3c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/SendmailTransportTest.php @@ -0,0 +1,152 @@ +_createEventDispatcher(); + } + $transport = new Swift_Transport_SendmailTransport($buf, $dispatcher); + $transport->setCommand($command); + + return $transport; + } + + protected function _getSendmail($buf, $dispatcher = null) + { + if (!$dispatcher) { + $dispatcher = $this->_createEventDispatcher(); + } + $sendmail = new Swift_Transport_SendmailTransport($buf, $dispatcher); + + return $sendmail; + } + + public function testCommandCanBeSetAndFetched() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + + $sendmail->setCommand('/usr/sbin/sendmail -bs'); + $this->assertEquals('/usr/sbin/sendmail -bs', $sendmail->getCommand()); + $sendmail->setCommand('/usr/sbin/sendmail -oi -t'); + $this->assertEquals('/usr/sbin/sendmail -oi -t', $sendmail->getCommand()); + } + + public function testSendingMessageIn_t_ModeUsesSimplePipe() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('toByteStream') + ->once() + ->with($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n", "\n." => "\n..")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testSendingIn_t_ModeWith_i_FlagDoesntEscapeDot() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('toByteStream') + ->once() + ->with($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -i -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testSendingInTModeWith_oi_FlagDoesntEscapeDot() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('toByteStream') + ->once() + ->with($buf); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -oi -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testSendingMessageRegeneratesId() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getSendmail($buf); + $message = $this->_createMessage(); + + $message->shouldReceive('getTo') + ->zeroOrMoreTimes() + ->andReturn(array('foo@bar' => 'Foobar', 'zip@button' => 'Zippy')); + $message->shouldReceive('generateId'); + $buf->shouldReceive('initialize') + ->once(); + $buf->shouldReceive('terminate') + ->once(); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array("\r\n" => "\n", "\n." => "\n..")); + $buf->shouldReceive('setWriteTranslations') + ->once() + ->with(array()); + + $sendmail->setCommand('/usr/sbin/sendmail -t'); + $this->assertEquals(2, $sendmail->send($message)); + } + + public function testFluidInterface() + { + $buf = $this->_getBuffer(); + $sendmail = $this->_getTransport($buf); + + $ref = $sendmail->setCommand('/foo'); + $this->assertEquals($ref, $sendmail); + } +} diff --git a/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php new file mode 100644 index 0000000..3ec74b3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Transport/StreamBufferTest.php @@ -0,0 +1,45 @@ +_createFactory(); + $factory->expects($this->once()) + ->method('createFilter') + ->with('a', 'b') + ->will($this->returnCallback(array($this, '_createFilter'))); + + $buffer = $this->_createBuffer($factory); + $buffer->setWriteTranslations(array('a' => 'b')); + } + + public function testOverridingTranslationsOnlyAddsNeededFilters() + { + $factory = $this->_createFactory(); + $factory->expects($this->exactly(2)) + ->method('createFilter') + ->will($this->returnCallback(array($this, '_createFilter'))); + + $buffer = $this->_createBuffer($factory); + $buffer->setWriteTranslations(array('a' => 'b')); + $buffer->setWriteTranslations(array('x' => 'y', 'a' => 'b')); + } + + // -- Creation methods + + private function _createBuffer($replacementFactory) + { + return new Swift_Transport_StreamBuffer($replacementFactory); + } + + private function _createFactory() + { + return $this->getMock('Swift_ReplacementFilterFactory'); + } + + public function _createFilter() + { + return $this->getMock('Swift_StreamFilter'); + } +} diff --git a/vendor/symfony/console/.gitignore b/vendor/symfony/console/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/console/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/console/Application.php b/vendor/symfony/console/Application.php new file mode 100644 index 0000000..41c6657 --- /dev/null +++ b/vendor/symfony/console/Application.php @@ -0,0 +1,1088 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + */ +class Application +{ + private $commands = array(); + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $catchExceptions = true; + private $autoExit = true; + private $definition; + private $helperSet; + private $dispatcher; + private $terminalDimensions; + private $defaultCommand; + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->defaultCommand = 'list'; + $this->helperSet = $this->getDefaultHelperSet(); + $this->definition = $this->getDefaultInputDefinition(); + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } + + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + */ + public function run(InputInterface $input = null, OutputInterface $output = null) + { + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + $this->configureIO($input, $output); + + try { + $exitCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + + $exitCode = $e->getCode(); + if (is_numeric($exitCode)) { + $exitCode = (int) $exitCode; + if (0 === $exitCode) { + $exitCode = 1; + } + } else { + $exitCode = 1; + } + } + + if ($this->autoExit) { + if ($exitCode > 255) { + $exitCode = 255; + } + + exit($exitCode); + } + + return $exitCode; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--version', '-V'), true)) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + $name = $this->getCommandName($input); + if (true === $input->hasParameterOption(array('--help', '-h'), true)) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = $this->defaultCommand; + $input = new ArrayInput(array('command' => $this->defaultCommand)); + } + + // the command name MUST be the first element of the input + $command = $this->find($name); + + $this->runningCommand = $command; + $exitCode = $this->doRunCommand($command, $input, $output); + $this->runningCommand = null; + + return $exitCode; + } + + /** + * Set a helper set to be used with the command. + * + * @param HelperSet $helperSet The helper set + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + * + * @return HelperSet The HelperSet instance associated with this command + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Set an input definition set to be used with this application. + * + * @param InputDefinition $definition The input definition + */ + public function setDefinition(InputDefinition $definition) + { + $this->definition = $definition; + } + + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the help message. + * + * @return string A help message. + */ + public function getHelp() + { + return $this->getLongVersion(); + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param bool $boolean Whether to catch exceptions or not during commands execution + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (bool) $boolean; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param bool $boolean Whether to automatically exit after a command execution or not + */ + public function setAutoExit($boolean) + { + $this->autoExit = (bool) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName()) { + if ('UNKNOWN' !== $this->getVersion()) { + return sprintf('%s version %s', $this->getName(), $this->getVersion()); + } + + return sprintf('%s', $this->getName()); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * + * @param Command $command A Command object + * + * @return Command The registered command + */ + public function add(Command $command) + { + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + if (null === $command->getDefinition()) { + throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))); + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws CommandNotFoundException When command name given does not exist + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + * + * @param string $name The command name or alias + * + * @return bool true if the command exists, false otherwise + */ + public function has($name) + { + return isset($this->commands[$name]); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not returns the global namespace which always exists. + * + * @return array An array of namespaces + */ + public function getNamespaces() + { + $namespaces = array(); + foreach ($this->commands as $command) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName())); + + foreach ($command->getAliases() as $alias) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @param string $namespace A namespace or abbreviation to search for + * + * @return string A registered namespace + * + * @throws CommandNotFoundException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = $this->getNamespaces(); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace); + $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); + + if (empty($namespaces)) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + $exact = in_array($namespace, $namespaces, true); + if (count($namespaces) > 1 && !$exact) { + throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); + } + + return $exact ? $namespace : reset($namespaces); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws CommandNotFoundException When command name is incorrect or ambiguous + */ + public function find($name) + { + $allCommands = array_keys($this->commands); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); + $commands = preg_grep('{^'.$expr.'}', $allCommands); + + if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) { + if (false !== $pos = strrpos($name, ':')) { + // check if a namespace exists and contains commands + $this->findNamespace(substr($name, 0, $pos)); + } + + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternatives($name, $allCommands)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + // filter out aliases for commands which are already on the list + if (count($commands) > 1) { + $commandList = $this->commands; + $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { + $commandName = $commandList[$nameOrAlias]->getName(); + + return $commandName === $nameOrAlias || !in_array($commandName, $commands); + }); + } + + $exact = in_array($name, $commands, true); + if (count($commands) > 1 && !$exact) { + $suggestions = $this->getAbbreviationSuggestions(array_values($commands)); + + throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands)); + } + + return $this->get($exact ? $name : reset($commands)); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return Command[] An array of Command instances + */ + public function all($namespace = null) + { + if (null === $namespace) { + return $this->commands; + } + + $commands = array(); + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + public static function getAbbreviations($names) + { + $abbrevs = array(); + foreach ($names as $name) { + for ($len = strlen($name); $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + $abbrevs[$abbrev][] = $name; + } + } + + return $abbrevs; + } + + /** + * Renders a caught exception. + * + * @param \Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException(\Exception $e, OutputInterface $output) + { + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + + do { + $title = sprintf(' [%s] ', get_class($e)); + + $len = $this->stringWidth($title); + + $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX; + // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327 + if (defined('HHVM_VERSION') && $width > 1 << 31) { + $width = 1 << 31; + } + $formatter = $output->getFormatter(); + $lines = array(); + foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { + foreach ($this->splitStringByWidth($line, $width - 4) as $line) { + // pre-format lines to get the right string length + $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4; + $lines[] = array($line, $lineLength); + + $len = max($lineLength, $len); + } + } + + $messages = array(); + $messages[] = $emptyLine = $formatter->format(sprintf('%s', str_repeat(' ', $len))); + $messages[] = $formatter->format(sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))))); + foreach ($lines as $line) { + $messages[] = $formatter->format(sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1]))); + } + $messages[] = $emptyLine; + $messages[] = ''; + + $output->writeln($messages, OutputInterface::OUTPUT_RAW | OutputInterface::VERBOSITY_QUIET); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->writeln('Exception trace:', OutputInterface::VERBOSITY_QUIET); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; ++$i) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET); + } + + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } while ($e = $e->getPrevious()); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } + + /** + * Tries to figure out the terminal width in which this application runs. + * + * @return int|null + */ + protected function getTerminalWidth() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[0]; + } + + /** + * Tries to figure out the terminal height in which this application runs. + * + * @return int|null + */ + protected function getTerminalHeight() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[1]; + } + + /** + * Tries to figure out the terminal dimensions based on the current environment. + * + * @return array Array containing width and height + */ + public function getTerminalDimensions() + { + if ($this->terminalDimensions) { + return $this->terminalDimensions; + } + + if ('\\' === DIRECTORY_SEPARATOR) { + // extract [w, H] from "wxh (WxH)" + if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + // extract [w, h] from "wxh" + if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + } + + if ($sttyString = $this->getSttyColumns()) { + // extract [w, h] from "rows h; columns w;" + if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + // extract [w, h] from "; h rows; w columns" + if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + } + + return array(null, null); + } + + /** + * Sets terminal dimensions. + * + * Can be useful to force terminal dimensions for functional tests. + * + * @param int $width The width + * @param int $height The height + * + * @return Application The current application + */ + public function setTerminalDimensions($width, $height) + { + $this->terminalDimensions = array($width, $height); + + return $this; + } + + /** + * Configures the input and output instances based on the user arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function configureIO(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--ansi'), true)) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(array('--no-ansi'), true)) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'), true)) { + $input->setInteractive(false); + } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) { + $inputStream = $this->getHelperSet()->get('question')->getInputStream(); + if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { + $input->setInteractive(false); + } + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'), true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } else { + if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || $input->getParameterOption('--verbose', false, true) === 3) { + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || $input->getParameterOption('--verbose', false, true) === 2) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + } + } + } + + /** + * Runs the current command. + * + * If an event dispatcher has been attached to the application, + * events are also dispatched during the life-cycle of the command. + * + * @param Command $command A Command instance + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception when the command being run threw an exception + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + { + foreach ($command->getHelperSet() as $helper) { + if ($helper instanceof InputAwareInterface) { + $helper->setInput($input); + } + } + + if (null === $this->dispatcher) { + return $command->run($input, $output); + } + + // bind before the console.command event, so the listeners have access to input options/arguments + try { + $command->mergeApplicationDefinition(); + $input->bind($command->getDefinition()); + } catch (ExceptionInterface $e) { + // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition + } + + $event = new ConsoleCommandEvent($command, $input, $output); + $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + + if ($event->commandShouldRun()) { + try { + $exitCode = $command->run($input, $output); + } catch (\Exception $e) { + $event = new ConsoleExceptionEvent($command, $input, $output, $e, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); + + $e = $event->getException(); + + $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + throw $e; + } + } else { + $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; + } + + $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + return $event->getExitCode(); + } + + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + return $input->getFirstArgument(); + } + + /** + * Gets the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array( + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), + new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), + )); + } + + /** + * Gets the default commands that should always be available. + * + * @return Command[] An array of default Command instances + */ + protected function getDefaultCommands() + { + return array(new HelpCommand(), new ListCommand()); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array( + new FormatterHelper(), + new DebugFormatterHelper(), + new ProcessHelper(), + new QuestionHelper(), + )); + } + + /** + * Runs and parses stty -a if it's available, suppressing any error output. + * + * @return string + */ + private function getSttyColumns() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; + } + } + + /** + * Runs and parses mode CON if it's available, suppressing any error output. + * + * @return string x or null if it could not be parsed + */ + private function getConsoleMode() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return $matches[2].'x'.$matches[1]; + } + } + } + + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ + private function getAbbreviationSuggestions($abbrevs) + { + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + } + + /** + * Returns the namespace part of the command name. + * + * This method is not part of public API and should not be used directly. + * + * @param string $name The full name of the command + * @param string $limit The maximum number of parts of the namespace + * + * @return string The namespace of the command + */ + public function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs. + * + * @param string $name The string + * @param array|\Traversable $collection The collection + * + * @return array A sorted array of similar string + */ + private function findAlternatives($name, $collection) + { + $threshold = 1e3; + $alternatives = array(); + + $collectionParts = array(); + foreach ($collection as $item) { + $collectionParts[$item] = explode(':', $item); + } + + foreach (explode(':', $name) as $i => $subname) { + foreach ($collectionParts as $collectionName => $parts) { + $exists = isset($alternatives[$collectionName]); + if (!isset($parts[$i]) && $exists) { + $alternatives[$collectionName] += $threshold; + continue; + } elseif (!isset($parts[$i])) { + continue; + } + + $lev = levenshtein($subname, $parts[$i]); + if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) { + $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; + } elseif ($exists) { + $alternatives[$collectionName] += $threshold; + } + } + } + + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + asort($alternatives); + + return array_keys($alternatives); + } + + /** + * Sets the default Command name. + * + * @param string $commandName The Command name + */ + public function setDefaultCommand($commandName) + { + $this->defaultCommand = $commandName; + } + + private function stringWidth($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + private function splitStringByWidth($string, $width) + { + // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. + // additionally, array_slice() is not enough as some character has doubled width. + // we need a function to split string not by character count but by string width + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return str_split($string, $width); + } + + $utf8String = mb_convert_encoding($string, 'utf8', $encoding); + $lines = array(); + $line = ''; + foreach (preg_split('//u', $utf8String) as $char) { + // test if $char could be appended to current line + if (mb_strwidth($line.$char, 'utf8') <= $width) { + $line .= $char; + continue; + } + // if not, push current line to array and make new line + $lines[] = str_pad($line, $width); + $line = $char; + } + if ('' !== $line) { + $lines[] = count($lines) ? str_pad($line, $width) : $line; + } + + mb_convert_variables($encoding, 'utf8', $lines); + + return $lines; + } + + /** + * Returns all namespaces of the command name. + * + * @param string $name The full name of the command + * + * @return array The namespaces of the command + */ + private function extractAllNamespaces($name) + { + // -1 as third argument is needed to skip the command short name when exploding + $parts = explode(':', $name, -1); + $namespaces = array(); + + foreach ($parts as $part) { + if (count($namespaces)) { + $namespaces[] = end($namespaces).':'.$part; + } else { + $namespaces[] = $part; + } + } + + return $namespaces; + } +} diff --git a/vendor/symfony/console/CHANGELOG.md b/vendor/symfony/console/CHANGELOG.md new file mode 100644 index 0000000..4e29d2e --- /dev/null +++ b/vendor/symfony/console/CHANGELOG.md @@ -0,0 +1,68 @@ +CHANGELOG +========= + +2.8.0 +----- + + * use readline for user input in the question helper when available to allow + the use of arrow keys + +2.6.0 +----- + + * added a Process helper + * added a DebugFormatter helper + +2.5.0 +----- + + * deprecated the dialog helper (use the question helper instead) + * deprecated TableHelper in favor of Table + * deprecated ProgressHelper in favor of ProgressBar + * added ConsoleLogger + * added a question helper + * added a way to set the process name of a command + * added a way to set a default command instead of `ListCommand` + +2.4.0 +----- + + * added a way to force terminal dimensions + * added a convenient method to detect verbosity level + * [BC BREAK] made descriptors use output instead of returning a string + +2.3.0 +----- + + * added multiselect support to the select dialog helper + * added Table Helper for tabular data rendering + * added support for events in `Application` + * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()` + * added a way to set the progress bar progress via the `setCurrent` method + * added support for multiple InputOption shortcuts, written as `'-a|-b|-c'` + * added two additional verbosity levels, VERBOSITY_VERY_VERBOSE and VERBOSITY_DEBUG + +2.2.0 +----- + + * added support for colorization on Windows via ConEmu + * add a method to Dialog Helper to ask for a question and hide the response + * added support for interactive selections in console (DialogHelper::select()) + * added support for autocompletion as you type in Dialog Helper + +2.1.0 +----- + + * added ConsoleOutputInterface + * added the possibility to disable a command (Command::isEnabled()) + * added suggestions when a command does not exist + * added a --raw option to the list command + * added support for STDERR in the console output class (errors are now sent + to STDERR) + * made the defaults (helper set, commands, input definition) in Application + more easily customizable + * added support for the shell even if readline is not available + * added support for process isolation in Symfony shell via + `--process-isolation` switch + * added support for `--`, which disables options parsing after that point + (tokens will be parsed as arguments) diff --git a/vendor/symfony/console/Command/Command.php b/vendor/symfony/console/Command/Command.php new file mode 100644 index 0000000..cb87093 --- /dev/null +++ b/vendor/symfony/console/Command/Command.php @@ -0,0 +1,626 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + */ +class Command +{ + private $application; + private $name; + private $processTitle; + private $aliases = array(); + private $definition; + private $help; + private $description; + private $ignoreValidationErrors = false; + private $applicationDefinitionMerged = false; + private $applicationDefinitionMergedWithArgs = false; + private $code; + private $synopsis = array(); + private $usages = array(); + private $helperSet; + + /** + * Constructor. + * + * @param string|null $name The name of the command; passing null means it must be set in configure() + * + * @throws LogicException When the command name is empty + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this))); + } + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * Sets the application instance for this command. + * + * @param Application $application An Application instance + */ + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + /** + * Sets the helper set. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + * + * @return Application An Application instance + */ + public function getApplication() + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment. + * + * Override this to check for x or y and return false if the command can not + * run properly under the current conditions. + * + * @return bool + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return null|int null or 0 if everything went fine, or an error code + * + * @throws LogicException When this abstract method is not implemented + * + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * This method is executed before the InputDefinition is validated. + * This means that this is the only place where the command can + * interactively ask for values of missing required arguments. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command just after the input has been validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return int The command exit code + * + * @throws \Exception + * + * @see setCode() + * @see execute() + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(true); + $this->getSynopsis(false); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (ExceptionInterface $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if (null !== $this->processTitle) { + if (function_exists('cli_set_process_title')) { + cli_set_process_title($this->processTitle); + } elseif (function_exists('setproctitle')) { + setproctitle($this->processTitle); + } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Install the proctitle PECL to be able to change the process title.'); + } + } + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + // The command name argument is often omitted when a command is executed directly with its run() method. + // It would fail the validation if we didn't make sure the command argument is present, + // since it's required by the application. + if ($input->hasArgument('command') && null === $input->getArgument('command')) { + $input->setArgument('command', $this->getName()); + } + + $input->validate(); + + if ($this->code) { + $statusCode = call_user_func($this->code, $input, $output); + } else { + $statusCode = $this->execute($input, $output); + } + + return is_numeric($statusCode) ? (int) $statusCode : 0; + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param callable $code A callable(InputInterface $input, OutputInterface $output) + * + * @return Command The current instance + * + * @throws InvalidArgumentException + * + * @see execute() + */ + public function setCode(callable $code) + { + if ($code instanceof \Closure) { + $r = new \ReflectionFunction($code); + if (null === $r->getClosureThis()) { + $code = \Closure::bind($code, $this); + } + } + + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + * + * This method is not part of public API and should not be used directly. + * + * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments + */ + public function mergeApplicationDefinition($mergeArgs = true) + { + if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) { + return; + } + + if ($mergeArgs) { + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->application->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + } + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + $this->applicationDefinitionMerged = true; + if ($mergeArgs) { + $this->applicationDefinitionMergedWithArgs = true; + } + } + + /** + * Sets an array of argument and option instances. + * + * @param array|InputDefinition $definition An array of argument and option instances or a definition instance + * + * @return Command The current instance + */ + public function setDefinition($definition) + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * This method is not part of public API and should not be used directly. + * + * @return InputDefinition An InputDefinition instance + */ + public function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * Adds an argument. + * + * @param string $name The argument name + * @param int $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @return Command The current instance + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param int $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE) + * + * @return Command The current instance + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return Command The current instance + * + * @throws InvalidArgumentException When the name is invalid + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Sets the process title of the command. + * + * This feature should be used only when creating a long process command, + * like a daemon. + * + * PHP 5.5+ or the proctitle PECL library is required + * + * @param string $title The process title + * + * @return Command The current instance + */ + public function setProcessTitle($title) + { + $this->processTitle = $title; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return Command The current instance + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return Command The current instance + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->name; + + $placeholders = array( + '%command.name%', + '%command.full_name%', + ); + $replacements = array( + $name, + $_SERVER['PHP_SELF'].' '.$name, + ); + + return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription()); + } + + /** + * Sets the aliases for the command. + * + * @param string[] $aliases An array of aliases for the command + * + * @return Command The current instance + * + * @throws InvalidArgumentException When an alias is invalid + */ + public function setAliases($aliases) + { + if (!is_array($aliases) && !$aliases instanceof \Traversable) { + throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable'); + } + + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @param bool $short Whether to show the short version of the synopsis (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $key = $short ? 'short' : 'long'; + + if (!isset($this->synopsis[$key])) { + $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); + } + + return $this->synopsis[$key]; + } + + /** + * Add a command usage example. + * + * @param string $usage The usage, it'll be prefixed with the command name + */ + public function addUsage($usage) + { + if (0 !== strpos($usage, $this->name)) { + $usage = sprintf('%s %s', $this->name, $usage); + } + + $this->usages[] = $usage; + + return $this; + } + + /** + * Returns alternative usages of the command. + * + * @return array + */ + public function getUsages() + { + return $this->usages; + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws InvalidArgumentException if the helper is not defined + */ + public function getHelper($name) + { + return $this->helperSet->get($name); + } + + /** + * Validates a command name. + * + * It must be non-empty and parts can optionally be separated by ":". + * + * @param string $name + * + * @throws InvalidArgumentException When the name is invalid + */ + private function validateName($name) + { + if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { + throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/vendor/symfony/console/Command/HelpCommand.php b/vendor/symfony/console/Command/HelpCommand.php new file mode 100644 index 0000000..b8fd911 --- /dev/null +++ b/vendor/symfony/console/Command/HelpCommand.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier + */ +class HelpCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition(array( + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), + )) + ->setDescription('Displays help for a command') + ->setHelp(<<<'EOF' +The %command.name% command displays help for a given command: + + php %command.full_name% list + +You can also output the help in other formats by using the --format option: + + php %command.full_name% --format=xml list + +To display the list of available commands, please use the list command. +EOF + ) + ; + } + + /** + * Sets the command. + * + * @param Command $command The command to set + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (null === $this->command) { + $this->command = $this->getApplication()->find($input->getArgument('command_name')); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->command, array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + )); + + $this->command = null; + } +} diff --git a/vendor/symfony/console/Command/ListCommand.php b/vendor/symfony/console/Command/ListCommand.php new file mode 100644 index 0000000..179ddea --- /dev/null +++ b/vendor/symfony/console/Command/ListCommand.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputDefinition; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition($this->createDefinition()) + ->setDescription('Lists commands') + ->setHelp(<<<'EOF' +The %command.name% command lists all commands: + + php %command.full_name% + +You can also display the commands for a specific namespace: + + php %command.full_name% test + +You can also output the information in other formats by using the --format option: + + php %command.full_name% --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php %command.full_name% --raw +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + public function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $helper = new DescriptorHelper(); + $helper->describe($output, $this->getApplication(), array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'namespace' => $input->getArgument('namespace'), + )); + } + + /** + * {@inheritdoc} + */ + private function createDefinition() + { + return new InputDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + )); + } +} diff --git a/vendor/symfony/console/ConsoleEvents.php b/vendor/symfony/console/ConsoleEvents.php new file mode 100644 index 0000000..1ed41b7 --- /dev/null +++ b/vendor/symfony/console/ConsoleEvents.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +/** + * Contains all events dispatched by an Application. + * + * @author Francesco Levorato + */ +final class ConsoleEvents +{ + /** + * The COMMAND event allows you to attach listeners before any command is + * executed by the console. It also allows you to modify the command, input and output + * before they are handled to the command. + * + * The event listener method receives a Symfony\Component\Console\Event\ConsoleCommandEvent + * instance. + * + * @Event + * + * @var string + */ + const COMMAND = 'console.command'; + + /** + * The TERMINATE event allows you to attach listeners after a command is + * executed by the console. + * + * The event listener method receives a Symfony\Component\Console\Event\ConsoleTerminateEvent + * instance. + * + * @Event + * + * @var string + */ + const TERMINATE = 'console.terminate'; + + /** + * The EXCEPTION event occurs when an uncaught exception appears. + * + * This event allows you to deal with the exception or + * to modify the thrown exception. The event listener method receives + * a Symfony\Component\Console\Event\ConsoleExceptionEvent + * instance. + * + * @Event + * + * @var string + */ + const EXCEPTION = 'console.exception'; +} diff --git a/vendor/symfony/console/Descriptor/ApplicationDescription.php b/vendor/symfony/console/Descriptor/ApplicationDescription.php new file mode 100644 index 0000000..89961b9 --- /dev/null +++ b/vendor/symfony/console/Descriptor/ApplicationDescription.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Jean-François Simon + * + * @internal + */ +class ApplicationDescription +{ + const GLOBAL_NAMESPACE = '_global'; + + /** + * @var Application + */ + private $application; + + /** + * @var null|string + */ + private $namespace; + + /** + * @var array + */ + private $namespaces; + + /** + * @var Command[] + */ + private $commands; + + /** + * @var Command[] + */ + private $aliases; + + /** + * Constructor. + * + * @param Application $application + * @param string|null $namespace + */ + public function __construct(Application $application, $namespace = null) + { + $this->application = $application; + $this->namespace = $namespace; + } + + /** + * @return array + */ + public function getNamespaces() + { + if (null === $this->namespaces) { + $this->inspectApplication(); + } + + return $this->namespaces; + } + + /** + * @return Command[] + */ + public function getCommands() + { + if (null === $this->commands) { + $this->inspectApplication(); + } + + return $this->commands; + } + + /** + * @param string $name + * + * @return Command + * + * @throws CommandNotFoundException + */ + public function getCommand($name) + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name)); + } + + return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; + } + + private function inspectApplication() + { + $this->commands = array(); + $this->namespaces = array(); + + $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null); + foreach ($this->sortCommands($all) as $namespace => $commands) { + $names = array(); + + /** @var Command $command */ + foreach ($commands as $name => $command) { + if (!$command->getName()) { + continue; + } + + if ($command->getName() === $name) { + $this->commands[$name] = $command; + } else { + $this->aliases[$name] = $command; + } + + $names[] = $name; + } + + $this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names); + } + } + + /** + * @param array $commands + * + * @return array + */ + private function sortCommands(array $commands) + { + $namespacedCommands = array(); + $globalCommands = array(); + foreach ($commands as $name => $command) { + $key = $this->application->extractNamespace($name, 1); + if (!$key) { + $globalCommands['_global'][$name] = $command; + } else { + $namespacedCommands[$key][$name] = $command; + } + } + ksort($namespacedCommands); + $namespacedCommands = array_merge($globalCommands, $namespacedCommands); + + foreach ($namespacedCommands as &$commandsSet) { + ksort($commandsSet); + } + // unset reference to keep scope clear + unset($commandsSet); + + return $namespacedCommands; + } +} diff --git a/vendor/symfony/console/Descriptor/Descriptor.php b/vendor/symfony/console/Descriptor/Descriptor.php new file mode 100644 index 0000000..50dd86c --- /dev/null +++ b/vendor/symfony/console/Descriptor/Descriptor.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon + * + * @internal + */ +abstract class Descriptor implements DescriptorInterface +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $this->output = $output; + + switch (true) { + case $object instanceof InputArgument: + $this->describeInputArgument($object, $options); + break; + case $object instanceof InputOption: + $this->describeInputOption($object, $options); + break; + case $object instanceof InputDefinition: + $this->describeInputDefinition($object, $options); + break; + case $object instanceof Command: + $this->describeCommand($object, $options); + break; + case $object instanceof Application: + $this->describeApplication($object, $options); + break; + default: + throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); + } + } + + /** + * Writes content to output. + * + * @param string $content + * @param bool $decorated + */ + protected function write($content, $decorated = false) + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + * + * @param InputArgument $argument + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputArgument(InputArgument $argument, array $options = array()); + + /** + * Describes an InputOption instance. + * + * @param InputOption $option + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputOption(InputOption $option, array $options = array()); + + /** + * Describes an InputDefinition instance. + * + * @param InputDefinition $definition + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array()); + + /** + * Describes a Command instance. + * + * @param Command $command + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeCommand(Command $command, array $options = array()); + + /** + * Describes an Application instance. + * + * @param Application $application + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeApplication(Application $application, array $options = array()); +} diff --git a/vendor/symfony/console/Descriptor/DescriptorInterface.php b/vendor/symfony/console/Descriptor/DescriptorInterface.php new file mode 100644 index 0000000..3929b6d --- /dev/null +++ b/vendor/symfony/console/Descriptor/DescriptorInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Descriptor interface. + * + * @author Jean-François Simon + */ +interface DescriptorInterface +{ + /** + * Describes an InputArgument instance. + * + * @param OutputInterface $output + * @param object $object + * @param array $options + */ + public function describe(OutputInterface $output, $object, array $options = array()); +} diff --git a/vendor/symfony/console/Descriptor/JsonDescriptor.php b/vendor/symfony/console/Descriptor/JsonDescriptor.php new file mode 100644 index 0000000..87e38fd --- /dev/null +++ b/vendor/symfony/console/Descriptor/JsonDescriptor.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * JSON descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeData($this->getInputArgumentData($argument), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeData($this->getInputOptionData($option), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeData($this->getInputDefinitionData($definition), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeData($this->getCommandData($command), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + $commands = array(); + + foreach ($description->getCommands() as $command) { + $commands[] = $this->getCommandData($command); + } + + $data = $describedNamespace + ? array('commands' => $commands, 'namespace' => $describedNamespace) + : array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces())); + + $this->writeData($data, $options); + } + + /** + * Writes data as json. + * + * @param array $data + * @param array $options + * + * @return array|string + */ + private function writeData(array $data, array $options) + { + $this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0)); + } + + /** + * @param InputArgument $argument + * + * @return array + */ + private function getInputArgumentData(InputArgument $argument) + { + return array( + 'name' => $argument->getName(), + 'is_required' => $argument->isRequired(), + 'is_array' => $argument->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()), + 'default' => $argument->getDefault(), + ); + } + + /** + * @param InputOption $option + * + * @return array + */ + private function getInputOptionData(InputOption $option) + { + return array( + 'name' => '--'.$option->getName(), + 'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '', + 'accept_value' => $option->acceptValue(), + 'is_value_required' => $option->isValueRequired(), + 'is_multiple' => $option->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()), + 'default' => $option->getDefault(), + ); + } + + /** + * @param InputDefinition $definition + * + * @return array + */ + private function getInputDefinitionData(InputDefinition $definition) + { + $inputArguments = array(); + foreach ($definition->getArguments() as $name => $argument) { + $inputArguments[$name] = $this->getInputArgumentData($argument); + } + + $inputOptions = array(); + foreach ($definition->getOptions() as $name => $option) { + $inputOptions[$name] = $this->getInputOptionData($option); + } + + return array('arguments' => $inputArguments, 'options' => $inputOptions); + } + + /** + * @param Command $command + * + * @return array + */ + private function getCommandData(Command $command) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + return array( + 'name' => $command->getName(), + 'usage' => array_merge(array($command->getSynopsis()), $command->getUsages(), $command->getAliases()), + 'description' => $command->getDescription(), + 'help' => $command->getProcessedHelp(), + 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()), + ); + } +} diff --git a/vendor/symfony/console/Descriptor/MarkdownDescriptor.php b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php new file mode 100644 index 0000000..d3d76a4 --- /dev/null +++ b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Markdown descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->write( + '**'.$argument->getName().':**'."\n\n" + .'* Name: '.($argument->getName() ?: '')."\n" + .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n" + .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $argument->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->write( + '**'.$option->getName().':**'."\n\n" + .'* Name: `--'.$option->getName().'`'."\n" + .'* Shortcut: '.($option->getShortcut() ? '`-'.implode('|-', explode('|', $option->getShortcut())).'`' : '')."\n" + .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n" + .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n" + .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $option->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + if ($showArguments = count($definition->getArguments()) > 0) { + $this->write('### Arguments:'); + foreach ($definition->getArguments() as $argument) { + $this->write("\n\n"); + $this->write($this->describeInputArgument($argument)); + } + } + + if (count($definition->getOptions()) > 0) { + if ($showArguments) { + $this->write("\n\n"); + } + + $this->write('### Options:'); + foreach ($definition->getOptions() as $option) { + $this->write("\n\n"); + $this->write($this->describeInputOption($option)); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $this->write( + $command->getName()."\n" + .str_repeat('-', strlen($command->getName()))."\n\n" + .'* Description: '.($command->getDescription() ?: '')."\n" + .'* Usage:'."\n\n" + .array_reduce(array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()), function ($carry, $usage) { + return $carry .= ' * `'.$usage.'`'."\n"; + }) + ); + + if ($help = $command->getProcessedHelp()) { + $this->write("\n"); + $this->write($help); + } + + if ($command->getNativeDefinition()) { + $this->write("\n\n"); + $this->describeInputDefinition($command->getNativeDefinition()); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + $this->write($application->getName()."\n".str_repeat('=', strlen($application->getName()))); + + foreach ($description->getNamespaces() as $namespace) { + if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->write("\n\n"); + $this->write('**'.$namespace['id'].':**'); + } + + $this->write("\n\n"); + $this->write(implode("\n", array_map(function ($commandName) { + return '* '.$commandName; + }, $namespace['commands']))); + } + + foreach ($description->getCommands() as $command) { + $this->write("\n\n"); + $this->write($this->describeCommand($command)); + } + } +} diff --git a/vendor/symfony/console/Descriptor/TextDescriptor.php b/vendor/symfony/console/Descriptor/TextDescriptor.php new file mode 100644 index 0000000..560ce15 --- /dev/null +++ b/vendor/symfony/console/Descriptor/TextDescriptor.php @@ -0,0 +1,283 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Text descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class TextDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName()); + $spacingWidth = $totalWidth - strlen($argument->getName()) + 2; + + $this->writeText(sprintf(' %s%s%s%s', + $argument->getName(), + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $argument->getDescription()), + $default + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $value = ''; + if ($option->acceptValue()) { + $value = '='.strtoupper($option->getName()); + + if ($option->isValueOptional()) { + $value = '['.$value.']'; + } + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions(array($option)); + $synopsis = sprintf('%s%s', + $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', + sprintf('--%s%s', $option->getName(), $value) + ); + + $spacingWidth = $totalWidth - strlen($synopsis) + 2; + + $this->writeText(sprintf(' %s%s%s%s%s', + $synopsis, + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $option->getDescription()), + $default, + $option->isArray() ? ' (multiple values allowed)' : '' + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); + foreach ($definition->getArguments() as $argument) { + $totalWidth = max($totalWidth, strlen($argument->getName())); + } + + if ($definition->getArguments()) { + $this->writeText('Arguments:', $options); + $this->writeText("\n"); + foreach ($definition->getArguments() as $argument) { + $this->describeInputArgument($argument, array_merge($options, array('total_width' => $totalWidth))); + $this->writeText("\n"); + } + } + + if ($definition->getArguments() && $definition->getOptions()) { + $this->writeText("\n"); + } + + if ($definition->getOptions()) { + $laterOptions = array(); + + $this->writeText('Options:', $options); + foreach ($definition->getOptions() as $option) { + if (strlen($option->getShortcut()) > 1) { + $laterOptions[] = $option; + continue; + } + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + foreach ($laterOptions as $option) { + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(true); + $command->getSynopsis(false); + $command->mergeApplicationDefinition(false); + + $this->writeText('Usage:', $options); + foreach (array_merge(array($command->getSynopsis(true)), $command->getAliases(), $command->getUsages()) as $usage) { + $this->writeText("\n"); + $this->writeText(' '.$usage, $options); + } + $this->writeText("\n"); + + $definition = $command->getNativeDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { + $this->writeText("\n"); + $this->describeInputDefinition($definition, $options); + $this->writeText("\n"); + } + + if ($help = $command->getProcessedHelp()) { + $this->writeText("\n"); + $this->writeText('Help:', $options); + $this->writeText("\n"); + $this->writeText(' '.str_replace("\n", "\n ", $help), $options); + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + if (isset($options['raw_text']) && $options['raw_text']) { + $width = $this->getColumnWidth($description->getCommands()); + + foreach ($description->getCommands() as $command) { + $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options); + $this->writeText("\n"); + } + } else { + if ('' != $help = $application->getHelp()) { + $this->writeText("$help\n\n", $options); + } + + $this->writeText("Usage:\n", $options); + $this->writeText(" command [options] [arguments]\n\n", $options); + + $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options); + + $this->writeText("\n"); + $this->writeText("\n"); + + $width = $this->getColumnWidth($description->getCommands()); + + if ($describedNamespace) { + $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); + } else { + $this->writeText('Available commands:', $options); + } + + // add commands by namespace + foreach ($description->getNamespaces() as $namespace) { + if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->writeText("\n"); + $this->writeText(' '.$namespace['id'].'', $options); + } + + foreach ($namespace['commands'] as $name) { + $this->writeText("\n"); + $spacingWidth = $width - strlen($name); + $this->writeText(sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)->getDescription()), $options); + } + } + + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + private function writeText($content, array $options = array()) + { + $this->write( + isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, + isset($options['raw_output']) ? !$options['raw_output'] : true + ); + } + + /** + * Formats input option/argument default value. + * + * @param mixed $default + * + * @return string + */ + private function formatDefaultValue($default) + { + return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } + + /** + * @param Command[] $commands + * + * @return int + */ + private function getColumnWidth(array $commands) + { + $widths = array(); + + foreach ($commands as $command) { + $widths[] = strlen($command->getName()); + foreach ($command->getAliases() as $alias) { + $widths[] = strlen($alias); + } + } + + return max($widths) + 2; + } + + /** + * @param InputOption[] $options + * + * @return int + */ + private function calculateTotalWidthForOptions($options) + { + $totalWidth = 0; + foreach ($options as $option) { + // "-" + shortcut + ", --" + name + $nameLength = 1 + max(strlen($option->getShortcut()), 1) + 4 + strlen($option->getName()); + + if ($option->acceptValue()) { + $valueLength = 1 + strlen($option->getName()); // = + value + $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ] + + $nameLength += $valueLength; + } + $totalWidth = max($totalWidth, $nameLength); + } + + return $totalWidth; + } +} diff --git a/vendor/symfony/console/Descriptor/XmlDescriptor.php b/vendor/symfony/console/Descriptor/XmlDescriptor.php new file mode 100644 index 0000000..b5676be --- /dev/null +++ b/vendor/symfony/console/Descriptor/XmlDescriptor.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * XML descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class XmlDescriptor extends Descriptor +{ + /** + * @param InputDefinition $definition + * + * @return \DOMDocument + */ + public function getInputDefinitionDocument(InputDefinition $definition) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($definition->getArguments() as $argument) { + $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument)); + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($definition->getOptions() as $option) { + $this->appendDocument($optionsXML, $this->getInputOptionDocument($option)); + } + + return $dom; + } + + /** + * @param Command $command + * + * @return \DOMDocument + */ + public function getCommandDocument(Command $command) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($commandXML = $dom->createElement('command')); + + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $commandXML->setAttribute('id', $command->getName()); + $commandXML->setAttribute('name', $command->getName()); + + $commandXML->appendChild($usagesXML = $dom->createElement('usages')); + + foreach (array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()) as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription()))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); + + $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition()); + $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + + return $dom; + } + + /** + * @param Application $application + * @param string|null $namespace + * + * @return \DOMDocument + */ + public function getApplicationDocument(Application $application, $namespace = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($rootXml = $dom->createElement('symfony')); + + if ($application->getName() !== 'UNKNOWN') { + $rootXml->setAttribute('name', $application->getName()); + if ($application->getVersion() !== 'UNKNOWN') { + $rootXml->setAttribute('version', $application->getVersion()); + } + } + + $rootXml->appendChild($commandsXML = $dom->createElement('commands')); + + $description = new ApplicationDescription($application, $namespace); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } + + foreach ($description->getCommands() as $command) { + $this->appendDocument($commandsXML, $this->getCommandDocument($command)); + } + + if (!$namespace) { + $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces')); + + foreach ($description->getNamespaces() as $namespaceDescription) { + $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); + $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']); + + foreach ($namespaceDescription['commands'] as $name) { + $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); + $commandXML->appendChild($dom->createTextNode($name)); + } + } + } + + return $dom; + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeDocument($this->getInputArgumentDocument($argument)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeDocument($this->getInputOptionDocument($option)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeDocument($this->getInputDefinitionDocument($definition)); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeDocument($this->getCommandDocument($command)); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null)); + } + + /** + * Appends document children to parent node. + * + * @param \DOMNode $parentNode + * @param \DOMNode $importedParent + */ + private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) + { + foreach ($importedParent->childNodes as $childNode) { + $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true)); + } + } + + /** + * Writes DOM document. + * + * @param \DOMDocument $dom + * + * @return \DOMDocument|string + */ + private function writeDocument(\DOMDocument $dom) + { + $dom->formatOutput = true; + $this->write($dom->saveXML()); + } + + /** + * @param InputArgument $argument + * + * @return \DOMDocument + */ + private function getInputArgumentDocument(InputArgument $argument) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('argument')); + $objectXML->setAttribute('name', $argument->getName()); + $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + + return $dom; + } + + /** + * @param InputOption $option + * + * @return \DOMDocument + */ + private function getInputOptionDocument(InputOption $option) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('option')); + $objectXML->setAttribute('name', '--'.$option->getName()); + $pos = strpos($option->getShortcut(), '|'); + if (false !== $pos) { + $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos)); + $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut()))); + } else { + $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + } + $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array())); + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + + if (!empty($defaults)) { + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $dom; + } +} diff --git a/vendor/symfony/console/Event/ConsoleCommandEvent.php b/vendor/symfony/console/Event/ConsoleCommandEvent.php new file mode 100644 index 0000000..92adf1e --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleCommandEvent.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +/** + * Allows to do things before the command is executed, like skipping the command or changing the input. + * + * @author Fabien Potencier + */ +class ConsoleCommandEvent extends ConsoleEvent +{ + /** + * The return code for skipped commands, this will also be passed into the terminate event. + */ + const RETURN_CODE_DISABLED = 113; + + /** + * Indicates if the command should be run or skipped. + * + * @var bool + */ + private $commandShouldRun = true; + + /** + * Disables the command, so it won't be run. + * + * @return bool + */ + public function disableCommand() + { + return $this->commandShouldRun = false; + } + + /** + * Enables the command. + * + * @return bool + */ + public function enableCommand() + { + return $this->commandShouldRun = true; + } + + /** + * Returns true if the command is runnable, false otherwise. + * + * @return bool + */ + public function commandShouldRun() + { + return $this->commandShouldRun; + } +} diff --git a/vendor/symfony/console/Event/ConsoleEvent.php b/vendor/symfony/console/Event/ConsoleEvent.php new file mode 100644 index 0000000..ab620c4 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleEvent.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\Event; + +/** + * Allows to inspect input and output of a command. + * + * @author Francesco Levorato + */ +class ConsoleEvent extends Event +{ + protected $command; + + private $input; + private $output; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output) + { + $this->command = $command; + $this->input = $input; + $this->output = $output; + } + + /** + * Gets the command that is executed. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + /** + * Gets the input instance. + * + * @return InputInterface An InputInterface instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance. + * + * @return OutputInterface An OutputInterface instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/vendor/symfony/console/Event/ConsoleExceptionEvent.php b/vendor/symfony/console/Event/ConsoleExceptionEvent.php new file mode 100644 index 0000000..603b7ee --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleExceptionEvent.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to handle exception thrown in a command. + * + * @author Fabien Potencier + */ +class ConsoleExceptionEvent extends ConsoleEvent +{ + private $exception; + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setException($exception); + $this->exitCode = (int) $exitCode; + } + + /** + * Returns the thrown exception. + * + * @return \Exception The thrown exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Replaces the thrown exception. + * + * This exception will be thrown if no response is set in the event. + * + * @param \Exception $exception The thrown exception + */ + public function setException(\Exception $exception) + { + $this->exception = $exception; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/Event/ConsoleTerminateEvent.php b/vendor/symfony/console/Event/ConsoleTerminateEvent.php new file mode 100644 index 0000000..b6a5d7c --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleTerminateEvent.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to manipulate the exit code of a command after its execution. + * + * @author Francesco Levorato + */ +class ConsoleTerminateEvent extends ConsoleEvent +{ + /** + * The exit code of the command. + * + * @var int + */ + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setExitCode($exitCode); + } + + /** + * Sets the exit code. + * + * @param int $exitCode The command exit code + */ + public function setExitCode($exitCode) + { + $this->exitCode = (int) $exitCode; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/Exception/CommandNotFoundException.php b/vendor/symfony/console/Exception/CommandNotFoundException.php new file mode 100644 index 0000000..ce6fefe --- /dev/null +++ b/vendor/symfony/console/Exception/CommandNotFoundException.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect command name typed in the console. + * + * @author Jérôme Tamarelle + */ +class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface +{ + private $alternatives; + + /** + * @param string $message Exception message to throw. + * @param array $alternatives List of similar defined names. + * @param int $code Exception code. + * @param Exception $previous previous exception used for the exception chaining. + */ + public function __construct($message, array $alternatives = array(), $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->alternatives = $alternatives; + } + + /** + * @return array A list of similar defined names. + */ + public function getAlternatives() + { + return $this->alternatives; + } +} diff --git a/vendor/symfony/console/Exception/ExceptionInterface.php b/vendor/symfony/console/Exception/ExceptionInterface.php new file mode 100644 index 0000000..491cc4c --- /dev/null +++ b/vendor/symfony/console/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * ExceptionInterface. + * + * @author Jérôme Tamarelle + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/InvalidArgumentException.php b/vendor/symfony/console/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..07cc0b6 --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/InvalidOptionException.php b/vendor/symfony/console/Exception/InvalidOptionException.php new file mode 100644 index 0000000..b2eec61 --- /dev/null +++ b/vendor/symfony/console/Exception/InvalidOptionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect option name typed in the console. + * + * @author Jérôme Tamarelle + */ +class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/LogicException.php b/vendor/symfony/console/Exception/LogicException.php new file mode 100644 index 0000000..fc37b8d --- /dev/null +++ b/vendor/symfony/console/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Exception/RuntimeException.php b/vendor/symfony/console/Exception/RuntimeException.php new file mode 100644 index 0000000..51d7d80 --- /dev/null +++ b/vendor/symfony/console/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/console/Formatter/OutputFormatter.php b/vendor/symfony/console/Formatter/OutputFormatter.php new file mode 100644 index 0000000..244e25c --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatter.php @@ -0,0 +1,228 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + */ +class OutputFormatter implements OutputFormatterInterface +{ + private $decorated; + private $styles = array(); + private $styleStack; + + /** + * Escapes "<" special char in given text. + * + * @param string $text Text to escape + * + * @return string Escaped text + */ + public static function escape($text) + { + return preg_replace('/([^\\\\]?) FormatterStyle" instances + */ + public function __construct($decorated = false, array $styles = array()) + { + $this->decorated = (bool) $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + + $this->styleStack = new OutputFormatterStyleStack(); + } + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated) + { + $this->decorated = (bool) $decorated; + } + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style isn't defined + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new InvalidArgumentException(sprintf('Undefined style: %s', $name)); + } + + return $this->styles[strtolower($name)]; + } + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message) + { + $message = (string) $message; + $offset = 0; + $output = ''; + $tagRegex = '[a-z][a-z0-9_=;-]*'; + preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $i => $match) { + $pos = $match[1]; + $text = $match[0]; + + if (0 != $pos && '\\' == $message[$pos - 1]) { + continue; + } + + // add the text up to the next tag + $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); + $offset = $pos + strlen($text); + + // opening tag? + if ($open = '/' != $text[1]) { + $tag = $matches[1][$i][0]; + } else { + $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; + } + + if (!$open && !$tag) { + // + $this->styleStack->pop(); + } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) { + $output .= $this->applyCurrentStyle($text); + } elseif ($open) { + $this->styleStack->push($style); + } else { + $this->styleStack->pop($style); + } + } + + $output .= $this->applyCurrentStyle(substr($message, $offset)); + + return str_replace('\\<', '<', $output); + } + + /** + * @return OutputFormatterStyleStack + */ + public function getStyleStack() + { + return $this->styleStack; + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return OutputFormatterStyle|bool false if string is not format string + */ + private function createStyleFromString($string) + { + if (isset($this->styles[$string])) { + return $this->styles[$string]; + } + + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + + if ('fg' == $match[0]) { + $style->setForeground($match[1]); + } elseif ('bg' == $match[0]) { + $style->setBackground($match[1]); + } else { + try { + $style->setOption($match[1]); + } catch (\InvalidArgumentException $e) { + return false; + } + } + } + + return $style; + } + + /** + * Applies current style from stack to text, if must be applied. + * + * @param string $text Input text + * + * @return string Styled text + */ + private function applyCurrentStyle($text) + { + return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterInterface.php b/vendor/symfony/console/Formatter/OutputFormatterInterface.php new file mode 100644 index 0000000..5a52ba0 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterInterface.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + */ + public function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message); +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyle.php b/vendor/symfony/console/Formatter/OutputFormatterStyle.php new file mode 100644 index 0000000..c7c6b4a --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyle.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private static $availableForegroundColors = array( + 'black' => array('set' => 30, 'unset' => 39), + 'red' => array('set' => 31, 'unset' => 39), + 'green' => array('set' => 32, 'unset' => 39), + 'yellow' => array('set' => 33, 'unset' => 39), + 'blue' => array('set' => 34, 'unset' => 39), + 'magenta' => array('set' => 35, 'unset' => 39), + 'cyan' => array('set' => 36, 'unset' => 39), + 'white' => array('set' => 37, 'unset' => 39), + 'default' => array('set' => 39, 'unset' => 39), + ); + private static $availableBackgroundColors = array( + 'black' => array('set' => 40, 'unset' => 49), + 'red' => array('set' => 41, 'unset' => 49), + 'green' => array('set' => 42, 'unset' => 49), + 'yellow' => array('set' => 43, 'unset' => 49), + 'blue' => array('set' => 44, 'unset' => 49), + 'magenta' => array('set' => 45, 'unset' => 49), + 'cyan' => array('set' => 46, 'unset' => 49), + 'white' => array('set' => 47, 'unset' => 49), + 'default' => array('set' => 49, 'unset' => 49), + ); + private static $availableOptions = array( + 'bold' => array('set' => 1, 'unset' => 22), + 'underscore' => array('set' => 4, 'unset' => 24), + 'blink' => array('set' => 5, 'unset' => 25), + 'reverse' => array('set' => 7, 'unset' => 27), + 'conceal' => array('set' => 8, 'unset' => 28), + ); + + private $foreground; + private $background; + private $options = array(); + + /** + * Initializes output formatter style. + * + * @param string|null $foreground The style foreground color name + * @param string|null $background The style background color name + * @param array $options The style options + */ + public function __construct($foreground = null, $background = null, array $options = array()) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (count($options)) { + $this->setOptions($options); + } + } + + /** + * Sets style foreground color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid foreground color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableForegroundColors)) + )); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * Sets style background color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid background color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableBackgroundColors)) + )); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * Sets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + if (!in_array(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * Unsets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options) + { + $this->options = array(); + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text) + { + $setCodes = array(); + $unsetCodes = array(); + + if (null !== $this->foreground) { + $setCodes[] = $this->foreground['set']; + $unsetCodes[] = $this->foreground['unset']; + } + if (null !== $this->background) { + $setCodes[] = $this->background['set']; + $unsetCodes[] = $this->background['unset']; + } + if (count($this->options)) { + foreach ($this->options as $option) { + $setCodes[] = $option['set']; + $unsetCodes[] = $option['unset']; + } + } + + if (0 === count($setCodes)) { + return $text; + } + + return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes)); + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 0000000..c36fda8 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string $color The color name + */ + public function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color The color name + */ + public function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option The option name + */ + public function setOption($option); + + /** + * Unsets some specific style option. + * + * @param string $option The option name + */ + public function unsetOption($option); + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options); + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text); +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php new file mode 100644 index 0000000..e5d14ea --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon + */ +class OutputFormatterStyleStack +{ + /** + * @var OutputFormatterStyleInterface[] + */ + private $styles; + + /** + * @var OutputFormatterStyleInterface + */ + private $emptyStyle; + + /** + * Constructor. + * + * @param OutputFormatterStyleInterface|null $emptyStyle + */ + public function __construct(OutputFormatterStyleInterface $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + */ + public function reset() + { + $this->styles = array(); + } + + /** + * Pushes a style in the stack. + * + * @param OutputFormatterStyleInterface $style + */ + public function push(OutputFormatterStyleInterface $style) + { + $this->styles[] = $style; + } + + /** + * Pops a style from the stack. + * + * @param OutputFormatterStyleInterface|null $style + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style tags incorrectly nested + */ + public function pop(OutputFormatterStyleInterface $style = null) + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + if (null === $style) { + return array_pop($this->styles); + } + + foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { + if ($style->apply('') === $stackedStyle->apply('')) { + $this->styles = array_slice($this->styles, 0, $index); + + return $stackedStyle; + } + } + + throw new InvalidArgumentException('Incorrectly nested style tag found.'); + } + + /** + * Computes current style with stacks top codes. + * + * @return OutputFormatterStyle + */ + public function getCurrent() + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + return $this->styles[count($this->styles) - 1]; + } + + /** + * @param OutputFormatterStyleInterface $emptyStyle + * + * @return OutputFormatterStyleStack + */ + public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) + { + $this->emptyStyle = $emptyStyle; + + return $this; + } + + /** + * @return OutputFormatterStyleInterface + */ + public function getEmptyStyle() + { + return $this->emptyStyle; + } +} diff --git a/vendor/symfony/console/Helper/DebugFormatterHelper.php b/vendor/symfony/console/Helper/DebugFormatterHelper.php new file mode 100644 index 0000000..1119b79 --- /dev/null +++ b/vendor/symfony/console/Helper/DebugFormatterHelper.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Helps outputting debug information when running an external program from a command. + * + * An external program can be a Process, an HTTP request, or anything else. + * + * @author Fabien Potencier + */ +class DebugFormatterHelper extends Helper +{ + private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'); + private $started = array(); + private $count = -1; + + /** + * Starts a debug formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param string $prefix The prefix to use + * + * @return string + */ + public function start($id, $message, $prefix = 'RUN') + { + $this->started[$id] = array('border' => ++$this->count % count($this->colors)); + + return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); + } + + /** + * Adds progress to a formatting session. + * + * @param string $id The id of the formatting session + * @param string $buffer The message to display + * @param bool $error Whether to consider the buffer as error + * @param string $prefix The prefix for output + * @param string $errorPrefix The prefix for error output + * + * @return string + */ + public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR') + { + $message = ''; + + if ($error) { + if (isset($this->started[$id]['out'])) { + $message .= "\n"; + unset($this->started[$id]['out']); + } + if (!isset($this->started[$id]['err'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $errorPrefix); + $this->started[$id]['err'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $errorPrefix), $buffer); + } else { + if (isset($this->started[$id]['err'])) { + $message .= "\n"; + unset($this->started[$id]['err']); + } + if (!isset($this->started[$id]['out'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $prefix); + $this->started[$id]['out'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); + } + + return $message; + } + + /** + * Stops a formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param bool $successful Whether to consider the result as success + * @param string $prefix The prefix for the end output + * + * @return string + */ + public function stop($id, $message, $successful, $prefix = 'RES') + { + $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; + + if ($successful) { + return sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + } + + $message = sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + + unset($this->started[$id]['out'], $this->started[$id]['err']); + + return $message; + } + + /** + * @param string $id The id of the formatting session + * + * @return string + */ + private function getBorder($id) + { + return sprintf(' ', $this->colors[$this->started[$id]['border']]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'debug_formatter'; + } +} diff --git a/vendor/symfony/console/Helper/DescriptorHelper.php b/vendor/symfony/console/Helper/DescriptorHelper.php new file mode 100644 index 0000000..a53b476 --- /dev/null +++ b/vendor/symfony/console/Helper/DescriptorHelper.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * This class adds helper method to describe objects in various formats. + * + * @author Jean-François Simon + */ +class DescriptorHelper extends Helper +{ + /** + * @var DescriptorInterface[] + */ + private $descriptors = array(); + + /** + * Constructor. + */ + public function __construct() + { + $this + ->register('txt', new TextDescriptor()) + ->register('xml', new XmlDescriptor()) + ->register('json', new JsonDescriptor()) + ->register('md', new MarkdownDescriptor()) + ; + } + + /** + * Describes an object if supported. + * + * Available options are: + * * format: string, the output format name + * * raw_text: boolean, sets output type as raw + * + * @param OutputInterface $output + * @param object $object + * @param array $options + * + * @throws InvalidArgumentException when the given format is not supported + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $options = array_merge(array( + 'raw_text' => false, + 'format' => 'txt', + ), $options); + + if (!isset($this->descriptors[$options['format']])) { + throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); + } + + $descriptor = $this->descriptors[$options['format']]; + $descriptor->describe($output, $object, $options); + } + + /** + * Registers a descriptor. + * + * @param string $format + * @param DescriptorInterface $descriptor + * + * @return DescriptorHelper + */ + public function register($format, DescriptorInterface $descriptor) + { + $this->descriptors[$format] = $descriptor; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'descriptor'; + } +} diff --git a/vendor/symfony/console/Helper/FormatterHelper.php b/vendor/symfony/console/Helper/FormatterHelper.php new file mode 100644 index 0000000..ac736f9 --- /dev/null +++ b/vendor/symfony/console/Helper/FormatterHelper.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + * + * @return string The format section + */ + public function formatSection($section, $message, $style = 'info') + { + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param bool $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + if (!is_array($messages)) { + $messages = array($messages); + } + + $len = 0; + $lines = array(); + foreach ($messages as $message) { + $message = OutputFormatter::escape($message); + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? array(str_repeat(' ', $len)) : array(); + for ($i = 0; isset($lines[$i]); ++$i) { + $messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i])); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + for ($i = 0; isset($messages[$i]); ++$i) { + $messages[$i] = sprintf('<%s>%s', $style, $messages[$i], $style); + } + + return implode("\n", $messages); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'formatter'; + } +} diff --git a/vendor/symfony/console/Helper/Helper.php b/vendor/symfony/console/Helper/Helper.php new file mode 100644 index 0000000..b542be3 --- /dev/null +++ b/vendor/symfony/console/Helper/Helper.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Returns the length of a string, using mb_strwidth if it is available. + * + * @param string $string The string to check its length + * + * @return int The length of the string + */ + public static function strlen($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + public static function formatTime($secs) + { + static $timeFormats = array( + array(0, '< 1 sec'), + array(2, '1 sec'), + array(59, 'secs', 1), + array(60, '1 min'), + array(3600, 'mins', 60), + array(5400, '1 hr'), + array(86400, 'hrs', 3600), + array(129600, '1 day'), + array(604800, 'days', 86400), + ); + + foreach ($timeFormats as $format) { + if ($secs >= $format[0]) { + continue; + } + + if (2 == count($format)) { + return $format[1]; + } + + return ceil($secs / $format[2]).' '.$format[1]; + } + } + + public static function formatMemory($memory) + { + if ($memory >= 1024 * 1024 * 1024) { + return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); + } + + if ($memory >= 1024 * 1024) { + return sprintf('%.1f MiB', $memory / 1024 / 1024); + } + + if ($memory >= 1024) { + return sprintf('%d KiB', $memory / 1024); + } + + return sprintf('%d B', $memory); + } + + public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) + { + $isDecorated = $formatter->isDecorated(); + $formatter->setDecorated(false); + // remove <...> formatting + $string = $formatter->format($string); + // remove already formatted characters + $string = preg_replace("/\033\[[^m]*m/", '', $string); + $formatter->setDecorated($isDecorated); + + return self::strlen($string); + } +} diff --git a/vendor/symfony/console/Helper/HelperInterface.php b/vendor/symfony/console/Helper/HelperInterface.php new file mode 100644 index 0000000..5a923e0 --- /dev/null +++ b/vendor/symfony/console/Helper/HelperInterface.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + public function getName(); +} diff --git a/vendor/symfony/console/Helper/HelperSet.php b/vendor/symfony/console/Helper/HelperSet.php new file mode 100644 index 0000000..f7d703a --- /dev/null +++ b/vendor/symfony/console/Helper/HelperSet.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + */ +class HelperSet implements \IteratorAggregate +{ + private $helpers = array(); + private $command; + + /** + * Constructor. + * + * @param Helper[] $helpers An array of helper. + */ + public function __construct(array $helpers = array()) + { + foreach ($helpers as $alias => $helper) { + $this->set($helper, is_int($alias) ? null : $alias); + } + } + + /** + * Sets a helper. + * + * @param HelperInterface $helper The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return bool true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + return $this->helpers[$name]; + } + + /** + * Sets the command associated with this helper set. + * + * @param Command $command A Command instance + */ + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + public function getIterator() + { + return new \ArrayIterator($this->helpers); + } +} diff --git a/vendor/symfony/console/Helper/InputAwareHelper.php b/vendor/symfony/console/Helper/InputAwareHelper.php new file mode 100644 index 0000000..4261767 --- /dev/null +++ b/vendor/symfony/console/Helper/InputAwareHelper.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputAwareInterface; + +/** + * An implementation of InputAwareInterface for Helpers. + * + * @author Wouter J + */ +abstract class InputAwareHelper extends Helper implements InputAwareInterface +{ + protected $input; + + /** + * {@inheritdoc} + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + } +} diff --git a/vendor/symfony/console/Helper/ProcessHelper.php b/vendor/symfony/console/Helper/ProcessHelper.php new file mode 100644 index 0000000..2c46a2c --- /dev/null +++ b/vendor/symfony/console/Helper/ProcessHelper.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; +use Symfony\Component\Process\ProcessBuilder; + +/** + * The ProcessHelper class provides helpers to run external processes. + * + * @author Fabien Potencier + */ +class ProcessHelper extends Helper +{ + /** + * Runs an external process. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|array|Process $cmd An instance of Process or an array of arguments to escape and run or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * @param int $verbosity The threshold for verbosity + * + * @return Process The process that ran + */ + public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + if (is_array($cmd)) { + $process = ProcessBuilder::create($cmd)->getProcess(); + } elseif ($cmd instanceof Process) { + $process = $cmd; + } else { + $process = new Process($cmd); + } + + if ($verbosity <= $output->getVerbosity()) { + $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine()))); + } + + if ($output->isDebug()) { + $callback = $this->wrapCallback($output, $process, $callback); + } + + $process->run($callback); + + if ($verbosity <= $output->getVerbosity()) { + $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); + $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); + } + + if (!$process->isSuccessful() && null !== $error) { + $output->writeln(sprintf('%s', $this->escapeString($error))); + } + + return $process; + } + + /** + * Runs the process. + * + * This is identical to run() except that an exception is thrown if the process + * exits with a non-zero exit code. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|Process $cmd An instance of Process or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return Process The process that ran + * + * @throws ProcessFailedException + * + * @see run() + */ + public function mustRun(OutputInterface $output, $cmd, $error = null, callable $callback = null) + { + $process = $this->run($output, $cmd, $error, $callback); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + return $process; + } + + /** + * Wraps a Process callback to add debugging output. + * + * @param OutputInterface $output An OutputInterface interface + * @param Process $process The Process + * @param callable|null $callback A PHP callable + * + * @return callable + */ + public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + return function ($type, $buffer) use ($output, $process, $callback, $formatter) { + $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type)); + + if (null !== $callback) { + call_user_func($callback, $type, $buffer); + } + }; + } + + private function escapeString($str) + { + return str_replace('<', '\\<', $str); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'process'; + } +} diff --git a/vendor/symfony/console/Helper/ProgressBar.php b/vendor/symfony/console/Helper/ProgressBar.php new file mode 100644 index 0000000..51083aa --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressBar.php @@ -0,0 +1,601 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\LogicException; + +/** + * The ProgressBar provides helpers to display progress output. + * + * @author Fabien Potencier + * @author Chris Jones + */ +class ProgressBar +{ + // options + private $barWidth = 28; + private $barChar; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format; + private $internalFormat; + private $redrawFreq = 1; + + /** + * @var OutputInterface + */ + private $output; + private $step = 0; + private $max; + private $startTime; + private $stepWidth; + private $percent = 0.0; + private $lastMessagesLength = 0; + private $formatLineCount; + private $messages; + private $overwrite = true; + + private static $formatters; + private static $formats; + + /** + * Constructor. + * + * @param OutputInterface $output An OutputInterface instance + * @param int $max Maximum steps (0 if unknown) + */ + public function __construct(OutputInterface $output, $max = 0) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->output = $output; + $this->setMaxSteps($max); + + if (!$this->output->isDecorated()) { + // disable overwrite when output does not support ANSI codes. + $this->overwrite = false; + + // set a reasonable redraw frequency so output isn't flooded + $this->setRedrawFrequency($max / 10); + } + + $this->startTime = time(); + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, callable $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + /** + * Sets a format for a given name. + * + * This method also allow you to override an existing format. + * + * @param string $name The format name + * @param string $format A format string + */ + public static function setFormatDefinition($name, $format) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + self::$formats[$name] = $format; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + public function setMessage($message, $name = 'message') + { + $this->messages[$name] = $message; + } + + public function getMessage($name = 'message') + { + return $this->messages[$name]; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the progress bar maximal steps. + * + * @return int The progress bar max steps + */ + public function getMaxSteps() + { + return $this->max; + } + + /** + * Gets the current step position. + * + * @return int The progress bar step + */ + public function getProgress() + { + return $this->step; + } + + /** + * Gets the progress bar step width. + * + * @return int The progress bar step width + */ + private function getStepWidth() + { + return $this->stepWidth; + } + + /** + * Gets the current progress bar percent. + * + * @return float The current progress bar percent + */ + public function getProgressPercent() + { + return $this->percent; + } + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = (int) $size; + } + + /** + * Gets the progress bar width. + * + * @return int The progress bar size + */ + public function getBarWidth() + { + return $this->barWidth; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Gets the bar character. + * + * @return string A character + */ + public function getBarCharacter() + { + if (null === $this->barChar) { + return $this->max ? '=' : $this->emptyBarChar; + } + + return $this->barChar; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Gets the empty bar character. + * + * @return string A character + */ + public function getEmptyBarCharacter() + { + return $this->emptyBarChar; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Gets the progress bar character. + * + * @return string A character + */ + public function getProgressCharacter() + { + return $this->progressChar; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = null; + $this->internalFormat = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int|float $freq The frequency in steps + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = max((int) $freq, 1); + } + + /** + * Starts the progress output. + * + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged + */ + public function start($max = null) + { + $this->startTime = time(); + $this->step = 0; + $this->percent = 0.0; + + if (null !== $max) { + $this->setMaxSteps($max); + } + + $this->display(); + } + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + * + * @throws LogicException + */ + public function advance($step = 1) + { + $this->setProgress($this->step + $step); + } + + /** + * Sets whether to overwrite the progressbar, false for new line. + * + * @param bool $overwrite + */ + public function setOverwrite($overwrite) + { + $this->overwrite = (bool) $overwrite; + } + + /** + * Sets the current progress. + * + * @param int $step The current progress + * + * @throws LogicException + */ + public function setProgress($step) + { + $step = (int) $step; + if ($step < $this->step) { + throw new LogicException('You can\'t regress the progress bar.'); + } + + if ($this->max && $step > $this->max) { + $this->max = $step; + } + + $prevPeriod = (int) ($this->step / $this->redrawFreq); + $currPeriod = (int) ($step / $this->redrawFreq); + $this->step = $step; + $this->percent = $this->max ? (float) $this->step / $this->max : 0; + if ($prevPeriod !== $currPeriod || $this->max === $step) { + $this->display(); + } + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (!$this->max) { + $this->max = $this->step; + } + + if ($this->step === $this->max && !$this->overwrite) { + // prevent double 100% output + return; + } + + $this->setProgress($this->max); + } + + /** + * Outputs the current progress string. + */ + public function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) { + if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { + $text = call_user_func($formatter, $this, $this->output); + } elseif (isset($this->messages[$matches[1]])) { + $text = $this->messages[$matches[1]]; + } else { + return $matches[0]; + } + + if (isset($matches[2])) { + $text = sprintf('%'.$matches[2], $text); + } + + return $text; + }, $this->format)); + } + + /** + * Removes the progress bar from the current line. + * + * This is useful if you wish to write some output + * while a progress bar is running. + * Call display() to show the progress bar again. + */ + public function clear() + { + if (!$this->overwrite) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(str_repeat("\n", $this->formatLineCount)); + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + private function setRealFormat($format) + { + // try to use the _nomax variant if available + if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { + $this->format = self::getFormatDefinition($format.'_nomax'); + } elseif (null !== self::getFormatDefinition($format)) { + $this->format = self::getFormatDefinition($format); + } else { + $this->format = $format; + } + + $this->formatLineCount = substr_count($this->format, "\n"); + } + + /** + * Sets the progress bar maximal steps. + * + * @param int $max The progress bar max steps + */ + private function setMaxSteps($max) + { + $this->max = max(0, (int) $max); + $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + $lines = explode("\n", $message); + + // append whitespace to match the line's length + if (null !== $this->lastMessagesLength) { + foreach ($lines as $i => $line) { + if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $line)) { + $lines[$i] = str_pad($line, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + } + } + + if ($this->overwrite) { + // move back to the beginning of the progress bar before redrawing it + $this->output->write("\x0D"); + } elseif ($this->step > 0) { + // move to new line + $this->output->writeln(''); + } + + if ($this->formatLineCount) { + $this->output->write(sprintf("\033[%dA", $this->formatLineCount)); + } + $this->output->write(implode("\n", $lines)); + + $this->lastMessagesLength = 0; + foreach ($lines as $line) { + $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $line); + if ($len > $this->lastMessagesLength) { + $this->lastMessagesLength = $len; + } + } + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->max ? 'verbose' : 'verbose_nomax'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + return $this->max ? 'very_verbose' : 'very_verbose_nomax'; + case OutputInterface::VERBOSITY_DEBUG: + return $this->max ? 'debug' : 'debug_nomax'; + default: + return $this->max ? 'normal' : 'normal_nomax'; + } + } + + private static function initPlaceholderFormatters() + { + return array( + 'bar' => function (ProgressBar $bar, OutputInterface $output) { + $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth()); + $display = str_repeat($bar->getBarCharacter(), $completeBars); + if ($completeBars < $bar->getBarWidth()) { + $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); + $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); + } + + return $display; + }, + 'elapsed' => function (ProgressBar $bar) { + return Helper::formatTime(time() - $bar->getStartTime()); + }, + 'remaining' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $remaining = 0; + } else { + $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress())); + } + + return Helper::formatTime($remaining); + }, + 'estimated' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $estimated = 0; + } else { + $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps()); + } + + return Helper::formatTime($estimated); + }, + 'memory' => function (ProgressBar $bar) { + return Helper::formatMemory(memory_get_usage(true)); + }, + 'current' => function (ProgressBar $bar) { + return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); + }, + 'max' => function (ProgressBar $bar) { + return $bar->getMaxSteps(); + }, + 'percent' => function (ProgressBar $bar) { + return floor($bar->getProgressPercent() * 100); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', + 'normal_nomax' => ' %current% [%bar%]', + + 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', + 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', + 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', + 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%', + ); + } +} diff --git a/vendor/symfony/console/Helper/ProgressIndicator.php b/vendor/symfony/console/Helper/ProgressIndicator.php new file mode 100644 index 0000000..8c8a49c --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressIndicator.php @@ -0,0 +1,322 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Kevin Bond + */ +class ProgressIndicator +{ + private $output; + private $startTime; + private $format; + private $message; + private $indicatorValues; + private $indicatorCurrent; + private $indicatorChangeInterval; + private $indicatorUpdateTime; + private $lastMessagesLength; + private $started = false; + + private static $formatters; + private static $formats; + + /** + * @param OutputInterface $output + * @param string|null $format Indicator format + * @param int $indicatorChangeInterval Change interval in milliseconds + * @param array|null $indicatorValues Animated indicator characters + */ + public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null) + { + $this->output = $output; + + if (null === $format) { + $format = $this->determineBestFormat(); + } + + if (null === $indicatorValues) { + $indicatorValues = array('-', '\\', '|', '/'); + } + + $indicatorValues = array_values($indicatorValues); + + if (2 > count($indicatorValues)) { + throw new \InvalidArgumentException('Must have at least 2 indicator value characters.'); + } + + $this->format = self::getFormatDefinition($format); + $this->indicatorChangeInterval = $indicatorChangeInterval; + $this->indicatorValues = $indicatorValues; + $this->startTime = time(); + } + + /** + * Sets the current indicator message. + * + * @param string|null $message + */ + public function setMessage($message) + { + $this->message = $message; + + $this->display(); + } + + /** + * Gets the current indicator message. + * + * @return string|null + * + * @internal for PHP 5.3 compatibility + */ + public function getMessage() + { + return $this->message; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + * + * @internal for PHP 5.3 compatibility + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the current animated indicator character. + * + * @return string + * + * @internal for PHP 5.3 compatibility + */ + public function getCurrentValue() + { + return $this->indicatorValues[$this->indicatorCurrent % count($this->indicatorValues)]; + } + + /** + * Starts the indicator output. + * + * @param $message + */ + public function start($message) + { + if ($this->started) { + throw new \LogicException('Progress indicator already started.'); + } + + $this->message = $message; + $this->started = true; + $this->lastMessagesLength = 0; + $this->startTime = time(); + $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; + $this->indicatorCurrent = 0; + + $this->display(); + } + + /** + * Advances the indicator. + */ + public function advance() + { + if (!$this->started) { + throw new \LogicException('Progress indicator has not yet been started.'); + } + + if (!$this->output->isDecorated()) { + return; + } + + $currentTime = $this->getCurrentTimeInMilliseconds(); + + if ($currentTime < $this->indicatorUpdateTime) { + return; + } + + $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval; + ++$this->indicatorCurrent; + + $this->display(); + } + + /** + * Finish the indicator with message. + * + * @param $message + */ + public function finish($message) + { + if (!$this->started) { + throw new \LogicException('Progress indicator has not yet been started.'); + } + + $this->message = $message; + $this->display(); + $this->output->writeln(''); + $this->started = false; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + private function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + $self = $this; + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) { + if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { + return call_user_func($formatter, $self); + } + + return $matches[0]; + }, $this->format)); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi'; + default: + return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi'; + } + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + // append whitespace to match the line's length + if (null !== $this->lastMessagesLength) { + if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $message)) { + $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + } + + if ($this->output->isDecorated()) { + $this->output->write("\x0D"); + $this->output->write($message); + } else { + $this->output->writeln($message); + } + + $this->lastMessagesLength = 0; + + $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $message); + + if ($len > $this->lastMessagesLength) { + $this->lastMessagesLength = $len; + } + } + + private function getCurrentTimeInMilliseconds() + { + return round(microtime(true) * 1000); + } + + private static function initPlaceholderFormatters() + { + return array( + 'indicator' => function (ProgressIndicator $indicator) { + return $indicator->getCurrentValue(); + }, + 'message' => function (ProgressIndicator $indicator) { + return $indicator->getMessage(); + }, + 'elapsed' => function (ProgressIndicator $indicator) { + return Helper::formatTime(time() - $indicator->getStartTime()); + }, + 'memory' => function () { + return Helper::formatMemory(memory_get_usage(true)); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %indicator% %message%', + 'normal_no_ansi' => ' %message%', + + 'verbose' => ' %indicator% %message% (%elapsed:6s%)', + 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', + + 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', + 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', + ); + } +} diff --git a/vendor/symfony/console/Helper/QuestionHelper.php b/vendor/symfony/console/Helper/QuestionHelper.php new file mode 100644 index 0000000..15b7cd6 --- /dev/null +++ b/vendor/symfony/console/Helper/QuestionHelper.php @@ -0,0 +1,465 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Question\ChoiceQuestion; + +/** + * The QuestionHelper class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class QuestionHelper extends Helper +{ + private $inputStream; + private static $shell; + private static $stty; + + /** + * Asks a question to the user. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * @param Question $question The question to ask + * + * @return string The user answer + * + * @throws RuntimeException If there is no data to read in the input stream + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + if (!$input->isInteractive()) { + return $question->getDefault(); + } + + if (!$question->getValidator()) { + return $this->doAsk($output, $question); + } + + $interviewer = function () use ($output, $question) { + return $this->doAsk($output, $question); + }; + + return $this->validateAttempts($interviewer, $output, $question); + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + * + * @throws InvalidArgumentException In case the stream is not a resource + */ + public function setInputStream($stream) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Input stream must be a valid resource.'); + } + + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream. + * + * @return resource + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'question'; + } + + /** + * Asks the question to the user. + * + * @param OutputInterface $output + * @param Question $question + * + * @return bool|mixed|null|string + * + * @throws \Exception + * @throws \RuntimeException + */ + private function doAsk(OutputInterface $output, Question $question) + { + $this->writePrompt($output, $question); + + $inputStream = $this->inputStream ?: STDIN; + $autocomplete = $question->getAutocompleterValues(); + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = false; + if ($question->isHidden()) { + try { + $ret = trim($this->getHiddenResponse($output, $inputStream)); + } catch (\RuntimeException $e) { + if (!$question->isHiddenFallback()) { + throw $e; + } + } + } + + if (false === $ret) { + $ret = $this->readFromInput($inputStream); + } + } else { + $ret = trim($this->autocomplete($output, $question, $inputStream)); + } + + $ret = strlen($ret) > 0 ? $ret : $question->getDefault(); + + if ($normalizer = $question->getNormalizer()) { + return $normalizer($ret); + } + + return $ret; + } + + /** + * Outputs the question prompt. + * + * @param OutputInterface $output + * @param Question $question + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $message = $question->getQuestion(); + + if ($question instanceof ChoiceQuestion) { + $maxWidth = max(array_map(array($this, 'strlen'), array_keys($question->getChoices()))); + + $messages = (array) $question->getQuestion(); + foreach ($question->getChoices() as $key => $value) { + $width = $maxWidth - $this->strlen($key); + $messages[] = ' ['.$key.str_repeat(' ', $width).'] '.$value; + } + + $output->writeln($messages); + + $message = $question->getPrompt(); + } + + $output->write($message); + } + + /** + * Outputs an error message. + * + * @param OutputInterface $output + * @param \Exception $error + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) { + $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'); + } else { + $message = ''.$error->getMessage().''; + } + + $output->writeln($message); + } + + /** + * Autocompletes a question. + * + * @param OutputInterface $output + * @param Question $question + * + * @return string + */ + private function autocomplete(OutputInterface $output, Question $question, $inputStream) + { + $autocomplete = $question->getAutocompleterValues(); + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + + $sttyMode = shell_exec('stty -g'); + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while (!feof($inputStream)) { + $c = fread($inputStream, 1); + + // Backspace Character + if ("\177" === $c) { + if (0 === $numMatches && 0 !== $i) { + --$i; + // Move cursor backwards + $output->write("\033[1D"); + } + + if ($i === 0) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { + // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + // Echo out remaining chars for current match + $output->write(substr($ret, $i)); + $i = strlen($ret); + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + $output->write($c); + $ret .= $c; + ++$i; + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (0 === strpos($value, $ret) && $i !== strlen($value)) { + $matches[$numMatches++] = $value; + } + } + } + + // Erase characters from cursor to end of line + $output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + // Save cursor position + $output->write("\0337"); + // Write highlighted text + $output->write(''.substr($matches[$ofs], $i).''); + // Restore cursor position + $output->write("\0338"); + } + } + + // Reset stty so it behaves normally again + shell_exec(sprintf('stty %s', $sttyMode)); + + return $ret; + } + + /** + * Gets a hidden response from user. + * + * @param OutputInterface $output An Output instance + * + * @return string The answer + * + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden + */ + private function getHiddenResponse(OutputInterface $output, $inputStream) + { + if ('\\' === DIRECTORY_SEPARATOR) { + $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($inputStream, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new RuntimeException('Aborted'); + } + + $value = trim($value); + $output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $output->writeln(''); + + return $value; + } + + throw new RuntimeException('Unable to hide the response.'); + } + + /** + * Validates an attempt. + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param Question $question A Question instance + * + * @return string The validated response + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question) + { + $error = null; + $attempts = $question->getMaxAttempts(); + while (null === $attempts || $attempts--) { + if (null !== $error) { + $this->writeError($output, $error); + } + + try { + return call_user_func($question->getValidator(), $interviewer()); + } catch (\Exception $error) { + } + } + + throw $error; + } + + /** + * Returns a valid unix shell. + * + * @return string|bool The valid shell name, false in case no valid shell is found + */ + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + // handle other OSs with bash/zsh/ksh/csh if available to hide the answer + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + /** + * Reads user input. + * + * @param resource $stream The input stream + * + * @return string User input + * + * @throws RuntimeException + */ + private function readFromInput($stream) + { + if (STDIN === $stream && function_exists('readline')) { + $ret = readline(''); + } else { + $ret = fgets($stream, 4096); + } + + if (false === $ret) { + throw new RuntimeException('Aborted'); + } + + return trim($ret); + } + + /** + * Returns whether Stty is available or not. + * + * @return bool + */ + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Helper/SymfonyQuestionHelper.php b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php new file mode 100644 index 0000000..942278b --- /dev/null +++ b/vendor/symfony/console/Helper/SymfonyQuestionHelper.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Symfony Style Guide compliant question helper. + * + * @author Kevin Bond + */ +class SymfonyQuestionHelper extends QuestionHelper +{ + /** + * {@inheritdoc} + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + $validator = $question->getValidator(); + $question->setValidator(function ($value) use ($validator) { + if (null !== $validator) { + $value = $validator($value); + } + + // make required + if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) { + throw new LogicException('A value is required.'); + } + + return $value; + }); + + return parent::ask($input, $output, $question); + } + + /** + * {@inheritdoc} + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $text = $question->getQuestion(); + $default = $question->getDefault(); + + switch (true) { + case null === $default: + $text = sprintf(' %s:', $text); + + break; + + case $question instanceof ConfirmationQuestion: + $text = sprintf(' %s (yes/no) [%s]:', $text, $default ? 'yes' : 'no'); + + break; + + case $question instanceof ChoiceQuestion: + $choices = $question->getChoices(); + $text = sprintf(' %s [%s]:', $text, $choices[$default]); + + break; + + default: + $text = sprintf(' %s [%s]:', $text, $default); + } + + $output->writeln($text); + + if ($question instanceof ChoiceQuestion) { + $width = max(array_map('strlen', array_keys($question->getChoices()))); + + foreach ($question->getChoices() as $key => $value) { + $output->writeln(sprintf(" [%-${width}s] %s", $key, $value)); + } + } + + $output->write(' > '); + } + + /** + * {@inheritdoc} + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if ($output instanceof SymfonyStyle) { + $output->newLine(); + $output->error($error->getMessage()); + + return; + } + + parent::writeError($output, $error); + } +} diff --git a/vendor/symfony/console/Helper/Table.php b/vendor/symfony/console/Helper/Table.php new file mode 100644 index 0000000..1f103ad --- /dev/null +++ b/vendor/symfony/console/Helper/Table.php @@ -0,0 +1,654 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Provides helpers to display a table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + * @author Abdellatif Ait boudad + * @author Max Grigorian + */ +class Table +{ + /** + * Table headers. + * + * @var array + */ + private $headers = array(); + + /** + * Table rows. + * + * @var array + */ + private $rows = array(); + + /** + * Column widths cache. + * + * @var array + */ + private $columnWidths = array(); + + /** + * Number of columns cache. + * + * @var array + */ + private $numberOfColumns; + + /** + * @var OutputInterface + */ + private $output; + + /** + * @var TableStyle + */ + private $style; + + /** + * @var array + */ + private $columnStyles = array(); + + private static $styles; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + $this->setStyle('default'); + } + + /** + * Sets a style definition. + * + * @param string $name The style name + * @param TableStyle $style A TableStyle instance + */ + public static function setStyleDefinition($name, TableStyle $style) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + self::$styles[$name] = $style; + } + + /** + * Gets a style definition by name. + * + * @param string $name The style name + * + * @return TableStyle A TableStyle instance + */ + public static function getStyleDefinition($name) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + if (!self::$styles[$name]) { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return self::$styles[$name]; + } + + /** + * Sets table style. + * + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setStyle($name) + { + if ($name instanceof TableStyle) { + $this->style = $name; + } elseif (isset(self::$styles[$name])) { + $this->style = self::$styles[$name]; + } else { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current table style. + * + * @return TableStyle + */ + public function getStyle() + { + return $this->style; + } + + /** + * Sets table column style. + * + * @param int $columnIndex Column index + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setColumnStyle($columnIndex, $name) + { + $columnIndex = intval($columnIndex); + + if ($name instanceof TableStyle) { + $this->columnStyles[$columnIndex] = $name; + } elseif (isset(self::$styles[$name])) { + $this->columnStyles[$columnIndex] = self::$styles[$name]; + } else { + throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current style for a column. + * + * If style was not set, it returns the global table style. + * + * @param int $columnIndex Column index + * + * @return TableStyle + */ + public function getColumnStyle($columnIndex) + { + if (isset($this->columnStyles[$columnIndex])) { + return $this->columnStyles[$columnIndex]; + } + + return $this->getStyle(); + } + + public function setHeaders(array $headers) + { + $headers = array_values($headers); + if (!empty($headers) && !is_array($headers[0])) { + $headers = array($headers); + } + + $this->headers = $headers; + + return $this; + } + + public function setRows(array $rows) + { + $this->rows = array(); + + return $this->addRows($rows); + } + + public function addRows(array $rows) + { + foreach ($rows as $row) { + $this->addRow($row); + } + + return $this; + } + + public function addRow($row) + { + if ($row instanceof TableSeparator) { + $this->rows[] = $row; + + return $this; + } + + if (!is_array($row)) { + throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); + } + + $this->rows[] = array_values($row); + + return $this; + } + + public function setRow($column, array $row) + { + $this->rows[$column] = $row; + + return $this; + } + + /** + * Renders table to output. + * + * Example: + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ + */ + public function render() + { + $this->calculateNumberOfColumns(); + $rows = $this->buildTableRows($this->rows); + $headers = $this->buildTableRows($this->headers); + + $this->calculateColumnsWidth(array_merge($headers, $rows)); + + $this->renderRowSeparator(); + if (!empty($headers)) { + foreach ($headers as $header) { + $this->renderRow($header, $this->style->getCellHeaderFormat()); + $this->renderRowSeparator(); + } + } + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + $this->renderRowSeparator(); + } else { + $this->renderRow($row, $this->style->getCellRowFormat()); + } + } + if (!empty($rows)) { + $this->renderRowSeparator(); + } + + $this->cleanup(); + } + + /** + * Renders horizontal header separator. + * + * Example: +-----+-----------+-------+ + */ + private function renderRowSeparator() + { + if (0 === $count = $this->numberOfColumns) { + return; + } + + if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) { + return; + } + + $markup = $this->style->getCrossingChar(); + for ($column = 0; $column < $count; ++$column) { + $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->columnWidths[$column]).$this->style->getCrossingChar(); + } + + $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); + } + + /** + * Renders vertical column separator. + */ + private function renderColumnSeparator() + { + $this->output->write(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + } + + /** + * Renders table row. + * + * Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * + * @param array $row + * @param string $cellFormat + */ + private function renderRow(array $row, $cellFormat) + { + if (empty($row)) { + return; + } + + $this->renderColumnSeparator(); + foreach ($this->getRowColumns($row) as $column) { + $this->renderCell($row, $column, $cellFormat); + $this->renderColumnSeparator(); + } + $this->output->writeln(''); + } + + /** + * Renders table cell with padding. + * + * @param array $row + * @param int $column + * @param string $cellFormat + */ + private function renderCell(array $row, $column, $cellFormat) + { + $cell = isset($row[$column]) ? $row[$column] : ''; + $width = $this->columnWidths[$column]; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // add the width of the following columns(numbers of colspan). + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { + $width += $this->getColumnSeparatorWidth() + $this->columnWidths[$nextColumn]; + } + } + + // str_pad won't work properly with multi-byte strings, we need to fix the padding + if (false !== $encoding = mb_detect_encoding($cell, null, true)) { + $width += strlen($cell) - mb_strwidth($cell, $encoding); + } + + $style = $this->getColumnStyle($column); + + if ($cell instanceof TableSeparator) { + $this->output->write(sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width))); + } else { + $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $content = sprintf($style->getCellRowContentFormat(), $cell); + $this->output->write(sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType()))); + } + } + + /** + * Calculate number of columns for this table. + */ + private function calculateNumberOfColumns() + { + if (null !== $this->numberOfColumns) { + return; + } + + $columns = array(0); + foreach (array_merge($this->headers, $this->rows) as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + $columns[] = $this->getNumberOfColumns($row); + } + + $this->numberOfColumns = max($columns); + } + + private function buildTableRows($rows) + { + $unmergedRows = array(); + for ($rowKey = 0; $rowKey < count($rows); ++$rowKey) { + $rows = $this->fillNextRows($rows, $rowKey); + + // Remove any new line breaks and replace it with a new line + foreach ($rows[$rowKey] as $column => $cell) { + if (!strstr($cell, "\n")) { + continue; + } + $lines = explode("\n", $cell); + foreach ($lines as $lineKey => $line) { + if ($cell instanceof TableCell) { + $line = new TableCell($line, array('colspan' => $cell->getColspan())); + } + if (0 === $lineKey) { + $rows[$rowKey][$column] = $line; + } else { + $unmergedRows[$rowKey][$lineKey][$column] = $line; + } + } + } + } + + $tableRows = array(); + foreach ($rows as $rowKey => $row) { + $tableRows[] = $this->fillCells($row); + if (isset($unmergedRows[$rowKey])) { + $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); + } + } + + return $tableRows; + } + + /** + * fill rows that contains rowspan > 1. + * + * @param array $rows + * @param int $line + * + * @return array + */ + private function fillNextRows($rows, $line) + { + $unmergedRows = array(); + foreach ($rows[$line] as $column => $cell) { + if ($cell instanceof TableCell && $cell->getRowspan() > 1) { + $nbLines = $cell->getRowspan() - 1; + $lines = array($cell); + if (strstr($cell, "\n")) { + $lines = explode("\n", $cell); + $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; + + $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan())); + unset($lines[0]); + } + + // create a two dimensional array (rowspan x colspan) + $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, ''), $unmergedRows); + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; + $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan())); + } + } + } + + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + // we need to know if $unmergedRow will be merged or inserted into $rows + if (isset($rows[$unmergedRowKey]) && is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { + foreach ($unmergedRow as $cellKey => $cell) { + // insert cell into row at cellKey position + array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell)); + } + } else { + $row = $this->copyRow($rows, $unmergedRowKey - 1); + foreach ($unmergedRow as $column => $cell) { + if (!empty($cell)) { + $row[$column] = $unmergedRow[$column]; + } + } + array_splice($rows, $unmergedRowKey, 0, array($row)); + } + } + + return $rows; + } + + /** + * fill cells for a row that contains colspan > 1. + * + * @param array $row + * + * @return array + */ + private function fillCells($row) + { + $newRow = array(); + foreach ($row as $column => $cell) { + $newRow[] = $cell; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { + // insert empty value at column position + $newRow[] = ''; + } + } + } + + return $newRow ?: $row; + } + + /** + * @param array $rows + * @param int $line + * + * @return array + */ + private function copyRow($rows, $line) + { + $row = $rows[$line]; + foreach ($row as $cellKey => $cellValue) { + $row[$cellKey] = ''; + if ($cellValue instanceof TableCell) { + $row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan())); + } + } + + return $row; + } + + /** + * Gets number of columns by row. + * + * @param array $row + * + * @return int + */ + private function getNumberOfColumns(array $row) + { + $columns = count($row); + foreach ($row as $column) { + $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; + } + + return $columns; + } + + /** + * Gets list of columns for the given row. + * + * @param array $row + * + * @return array + */ + private function getRowColumns($row) + { + $columns = range(0, $this->numberOfColumns - 1); + foreach ($row as $cellKey => $cell) { + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // exclude grouped columns. + $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); + } + } + + return $columns; + } + + /** + * Calculates columns widths. + * + * @param array $rows + */ + private function calculateColumnsWidth($rows) + { + for ($column = 0; $column < $this->numberOfColumns; ++$column) { + $lengths = array(); + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + $lengths[] = $this->getCellWidth($row, $column); + } + + $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2; + } + } + + /** + * Gets column width. + * + * @return int + */ + private function getColumnSeparatorWidth() + { + return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + } + + /** + * Gets cell width. + * + * @param array $row + * @param int $column + * + * @return int + */ + private function getCellWidth(array $row, $column) + { + if (isset($row[$column])) { + $cell = $row[$column]; + $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // we assume that cell value will be across more than one column. + $cellWidth = $cellWidth / $cell->getColspan(); + } + + return $cellWidth; + } + + return 0; + } + + /** + * Called after rendering to cleanup cache data. + */ + private function cleanup() + { + $this->columnWidths = array(); + $this->numberOfColumns = null; + } + + private static function initStyles() + { + $borderless = new TableStyle(); + $borderless + ->setHorizontalBorderChar('=') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ; + + $compact = new TableStyle(); + $compact + ->setHorizontalBorderChar('') + ->setVerticalBorderChar(' ') + ->setCrossingChar('') + ->setCellRowContentFormat('%s') + ; + + $styleGuide = new TableStyle(); + $styleGuide + ->setHorizontalBorderChar('-') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ->setCellHeaderFormat('%s') + ; + + return array( + 'default' => new TableStyle(), + 'borderless' => $borderless, + 'compact' => $compact, + 'symfony-style-guide' => $styleGuide, + ); + } +} diff --git a/vendor/symfony/console/Helper/TableCell.php b/vendor/symfony/console/Helper/TableCell.php new file mode 100644 index 0000000..69442d4 --- /dev/null +++ b/vendor/symfony/console/Helper/TableCell.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Abdellatif Ait boudad + */ +class TableCell +{ + /** + * @var string + */ + private $value; + + /** + * @var array + */ + private $options = array( + 'rowspan' => 1, + 'colspan' => 1, + ); + + /** + * @param string $value + * @param array $options + */ + public function __construct($value = '', array $options = array()) + { + $this->value = $value; + + // check option names + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + $this->options = array_merge($this->options, $options); + } + + /** + * Returns the cell value. + * + * @return string + */ + public function __toString() + { + return $this->value; + } + + /** + * Gets number of colspan. + * + * @return int + */ + public function getColspan() + { + return (int) $this->options['colspan']; + } + + /** + * Gets number of rowspan. + * + * @return int + */ + public function getRowspan() + { + return (int) $this->options['rowspan']; + } +} diff --git a/vendor/symfony/console/Helper/TableSeparator.php b/vendor/symfony/console/Helper/TableSeparator.php new file mode 100644 index 0000000..8cbbc66 --- /dev/null +++ b/vendor/symfony/console/Helper/TableSeparator.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Marks a row as being a separator. + * + * @author Fabien Potencier + */ +class TableSeparator extends TableCell +{ + /** + * @param string $value + * @param array $options + */ + public function __construct(array $options = array()) + { + parent::__construct('', $options); + } +} diff --git a/vendor/symfony/console/Helper/TableStyle.php b/vendor/symfony/console/Helper/TableStyle.php new file mode 100644 index 0000000..d7e28ff --- /dev/null +++ b/vendor/symfony/console/Helper/TableStyle.php @@ -0,0 +1,258 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Defines the styles for a Table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + */ +class TableStyle +{ + private $paddingChar = ' '; + private $horizontalBorderChar = '-'; + private $verticalBorderChar = '|'; + private $crossingChar = '+'; + private $cellHeaderFormat = '%s'; + private $cellRowFormat = '%s'; + private $cellRowContentFormat = ' %s '; + private $borderFormat = '%s'; + private $padType = STR_PAD_RIGHT; + + /** + * Sets padding character, used for cell padding. + * + * @param string $paddingChar + * + * @return TableStyle + */ + public function setPaddingChar($paddingChar) + { + if (!$paddingChar) { + throw new LogicException('The padding char must not be empty'); + } + + $this->paddingChar = $paddingChar; + + return $this; + } + + /** + * Gets padding character, used for cell padding. + * + * @return string + */ + public function getPaddingChar() + { + return $this->paddingChar; + } + + /** + * Sets horizontal border character. + * + * @param string $horizontalBorderChar + * + * @return TableStyle + */ + public function setHorizontalBorderChar($horizontalBorderChar) + { + $this->horizontalBorderChar = $horizontalBorderChar; + + return $this; + } + + /** + * Gets horizontal border character. + * + * @return string + */ + public function getHorizontalBorderChar() + { + return $this->horizontalBorderChar; + } + + /** + * Sets vertical border character. + * + * @param string $verticalBorderChar + * + * @return TableStyle + */ + public function setVerticalBorderChar($verticalBorderChar) + { + $this->verticalBorderChar = $verticalBorderChar; + + return $this; + } + + /** + * Gets vertical border character. + * + * @return string + */ + public function getVerticalBorderChar() + { + return $this->verticalBorderChar; + } + + /** + * Sets crossing character. + * + * @param string $crossingChar + * + * @return TableStyle + */ + public function setCrossingChar($crossingChar) + { + $this->crossingChar = $crossingChar; + + return $this; + } + + /** + * Gets crossing character. + * + * @return string $crossingChar + */ + public function getCrossingChar() + { + return $this->crossingChar; + } + + /** + * Sets header cell format. + * + * @param string $cellHeaderFormat + * + * @return TableStyle + */ + public function setCellHeaderFormat($cellHeaderFormat) + { + $this->cellHeaderFormat = $cellHeaderFormat; + + return $this; + } + + /** + * Gets header cell format. + * + * @return string + */ + public function getCellHeaderFormat() + { + return $this->cellHeaderFormat; + } + + /** + * Sets row cell format. + * + * @param string $cellRowFormat + * + * @return TableStyle + */ + public function setCellRowFormat($cellRowFormat) + { + $this->cellRowFormat = $cellRowFormat; + + return $this; + } + + /** + * Gets row cell format. + * + * @return string + */ + public function getCellRowFormat() + { + return $this->cellRowFormat; + } + + /** + * Sets row cell content format. + * + * @param string $cellRowContentFormat + * + * @return TableStyle + */ + public function setCellRowContentFormat($cellRowContentFormat) + { + $this->cellRowContentFormat = $cellRowContentFormat; + + return $this; + } + + /** + * Gets row cell content format. + * + * @return string + */ + public function getCellRowContentFormat() + { + return $this->cellRowContentFormat; + } + + /** + * Sets table border format. + * + * @param string $borderFormat + * + * @return TableStyle + */ + public function setBorderFormat($borderFormat) + { + $this->borderFormat = $borderFormat; + + return $this; + } + + /** + * Gets table border format. + * + * @return string + */ + public function getBorderFormat() + { + return $this->borderFormat; + } + + /** + * Sets cell padding type. + * + * @param int $padType STR_PAD_* + * + * @return TableStyle + */ + public function setPadType($padType) + { + if (!in_array($padType, array(STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH), true)) { + throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).'); + } + + $this->padType = $padType; + + return $this; + } + + /** + * Gets cell padding type. + * + * @return int + */ + public function getPadType() + { + return $this->padType; + } +} diff --git a/vendor/symfony/console/Input/ArgvInput.php b/vendor/symfony/console/Input/ArgvInput.php new file mode 100644 index 0000000..fec04e2 --- /dev/null +++ b/vendor/symfony/console/Input/ArgvInput.php @@ -0,0 +1,358 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + */ +class ArgvInput extends Input +{ + private $tokens; + private $parsed; + + /** + * Constructor. + * + * @param array $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $argv = null, InputDefinition $definition = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * Parses a short option. + * + * @param string $token The current token. + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @param string $name The current token + * + * @throws RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet($name) + { + $len = strlen($name); + for ($i = 0; $i < $len; ++$i) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), null); + } + } + } + + /** + * Parses a long option. + * + * @param string $token The current token + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws RuntimeException When too many arguments are given + */ + private function parseArgument($token) + { + $c = count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + throw new RuntimeException('Too many arguments.'); + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + // Convert empty values to null + if (!isset($value[0])) { + $value = null; + } + + if (null !== $value && !$option->acceptValue()) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + + if (null === $value && $option->acceptValue() && count($this->parsed)) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if (isset($next[0]) && '-' !== $next[0]) { + $value = $next; + } elseif (empty($next)) { + $value = ''; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + if (!$option->isArray()) { + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->tokens as $token) { + if ($onlyParams && $token === '--') { + return false; + } + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + return true; + } + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false, $onlyParams = false) + { + $values = (array) $values; + $tokens = $this->tokens; + + while (0 < count($tokens)) { + $token = array_shift($tokens); + if ($onlyParams && $token === '--') { + return false; + } + + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } + + return array_shift($tokens); + } + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $tokens = array_map(function ($token) { + if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { + return $match[1].$this->escapeToken($match[2]); + } + + if ($token && $token[0] !== '-') { + return $this->escapeToken($token); + } + + return $token; + }, $this->tokens); + + return implode(' ', $tokens); + } +} diff --git a/vendor/symfony/console/Input/ArrayInput.php b/vendor/symfony/console/Input/ArrayInput.php new file mode 100644 index 0000000..6705d4b --- /dev/null +++ b/vendor/symfony/console/Input/ArrayInput.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\InvalidOptionException; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * + * @author Fabien Potencier + */ +class ArrayInput extends Input +{ + private $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } + + return $value; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + if ($onlyParams && $v === '--') { + return false; + } + + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if ($onlyParams && ($k === '--' || (is_int($k) && $v === '--'))) { + return false; + } + + if (is_int($k)) { + if (in_array($v, $values)) { + return true; + } + } elseif (in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $params = array(); + foreach ($this->parameters as $param => $val) { + if ($param && '-' === $param[0]) { + $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); + } else { + $params[] = $this->escapeToken($val); + } + } + + return implode(' ', $params); + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if ($key === '--') { + return; + } + if (0 === strpos($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + * @throws InvalidOptionException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + private function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/vendor/symfony/console/Input/Input.php b/vendor/symfony/console/Input/Input.php new file mode 100644 index 0000000..85499fc --- /dev/null +++ b/vendor/symfony/console/Input/Input.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier + */ +abstract class Input implements InputInterface +{ + /** + * @var InputDefinition + */ + protected $definition; + protected $options = array(); + protected $arguments = array(); + protected $interactive = true; + + /** + * Constructor. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition) + { + $this->arguments = array(); + $this->options = array(); + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * Validates the input. + * + * @throws RuntimeException When not enough arguments are given + */ + public function validate() + { + $definition = $this->definition; + $givenArguments = $this->arguments; + + $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) { + return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); + }); + + if (count($missingArguments) > 0) { + throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); + } + } + + /** + * Checks if the input is interactive. + * + * @return bool Returns true if the input is interactive + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive) + { + $this->interactive = (bool) $interactive; + } + + /** + * Returns the argument values. + * + * @return array An array of argument values + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return mixed The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * Returns the options values. + * + * @return array An array of option values + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return mixed The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|bool $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } + + /** + * Escapes a token through escapeshellarg if it contains unsafe chars. + * + * @param string $token + * + * @return string + */ + public function escapeToken($token) + { + return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); + } +} diff --git a/vendor/symfony/console/Input/InputArgument.php b/vendor/symfony/console/Input/InputArgument.php new file mode 100644 index 0000000..048ee4f --- /dev/null +++ b/vendor/symfony/console/Input/InputArgument.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + */ +class InputArgument +{ + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The argument name + * @param int $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for self::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return bool true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return bool true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/vendor/symfony/console/Input/InputAwareInterface.php b/vendor/symfony/console/Input/InputAwareInterface.php new file mode 100644 index 0000000..d0f11e9 --- /dev/null +++ b/vendor/symfony/console/Input/InputAwareInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputAwareInterface should be implemented by classes that depends on the + * Console Input. + * + * @author Wouter J + */ +interface InputAwareInterface +{ + /** + * Sets the Console Input. + * + * @param InputInterface + */ + public function setInput(InputInterface $input); +} diff --git a/vendor/symfony/console/Input/InputDefinition.php b/vendor/symfony/console/Input/InputDefinition.php new file mode 100644 index 0000000..e9944db --- /dev/null +++ b/vendor/symfony/console/Input/InputDefinition.php @@ -0,0 +1,411 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition(array( + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * )); + * + * @author Fabien Potencier + */ +class InputDefinition +{ + private $arguments; + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + private $options; + private $shortcuts; + + /** + * Constructor. + * + * @param array $definition An array of InputArgument and InputOption instance + */ + public function __construct(array $definition = array()) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + */ + public function setDefinition(array $definition) + { + $arguments = array(); + $options = array(); + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function setArguments($arguments = array()) + { + $this->arguments = array(); + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function addArguments($arguments = array()) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * Adds an InputArgument object. + * + * @param InputArgument $argument An InputArgument object + * + * @throws LogicException When incorrect argument is given + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @param string|int $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return InputArgument[] An array of InputArgument objects + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return int The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return int The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = array(); + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function setOptions($options = array()) + { + $this->options = array(); + $this->shortcuts = array(); + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function addOptions($options = array()) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * Adds an InputOption object. + * + * @param InputOption $option An InputOption object + * + * @throws LogicException When option given already exist + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + } + + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) { + throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut)); + } + } + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + $this->shortcuts[$shortcut] = $option->getName(); + } + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return InputOption[] An array of InputOption objects + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @param string $shortcut the Shortcut name + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = array(); + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws InvalidArgumentException When option given does not exist + */ + private function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @param bool $short Whether to return the short version (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $elements = array(); + + if ($short && $this->getOptions()) { + $elements[] = '[options]'; + } elseif (!$short) { + foreach ($this->getOptions() as $option) { + $value = ''; + if ($option->acceptValue()) { + $value = sprintf( + ' %s%s%s', + $option->isValueOptional() ? '[' : '', + strtoupper($option->getName()), + $option->isValueOptional() ? ']' : '' + ); + } + + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value); + } + } + + if (count($elements) && $this->getArguments()) { + $elements[] = '[--]'; + } + + foreach ($this->getArguments() as $argument) { + $element = '<'.$argument->getName().'>'; + if (!$argument->isRequired()) { + $element = '['.$element.']'; + } elseif ($argument->isArray()) { + $element = $element.' ('.$element.')'; + } + + if ($argument->isArray()) { + $element .= '...'; + } + + $elements[] = $element; + } + + return implode(' ', $elements); + } +} diff --git a/vendor/symfony/console/Input/InputInterface.php b/vendor/symfony/console/Input/InputInterface.php new file mode 100644 index 0000000..2e1fb9f --- /dev/null +++ b/vendor/symfony/console/Input/InputInterface.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument(); + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values, $onlyParams = false); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false, $onlyParams = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition); + + /** + * Validates if arguments given are correct. + * + * Throws an exception when not enough arguments are given. + * + * @throws \RuntimeException + */ + public function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + public function getArguments(); + + /** + * Gets argument by name. + * + * @param string $name The name of the argument + * + * @return mixed + */ + public function getArgument($name); + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value); + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name); + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + public function getOptions(); + + /** + * Gets an option by name. + * + * @param string $name The name of the option + * + * @return mixed + */ + public function getOption($name); + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|bool $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value); + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name); + + /** + * Is this input means interactive? + * + * @return bool + */ + public function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive); +} diff --git a/vendor/symfony/console/Input/InputOption.php b/vendor/symfony/console/Input/InputOption.php new file mode 100644 index 0000000..3a752cb --- /dev/null +++ b/vendor/symfony/console/Input/InputOption.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + */ +class InputOption +{ + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The option name + * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($name)) { + throw new InvalidArgumentException('An option name cannot be empty.'); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if (is_array($shortcut)) { + $shortcut = implode('|', $shortcut); + } + $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); + $shortcuts = array_filter($shortcuts); + $shortcut = implode('|', $shortcuts); + + if (empty($shortcut)) { + throw new InvalidArgumentException('An option shortcut cannot be empty.'); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + * + * @return string The shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Returns the option name. + * + * @return string The name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return bool true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } + + /** + * Checks whether the given option equals this one. + * + * @param InputOption $option option to compare + * + * @return bool + */ + public function equals(InputOption $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/vendor/symfony/console/Input/StringInput.php b/vendor/symfony/console/Input/StringInput.php new file mode 100644 index 0000000..9ce0217 --- /dev/null +++ b/vendor/symfony/console/Input/StringInput.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^\s]+?)(?:\s|(?setTokens($this->tokenize($input)); + } + + /** + * Tokenizes a string. + * + * @param string $input The input to tokenize + * + * @return array An array of tokens + * + * @throws InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize($input) + { + $tokens = array(); + $length = strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + } + + $cursor += strlen($match[0]); + } + + return $tokens; + } +} diff --git a/vendor/symfony/console/LICENSE b/vendor/symfony/console/LICENSE new file mode 100644 index 0000000..12a7453 --- /dev/null +++ b/vendor/symfony/console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/console/Logger/ConsoleLogger.php b/vendor/symfony/console/Logger/ConsoleLogger.php new file mode 100644 index 0000000..1f7417e --- /dev/null +++ b/vendor/symfony/console/Logger/ConsoleLogger.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Logger; + +use Psr\Log\AbstractLogger; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * PSR-3 compliant console logger. + * + * @author Kévin Dunglas + * + * @link http://www.php-fig.org/psr/psr-3/ + */ +class ConsoleLogger extends AbstractLogger +{ + const INFO = 'info'; + const ERROR = 'error'; + + /** + * @var OutputInterface + */ + private $output; + /** + * @var array + */ + private $verbosityLevelMap = array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE, + LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE, + LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG, + ); + /** + * @var array + */ + private $formatLevelMap = array( + LogLevel::EMERGENCY => self::ERROR, + LogLevel::ALERT => self::ERROR, + LogLevel::CRITICAL => self::ERROR, + LogLevel::ERROR => self::ERROR, + LogLevel::WARNING => self::INFO, + LogLevel::NOTICE => self::INFO, + LogLevel::INFO => self::INFO, + LogLevel::DEBUG => self::INFO, + ); + + /** + * @param OutputInterface $output + * @param array $verbosityLevelMap + * @param array $formatLevelMap + */ + public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array()) + { + $this->output = $output; + $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; + $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap; + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) + { + if (!isset($this->verbosityLevelMap[$level])) { + throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); + } + + // Write to the error output if necessary and available + if ($this->formatLevelMap[$level] === self::ERROR && $this->output instanceof ConsoleOutputInterface) { + $output = $this->output->getErrorOutput(); + } else { + $output = $this->output; + } + + if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) { + $output->writeln(sprintf('<%1$s>[%2$s] %3$s', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context))); + } + } + + /** + * Interpolates context values into the message placeholders. + * + * @author PHP Framework Interoperability Group + * + * @param string $message + * @param array $context + * + * @return string + */ + private function interpolate($message, array $context) + { + // build a replacement array with braces around the context keys + $replace = array(); + foreach ($context as $key => $val) { + if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace[sprintf('{%s}', $key)] = $val; + } + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } +} diff --git a/vendor/symfony/console/Output/BufferedOutput.php b/vendor/symfony/console/Output/BufferedOutput.php new file mode 100644 index 0000000..5682fc2 --- /dev/null +++ b/vendor/symfony/console/Output/BufferedOutput.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * @author Jean-François Simon + */ +class BufferedOutput extends Output +{ + /** + * @var string + */ + private $buffer = ''; + + /** + * Empties buffer and returns its content. + * + * @return string + */ + public function fetch() + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= "\n"; + } + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutput.php b/vendor/symfony/console/Output/ConsoleOutput.php new file mode 100644 index 0000000..f666c79 --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutput.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT. + * + * This class is a convenient wrapper around `StreamOutput`. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * @author Fabien Potencier + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + /** + * @var StreamOutput + */ + private $stderr; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); + + $actualDecorated = $this->isDecorated(); + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter()); + + if (null === $decorated) { + $this->setDecorated($actualDecorated && $this->stderr->isDecorated()); + } + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getErrorOutput() + { + return $this->stderr; + } + + /** + * {@inheritdoc} + */ + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } + + /** + * Returns true if current environment supports writing console output to + * STDOUT. + * + * @return bool + */ + protected function hasStdoutSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Returns true if current environment supports writing console output to + * STDERR. + * + * @return bool + */ + protected function hasStderrSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Checks if current executing environment is IBM iSeries (OS400), which + * doesn't properly convert character-encodings between ASCII to EBCDIC. + * + * @return bool + */ + private function isRunningOS400() + { + $checks = array( + function_exists('php_uname') ? php_uname('s') : '', + getenv('OSTYPE'), + PHP_OS, + ); + + return false !== stripos(implode(';', $checks), 'OS400'); + } + + /** + * @return resource + */ + private function openOutputStream() + { + $outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output'; + + return @fopen($outputStream, 'w') ?: fopen('php://output', 'w'); + } + + /** + * @return resource + */ + private function openErrorStream() + { + $errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output'; + + return fopen($errorStream, 'w'); + } +} diff --git a/vendor/symfony/console/Output/ConsoleOutputInterface.php b/vendor/symfony/console/Output/ConsoleOutputInterface.php new file mode 100644 index 0000000..5eb4fc7 --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutputInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr output stream. + * + * @author Dariusz Górecki + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * Gets the OutputInterface for errors. + * + * @return OutputInterface + */ + public function getErrorOutput(); + + /** + * Sets the OutputInterface used for errors. + * + * @param OutputInterface $error + */ + public function setErrorOutput(OutputInterface $error); +} diff --git a/vendor/symfony/console/Output/NullOutput.php b/vendor/symfony/console/Output/NullOutput.php new file mode 100644 index 0000000..682f9a4 --- /dev/null +++ b/vendor/symfony/console/Output/NullOutput.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class NullOutput implements OutputInterface +{ + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + // to comply with the interface we must return a OutputFormatterInterface + return new OutputFormatter(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return self::VERBOSITY_QUIET; + } + + public function isQuiet() + { + return true; + } + + public function isVerbose() + { + return false; + } + + public function isVeryVerbose() + { + return false; + } + + public function isDebug() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + // do nothing + } +} diff --git a/vendor/symfony/console/Output/Output.php b/vendor/symfony/console/Output/Output.php new file mode 100644 index 0000000..4476ffb --- /dev/null +++ b/vendor/symfony/console/Output/Output.php @@ -0,0 +1,165 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * Base class for output classes. + * + * There are five levels of verbosity: + * + * * normal: no option passed (normal output) + * * verbose: -v (more output) + * * very verbose: -vv (highly extended output) + * * debug: -vvv (all debug output) + * * quiet: -q (no output) + * + * @author Fabien Potencier + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool $decorated Whether to decorate messages + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = $formatter ?: new OutputFormatter(); + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->formatter; + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->formatter->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->verbosity; + } + + public function isQuiet() + { + return self::VERBOSITY_QUIET === $this->verbosity; + } + + public function isVerbose() + { + return self::VERBOSITY_VERBOSE <= $this->verbosity; + } + + public function isVeryVerbose() + { + return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; + } + + public function isDebug() + { + return self::VERBOSITY_DEBUG <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $options); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + $messages = (array) $messages; + + $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN; + $type = $types & $options ?: self::OUTPUT_NORMAL; + + $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG; + $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL; + + if ($verbosity > $this->getVerbosity()) { + return; + } + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param bool $newline Whether to add a newline or not + */ + abstract protected function doWrite($message, $newline); +} diff --git a/vendor/symfony/console/Output/OutputInterface.php b/vendor/symfony/console/Output/OutputInterface.php new file mode 100644 index 0000000..a291ca7 --- /dev/null +++ b/vendor/symfony/console/Output/OutputInterface.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 16; + const VERBOSITY_NORMAL = 32; + const VERBOSITY_VERBOSE = 64; + const VERBOSITY_VERY_VERBOSE = 128; + const VERBOSITY_DEBUG = 256; + + const OUTPUT_NORMAL = 1; + const OUTPUT_RAW = 2; + const OUTPUT_PLAIN = 4; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines or a single string + * @param bool $newline Whether to add a newline + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function write($messages, $newline = false, $options = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function writeln($messages, $options = 0); + + /** + * Sets the verbosity of the output. + * + * @param int $level The level of verbosity (one of the VERBOSITY constants) + */ + public function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return int The current level of verbosity (one of the VERBOSITY constants) + */ + public function getVerbosity(); + + /** + * Returns whether verbosity is quiet (-q). + * + * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise + */ + public function isQuiet(); + + /** + * Returns whether verbosity is verbose (-v). + * + * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise + */ + public function isVerbose(); + + /** + * Returns whether verbosity is very verbose (-vv). + * + * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise + */ + public function isVeryVerbose(); + + /** + * Returns whether verbosity is debug (-vvv). + * + * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise + */ + public function isDebug(); + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + */ + public function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + */ + public function getFormatter(); +} diff --git a/vendor/symfony/console/Output/StreamOutput.php b/vendor/symfony/console/Output/StreamOutput.php new file mode 100644 index 0000000..62d04c0 --- /dev/null +++ b/vendor/symfony/console/Output/StreamOutput.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * Constructor. + * + * @param resource $stream A stream resource + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * + * @throws InvalidArgumentException When first argument is not a real stream + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport(); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) { + // should never happen + throw new RuntimeException('Unable to write output.'); + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - Windows without Ansicon, ConEmu or Mintty + * - non tty consoles + * + * @return bool true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + if (DIRECTORY_SEPARATOR === '\\') { + return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + } + + return function_exists('posix_isatty') && @posix_isatty($this->stream); + } +} diff --git a/vendor/symfony/console/Question/ChoiceQuestion.php b/vendor/symfony/console/Question/ChoiceQuestion.php new file mode 100644 index 0000000..2c40638 --- /dev/null +++ b/vendor/symfony/console/Question/ChoiceQuestion.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Represents a choice question. + * + * @author Fabien Potencier + */ +class ChoiceQuestion extends Question +{ + private $choices; + private $multiselect = false; + private $prompt = ' > '; + private $errorMessage = 'Value "%s" is invalid'; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param array $choices The list of available choices + * @param mixed $default The default answer to return + */ + public function __construct($question, array $choices, $default = null) + { + parent::__construct($question, $default); + + $this->choices = $choices; + $this->setValidator($this->getDefaultValidator()); + $this->setAutocompleterValues($choices); + } + + /** + * Returns available choices. + * + * @return array + */ + public function getChoices() + { + return $this->choices; + } + + /** + * Sets multiselect option. + * + * When multiselect is set to true, multiple choices can be answered. + * + * @param bool $multiselect + * + * @return ChoiceQuestion The current instance + */ + public function setMultiselect($multiselect) + { + $this->multiselect = $multiselect; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Gets the prompt for choices. + * + * @return string + */ + public function getPrompt() + { + return $this->prompt; + } + + /** + * Sets the prompt for choices. + * + * @param string $prompt + * + * @return ChoiceQuestion The current instance + */ + public function setPrompt($prompt) + { + $this->prompt = $prompt; + + return $this; + } + + /** + * Sets the error message for invalid values. + * + * The error message has a string placeholder (%s) for the invalid value. + * + * @param string $errorMessage + * + * @return ChoiceQuestion The current instance + */ + public function setErrorMessage($errorMessage) + { + $this->errorMessage = $errorMessage; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Returns the default answer validator. + * + * @return callable + */ + private function getDefaultValidator() + { + $choices = $this->choices; + $errorMessage = $this->errorMessage; + $multiselect = $this->multiselect; + $isAssoc = $this->isAssoc($choices); + + return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { + // Collapse all spaces. + $selectedChoices = str_replace(' ', '', $selected); + + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + throw new InvalidArgumentException(sprintf($errorMessage, $selected)); + } + $selectedChoices = explode(',', $selectedChoices); + } else { + $selectedChoices = array($selected); + } + + $multiselectChoices = array(); + foreach ($selectedChoices as $value) { + $results = array(); + foreach ($choices as $key => $choice) { + if ($choice === $value) { + $results[] = $key; + } + } + + if (count($results) > 1) { + throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results))); + } + + $result = array_search($value, $choices); + + if (!$isAssoc) { + if (false !== $result) { + $result = $choices[$result]; + } elseif (isset($choices[$value])) { + $result = $choices[$value]; + } + } elseif (false === $result && isset($choices[$value])) { + $result = $value; + } + + if (false === $result) { + throw new InvalidArgumentException(sprintf($errorMessage, $value)); + } + + $multiselectChoices[] = (string) $result; + } + + if ($multiselect) { + return $multiselectChoices; + } + + return current($multiselectChoices); + }; + } +} diff --git a/vendor/symfony/console/Question/ConfirmationQuestion.php b/vendor/symfony/console/Question/ConfirmationQuestion.php new file mode 100644 index 0000000..29d9887 --- /dev/null +++ b/vendor/symfony/console/Question/ConfirmationQuestion.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +/** + * Represents a yes/no question. + * + * @author Fabien Potencier + */ +class ConfirmationQuestion extends Question +{ + private $trueAnswerRegex; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param bool $default The default answer to return, true or false + * @param string $trueAnswerRegex A regex to match the "yes" answer + */ + public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') + { + parent::__construct($question, (bool) $default); + + $this->trueAnswerRegex = $trueAnswerRegex; + $this->setNormalizer($this->getDefaultNormalizer()); + } + + /** + * Returns the default answer normalizer. + * + * @return callable + */ + private function getDefaultNormalizer() + { + $default = $this->getDefault(); + $regex = $this->trueAnswerRegex; + + return function ($answer) use ($default, $regex) { + if (is_bool($answer)) { + return $answer; + } + + $answerIsTrue = (bool) preg_match($regex, $answer); + if (false === $default) { + return $answer && $answerIsTrue; + } + + return !$answer || $answerIsTrue; + }; + } +} diff --git a/vendor/symfony/console/Question/Question.php b/vendor/symfony/console/Question/Question.php new file mode 100644 index 0000000..71cb8f5 --- /dev/null +++ b/vendor/symfony/console/Question/Question.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a Question. + * + * @author Fabien Potencier + */ +class Question +{ + private $question; + private $attempts; + private $hidden = false; + private $hiddenFallback = true; + private $autocompleterValues; + private $validator; + private $default; + private $normalizer; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param mixed $default The default answer to return if the user enters nothing + */ + public function __construct($question, $default = null) + { + $this->question = $question; + $this->default = $default; + } + + /** + * Returns the question. + * + * @return string + */ + public function getQuestion() + { + return $this->question; + } + + /** + * Returns the default answer. + * + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns whether the user response must be hidden. + * + * @return bool + */ + public function isHidden() + { + return $this->hidden; + } + + /** + * Sets whether the user response must be hidden or not. + * + * @param bool $hidden + * + * @return Question The current instance + * + * @throws LogicException In case the autocompleter is also used + */ + public function setHidden($hidden) + { + if ($this->autocompleterValues) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->hidden = (bool) $hidden; + + return $this; + } + + /** + * In case the response can not be hidden, whether to fallback on non-hidden question or not. + * + * @return bool + */ + public function isHiddenFallback() + { + return $this->hiddenFallback; + } + + /** + * Sets whether to fallback on non-hidden question if the response can not be hidden. + * + * @param bool $fallback + * + * @return Question The current instance + */ + public function setHiddenFallback($fallback) + { + $this->hiddenFallback = (bool) $fallback; + + return $this; + } + + /** + * Gets values for the autocompleter. + * + * @return null|array|\Traversable + */ + public function getAutocompleterValues() + { + return $this->autocompleterValues; + } + + /** + * Sets values for the autocompleter. + * + * @param null|array|\Traversable $values + * + * @return Question The current instance + * + * @throws InvalidArgumentException + * @throws LogicException + */ + public function setAutocompleterValues($values) + { + if (is_array($values) && $this->isAssoc($values)) { + $values = array_merge(array_keys($values), array_values($values)); + } + + if (null !== $values && !is_array($values)) { + if (!$values instanceof \Traversable || $values instanceof \Countable) { + throw new InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.'); + } + } + + if ($this->hidden) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->autocompleterValues = $values; + + return $this; + } + + /** + * Sets a validator for the question. + * + * @param null|callable $validator + * + * @return Question The current instance + */ + public function setValidator(callable $validator = null) + { + $this->validator = $validator; + + return $this; + } + + /** + * Gets the validator for the question. + * + * @return null|callable + */ + public function getValidator() + { + return $this->validator; + } + + /** + * Sets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @param null|int $attempts + * + * @return Question The current instance + * + * @throws InvalidArgumentException In case the number of attempts is invalid. + */ + public function setMaxAttempts($attempts) + { + if (null !== $attempts && $attempts < 1) { + throw new InvalidArgumentException('Maximum number of attempts must be a positive value.'); + } + + $this->attempts = $attempts; + + return $this; + } + + /** + * Gets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @return null|int + */ + public function getMaxAttempts() + { + return $this->attempts; + } + + /** + * Sets a normalizer for the response. + * + * The normalizer can be a callable (a string), a closure or a class implementing __invoke. + * + * @param callable $normalizer + * + * @return Question The current instance + */ + public function setNormalizer(callable $normalizer) + { + $this->normalizer = $normalizer; + + return $this; + } + + /** + * Gets the normalizer for the response. + * + * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. + * + * @return callable + */ + public function getNormalizer() + { + return $this->normalizer; + } + + protected function isAssoc($array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } +} diff --git a/vendor/symfony/console/README.md b/vendor/symfony/console/README.md new file mode 100644 index 0000000..25f700c --- /dev/null +++ b/vendor/symfony/console/README.md @@ -0,0 +1,67 @@ +Console Component +================= + +Console eases the creation of beautiful and testable command line interfaces. + +The Application object manages the CLI application: + +```php +use Symfony\Component\Console\Application; + +$console = new Application(); +$console->run(); +``` + +The ``run()`` method parses the arguments and options passed on the command +line and executes the right command. + +Registering a new command can easily be done via the ``register()`` method, +which returns a ``Command`` instance: + +```php +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +$console + ->register('ls') + ->setDefinition(array( + new InputArgument('dir', InputArgument::REQUIRED, 'Directory name'), + )) + ->setDescription('Displays the files in the given directory') + ->setCode(function (InputInterface $input, OutputInterface $output) { + $dir = $input->getArgument('dir'); + + $output->writeln(sprintf('Dir listing for %s', $dir)); + }) +; +``` + +You can also register new commands via classes. + +The component provides a lot of features like output coloring, input and +output abstractions (so that you can easily unit-test your commands), +validation, automatic help messages, ... + +Tests +----- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Console/ + $ composer install + $ phpunit + +Third Party +----------- + +`Resources/bin/hiddeninput.exe` is a third party binary provided within this +component. Find sources and license at https://github.com/Seldaek/hidden-input. + +Resources +--------- + +[The Console Component](https://symfony.com/doc/current/components/console.html) + +[How to create a Console Command](https://symfony.com/doc/current/cookbook/console/console_command.html) diff --git a/vendor/symfony/console/Resources/bin/hiddeninput.exe b/vendor/symfony/console/Resources/bin/hiddeninput.exe new file mode 100644 index 0000000000000000000000000000000000000000..c8cf65e8d819e6e525121cf6b21f1c2429746038 GIT binary patch literal 9216 zcmeHNe{@sVeZR8hV88~S)=Hp|Mpn({rC^@)BwNOI{ERJXCYlx+k1K6PLHo z_e!z_fhOzeA3JTX&-Z@s{rFOgjEwBlqjr!)9f zjyHz`A+ni`!0Taby{Uj5Y>jQq(k5A+X})PLWAi|{IZbtc8n^^trM{GI=P_15U6d?l zJJ3PW8XjfHpR}6`k{&5@JcEeH_SqQoQbU62o2YS30W)p_t&Fjy*RXQCZt$gCf|ao| zx&3R}m6|-Lfi@pua=$26n(UlnWo$>K67*|+#(qL_An=?l0M02AhOSJDv3;~?1ORfw z76EdK#MpSHqACHLcnJLIYlCSiX4eS@Pr8rN)Xwz0dk7O*y^0_C(Yks2Kvg! z-d-fJ)F9@k?>)m(XqDKIe2OKfhCQde9fpO0ko24yn*4xzX7q+ze`Z*=aJgwV?D?73 zaJ8UkSk|NN>@-|mB*f`EIK7$ElgAB<7p&p`^Vuq$58#;?B^*Bz7&d$B#+AYUC z(^m|`7{lqx&b^5$;i`j|S!+u|lcaQplp_&Nb)!>r>vGh3wb!tW zLq6%bkSt8jO|(vWH>LiPV(Xkp%BiGhl1q!PXXNKVKE!>Y5cHc2%cJOJA{-&ZsSn`T z#8~TA#(HWH4m>uCd+kCMTFgMI*s*n3!iCOwEI`{vGcVhzDu!Lw%-Ea^JATtrF`q3`+#KvvYJ0vM~A}D#LOD zlw`4ncB0U*Jji=--Wz#>I&5?hy;MgYW2u91d8ob=7MWfY`u;7Xe-J{Qsb0=0p|SM2 zG|=~mERIj4?gi)Ew|{LIN#oAsh20k_khIYjJBBN6rrIJ=eQO=nE;rTnPSiaQS$1$# z+|JRh0!IbQIa*f1(TZ}QM;|WO0+jTy(e)ggN4>zqp2E>C>hGPLHjHBh--2%@{EZNE zbUk{<3MABX&20QwK{MxK8`1Vk>^%dO5i@VTfu>NG3$K4NC=hSPsj9UYy`rNO}sBnB9QdKdIk7G+2_amnWstdTYVg z7HgLJGC~XLZG`63GwH8PdO_+G(k6~?J8Wj5mQos#21kC4W#2)guQXI)!z^{@F)U)5 z*re+r(2dib3D4P~%Z6TL=$PIkpmm<_#isu%t=%DcIwNkJhMeJ|bpahHO%8h|y~Ccf zUg#xVk+dyu>Q1O7JZ~8KS>tqi0qK**X*y6yHM71`bT=kFZ=@E%oe2!Km1^2sa>v+onZ%x_>aOJF+N0{i~z|<(IzgT*{0PpQq}E zQpU35@bm;qI?t_znGI&5&4sZV>+%m}w$(4hSDvLk)l<{5XyMlnCl7C%AjM3XnWvVz z{NoFsX)JB)SoqABZxUa*Yq+^^(cbq4mL%^lO12c${z{pf+)|kTTI~nQywyYF6}6|8 zlsN9&{-vwTrTyu<5^90_AsIU-ID#ZG@6d%poU44<**%xVe?`uxf}_Mr$SLHLS|K_N zQnw>(Lr2U=%$-<2D~RSzbG)2W2u^KMDnFFE?GmmbQ)V)fty957F`4OvQ_25E68ITr z5?`suu`|v?r!y=gFOGj$%9IJ zuTP=&2GcnoZZ0qSe6YL-*-lg>Q#>?Ew`a=GDc4vI#<1sNdKn?n7iSj0Orl$-#FMFi zykr>X-Xvi>sVr;92+8*H!r|3L$#o~hXa0z>AmF=z z?|@FF;*S|S0yqsw0j>Z(3mX-HD!|{N-vYc9paC8Ld=|6?00!6(_%lERupO`&um*4k z0b~W>e*uhTe4;V;mq>(ox$9FB`wLt!*DKj~!aOh|fL&#Pg*b??tm%5~_6M#02wqeC zS~wO>TWGnSp^r<0&8f2V6W->w=C+p~daC5e5wNQM*(* z66^}b0(!q3)zq$mu&VnbR#nr3;h5DS*o7{y66=!#;Dy4$pd1ZH<6WEOi0oJ8SxRL* z*v-9@Z^2w%^S(w5dO{_9Duby%2RT~;ppxaE$l()x6&}>7Wcg=u_&>f`Vs8OJGTy{X z2HpG=ThJz<{%|4Qq-~ad0qcrc87n88DHpM(nypwXIkZn<{zIT$ul&BQ?{ApCAZtyr zs2YpNt@x(G*faTU*HCKnAk(G=Tl~>r1QK8LY~J8mFFGoN5iIkYSwlm4Lsj#g4dsE5 zU-4;*Kdh-zv!rT4N$O}Q&n)?v0-9Y)lRFz58^P-KtKonzrfQ1p@0V_10^0||cGRn9 zRG<-#_TEV2nn4{BOh{YVBR4e!V!D?0K%BAlQN!D%M#k1bHypiIHT)5tlj>p0Pp_;+ z!cqC-JIs@JRhB+#teGs$Cib_=(yjRo4OJg^YPg%58aJVsC(LQ?W6%pn!-#aMZwoPcopo^Rn6BE z3=c5&W5~pP(C(-2r;PnH-S0{F`runM0ERCf3rESX$+S(MKOXmKJL9zXF}9-lf^xUs z+bb)+P%L&gV@<4q{6w^xEJ>Y>TQFUeoz0o-yq)jUqww=?wjUO8Y{a5G;DJ0Jr!LL+ zWhgsLuzi&eDrGDn$2DJwpFfH-?SGWbr>qRb?v{P`_%)So)CQgzO^HQ%;y#tJ=knH4 z95jX;^bF#BiuTH^%-j}{9VrZD=R%Q%wselH^p>5 z7d>gWB-st&3Fj%Mt*|tR5iK3J=`xhs&G)I7E>`FO@o7L z@S$B!pYMuzz5DN@X!O4DPm5n@raPJn-Q#o*m*e^5lk$g?0esg%$;>g5QW-|;c=H2GM}bo2tW^D924wmOkrUbWxcQ# z#v6bP%Tdfe~jtCRzAL;-OahZ=#yvUixu2-9fD2j$*|YY`F?0wF-{a# ztr<&kZjZ+81}6ZESqtgW)8kP#s@VLTSUR{}6?U^R*x7RE3Rl&n=VnFFqg9Uqz1n@N9N|=9<4} zuJfy^+}|D9X&vm3MAdqmu0&UMd^=K>b1hLAm_E!$rZC2b;;T~Dl zI`Eo_yRY76uM})|6wk9->of(=9&4jLv5#p@OzS~Yl>@pG)^>6`R+KtL{<4ly4o9WiM!%p_pfROU354)e8PIeE z1_s?#;OX6waNvvb&UQRN(WLbR+}&b#jo&WY-LlwCX}Q*$jGuKYuOGoIoyR(>e}}ix z+t}Q^cEcC8Y{@h}>HmJ^gD!l@gzwHmiBKl26x_lZVZG2UY!`w;RJd122;US&geQdW z3Qq}R!gIo5;ka;0I4c-Jq5X6A6?VzK&c4y!ZXdAUYu{r}*!SBXw?Aor+J4-A(*COb zb^CwV-?3k`zi-cX*c`VzL`RLI(b4MgIrGN z%ojf`E*6)Gg1A9!7q^N##2zsss^V9~-Qt7d!{UDNZ^XY9pA^3@9ui*?e=7c5d`nD; z?}~R(p>y1Kw!>|X4ycYEAkcZa*n-R%y! zqi)Up756UpqwfE7=hfigw$k~G@25gaxF9UGTkV>C(7x1Rbx4jb#|}rxq0vQ!n-c#f J0sQ~1{4brj`U(I5 literal 0 HcmV?d00001 diff --git a/vendor/symfony/console/Style/OutputStyle.php b/vendor/symfony/console/Style/OutputStyle.php new file mode 100644 index 0000000..de7be1e --- /dev/null +++ b/vendor/symfony/console/Style/OutputStyle.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Decorates output to add console style guide helpers. + * + * @author Kevin Bond + */ +abstract class OutputStyle implements OutputInterface, StyleInterface +{ + private $output; + + /** + * @param OutputInterface $output + */ + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + $this->output->write(str_repeat(PHP_EOL, $count)); + } + + /** + * @param int $max + * + * @return ProgressBar + */ + public function createProgressBar($max = 0) + { + return new ProgressBar($this->output, $max); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + $this->output->write($messages, $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + $this->output->writeln($messages, $type); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->output->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->output->getVerbosity(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->output->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->output->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->output->getFormatter(); + } + + /** + * {@inheritdoc} + */ + public function isQuiet() + { + return $this->output->isQuiet(); + } + + /** + * {@inheritdoc} + */ + public function isVerbose() + { + return $this->output->isVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose() + { + return $this->output->isVeryVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return $this->output->isDebug(); + } +} diff --git a/vendor/symfony/console/Style/StyleInterface.php b/vendor/symfony/console/Style/StyleInterface.php new file mode 100644 index 0000000..2448547 --- /dev/null +++ b/vendor/symfony/console/Style/StyleInterface.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +/** + * Output style helpers. + * + * @author Kevin Bond + */ +interface StyleInterface +{ + /** + * Formats a command title. + * + * @param string $message + */ + public function title($message); + + /** + * Formats a section title. + * + * @param string $message + */ + public function section($message); + + /** + * Formats a list. + * + * @param array $elements + */ + public function listing(array $elements); + + /** + * Formats informational text. + * + * @param string|array $message + */ + public function text($message); + + /** + * Formats a success result bar. + * + * @param string|array $message + */ + public function success($message); + + /** + * Formats an error result bar. + * + * @param string|array $message + */ + public function error($message); + + /** + * Formats an warning result bar. + * + * @param string|array $message + */ + public function warning($message); + + /** + * Formats a note admonition. + * + * @param string|array $message + */ + public function note($message); + + /** + * Formats a caution admonition. + * + * @param string|array $message + */ + public function caution($message); + + /** + * Formats a table. + * + * @param array $headers + * @param array $rows + */ + public function table(array $headers, array $rows); + + /** + * Asks a question. + * + * @param string $question + * @param string|null $default + * @param callable|null $validator + * + * @return string + */ + public function ask($question, $default = null, $validator = null); + + /** + * Asks a question with the user input hidden. + * + * @param string $question + * @param callable|null $validator + * + * @return string + */ + public function askHidden($question, $validator = null); + + /** + * Asks for confirmation. + * + * @param string $question + * @param bool $default + * + * @return bool + */ + public function confirm($question, $default = true); + + /** + * Asks a choice question. + * + * @param string $question + * @param array $choices + * @param string|int|null $default + * + * @return string + */ + public function choice($question, array $choices, $default = null); + + /** + * Add newline(s). + * + * @param int $count The number of newlines + */ + public function newLine($count = 1); + + /** + * Starts the progress output. + * + * @param int $max Maximum steps (0 if unknown) + */ + public function progressStart($max = 0); + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + */ + public function progressAdvance($step = 1); + + /** + * Finishes the progress output. + */ + public function progressFinish(); +} diff --git a/vendor/symfony/console/Style/SymfonyStyle.php b/vendor/symfony/console/Style/SymfonyStyle.php new file mode 100644 index 0000000..47d7d12 --- /dev/null +++ b/vendor/symfony/console/Style/SymfonyStyle.php @@ -0,0 +1,415 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\SymfonyQuestionHelper; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * Output decorator helpers for the Symfony Style Guide. + * + * @author Kevin Bond + */ +class SymfonyStyle extends OutputStyle +{ + const MAX_LINE_LENGTH = 120; + + private $input; + private $questionHelper; + private $progressBar; + private $lineLength; + private $bufferedOutput; + + /** + * @param InputInterface $input + * @param OutputInterface $output + */ + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter()); + // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. + $this->lineLength = min($this->getTerminalWidth() - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); + + parent::__construct($output); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string|null $type The block type (added in [] on first line) + * @param string|null $style The style to apply to the whole block + * @param string $prefix The prefix for the block + * @param bool $padding Whether to add vertical padding + */ + public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false) + { + $this->autoPrependBlock(); + $messages = is_array($messages) ? array_values($messages) : array($messages); + $lines = array(); + + // add type + if (null !== $type) { + $messages[0] = sprintf('[%s] %s', $type, $messages[0]); + } + + // wrap and add newlines for each element + foreach ($messages as $key => $message) { + $message = OutputFormatter::escape($message); + $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix), PHP_EOL, true))); + + if (count($messages) > 1 && $key < count($messages) - 1) { + $lines[] = ''; + } + } + + if ($padding && $this->isDecorated()) { + array_unshift($lines, ''); + $lines[] = ''; + } + + foreach ($lines as &$line) { + $line = sprintf('%s%s', $prefix, $line); + $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line)); + + if ($style) { + $line = sprintf('<%s>%s', $style, $line); + } + } + + $this->writeln($lines); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function title($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('%s', $message), + sprintf('%s', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function section($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('%s', $message), + sprintf('%s', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function listing(array $elements) + { + $this->autoPrependText(); + $elements = array_map(function ($element) { + return sprintf(' * %s', $element); + }, $elements); + + $this->writeln($elements); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function text($message) + { + $this->autoPrependText(); + + $messages = is_array($message) ? array_values($message) : array($message); + foreach ($messages as $message) { + $this->writeln(sprintf(' %s', $message)); + } + } + + /** + * {@inheritdoc} + */ + public function comment($message) + { + $this->autoPrependText(); + + $messages = is_array($message) ? array_values($message) : array($message); + foreach ($messages as $message) { + $this->writeln(sprintf(' // %s', $message)); + } + } + + /** + * {@inheritdoc} + */ + public function success($message) + { + $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function error($message) + { + $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function warning($message) + { + $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function note($message) + { + $this->block($message, 'NOTE', 'fg=yellow', ' ! '); + } + + /** + * {@inheritdoc} + */ + public function caution($message) + { + $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); + } + + /** + * {@inheritdoc} + */ + public function table(array $headers, array $rows) + { + $headers = array_map(function ($value) { return sprintf('%s', $value); }, $headers); + + $table = new Table($this); + $table->setHeaders($headers); + $table->setRows($rows); + $table->setStyle('symfony-style-guide'); + + $table->render(); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function ask($question, $default = null, $validator = null) + { + $question = new Question($question, $default); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function askHidden($question, $validator = null) + { + $question = new Question($question); + + $question->setHidden(true); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function confirm($question, $default = true) + { + return $this->askQuestion(new ConfirmationQuestion($question, $default)); + } + + /** + * {@inheritdoc} + */ + public function choice($question, array $choices, $default = null) + { + if (null !== $default) { + $values = array_flip($choices); + $default = $values[$default]; + } + + return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); + } + + /** + * {@inheritdoc} + */ + public function progressStart($max = 0) + { + $this->progressBar = $this->createProgressBar($max); + $this->progressBar->start(); + } + + /** + * {@inheritdoc} + */ + public function progressAdvance($step = 1) + { + $this->getProgressBar()->advance($step); + } + + /** + * {@inheritdoc} + */ + public function progressFinish() + { + $this->getProgressBar()->finish(); + $this->newLine(2); + $this->progressBar = null; + } + + /** + * {@inheritdoc} + */ + public function createProgressBar($max = 0) + { + $progressBar = parent::createProgressBar($max); + + if ('\\' !== DIRECTORY_SEPARATOR) { + $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 + $progressBar->setProgressCharacter(''); + $progressBar->setBarCharacter('▓'); // dark shade character \u2593 + } + + return $progressBar; + } + + /** + * @param Question $question + * + * @return string + */ + public function askQuestion(Question $question) + { + if ($this->input->isInteractive()) { + $this->autoPrependBlock(); + } + + if (!$this->questionHelper) { + $this->questionHelper = new SymfonyQuestionHelper(); + } + + $answer = $this->questionHelper->ask($this->input, $this, $question); + + if ($this->input->isInteractive()) { + $this->newLine(); + $this->bufferedOutput->write("\n"); + } + + return $answer; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + parent::writeln($messages, $type); + $this->bufferedOutput->writeln($this->reduceBuffer($messages), $type); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + parent::write($messages, $newline, $type); + $this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + parent::newLine($count); + $this->bufferedOutput->write(str_repeat("\n", $count)); + } + + /** + * @return ProgressBar + */ + private function getProgressBar() + { + if (!$this->progressBar) { + throw new RuntimeException('The ProgressBar is not started.'); + } + + return $this->progressBar; + } + + private function getTerminalWidth() + { + $application = new Application(); + $dimensions = $application->getTerminalDimensions(); + + return $dimensions[0] ?: self::MAX_LINE_LENGTH; + } + + private function autoPrependBlock() + { + $chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); + + if (!isset($chars[0])) { + return $this->newLine(); //empty history, so we should start with a new line. + } + //Prepend new line for each non LF chars (This means no blank line was output before) + $this->newLine(2 - substr_count($chars, "\n")); + } + + private function autoPrependText() + { + $fetched = $this->bufferedOutput->fetch(); + //Prepend new line if last char isn't EOL: + if ("\n" !== substr($fetched, -1)) { + $this->newLine(); + } + } + + private function reduceBuffer($messages) + { + // We need to know if the two last chars are PHP_EOL + // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer + return array_map(function ($value) { + return substr($value, -4); + }, array_merge(array($this->bufferedOutput->fetch()), (array) $messages)); + } +} diff --git a/vendor/symfony/console/Tester/ApplicationTester.php b/vendor/symfony/console/Tester/ApplicationTester.php new file mode 100644 index 0000000..da8a19c --- /dev/null +++ b/vendor/symfony/console/Tester/ApplicationTester.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console applications. + * + * When testing an application, don't forget to disable the auto exit flag: + * + * $application = new Application(); + * $application->setAutoExit(false); + * + * @author Fabien Potencier + */ +class ApplicationTester +{ + private $application; + private $input; + private $output; + private $statusCode; + + /** + * Constructor. + * + * @param Application $application An Application instance to test. + */ + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return int The command exit code + */ + public function run(array $input, $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/console/Tester/CommandTester.php b/vendor/symfony/console/Tester/CommandTester.php new file mode 100644 index 0000000..8d6486e --- /dev/null +++ b/vendor/symfony/console/Tester/CommandTester.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Eases the testing of console commands. + * + * @author Fabien Potencier + */ +class CommandTester +{ + private $command; + private $input; + private $output; + private $statusCode; + + /** + * Constructor. + * + * @param Command $command A Command instance to test. + */ + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available execution options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of command arguments and options + * @param array $options An array of execution options + * + * @return int The command exit code + */ + public function execute(array $input, array $options = array()) + { + // set the command name automatically if the application requires + // this argument and no command name was passed + if (!isset($input['command']) + && (null !== $application = $this->command->getApplication()) + && $application->getDefinition()->hasArgument('command') + ) { + $input = array_merge(array('command' => $this->command->getName()), $input); + } + + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/console/Tests/ApplicationTest.php b/vendor/symfony/console/Tests/ApplicationTest.php new file mode 100644 index 0000000..7eaa28d --- /dev/null +++ b/vendor/symfony/console/Tests/ApplicationTest.php @@ -0,0 +1,1093 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Tester\ApplicationTester; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class ApplicationTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); + require_once self::$fixturesPath.'/FooCommand.php'; + require_once self::$fixturesPath.'/Foo1Command.php'; + require_once self::$fixturesPath.'/Foo2Command.php'; + require_once self::$fixturesPath.'/Foo3Command.php'; + require_once self::$fixturesPath.'/Foo4Command.php'; + require_once self::$fixturesPath.'/Foo5Command.php'; + require_once self::$fixturesPath.'/FoobarCommand.php'; + require_once self::$fixturesPath.'/BarBucCommand.php'; + require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; + require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; + } + + protected function normalizeLineBreaks($text) + { + return str_replace(PHP_EOL, "\n", $text); + } + + /** + * Replaces the dynamic placeholders of the command help text with a static version. + * The placeholder %command.full_name% includes the script path that is not predictable + * and can not be tested against. + */ + protected function ensureStaticCommandHelp(Application $application) + { + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + } + + public function testConstructor() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument'); + $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its second argument'); + $this->assertEquals(array('help', 'list'), array_keys($application->all()), '__construct() registered the help and list commands by default'); + } + + public function testSetGetName() + { + $application = new Application(); + $application->setName('foo'); + $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application'); + } + + public function testSetGetVersion() + { + $application = new Application(); + $application->setVersion('bar'); + $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application'); + } + + public function testGetLongVersion() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo version bar', $application->getLongVersion(), '->getLongVersion() returns the long version of the application'); + } + + public function testHelp() + { + $application = new Application(); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->getHelp() returns a help message'); + } + + public function testAll() + { + $application = new Application(); + $commands = $application->all(); + $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands'); + + $application->add(new \FooCommand()); + $commands = $application->all('foo'); + $this->assertCount(1, $commands, '->all() takes a namespace as its first argument'); + } + + public function testRegister() + { + $application = new Application(); + $command = $application->register('foo'); + $this->assertEquals('foo', $command->getName(), '->register() registers a new command'); + } + + public function testAdd() + { + $application = new Application(); + $application->add($foo = new \FooCommand()); + $commands = $application->all(); + $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command'); + + $application = new Application(); + $application->addCommands(array($foo = new \FooCommand(), $foo1 = new \Foo1Command())); + $commands = $application->all(); + $this->assertEquals(array($foo, $foo1), array($commands['foo:bar'], $commands['foo:bar1']), '->addCommands() registers an array of commands'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Command class "Foo5Command" is not correctly initialized. You probably forgot to call the parent constructor. + */ + public function testAddCommandWithEmptyConstructor() + { + $application = new Application(); + $application->add(new \Foo5Command()); + } + + public function testHasGet() + { + $application = new Application(); + $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered'); + $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered'); + + $application->add($foo = new \FooCommand()); + $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered'); + $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name'); + $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias'); + + $application = new Application(); + $application->add($foo = new \FooCommand()); + // simulate --help + $r = new \ReflectionObject($application); + $p = $r->getProperty('wantHelps'); + $p->setAccessible(true); + $p->setValue($application, true); + $command = $application->get('foo:bar'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input'); + } + + public function testSilentHelp() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $tester = new ApplicationTester($application); + $tester->run(array('-h' => true, '-q' => true), array('decorated' => false)); + + $this->assertEmpty($tester->getDisplay(true)); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The command "foofoo" does not exist. + */ + public function testGetInvalidCommand() + { + $application = new Application(); + $application->get('foofoo'); + } + + public function testGetNamespaces() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $this->assertEquals(array('foo'), $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces'); + } + + public function testFindNamespace() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation'); + $application->add(new \Foo2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + } + + public function testFindNamespaceWithSubnamespaces() + { + $application = new Application(); + $application->add(new \FooSubnamespaced1Command()); + $application->add(new \FooSubnamespaced2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1). + */ + public function testFindAmbiguousNamespace() + { + $application = new Application(); + $application->add(new \BarBucCommand()); + $application->add(new \FooCommand()); + $application->add(new \Foo2Command()); + $application->findNamespace('f'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage There are no commands defined in the "bar" namespace. + */ + public function testFindInvalidNamespace() + { + $application = new Application(); + $application->findNamespace('bar'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo1" is not defined + */ + public function testFindUniqueNameButNamespaceName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($commandName = 'foo1'); + } + + public function testFind() + { + $application = new Application(); + $application->add(new \FooCommand()); + + $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist'); + $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias'); + } + + /** + * @dataProvider provideAmbiguousAbbreviations + */ + public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage) + { + $this->setExpectedException('Symfony\Component\Console\Exception\CommandNotFoundException', $expectedExceptionMessage); + + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($abbreviation); + } + + public function provideAmbiguousAbbreviations() + { + return array( + array('f', 'Command "f" is not defined.'), + array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'), + array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1 and 1 more).'), + ); + } + + public function testFindCommandEqualNamespace() + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo3Command', $application->find('foo3:bar'), '->find() returns the good command even if a namespace has same name'); + $this->assertInstanceOf('Foo4Command', $application->find('foo3:bar:toh'), '->find() returns a command even if its namespace equals another command name'); + } + + public function testFindCommandWithAmbiguousNamespacesButUniqueName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \FoobarCommand()); + + $this->assertInstanceOf('FoobarCommand', $application->find('f:f')); + } + + public function testFindCommandWithMissingNamespace() + { + $application = new Application(); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo4Command', $application->find('f::t')); + } + + /** + * @dataProvider provideInvalidCommandNamesSingle + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Did you mean this + */ + public function testFindAlternativeExceptionMessageSingle($name) + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->find($name); + } + + public function provideInvalidCommandNamesSingle() + { + return array( + array('foo3:baR'), + array('foO3:bar'), + ); + } + + public function testFindAlternativeExceptionMessageMultiple() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + // Command + plural + try { + $application->find('foo:baR'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1:bar/', $e->getMessage()); + $this->assertRegExp('/foo:bar/', $e->getMessage()); + } + + // Namespace + plural + try { + $application->find('foo2:bar'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1/', $e->getMessage()); + } + + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + // Subnamespace + plural + try { + $a = $application->find('foo3:'); + $this->fail('->find() should throw an Symfony\Component\Console\Exception\CommandNotFoundException if a command is ambiguous because of a subnamespace, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e); + $this->assertRegExp('/foo3:bar/', $e->getMessage()); + $this->assertRegExp('/foo3:bar:toh/', $e->getMessage()); + } + } + + public function testFindAlternativeCommands() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + try { + $application->find($commandName = 'Unknown command'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives'); + } + + // Test if "bar1" command throw a "CommandNotFoundException" and does not contain + // "foo:bar" as alternative because "bar1" is too far from "foo:bar" + try { + $application->find($commandName = 'bar1'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array('afoobar1', 'foo:bar1'), $e->getAlternatives()); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"'); + $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"'); + $this->assertNotRegExp('/foo:bar(?>!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); + } + } + + public function testFindAlternativeCommandsWithAnAlias() + { + $fooCommand = new \FooCommand(); + $fooCommand->setAliases(array('foo2')); + + $application = new Application(); + $application->add($fooCommand); + + $result = $application->find('foo'); + + $this->assertSame($fooCommand, $result); + } + + public function testFindAlternativeNamespace() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + $application->add(new \foo3Command()); + + try { + $application->find('Unknown-namespace:Unknown-command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, without alternatives'); + } + + try { + $application->find('foo2:command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertCount(3, $e->getAlternatives()); + $this->assertContains('foo', $e->getAlternatives()); + $this->assertContains('foo1', $e->getAlternatives()); + $this->assertContains('foo3', $e->getAlternatives()); + $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative'); + $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo"'); + $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo1"'); + $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo3"'); + } + } + + public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getNamespaces')); + $application->expects($this->once()) + ->method('getNamespaces') + ->will($this->returnValue(array('foo:sublong', 'bar:sub'))); + + $this->assertEquals('foo:sublong', $application->findNamespace('f:sub')); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo::bar" is not defined. + */ + public function testFindWithDoubleColonInNameThrowsException() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo4Command()); + $application->find('foo::bar'); + } + + public function testSetCatchExceptions() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $application->setCatchExceptions(true); + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag'); + + $application->setCatchExceptions(false); + try { + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->fail('->setCatchExceptions() sets the catch exception flag'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag'); + $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag'); + } + } + + public function testRenderException() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exception'); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + $this->assertContains('Exception trace', $tester->getDisplay(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); + + $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getDisplay(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); + + $application->add(new \Foo3Command()); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo3:bar'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo3:bar'), array('decorated' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRenderExceptionWithDoubleWidthCharacters() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $application->register('foo')->setCode(function () { + throw new \Exception('エラーメッセージ'); + }); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo'), array('decorated' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $application->register('foo')->setCode(function () { + throw new \Exception('コマンドの実行中にエラーが発生しました。'); + }); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRun() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add($command = new \Foo1Command()); + $_SERVER['argv'] = array('cli.php', 'foo:bar1'); + + ob_start(); + $application->run(); + ob_end_clean(); + + $this->assertInstanceOf('Symfony\Component\Console\Input\ArgvInput', $command->input, '->run() creates an ArgvInput by default if none is given'); + $this->assertInstanceOf('Symfony\Component\Console\Output\ConsoleOutput', $command->output, '->run() creates a ConsoleOutput by default if none is given'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $this->ensureStaticCommandHelp($application); + $tester = new ApplicationTester($application); + + $tester->run(array(), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $tester->getDisplay(true), '->run() runs the list command if no argument is passed'); + + $tester->run(array('--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if --help is passed'); + + $tester->run(array('-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if -h is passed'); + + $tester->run(array('command' => 'list', '--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed'); + + $tester->run(array('command' => 'list', '-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed'); + + $tester->run(array('--ansi' => true)); + $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed'); + + $tester->run(array('--no-ansi' => true)); + $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed'); + + $tester->run(array('--version' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if --version is passed'); + + $tester->run(array('-V' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if -v is passed'); + + $tester->run(array('command' => 'list', '--quiet' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed'); + + $tester->run(array('command' => 'list', '-q' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed'); + + $tester->run(array('command' => 'list', '--verbose' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 1)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose=1 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 2)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to very verbose if --verbose=2 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 3)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to debug if --verbose=3 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 4)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if unknown --verbose level is passed'); + + $tester->run(array('command' => 'list', '-v' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vv' => true)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vvv' => true)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo:bar', '--no-interaction' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed'); + + $tester->run(array('command' => 'foo:bar', '-n' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); + } + + /** + * Issue #9285. + * + * If the "verbose" option is just before an argument in ArgvInput, + * an argument value should not be treated as verbosity value. + * This test will fail with "Not enough arguments." if broken + */ + public function testVerboseValueNotBreakArguments() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + + $output = new StreamOutput(fopen('php://memory', 'w', false)); + + $input = new ArgvInput(array('cli.php', '-v', 'foo:bar')); + $application->run($input, $output); + + $input = new ArgvInput(array('cli.php', '--verbose', 'foo:bar')); + $application->run($input, $output); + } + + public function testRunReturnsIntegerExitCode() + { + $exception = new \Exception('', 4); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception'); + } + + public function testRunReturnsExitCodeOneForExceptionCodeZero() + { + $exception = new \Exception('', 0); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0'); + } + + /** + * @expectedException \LogicException + * @dataProvider getAddingAlreadySetDefinitionElementData + */ + public function testAddingAlreadySetDefinitionElementData($def) + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application + ->register('foo') + ->setDefinition(array($def)) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + $application->run($input, $output); + } + + public function getAddingAlreadySetDefinitionElementData() + { + return array( + array(new InputArgument('command', InputArgument::REQUIRED)), + array(new InputOption('quiet', '', InputOption::VALUE_NONE)), + array(new InputOption('query', 'q', InputOption::VALUE_NONE)), + ); + } + + public function testGetDefaultHelperSetReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + } + + public function testAddingSingleHelperSetOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testOverwritingDefaultHelperSetOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testGetDefaultInputDefinitionReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + $this->assertTrue($inputDefinition->hasArgument('command')); + + $this->assertTrue($inputDefinition->hasOption('help')); + $this->assertTrue($inputDefinition->hasOption('quiet')); + $this->assertTrue($inputDefinition->hasOption('verbose')); + $this->assertTrue($inputDefinition->hasOption('version')); + $this->assertTrue($inputDefinition->hasOption('ansi')); + $this->assertTrue($inputDefinition->hasOption('no-ansi')); + $this->assertTrue($inputDefinition->hasOption('no-interaction')); + } + + public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testSettingCustomInputDefinitionOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setDefinition(new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')))); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testRunWithDispatcher() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setDispatcher($this->getDispatcher()); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertEquals('before.foo.after.'.PHP_EOL, $tester->getDisplay()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage caught + */ + public function testRunWithExceptionAndDispatcher() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + } + + public function testRunDispatchesAllEventsWithException() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertContains('before.foo.caught.after.', $tester->getDisplay()); + } + + public function testRunWithDispatcherSkippingCommand() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher(true)); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $exitCode = $tester->run(array('command' => 'foo')); + $this->assertContains('before.after.', $tester->getDisplay()); + $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $exitCode); + } + + public function testRunWithDispatcherAccessingInputOptions() + { + $noInteractionValue = null; + $quietValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$noInteractionValue, &$quietValue) { + $input = $event->getInput(); + + $noInteractionValue = $input->getOption('no-interaction'); + $quietValue = $input->getOption('quiet'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--no-interaction' => true)); + + $this->assertTrue($noInteractionValue); + $this->assertFalse($quietValue); + } + + public function testRunWithDispatcherAddingInputOptions() + { + $extraValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$extraValue) { + $definition = $event->getCommand()->getDefinition(); + $input = $event->getInput(); + + $definition->addOption(new InputOption('extra', null, InputOption::VALUE_REQUIRED)); + $input->bind($definition); + + $extraValue = $input->getOption('extra'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--extra' => 'some test value')); + + $this->assertEquals('some test value', $extraValue); + } + + public function testTerminalDimensions() + { + $application = new Application(); + $originalDimensions = $application->getTerminalDimensions(); + $this->assertCount(2, $originalDimensions); + + $width = 80; + if ($originalDimensions[0] == $width) { + $width = 100; + } + + $application->setTerminalDimensions($width, 80); + $this->assertSame(array($width, 80), $application->getTerminalDimensions()); + } + + protected function getDispatcher($skipCommand = false) + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) { + $event->getOutput()->write('before.'); + + if ($skipCommand) { + $event->disableCommand(); + } + }); + $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) { + $event->getOutput()->writeln('after.'); + + if (!$skipCommand) { + $event->setExitCode(113); + } + }); + $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) { + $event->getOutput()->write('caught.'); + + $event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException())); + }); + + return $dispatcher; + } + + public function testSetRunCustomDefaultCommand() + { + $command = new \FooCommand(); + + $application = new Application(); + $application->setAutoExit(false); + $application->add($command); + $application->setDefaultCommand($command->getName()); + + $tester = new ApplicationTester($application); + $tester->run(array()); + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array()); + + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + } + + /** + * @requires function posix_isatty + */ + public function testCanCheckIfTerminalIsInteractive() + { + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'help')); + + $this->assertFalse($tester->getInput()->hasParameterOption(array('--no-interaction', '-n'))); + + $inputStream = $application->getHelperSet()->get('question')->getInputStream(); + $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream)); + } +} + +class CustomApplication extends Application +{ + /** + * Overwrites the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.'))); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array(new FormatterHelper())); + } +} + +class CustomDefaultCommandApplication extends Application +{ + /** + * Overwrites the constructor in order to set a different default command. + */ + public function __construct() + { + parent::__construct(); + + $command = new \FooCommand(); + $this->add($command); + $this->setDefaultCommand($command->getName()); + } +} diff --git a/vendor/symfony/console/Tests/Command/CommandTest.php b/vendor/symfony/console/Tests/Command/CommandTest.php new file mode 100644 index 0000000..c39adab --- /dev/null +++ b/vendor/symfony/console/Tests/Command/CommandTest.php @@ -0,0 +1,350 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/../Fixtures/'; + require_once self::$fixturesPath.'/TestCommand.php'; + } + + public function testConstructor() + { + $command = new Command('foo:bar'); + $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The command defined in "Symfony\Component\Console\Command\Command" cannot have an empty name. + */ + public function testCommandNameCannotBeEmpty() + { + new Command(); + } + + public function testSetApplication() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application'); + } + + public function testSetGetDefinition() + { + $command = new \TestCommand(); + $ret = $command->setDefinition($definition = new InputDefinition()); + $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface'); + $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance'); + $command->setDefinition(array(new InputArgument('foo'), new InputOption('bar'))); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $command->setDefinition(new InputDefinition()); + } + + public function testAddArgument() + { + $command = new \TestCommand(); + $ret = $command->addArgument('foo'); + $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command'); + } + + public function testAddOption() + { + $command = new \TestCommand(); + $ret = $command->addOption('foo'); + $this->assertEquals($command, $ret, '->addOption() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command'); + } + + public function testGetNamespaceGetNameSetName() + { + $command = new \TestCommand(); + $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name'); + $command->setName('foo'); + $this->assertEquals('foo', $command->getName(), '->setName() sets the command name'); + + $ret = $command->setName('foobar:bar'); + $this->assertEquals($command, $ret, '->setName() implements a fluent interface'); + $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name'); + } + + /** + * @dataProvider provideInvalidCommandNames + */ + public function testInvalidCommandNames($name) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name)); + + $command = new \TestCommand(); + $command->setName($name); + } + + public function provideInvalidCommandNames() + { + return array( + array(''), + array('foo:'), + ); + } + + public function testGetSetDescription() + { + $command = new \TestCommand(); + $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description'); + $ret = $command->setDescription('description1'); + $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface'); + $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description'); + } + + public function testGetSetHelp() + { + $command = new \TestCommand(); + $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help'); + $ret = $command->setHelp('help1'); + $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface'); + $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help'); + $command->setHelp(''); + $this->assertEquals('', $command->getHelp(), '->getHelp() does not fall back to the description'); + } + + public function testGetProcessedHelp() + { + $command = new \TestCommand(); + $command->setHelp('The %command.name% command does... Example: php %command.full_name%.'); + $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly'); + $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%'); + + $command = new \TestCommand(); + $command->setHelp(''); + $this->assertContains('description', $command->getProcessedHelp(), '->getProcessedHelp() falls back to the description'); + } + + public function testGetSetAliases() + { + $command = new \TestCommand(); + $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases'); + $ret = $command->setAliases(array('name1')); + $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface'); + $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases'); + } + + public function testGetSynopsis() + { + $command = new \TestCommand(); + $command->addOption('foo'); + $command->addArgument('bar'); + $this->assertEquals('namespace:name [--foo] [--] []', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); + } + + public function testGetHelper() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $formatterHelper = new FormatterHelper(); + $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); + } + + public function testMergeApplicationDefinition() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo')))); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options'); + + $m->invoke($command); + $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options'); + } + + public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array())); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command, false); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the command options'); + $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments'); + + $m->invoke($command, true); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments'); + + $m->invoke($command); + $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments'); + } + + public function testRunInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => true)); + + $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive'); + } + + public function testRunNonInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => false)); + + $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You must override the execute() method in the concrete command class. + */ + public function testExecuteMethodNeedsToBeOverridden() + { + $command = new Command('foo'); + $command->run(new StringInput(''), new NullOutput()); + } + + /** + * @expectedException Symfony\Component\Console\Exception\InvalidOptionException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testRunWithInvalidOption() + { + $command = new \TestCommand(); + $tester = new CommandTester($command); + $tester->execute(array('--bar' => true)); + } + + public function testRunReturnsIntegerExitCode() + { + $command = new \TestCommand(); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)'); + + $command = $this->getMock('TestCommand', array('execute')); + $command->expects($this->once()) + ->method('execute') + ->will($this->returnValue('2.3')); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)'); + } + + public function testRunWithApplication() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + + $this->assertSame(0, $exitCode, '->run() returns an integer exit code'); + } + + public function testRunReturnsAlwaysInteger() + { + $command = new \TestCommand(); + + $this->assertSame(0, $command->run(new StringInput(''), new NullOutput())); + } + + public function testSetCode() + { + $command = new \TestCommand(); + $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) { + $output->writeln('from the code...'); + }); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function getSetCodeBindToClosureTests() + { + return array( + array(true, 'not bound to the command'), + array(false, 'bound to the command'), + ); + } + + /** + * @dataProvider getSetCodeBindToClosureTests + */ + public function testSetCodeBindToClosure($previouslyBound, $expected) + { + $code = createClosure(); + if ($previouslyBound) { + $code = $code->bindTo($this); + } + + $command = new \TestCommand(); + $command->setCode($code); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay()); + } + + public function testSetCodeWithNonClosureCallable() + { + $command = new \TestCommand(); + $ret = $command->setCode(array($this, 'callableMethodCommand')); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function callableMethodCommand(InputInterface $input, OutputInterface $output) + { + $output->writeln('from the code...'); + } +} + +// In order to get an unbound closure, we should create it outside a class +// scope. +function createClosure() +{ + return function (InputInterface $input, OutputInterface $output) { + $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command'); + }; +} diff --git a/vendor/symfony/console/Tests/Command/HelpCommandTest.php b/vendor/symfony/console/Tests/Command/HelpCommandTest.php new file mode 100644 index 0000000..10bb324 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/HelpCommandTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Application; + +class HelpCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteForCommandAlias() + { + $command = new HelpCommand(); + $command->setApplication(new Application()); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command_name' => 'li'), array('decorated' => false)); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + } + + public function testExecuteForCommand() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array(), array('decorated' => false)); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForCommandWithXmlOption() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array('--format' => 'xml')); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --xml is passed'); + } + + public function testExecuteForApplicationCommand() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list')); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForApplicationCommandWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list', '--format' => 'xml')); + $this->assertContains('list [--raw] [--format FORMAT] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --format=xml is passed'); + } +} diff --git a/vendor/symfony/console/Tests/Command/ListCommandTest.php b/vendor/symfony/console/Tests/Command/ListCommandTest.php new file mode 100644 index 0000000..a166a04 --- /dev/null +++ b/vendor/symfony/console/Tests/Command/ListCommandTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Application; + +class ListCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteListsCommands() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + + $this->assertRegExp('/help\s{2,}Displays help for a command/', $commandTester->getDisplay(), '->execute() returns a list of available commands'); + } + + public function testExecuteListsCommandsWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--format' => 'xml')); + $this->assertRegExp('//', $commandTester->getDisplay(), '->execute() returns a list of available commands in XML if --xml is passed'); + } + + public function testExecuteListsCommandsWithRawOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<<'EOF' +help Displays help for a command +list Lists commands + +EOF; + + $this->assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsWithNamespaceArgument() + { + require_once realpath(__DIR__.'/../Fixtures/FooCommand.php'); + $application = new Application(); + $application->add(new \FooCommand()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), 'namespace' => 'foo', '--raw' => true)); + $output = <<<'EOF' +foo:bar The foo:bar command + +EOF; + + $this->assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsOrder() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + $output = <<<'EOF' +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands + 0foo + 0foo:bar 0foo:bar command +EOF; + + $this->assertEquals($output, trim($commandTester->getDisplay(true))); + } + + public function testExecuteListsCommandsOrderRaw() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<<'EOF' +help Displays help for a command +list Lists commands +0foo:bar 0foo:bar command +EOF; + + $this->assertEquals($output, trim($commandTester->getDisplay(true))); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php new file mode 100644 index 0000000..c36c4a8 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\BufferedOutput; + +abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getDescribeInputArgumentTestData */ + public function testDescribeInputArgument(InputArgument $argument, $expectedDescription) + { + $this->assertDescription($expectedDescription, $argument); + } + + /** @dataProvider getDescribeInputOptionTestData */ + public function testDescribeInputOption(InputOption $option, $expectedDescription) + { + $this->assertDescription($expectedDescription, $option); + } + + /** @dataProvider getDescribeInputDefinitionTestData */ + public function testDescribeInputDefinition(InputDefinition $definition, $expectedDescription) + { + $this->assertDescription($expectedDescription, $definition); + } + + /** @dataProvider getDescribeCommandTestData */ + public function testDescribeCommand(Command $command, $expectedDescription) + { + $this->assertDescription($expectedDescription, $command); + } + + /** @dataProvider getDescribeApplicationTestData */ + public function testDescribeApplication(Application $application, $expectedDescription) + { + // Replaces the dynamic placeholders of the command help text with a static version. + // The placeholder %command.full_name% includes the script path that is not predictable + // and can not be tested against. + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + + $this->assertDescription($expectedDescription, $application); + } + + public function getDescribeInputArgumentTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputArguments()); + } + + public function getDescribeInputOptionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputOptions()); + } + + public function getDescribeInputDefinitionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputDefinitions()); + } + + public function getDescribeCommandTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getCommands()); + } + + public function getDescribeApplicationTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getApplications()); + } + + abstract protected function getDescriptor(); + + abstract protected function getFormat(); + + private function getDescriptionTestData(array $objects) + { + $data = array(); + foreach ($objects as $name => $object) { + $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, $this->getFormat())); + $data[] = array($object, $description); + } + + return $data; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php new file mode 100644 index 0000000..f9a1561 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Output\BufferedOutput; + +class JsonDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new JsonDescriptor(); + } + + protected function getFormat() + { + return 'json'; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(json_decode(trim($expectedDescription), true), json_decode(trim(str_replace(PHP_EOL, "\n", $output->fetch())), true)); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php new file mode 100644 index 0000000..c85e8a5 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; + +class MarkdownDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new MarkdownDescriptor(); + } + + protected function getFormat() + { + return 'md'; + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php b/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php new file mode 100644 index 0000000..45b3b2f --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication2; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand2; + +/** + * @author Jean-François Simon + */ +class ObjectsProvider +{ + public static function getInputArguments() + { + return array( + 'input_argument_1' => new InputArgument('argument_name', InputArgument::REQUIRED), + 'input_argument_2' => new InputArgument('argument_name', InputArgument::IS_ARRAY, 'argument description'), + 'input_argument_3' => new InputArgument('argument_name', InputArgument::OPTIONAL, 'argument description', 'default_value'), + 'input_argument_4' => new InputArgument('argument_name', InputArgument::REQUIRED, "multiline\nargument description"), + ); + } + + public static function getInputOptions() + { + return array( + 'input_option_1' => new InputOption('option_name', 'o', InputOption::VALUE_NONE), + 'input_option_2' => new InputOption('option_name', 'o', InputOption::VALUE_OPTIONAL, 'option description', 'default_value'), + 'input_option_3' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, 'option description'), + 'input_option_4' => new InputOption('option_name', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'option description', array()), + 'input_option_5' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, "multiline\noption description"), + 'input_option_6' => new InputOption('option_name', array('o', 'O'), InputOption::VALUE_REQUIRED, 'option with multiple shortcuts'), + ); + } + + public static function getInputDefinitions() + { + return array( + 'input_definition_1' => new InputDefinition(), + 'input_definition_2' => new InputDefinition(array(new InputArgument('argument_name', InputArgument::REQUIRED))), + 'input_definition_3' => new InputDefinition(array(new InputOption('option_name', 'o', InputOption::VALUE_NONE))), + 'input_definition_4' => new InputDefinition(array( + new InputArgument('argument_name', InputArgument::REQUIRED), + new InputOption('option_name', 'o', InputOption::VALUE_NONE), + )), + ); + } + + public static function getCommands() + { + return array( + 'command_1' => new DescriptorCommand1(), + 'command_2' => new DescriptorCommand2(), + ); + } + + public static function getApplications() + { + return array( + 'application_1' => new DescriptorApplication1(), + 'application_2' => new DescriptorApplication2(), + ); + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php new file mode 100644 index 0000000..350b679 --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\TextDescriptor; + +class TextDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new TextDescriptor(); + } + + protected function getFormat() + { + return 'txt'; + } +} diff --git a/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php b/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php new file mode 100644 index 0000000..59a5d1e --- /dev/null +++ b/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\XmlDescriptor; + +class XmlDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new XmlDescriptor(); + } + + protected function getFormat() + { + return 'xml'; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php b/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php new file mode 100644 index 0000000..52b619e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php @@ -0,0 +1,11 @@ +setName('bar:buc'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php new file mode 100644 index 0000000..132b6d5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication1 extends Application +{ +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php new file mode 100644 index 0000000..ff55135 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication2 extends Application +{ + public function __construct() + { + parent::__construct('My Symfony application', 'v1.0'); + $this->add(new DescriptorCommand1()); + $this->add(new DescriptorCommand2()); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php new file mode 100644 index 0000000..ede05d7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; + +class DescriptorCommand1 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command1') + ->setAliases(array('alias1', 'alias2')) + ->setDescription('command 1 description') + ->setHelp('command 1 help') + ; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php new file mode 100644 index 0000000..51106b9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class DescriptorCommand2 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command2') + ->setDescription('command 2 description') + ->setHelp('command 2 help') + ->addUsage('-o|--option_name ') + ->addUsage('') + ->addArgument('argument_name', InputArgument::REQUIRED) + ->addOption('option_name', 'o', InputOption::VALUE_NONE) + ; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/DummyOutput.php b/vendor/symfony/console/Tests/Fixtures/DummyOutput.php new file mode 100644 index 0000000..0070c0a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/DummyOutput.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Output\BufferedOutput; + +/** + * Dummy output. + * + * @author Kévin Dunglas + */ +class DummyOutput extends BufferedOutput +{ + /** + * @return array + */ + public function getLogs() + { + $logs = array(); + foreach (explode("\n", trim($this->fetch())) as $message) { + preg_match('/^\[(.*)\] (.*)/', $message, $matches); + $logs[] = sprintf('%s %s', $matches[1], $matches[2]); + } + + return $logs; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo1Command.php b/vendor/symfony/console/Tests/Fixtures/Foo1Command.php new file mode 100644 index 0000000..254162f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo1Command.php @@ -0,0 +1,26 @@ +setName('foo:bar1') + ->setDescription('The foo:bar1 command') + ->setAliases(array('afoobar1')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo2Command.php b/vendor/symfony/console/Tests/Fixtures/Foo2Command.php new file mode 100644 index 0000000..8071dc8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo2Command.php @@ -0,0 +1,21 @@ +setName('foo1:bar') + ->setDescription('The foo1:bar command') + ->setAliases(array('afoobar2')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo3Command.php b/vendor/symfony/console/Tests/Fixtures/Foo3Command.php new file mode 100644 index 0000000..6c890fa --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo3Command.php @@ -0,0 +1,29 @@ +setName('foo3:bar') + ->setDescription('The foo3:bar command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + try { + throw new \Exception('First exception

this is html

'); + } catch (\Exception $e) { + throw new \Exception('Second exception comment', 0, $e); + } + } catch (\Exception $e) { + throw new \Exception('Third exception comment', 0, $e); + } + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo4Command.php b/vendor/symfony/console/Tests/Fixtures/Foo4Command.php new file mode 100644 index 0000000..1c54639 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo4Command.php @@ -0,0 +1,11 @@ +setName('foo3:bar:toh'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Foo5Command.php b/vendor/symfony/console/Tests/Fixtures/Foo5Command.php new file mode 100644 index 0000000..a1c6082 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Foo5Command.php @@ -0,0 +1,10 @@ +setName('0foo:bar')->setDescription('0foo:bar command'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooCommand.php b/vendor/symfony/console/Tests/Fixtures/FooCommand.php new file mode 100644 index 0000000..355e0ad --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooCommand.php @@ -0,0 +1,33 @@ +setName('foo:bar') + ->setDescription('The foo:bar command') + ->setAliases(array('afoobar')) + ; + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + + $output->writeln('called'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php new file mode 100644 index 0000000..fc50c72 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php @@ -0,0 +1,26 @@ +setName('foo:bar:baz') + ->setDescription('The foo:bar:baz command') + ->setAliases(array('foobarbaz')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php new file mode 100644 index 0000000..1cf31ff --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php @@ -0,0 +1,26 @@ +setName('foo:go:bret') + ->setDescription('The foo:bar:go command') + ->setAliases(array('foobargo')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php b/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php new file mode 100644 index 0000000..9681628 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php @@ -0,0 +1,25 @@ +setName('foobar:foo') + ->setDescription('The foobar:foo command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php new file mode 100644 index 0000000..996fafb --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php @@ -0,0 +1,11 @@ +caution('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php new file mode 100644 index 0000000..6634cd5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php @@ -0,0 +1,13 @@ +title('Title'); + $output->warning('Lorem ipsum dolor sit amet'); + $output->title('Title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php new file mode 100644 index 0000000..6004e3d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php @@ -0,0 +1,16 @@ +warning('Warning'); + $output->caution('Caution'); + $output->error('Error'); + $output->success('Success'); + $output->note('Note'); + $output->block('Custom block', 'CUSTOM', 'fg=white;bg=green', 'X ', true); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php new file mode 100644 index 0000000..c7a08f1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php @@ -0,0 +1,12 @@ +title('First title'); + $output->title('Second title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php new file mode 100644 index 0000000..afea70c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php @@ -0,0 +1,34 @@ +write('Lorem ipsum dolor sit amet'); + $output->title('First title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->title('Second title'); + + $output->write('Lorem ipsum dolor sit amet'); + $output->write(''); + $output->title('Third title'); + + //Ensure edge case by appending empty strings to history: + $output->write('Lorem ipsum dolor sit amet'); + $output->write(array('', '', '')); + $output->title('Fourth title'); + + //Ensure have manual control over number of blank lines: + $output->writeln('Lorem ipsum dolor sit amet'); + $output->writeln(array('', '')); //Should append an extra blank line + $output->title('Fifth title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->newLine(2); //Should append an extra blank line + $output->title('Fifth title'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php new file mode 100644 index 0000000..d2c68a9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php @@ -0,0 +1,37 @@ +writeln('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + //Even using write: + $output->write('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->write('Lorem ipsum dolor sit amet'); + $output->text(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->newLine(); + + $output->write('Lorem ipsum dolor sit amet'); + $output->comment(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php new file mode 100644 index 0000000..f1d7990 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php @@ -0,0 +1,16 @@ +listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + $output->success('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php new file mode 100644 index 0000000..cbfea73 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php @@ -0,0 +1,15 @@ +title('Title'); + $output->askHidden('Hidden question'); + $output->choice('Choice question with default', array('choice1', 'choice2'), 'choice1'); + $output->confirm('Confirmation with yes default', true); + $output->text('Duis aute irure dolor in reprehenderit in voluptate velit esse'); +}; diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt new file mode 100644 index 0000000..a42e0f7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt @@ -0,0 +1,3 @@ + + ! [CAUTION] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt new file mode 100644 index 0000000..334875f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt @@ -0,0 +1,9 @@ + +Title +===== + + [WARNING] Lorem ipsum dolor sit amet + +Title +===== + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt new file mode 100644 index 0000000..ca60976 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt @@ -0,0 +1,13 @@ + + [WARNING] Warning + + ! [CAUTION] Caution + + [ERROR] Error + + [OK] Success + + ! [NOTE] Note + +X [CUSTOM] Custom block + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt new file mode 100644 index 0000000..f4b6d58 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt @@ -0,0 +1,7 @@ + +First title +=========== + +Second title +============ + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt new file mode 100644 index 0000000..2646d85 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt @@ -0,0 +1,32 @@ +Lorem ipsum dolor sit amet + +First title +=========== + +Lorem ipsum dolor sit amet + +Second title +============ + +Lorem ipsum dolor sit amet + +Third title +=========== + +Lorem ipsum dolor sit amet + +Fourth title +============ + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt new file mode 100644 index 0000000..738eb9e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt @@ -0,0 +1,15 @@ +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + Lorem ipsum dolor sit amet + consectetur adipiscing elit + +Lorem ipsum dolor sit amet + // Lorem ipsum dolor sit amet + // consectetur adipiscing elit diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt new file mode 100644 index 0000000..5f2d33c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt @@ -0,0 +1,6 @@ + + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + + [OK] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt new file mode 100644 index 0000000..ecea977 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt @@ -0,0 +1,5 @@ + +Title +===== + + Duis aute irure dolor in reprehenderit in voluptate velit esse diff --git a/vendor/symfony/console/Tests/Fixtures/TestCommand.php b/vendor/symfony/console/Tests/Fixtures/TestCommand.php new file mode 100644 index 0000000..dcd3273 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/TestCommand.php @@ -0,0 +1,28 @@ +setName('namespace:name') + ->setAliases(array('name')) + ->setDescription('description') + ->setHelp('help') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('execute called'); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } +} diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.json b/vendor/symfony/console/Tests/Fixtures/application_1.json new file mode 100644 index 0000000..b3ad97d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--format FORMAT] [--raw] [--] []"],"description":"Displays help for a command","help":"The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--raw] [--format FORMAT] [--] []"],"description":"Lists commands","help":"The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}}],"namespaces":[{"id":"_global","commands":["help","list"]}]} diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.md b/vendor/symfony/console/Tests/Fixtures/application_1.md new file mode 100644 index 0000000..f1d88c5 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.md @@ -0,0 +1,181 @@ +UNKNOWN +======= + +* help +* list + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--format FORMAT] [--raw] [--] []` + +The help command displays help for a given command: + + php app/console help list + +You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + +To display the list of available commands, please use the list command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--raw] [--format FORMAT] [--] []` + +The list command lists all commands: + + php app/console list + +You can also display the commands for a specific namespace: + + php app/console list test + +You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.txt b/vendor/symfony/console/Tests/Fixtures/application_1.txt new file mode 100644 index 0000000..c4cf8f2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/console/Tests/Fixtures/application_1.xml b/vendor/symfony/console/Tests/Fixtures/application_1.xml new file mode 100644 index 0000000..8514f23 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_1.xml @@ -0,0 +1,104 @@ + + + + + + help [--format FORMAT] [--raw] [--] [<command_name>] + + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + The command name + + help + + + + + + + + + + + + + + + + + + list [--raw] [--format FORMAT] [--] [<namespace>] + + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + The namespace name + + + + + + + + + + + + help + list + + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.json b/vendor/symfony/console/Tests/Fixtures/application_2.json new file mode 100644 index 0000000..e8870ba --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--format FORMAT] [--raw] [--] []"],"description":"Displays help for a command","help":"The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--raw] [--format FORMAT] [--] []"],"description":"Lists commands","help":"The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}},{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":{"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] ", "descriptor:command2 -o|--option_name ", "descriptor:command2 "],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}}],"namespaces":[{"id":"_global","commands":["alias1","alias2","help","list"]},{"id":"descriptor","commands":["descriptor:command1","descriptor:command2"]}]} \ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.md b/vendor/symfony/console/Tests/Fixtures/application_2.md new file mode 100644 index 0000000..63b2d9a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.md @@ -0,0 +1,376 @@ +My Symfony application +====================== + +* alias1 +* alias2 +* help +* list + +**descriptor:** + +* descriptor:command1 +* descriptor:command2 + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--format FORMAT] [--raw] [--] []` + +The help command displays help for a given command: + + php app/console help list + +You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + +To display the list of available commands, please use the list command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--raw] [--format FORMAT] [--] []` + +The list command lists all commands: + + php app/console list + +You can also display the commands for a specific namespace: + + php app/console list test + +You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help + +### Options: + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] ` + * `descriptor:command2 -o|--option_name ` + * `descriptor:command2 ` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.txt b/vendor/symfony/console/Tests/Fixtures/application_2.txt new file mode 100644 index 0000000..292aa82 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.txt @@ -0,0 +1,22 @@ +My Symfony application version v1.0 + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + alias1 command 1 description + alias2 command 1 description + help Displays help for a command + list Lists commands + descriptor + descriptor:command1 command 1 description + descriptor:command2 command 2 description diff --git a/vendor/symfony/console/Tests/Fixtures/application_2.xml b/vendor/symfony/console/Tests/Fixtures/application_2.xml new file mode 100644 index 0000000..62e3cfc --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_2.xml @@ -0,0 +1,184 @@ + + + + + + help [--format FORMAT] [--raw] [--] [<command_name>] + + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + The command name + + help + + + + + + + + + + + + + + + + + + list [--raw] [--format FORMAT] [--] [<namespace>] + + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + The namespace name + + + + + + + + + + + descriptor:command1 + alias1 + alias2 + + command 1 description + command 1 help + + + + + + + + + + + + + + descriptor:command2 [-o|--option_name] [--] <argument_name> + descriptor:command2 -o|--option_name <argument_name> + descriptor:command2 <argument_name> + + command 2 description + command 2 help + + + + + + + + + + + + + + + + + + + + + alias1 + alias2 + help + list + + + descriptor:command1 + descriptor:command2 + + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_astext1.txt b/vendor/symfony/console/Tests/Fixtures/application_astext1.txt new file mode 100644 index 0000000..19dacb2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_astext1.txt @@ -0,0 +1,20 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + afoobar The foo:bar command + help Displays help for a command + list Lists commands + foo + foo:bar The foo:bar command diff --git a/vendor/symfony/console/Tests/Fixtures/application_astext2.txt b/vendor/symfony/console/Tests/Fixtures/application_astext2.txt new file mode 100644 index 0000000..c99ccdd --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_astext2.txt @@ -0,0 +1,16 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands for the "foo" namespace: + foo:bar The foo:bar command diff --git a/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt b/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt new file mode 100644 index 0000000..0c16e3c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt @@ -0,0 +1 @@ +Console Tool \ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt new file mode 100644 index 0000000..919cec4 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt @@ -0,0 +1,6 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not defined. + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt new file mode 100644 index 0000000..6456171 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt @@ -0,0 +1,8 @@ + + + [Symfony\Component\Console\Exception\InvalidOptionException] + The "--foo" option does not exist. + + +list [--raw] [--format FORMAT] [--] [] + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt new file mode 100644 index 0000000..8276137 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt @@ -0,0 +1,18 @@ + + + [Exception] + Third exception comment + + + + [Exception] + Second exception comment + + + + [Exception] + First exception

this is html

+ + +foo3:bar + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt new file mode 100644 index 0000000..b4a7b01 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt @@ -0,0 +1,18 @@ + +  + [Exception]  + Third exception comment  +  + +  + [Exception]  + Second exception comment  +  + +  + [Exception]  + First exception 

this is html

  +  + +foo3:bar + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt new file mode 100644 index 0000000..cb080e9 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt @@ -0,0 +1,7 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not define + d. + + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt new file mode 100644 index 0000000..1ba5f8f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt @@ -0,0 +1,8 @@ + + + [Exception] + エラーメッセージ + + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt new file mode 100644 index 0000000..2064425 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt @@ -0,0 +1,8 @@ + +  + [Exception]  + エラーメッセージ  +  + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt new file mode 100644 index 0000000..e41fcfc --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt @@ -0,0 +1,9 @@ + + + [Exception] + コマンドの実行中にエラーが + 発生しました。 + + +foo + diff --git a/vendor/symfony/console/Tests/Fixtures/application_run1.txt b/vendor/symfony/console/Tests/Fixtures/application_run1.txt new file mode 100644 index 0000000..0dc2730 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/console/Tests/Fixtures/application_run2.txt b/vendor/symfony/console/Tests/Fixtures/application_run2.txt new file mode 100644 index 0000000..0098e9a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run2.txt @@ -0,0 +1,28 @@ +Usage: + help [options] [--] [] + +Arguments: + command The command to execute + command_name The command name [default: "help"] + +Options: + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + --raw To output raw command help + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Help: + The help command displays help for a given command: + + php app/console help list + + You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + + To display the list of available commands, please use the list command. diff --git a/vendor/symfony/console/Tests/Fixtures/application_run3.txt b/vendor/symfony/console/Tests/Fixtures/application_run3.txt new file mode 100644 index 0000000..fe566d7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run3.txt @@ -0,0 +1,26 @@ +Usage: + list [options] [--] [] + +Arguments: + namespace The namespace name + +Options: + --raw To output raw command list + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + +Help: + The list command lists all commands: + + php app/console list + + You can also display the commands for a specific namespace: + + php app/console list test + + You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + + It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw diff --git a/vendor/symfony/console/Tests/Fixtures/application_run4.txt b/vendor/symfony/console/Tests/Fixtures/application_run4.txt new file mode 100644 index 0000000..47187fc --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/application_run4.txt @@ -0,0 +1 @@ +Console Tool diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.json b/vendor/symfony/console/Tests/Fixtures/command_1.json new file mode 100644 index 0000000..20f310b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.json @@ -0,0 +1 @@ +{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":[]}} diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.md b/vendor/symfony/console/Tests/Fixtures/command_1.md new file mode 100644 index 0000000..34ed3ea --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.md @@ -0,0 +1,11 @@ +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.txt b/vendor/symfony/console/Tests/Fixtures/command_1.txt new file mode 100644 index 0000000..28e14a0 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.txt @@ -0,0 +1,7 @@ +Usage: + descriptor:command1 + alias1 + alias2 + +Help: + command 1 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_1.xml b/vendor/symfony/console/Tests/Fixtures/command_1.xml new file mode 100644 index 0000000..838b9bd --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_1.xml @@ -0,0 +1,12 @@ + + + + descriptor:command1 + alias1 + alias2 + + command 1 description + command 1 help + + + diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.json b/vendor/symfony/console/Tests/Fixtures/command_2.json new file mode 100644 index 0000000..38edd1e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.json @@ -0,0 +1 @@ +{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] ", "descriptor:command2 -o|--option_name ", "descriptor:command2 "],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}}} diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.md b/vendor/symfony/console/Tests/Fixtures/command_2.md new file mode 100644 index 0000000..6f538b6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.md @@ -0,0 +1,33 @@ +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] ` + * `descriptor:command2 -o|--option_name ` + * `descriptor:command2 ` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.txt b/vendor/symfony/console/Tests/Fixtures/command_2.txt new file mode 100644 index 0000000..72f7ce0 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.txt @@ -0,0 +1,13 @@ +Usage: + descriptor:command2 [options] [--] + descriptor:command2 -o|--option_name + descriptor:command2 + +Arguments: + argument_name + +Options: + -o, --option_name + +Help: + command 2 help diff --git a/vendor/symfony/console/Tests/Fixtures/command_2.xml b/vendor/symfony/console/Tests/Fixtures/command_2.xml new file mode 100644 index 0000000..67364ca --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_2.xml @@ -0,0 +1,21 @@ + + + + descriptor:command2 [-o|--option_name] [--] <argument_name> + descriptor:command2 -o|--option_name <argument_name> + descriptor:command2 <argument_name> + + command 2 description + command 2 help + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/command_astext.txt b/vendor/symfony/console/Tests/Fixtures/command_astext.txt new file mode 100644 index 0000000..7e20638 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_astext.txt @@ -0,0 +1,18 @@ +Usage: + namespace:name + name + +Arguments: + command The command to execute + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Help: + help diff --git a/vendor/symfony/console/Tests/Fixtures/command_asxml.txt b/vendor/symfony/console/Tests/Fixtures/command_asxml.txt new file mode 100644 index 0000000..5e77623 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/command_asxml.txt @@ -0,0 +1,38 @@ + + + + namespace:name + name + + description + help + + + The command to execute + + + + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/definition_astext.txt b/vendor/symfony/console/Tests/Fixtures/definition_astext.txt new file mode 100644 index 0000000..0431c07 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/definition_astext.txt @@ -0,0 +1,11 @@ +Arguments: + foo The foo argument + baz The baz argument [default: true] + bar The bar argument [default: ["http://foo.com/"]] + +Options: + -f, --foo=FOO The foo option + --baz[=BAZ] The baz option [default: false] + -b, --bar[=BAR] The bar option [default: "bar"] + --qux[=QUX] The qux option [default: ["http://foo.com/","bar"]] (multiple values allowed) + --qux2[=QUX2] The qux2 option [default: {"foo":"bar"}] (multiple values allowed) \ No newline at end of file diff --git a/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt b/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt new file mode 100644 index 0000000..eec8c07 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt @@ -0,0 +1,39 @@ + + + + + The foo argument + + + + The baz argument + + true + + + + The bar argument + + bar + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.json b/vendor/symfony/console/Tests/Fixtures/input_argument_1.json new file mode 100644 index 0000000..b8173b6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.md b/vendor/symfony/console/Tests/Fixtures/input_argument_1.md new file mode 100644 index 0000000..88f311a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt new file mode 100644 index 0000000..5503518 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt @@ -0,0 +1 @@ + argument_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml new file mode 100644 index 0000000..cb37f81 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.json b/vendor/symfony/console/Tests/Fixtures/input_argument_2.json new file mode 100644 index 0000000..ef06b09 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":true,"description":"argument description","default":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.md b/vendor/symfony/console/Tests/Fixtures/input_argument_2.md new file mode 100644 index 0000000..3cdb00c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: yes +* Description: argument description +* Default: `array ()` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt new file mode 100644 index 0000000..e713660 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt @@ -0,0 +1 @@ + argument_name argument description diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml new file mode 100644 index 0000000..629da5a --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml @@ -0,0 +1,5 @@ + + + argument description + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.json b/vendor/symfony/console/Tests/Fixtures/input_argument_3.json new file mode 100644 index 0000000..de8484e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":false,"description":"argument description","default":"default_value"} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.md b/vendor/symfony/console/Tests/Fixtures/input_argument_3.md new file mode 100644 index 0000000..be1c443 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: no +* Description: argument description +* Default: `'default_value'` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt new file mode 100644 index 0000000..6b76639 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt @@ -0,0 +1 @@ + argument_name argument description [default: "default_value"] diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml new file mode 100644 index 0000000..399a5c8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml @@ -0,0 +1,7 @@ + + + argument description + + default_value + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.json b/vendor/symfony/console/Tests/Fixtures/input_argument_4.json new file mode 100644 index 0000000..8067a4d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"multiline argument description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.md b/vendor/symfony/console/Tests/Fixtures/input_argument_4.md new file mode 100644 index 0000000..f026ab3 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.md @@ -0,0 +1,8 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: multiline + argument description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt b/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt new file mode 100644 index 0000000..aa74e8c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt @@ -0,0 +1,2 @@ + argument_name multiline + argument description diff --git a/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml b/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml new file mode 100644 index 0000000..5ca135e --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml @@ -0,0 +1,6 @@ + + + multiline +argument description + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.json b/vendor/symfony/console/Tests/Fixtures/input_definition_1.json new file mode 100644 index 0000000..c7a7d83 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.json @@ -0,0 +1 @@ +{"arguments":[],"options":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.md b/vendor/symfony/console/Tests/Fixtures/input_definition_1.md new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml new file mode 100644 index 0000000..b5481ce --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.json b/vendor/symfony/console/Tests/Fixtures/input_definition_2.json new file mode 100644 index 0000000..9964a55 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.md b/vendor/symfony/console/Tests/Fixtures/input_definition_2.md new file mode 100644 index 0000000..923191c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.md @@ -0,0 +1,9 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt new file mode 100644 index 0000000..73b0f30 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt @@ -0,0 +1,2 @@ +Arguments: + argument_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml new file mode 100644 index 0000000..102efc1 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.json b/vendor/symfony/console/Tests/Fixtures/input_definition_3.json new file mode 100644 index 0000000..6a86056 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.json @@ -0,0 +1 @@ +{"arguments":[],"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.md b/vendor/symfony/console/Tests/Fixtures/input_definition_3.md new file mode 100644 index 0000000..40fd7b0 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.md @@ -0,0 +1,11 @@ +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt new file mode 100644 index 0000000..c02766f --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt @@ -0,0 +1,2 @@ +Options: + -o, --option_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml new file mode 100644 index 0000000..bc95151 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.json b/vendor/symfony/console/Tests/Fixtures/input_definition_4.json new file mode 100644 index 0000000..c5a0019 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.md b/vendor/symfony/console/Tests/Fixtures/input_definition_4.md new file mode 100644 index 0000000..a31feea --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.md @@ -0,0 +1,21 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt b/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt new file mode 100644 index 0000000..63aa81d --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt @@ -0,0 +1,5 @@ +Arguments: + argument_name + +Options: + -o, --option_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml b/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml new file mode 100644 index 0000000..cffceec --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.json b/vendor/symfony/console/Tests/Fixtures/input_option_1.json new file mode 100644 index 0000000..60c5b56 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.md b/vendor/symfony/console/Tests/Fixtures/input_option_1.md new file mode 100644 index 0000000..6f9e9a7 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.txt b/vendor/symfony/console/Tests/Fixtures/input_option_1.txt new file mode 100644 index 0000000..3a5e4ee --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.txt @@ -0,0 +1 @@ + -o, --option_name diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_1.xml b/vendor/symfony/console/Tests/Fixtures/input_option_1.xml new file mode 100644 index 0000000..8a64ea6 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_1.xml @@ -0,0 +1,4 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.json b/vendor/symfony/console/Tests/Fixtures/input_option_2.json new file mode 100644 index 0000000..04e4228 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":false,"description":"option description","default":"default_value"} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.md b/vendor/symfony/console/Tests/Fixtures/input_option_2.md new file mode 100644 index 0000000..634ac0b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: no +* Description: option description +* Default: `'default_value'` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.txt b/vendor/symfony/console/Tests/Fixtures/input_option_2.txt new file mode 100644 index 0000000..1009eff --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.txt @@ -0,0 +1 @@ + -o, --option_name[=OPTION_NAME] option description [default: "default_value"] diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_2.xml b/vendor/symfony/console/Tests/Fixtures/input_option_2.xml new file mode 100644 index 0000000..4afac5b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_2.xml @@ -0,0 +1,7 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.json b/vendor/symfony/console/Tests/Fixtures/input_option_3.json new file mode 100644 index 0000000..c1ea120 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.md b/vendor/symfony/console/Tests/Fixtures/input_option_3.md new file mode 100644 index 0000000..3428289 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.txt b/vendor/symfony/console/Tests/Fixtures/input_option_3.txt new file mode 100644 index 0000000..947bb65 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.txt @@ -0,0 +1 @@ + -o, --option_name=OPTION_NAME option description diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_3.xml b/vendor/symfony/console/Tests/Fixtures/input_option_3.xml new file mode 100644 index 0000000..dcc0631 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_3.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.json b/vendor/symfony/console/Tests/Fixtures/input_option_4.json new file mode 100644 index 0000000..1b671d8 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":true,"description":"option description","default":[]} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.md b/vendor/symfony/console/Tests/Fixtures/input_option_4.md new file mode 100644 index 0000000..8ffba56 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: yes +* Description: option description +* Default: `array ()` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.txt b/vendor/symfony/console/Tests/Fixtures/input_option_4.txt new file mode 100644 index 0000000..27edf77 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.txt @@ -0,0 +1 @@ + -o, --option_name[=OPTION_NAME] option description (multiple values allowed) diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_4.xml b/vendor/symfony/console/Tests/Fixtures/input_option_4.xml new file mode 100644 index 0000000..5e2418b --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_4.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.json b/vendor/symfony/console/Tests/Fixtures/input_option_5.json new file mode 100644 index 0000000..35a1405 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"multiline option description","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.md b/vendor/symfony/console/Tests/Fixtures/input_option_5.md new file mode 100644 index 0000000..82f51ca --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.md @@ -0,0 +1,10 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: multiline + option description +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.txt b/vendor/symfony/console/Tests/Fixtures/input_option_5.txt new file mode 100644 index 0000000..4368883 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.txt @@ -0,0 +1,2 @@ + -o, --option_name=OPTION_NAME multiline + option description diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_5.xml b/vendor/symfony/console/Tests/Fixtures/input_option_5.xml new file mode 100644 index 0000000..90040cc --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_5.xml @@ -0,0 +1,6 @@ + + diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.json b/vendor/symfony/console/Tests/Fixtures/input_option_6.json new file mode 100644 index 0000000..d84e872 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o|-O","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option with multiple shortcuts","default":null} diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.md b/vendor/symfony/console/Tests/Fixtures/input_option_6.md new file mode 100644 index 0000000..ed1ea1c --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o|-O` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option with multiple shortcuts +* Default: `NULL` diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.txt b/vendor/symfony/console/Tests/Fixtures/input_option_6.txt new file mode 100644 index 0000000..0e6c975 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.txt @@ -0,0 +1 @@ + -o|O, --option_name=OPTION_NAME option with multiple shortcuts diff --git a/vendor/symfony/console/Tests/Fixtures/input_option_6.xml b/vendor/symfony/console/Tests/Fixtures/input_option_6.xml new file mode 100644 index 0000000..06126a2 --- /dev/null +++ b/vendor/symfony/console/Tests/Fixtures/input_option_6.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php new file mode 100644 index 0000000..774df26 --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyleStack; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleStackTest extends \PHPUnit_Framework_TestCase +{ + public function testPush() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->getCurrent()); + + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s3, $stack->getCurrent()); + } + + public function testPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->pop()); + $this->assertEquals($s1, $stack->pop()); + } + + public function testPopEmpty() + { + $stack = new OutputFormatterStyleStack(); + $style = new OutputFormatterStyle(); + + $this->assertEquals($style, $stack->pop()); + } + + public function testPopNotLast() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s2, $stack->pop($s2)); + $this->assertEquals($s1, $stack->pop()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push(new OutputFormatterStyle('white', 'black')); + $stack->pop(new OutputFormatterStyle('yellow', 'blue')); + } +} diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php new file mode 100644 index 0000000..0abfb3c --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $style = new OutputFormatterStyle('green', 'black', array('bold', 'underscore')); + $this->assertEquals("\033[32;40;1;4mfoo\033[39;49;22;24m", $style->apply('foo')); + + $style = new OutputFormatterStyle('red', null, array('blink')); + $this->assertEquals("\033[31;5mfoo\033[39;25m", $style->apply('foo')); + + $style = new OutputFormatterStyle(null, 'white'); + $this->assertEquals("\033[47mfoo\033[49m", $style->apply('foo')); + } + + public function testForeground() + { + $style = new OutputFormatterStyle(); + + $style->setForeground('black'); + $this->assertEquals("\033[30mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('blue'); + $this->assertEquals("\033[34mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('default'); + $this->assertEquals("\033[39mfoo\033[39m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setForeground('undefined-color'); + } + + public function testBackground() + { + $style = new OutputFormatterStyle(); + + $style->setBackground('black'); + $this->assertEquals("\033[40mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('yellow'); + $this->assertEquals("\033[43mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('default'); + $this->assertEquals("\033[49mfoo\033[49m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setBackground('undefined-color'); + } + + public function testOptions() + { + $style = new OutputFormatterStyle(); + + $style->setOptions(array('reverse', 'conceal')); + $this->assertEquals("\033[7;8mfoo\033[27;28m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[7;8;1mfoo\033[27;28;22m", $style->apply('foo')); + + $style->unsetOption('reverse'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOptions(array('bold')); + $this->assertEquals("\033[1mfoo\033[22m", $style->apply('foo')); + + try { + $style->setOption('foo'); + $this->fail('->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + + try { + $style->unsetOption('foo'); + $this->fail('->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + } +} diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php new file mode 100644 index 0000000..46d5fe1 --- /dev/null +++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function testEmptyTag() + { + $formatter = new OutputFormatter(true); + $this->assertEquals('foo<>bar', $formatter->format('foo<>bar')); + } + + public function testLGCharEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals('fooformat('foo\\assertEquals('some info', $formatter->format('\\some info\\')); + $this->assertEquals('\\some info\\', OutputFormatter::escape('some info')); + + $this->assertEquals( + "\033[33mSymfony\\Component\\Console does work very well!\033[39m", + $formatter->format('Symfony\Component\Console does work very well!') + ); + } + + public function testBundledStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", + $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[39m", + $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", + $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", + $formatter->format('some question') + ); + } + + public function testNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome \033[39;49m\033[32msome info\033[39m\033[37;41m error\033[39;49m", + $formatter->format('some some info error') + ); + } + + public function testAdjacentStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m\033[32msome info\033[39m", + $formatter->format('some errorsome info') + ); + } + + public function testStyleMatchingNotGreedy() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32m>=2.0,<2.3\033[39m)", + $formatter->format('(>=2.0,<2.3)') + ); + } + + public function testStyleEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32mz>=2.0,format('('.$formatter->escape('z>=2.0,)') + ); + + $this->assertEquals( + "\033[32msome error\033[39m", + $formatter->format(''.$formatter->escape('some error').'') + ); + } + + public function testDeepNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41merror\033[39;49m\033[32minfo\033[39m\033[33mcomment\033[39m\033[37;41merror\033[39;49m", + $formatter->format('errorinfocommenterror') + ); + } + + public function testNewStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('test', $style); + + $this->assertEquals($style, $formatter->getStyle('test')); + $this->assertNotEquals($style, $formatter->getStyle('info')); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('b', $style); + + $this->assertEquals("\033[34;47msome \033[39;49m\033[34;47mcustom\033[39;49m\033[34;47m msg\033[39;49m", $formatter->format('some custom msg')); + } + + public function testRedefineStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('info', $style); + + $this->assertEquals("\033[34;47msome custom msg\033[39;49m", $formatter->format('some custom msg')); + } + + public function testInlineStyle() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); + } + + public function testNonStyleTag() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[32msome \033[39m\033[32m\033[39m\033[32m \033[39m\033[32m\033[39m\033[32m styled \033[39m\033[32m

\033[39m\033[32msingle-char tag\033[39m\033[32m

\033[39m", $formatter->format('some styled

single-char tag

')); + } + + public function testFormatLongString() + { + $formatter = new OutputFormatter(true); + $long = str_repeat('\\', 14000); + $this->assertEquals("\033[37;41msome error\033[39;49m".$long, $formatter->format('some error'.$long)); + } + + public function testFormatToStringObject() + { + $formatter = new OutputFormatter(false); + $this->assertEquals( + 'some info', $formatter->format(new TableCell()) + ); + } + + public function testNotDecoratedFormatter() + { + $formatter = new OutputFormatter(false); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + 'some error', $formatter->format('some error') + ); + $this->assertEquals( + 'some info', $formatter->format('some info') + ); + $this->assertEquals( + 'some comment', $formatter->format('some comment') + ); + $this->assertEquals( + 'some question', $formatter->format('some question') + ); + + $formatter->setDecorated(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[39m", $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", $formatter->format('some question') + ); + } + + public function testContentWithLineBreaks() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals(<<format(<<<'EOF' + +some text +EOF + )); + + $this->assertEquals(<<format(<<<'EOF' +some text + +EOF + )); + + $this->assertEquals(<<format(<<<'EOF' + +some text + +EOF + )); + + $this->assertEquals(<<format(<<<'EOF' + +some text +more text + +EOF + )); + } +} + +class TableCell +{ + public function __toString() + { + return 'some info'; + } +} diff --git a/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php b/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php new file mode 100644 index 0000000..e0aa921 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\FormatterHelper; + +class FormatterHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testFormatSection() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + '[cli] Some text to display', + $formatter->formatSection('cli', 'Some text to display'), + '::formatSection() formats a message in a section' + ); + } + + public function testFormatBlock() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' Some text to display ', + $formatter->formatBlock('Some text to display', 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' Some text to display '."\n". + ' foo bar ', + $formatter->formatBlock(array('Some text to display', 'foo bar'), 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' '."\n". + ' Some text to display '."\n". + ' ', + $formatter->formatBlock('Some text to display', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDiacriticLetters() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' '."\n". + ' Du texte à afficher '."\n". + ' ', + $formatter->formatBlock('Du texte à afficher', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDoubleWidthDiacriticLetters() + { + $formatter = new FormatterHelper(); + $this->assertEquals( + ' '."\n". + ' 表示するテキスト '."\n". + ' ', + $formatter->formatBlock('表示するテキスト', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockLGEscaping() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' '."\n". + ' \some info\ '."\n". + ' ', + $formatter->formatBlock('some info', 'error', true), + '::formatBlock() escapes \'<\' chars' + ); + } +} diff --git a/vendor/symfony/console/Tests/Helper/HelperSetTest.php b/vendor/symfony/console/Tests/Helper/HelperSetTest.php new file mode 100644 index 0000000..04edd30 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/HelperSetTest.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Command\Command; + +class HelperSetTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $mock_helper = $this->getGenericMockHelper('fake_helper'); + $helperset = new HelperSet(array('fake_helper_alias' => $mock_helper)); + + $this->assertEquals($mock_helper, $helperset->get('fake_helper_alias'), '__construct sets given helper to helpers'); + $this->assertTrue($helperset->has('fake_helper_alias'), '__construct sets helper alias for given helper'); + } + + public function testSet() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset)); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper to helpers'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + $this->assertTrue($helperset->has('fake_helper_01'), '->set() will set multiple helpers on consecutive calls'); + $this->assertTrue($helperset->has('fake_helper_02'), '->set() will set multiple helpers on consecutive calls'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset), 'fake_helper_alias'); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper alias when set'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->set() adds helper alias when set'); + } + + public function testHas() + { + $helperset = new HelperSet(array('fake_helper_alias' => $this->getGenericMockHelper('fake_helper'))); + $this->assertTrue($helperset->has('fake_helper'), '->has() finds set helper'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->has() finds set helper by alias'); + } + + public function testGet() + { + $helper_01 = $this->getGenericMockHelper('fake_helper_01'); + $helper_02 = $this->getGenericMockHelper('fake_helper_02'); + $helperset = new HelperSet(array('fake_helper_01_alias' => $helper_01, 'fake_helper_02_alias' => $helper_02)); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01'), '->get() returns correct helper by name'); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01_alias'), '->get() returns correct helper by alias'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02'), '->get() returns correct helper by name'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02_alias'), '->get() returns correct helper by alias'); + + $helperset = new HelperSet(); + try { + $helperset->get('foo'); + $this->fail('->get() throws InvalidArgumentException when helper not found'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws InvalidArgumentException when helper not found'); + $this->assertInstanceOf('Symfony\Component\Console\Exception\ExceptionInterface', $e, '->get() throws domain specific exception when helper not found'); + $this->assertContains('The helper "foo" is not defined.', $e->getMessage(), '->get() throws InvalidArgumentException when helper not found'); + } + } + + public function testSetCommand() + { + $cmd_01 = new Command('foo'); + $cmd_02 = new Command('bar'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $this->assertEquals($cmd_01, $helperset->getCommand(), '->setCommand() stores given command'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $helperset->setCommand($cmd_02); + $this->assertEquals($cmd_02, $helperset->getCommand(), '->setCommand() overwrites stored command with consecutive calls'); + } + + public function testGetCommand() + { + $cmd = new Command('foo'); + $helperset = new HelperSet(); + $helperset->setCommand($cmd); + $this->assertEquals($cmd, $helperset->getCommand(), '->getCommand() retrieves stored command'); + } + + public function testIteration() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + + $helpers = array('fake_helper_01', 'fake_helper_02'); + $i = 0; + + foreach ($helperset as $helper) { + $this->assertEquals($helpers[$i++], $helper->getName()); + } + } + + /** + * Create a generic mock for the helper interface. Optionally check for a call to setHelperSet with a specific + * helperset instance. + * + * @param string $name + * @param HelperSet $helperset allows a mock to verify a particular helperset set is being added to the Helper + */ + private function getGenericMockHelper($name, HelperSet $helperset = null) + { + $mock_helper = $this->getMock('\Symfony\Component\Console\Helper\HelperInterface'); + $mock_helper->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + + if ($helperset) { + $mock_helper->expects($this->any()) + ->method('setHelperSet') + ->with($this->equalTo($helperset)); + } + + return $mock_helper; + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php b/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php new file mode 100644 index 0000000..a51fb43 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Process\Process; + +class ProcessHelperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideCommandsAndOutput + */ + public function testVariousProcessRuns($expected, $cmd, $verbosity, $error) + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream($verbosity); + $helper->run($output, $cmd, $error); + $this->assertEquals($expected, $this->getOutput($output)); + } + + public function testPassedCallbackIsExecuted() + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream(StreamOutput::VERBOSITY_NORMAL); + + $executed = false; + $callback = function () use (&$executed) { $executed = true; }; + + $helper->run($output, 'php -r "echo 42;"', null, $callback); + $this->assertTrue($executed); + } + + public function provideCommandsAndOutput() + { + $successOutputVerbose = <<42
';" + OUT 42 + RES Command ran successfully + +EOT; + $successOutputProcessDebug = <<42
\';"', StreamOutput::VERBOSITY_DEBUG, null), + array('', 'php -r "syntax error"', StreamOutput::VERBOSITY_VERBOSE, null), + array($syntaxErrorOutputVerbose, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, null), + array($syntaxErrorOutputDebug, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, null), + array($errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERBOSE, $errorMessage), + array($syntaxErrorOutputVerbose.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, $errorMessage), + array($syntaxErrorOutputDebug.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, $errorMessage), + array($successOutputProcessDebug, array('php', '-r', 'echo 42;'), StreamOutput::VERBOSITY_DEBUG, null), + array($successOutputDebug, new Process('php -r "echo 42;"'), StreamOutput::VERBOSITY_DEBUG, null), + ); + } + + private function getOutputStream($verbosity) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, false); + } + + private function getOutput(StreamOutput $output) + { + rewind($output->getStream()); + + return stream_get_contents($output->getStream()); + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProgressBarTest.php b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php new file mode 100644 index 0000000..4b25d1b --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php @@ -0,0 +1,664 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group time-sensitive + */ +class ProgressBarTest extends \PHPUnit_Framework_TestCase +{ + public function testMultipleStart() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + $bar->start(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 1 [->--------------------------]'). + $this->generateOutput(' 0 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvance() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 1 [->--------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceWithStep() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(5); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceMultipleTimes() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(3); + $bar->advance(2); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 3 [--->------------------------]'). + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceOverMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setProgress(9); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 9/10 [=========================>--] 90%'). + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 11/11 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testFormat() + { + $expected = + $this->generateOutput(' 0/10 [>---------------------------] 0%'). + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 10/10 [============================] 100%') + ; + + // max in construct, no format + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, no format + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in construct, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + } + + public function testCustomizations() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setBarWidth(10); + $bar->setBarCharacter('_'); + $bar->setEmptyBarCharacter(' '); + $bar->setProgressCharacter('/'); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/10 [/ ] 0%'). + $this->generateOutput(' 1/10 [_/ ] 10%'), + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'), + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithQuietVerbosity() + { + $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + '', + stream_get_contents($output->getStream()) + ); + } + + public function testFinishWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 50/50 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testPercent() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------] 4%'), + stream_get_contents($output->getStream()) + ); + } + + public function testOverwriteWithShorterLine() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->display(); + $bar->advance(); + + // set shorter format + $bar->setFormat(' %current%/%max% [%bar%]'); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------] '), + stream_get_contents($output->getStream()) + ); + } + + public function testStartWithMax() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('%current%/%max% [%bar%]'); + $bar->start(50); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------]'). + $this->generateOutput(' 1/50 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testSetCurrentProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->setProgress(15); + $bar->setProgress(25); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 15/50 [========>-------------------] 30%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'), + stream_get_contents($output->getStream()) + ); + } + + /** + */ + public function testSetCurrentBeforeStarting() + { + $bar = new ProgressBar($this->getOutputStream()); + $bar->setProgress(15); + $this->assertNotNull($bar->getStartTime()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can't regress the progress bar + */ + public function testRegressProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(15); + $bar->setProgress(10); + } + + public function testRedrawFrequency() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream(), 6)); + $bar->expects($this->exactly(4))->method('display'); + + $bar->setRedrawFrequency(2); + $bar->start(); + $bar->setProgress(1); + $bar->advance(2); + $bar->advance(2); + $bar->advance(1); + } + + public function testRedrawFrequencyIsAtLeastOneIfZeroGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0); + $bar->start(); + $bar->advance(); + } + + public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0.9); + $bar->start(); + $bar->advance(); + } + + public function testMultiByteSupport() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->setBarCharacter('■'); + $bar->advance(3); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 3 [■■■>------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'). + $this->generateOutput(' '), + stream_get_contents($output->getStream()) + ); + } + + public function testPercentNotHundredBeforeComplete() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 200); + $bar->start(); + $bar->display(); + $bar->advance(199); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/200 [>---------------------------] 0%'). + $this->generateOutput(' 0/200 [>---------------------------] 0%'). + $this->generateOutput(' 199/200 [===========================>] 99%'). + $this->generateOutput(' 200/200 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 200); + $bar->start(); + + for ($i = 0; $i < 200; ++$i) { + $bar->advance(); + } + + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/200 [>---------------------------] 0%'.PHP_EOL. + ' 20/200 [==>-------------------------] 10%'.PHP_EOL. + ' 40/200 [=====>----------------------] 20%'.PHP_EOL. + ' 60/200 [========>-------------------] 30%'.PHP_EOL. + ' 80/200 [===========>----------------] 40%'.PHP_EOL. + ' 100/200 [==============>-------------] 50%'.PHP_EOL. + ' 120/200 [================>-----------] 60%'.PHP_EOL. + ' 140/200 [===================>--------] 70%'.PHP_EOL. + ' 160/200 [======================>-----] 80%'.PHP_EOL. + ' 180/200 [=========================>--] 90%'.PHP_EOL. + ' 200/200 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + $bar->setProgress(50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%'.PHP_EOL. + ' 25/50 [==============>-------------] 50%'.PHP_EOL. + ' 50/50 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithoutMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(false)); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'.PHP_EOL. + ' 1 [->--------------------------]', + stream_get_contents($output->getStream()) + ); + } + + public function testParallelBars() + { + $output = $this->getOutputStream(); + $bar1 = new ProgressBar($output, 2); + $bar2 = new ProgressBar($output, 3); + $bar2->setProgressCharacter('#'); + $bar3 = new ProgressBar($output); + + $bar1->start(); + $output->write("\n"); + $bar2->start(); + $output->write("\n"); + $bar3->start(); + + for ($i = 1; $i <= 3; ++$i) { + // up two lines + $output->write("\033[2A"); + if ($i <= 2) { + $bar1->advance(); + } + $output->write("\n"); + $bar2->advance(); + $output->write("\n"); + $bar3->advance(); + } + $output->write("\033[2A"); + $output->write("\n"); + $output->write("\n"); + $bar3->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/2 [>---------------------------] 0%')."\n". + $this->generateOutput(' 0/3 [#---------------------------] 0%')."\n". + rtrim($this->generateOutput(' 0 [>---------------------------]')). + + "\033[2A". + $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n". + $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n". + rtrim($this->generateOutput(' 1 [->--------------------------]')). + + "\033[2A". + $this->generateOutput(' 2/2 [============================] 100%')."\n". + $this->generateOutput(' 2/3 [==================#---------] 66%')."\n". + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + + "\033[2A". + "\n". + $this->generateOutput(' 3/3 [============================] 100%')."\n". + rtrim($this->generateOutput(' 3 [--->------------------------]')). + + "\033[2A". + "\n". + "\n". + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testWithoutMax() + { + $output = $this->getOutputStream(); + + $bar = new ProgressBar($output); + $bar->start(); + $bar->advance(); + $bar->advance(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + rtrim($this->generateOutput(' 0 [>---------------------------]')). + rtrim($this->generateOutput(' 1 [->--------------------------]')). + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + rtrim($this->generateOutput(' 3 [--->------------------------]')). + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testAddingPlaceholderFormatter() + { + ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) { + return $bar->getMaxSteps() - $bar->getProgress(); + }); + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat(' %remaining_steps% [%bar%]'); + + $bar->start(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 3 [>---------------------------]'). + $this->generateOutput(' 2 [=========>------------------]'). + $this->generateOutput(' 0 [============================]'), + stream_get_contents($output->getStream()) + ); + } + + public function testMultilineFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat("%bar%\nfoobar"); + + $bar->start(); + $bar->advance(); + $bar->clear(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(">---------------------------\nfoobar"). + $this->generateOutput("=========>------------------\nfoobar "). + $this->generateOutput(" \n "). + $this->generateOutput("============================\nfoobar "), + stream_get_contents($output->getStream()) + ); + } + + public function testAnsiColorsAndEmojis() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 15); + ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) { + static $i = 0; + $mem = 100000 * $i; + $colors = $i++ ? '41;37' : '44;37'; + + return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m"; + }); + $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%"); + $bar->setBarCharacter($done = "\033[32m●\033[0m"); + $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m"); + $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m"); + + $bar->setMessage('Starting the demo... fingers crossed', 'title'); + $bar->start(); + $bar->setMessage('Looks good to me...', 'title'); + $bar->advance(4); + $bar->setMessage('Thanks, bye', 'title'); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput( + " \033[44;37m Starting the demo... fingers crossed \033[0m\n". + ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n". + " \xf0\x9f\x8f\x81 1 sec \033[44;37m 0 B \033[0m" + ). + $this->generateOutput( + " \033[44;37m Looks good to me... \033[0m\n". + ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n". + " \xf0\x9f\x8f\x81 1 sec \033[41;37m 97 KiB \033[0m" + ). + $this->generateOutput( + " \033[44;37m Thanks, bye \033[0m\n". + ' 15/15 '.str_repeat($done, 28)." 100%\n". + " \xf0\x9f\x8f\x81 1 sec \033[41;37m 195 KiB \033[0m" + ), + stream_get_contents($output->getStream()) + ); + } + + public function testSetFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/10 [>---------------------------] 0%'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @dataProvider provideFormat + */ + public function testFormatsWithoutMax($format) + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat($format); + $bar->start(); + + rewind($output->getStream()); + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected; + } +} diff --git a/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php b/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php new file mode 100644 index 0000000..6b83081 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php @@ -0,0 +1,179 @@ +getOutputStream()); + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->setMessage('Advancing...'); + $bar->advance(); + $bar->finish('Done...'); + $bar->start('Starting Again...'); + usleep(101000); + $bar->advance(); + $bar->finish('Done Again...'); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' | Starting...'). + $this->generateOutput(' / Starting...'). + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' \\ Advancing...'). + $this->generateOutput(' | Advancing...'). + $this->generateOutput(' | Done... '). + PHP_EOL. + $this->generateOutput(' - Starting Again...'). + $this->generateOutput(' \\ Starting Again...'). + $this->generateOutput(' \\ Done Again... '). + PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(false)); + + $bar->start('Starting...'); + $bar->advance(); + $bar->advance(); + $bar->setMessage('Midway...'); + $bar->advance(); + $bar->advance(); + $bar->finish('Done...'); + + rewind($output->getStream()); + + $this->assertEquals( + ' Starting...'.PHP_EOL. + ' Midway... '.PHP_EOL. + ' Done... '.PHP_EOL.PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testCustomIndicatorValues() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, array('a', 'b', 'c')); + + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' a Starting...'). + $this->generateOutput(' b Starting...'). + $this->generateOutput(' c Starting...'). + $this->generateOutput(' a Starting...'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Must have at least 2 indicator value characters. + */ + public function testCannotSetInvalidIndicatorCharacters() + { + $bar = new ProgressIndicator($this->getOutputStream(), null, 100, array('1')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator already started. + */ + public function testCannotStartAlreadyStartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->start('Starting...'); + $bar->start('Starting Again.'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotAdvanceUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->advance(); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotFinishUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->finish('Finished'); + } + + /** + * @dataProvider provideFormat + */ + public function testFormats($format) + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), $format); + $bar->start('Starting...'); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected; + } +} diff --git a/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php new file mode 100644 index 0000000..f062100 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php @@ -0,0 +1,415 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * @group tty + */ +class QuestionHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testAskChoice() + { + $questionHelper = new QuestionHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $questionHelper->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); + $question->setMaxAttempts(1); + // first answer is an empty answer, we're supposed to receive the default value + $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setMaxAttempts(1); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setErrorMessage('Input "%s" is not a superhero!'); + $question->setMaxAttempts(2); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $stream = stream_get_contents($output->getStream()); + $this->assertContains('Input "Fabien" is not a superhero!', $stream); + + try { + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); + $question->setMaxAttempts(1); + $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAsk() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("\n8AM\n")); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); + } + + public function testAskWithAutocomplete() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // Acm + // AcsTest + // + // + // Test + // + // S + // F00oo + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($inputStream); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new Question('Please select a bundle', 'FrameworkBundle'); + $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskHiddenResponse() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $question = new Question('What time is it?'); + $question->setHidden(true); + + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + /** + * @dataProvider getAskConfirmationData + */ + public function testAskConfirmation($question, $expected, $default = true) + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream($question."\n")); + $question = new ConfirmationQuestion('Do you like French fries?', $default); + $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); + } + + public function getAskConfirmationData() + { + return array( + array('', true), + array('', false, false), + array('y', true), + array('yes', true), + array('n', false), + array('no', false), + ); + } + + public function testAskConfirmationWithCustomTrueAnswer() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("j\ny\n")); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskAndValidate() + { + $dialog = new QuestionHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $error = 'This is not a color!'; + $validator = function ($color) use ($error) { + if (!in_array($color, array('white', 'black'))) { + throw new \InvalidArgumentException($error); + } + + return $color; + }; + + $question = new Question('What color was the white horse of Henry IV?', 'white'); + $question->setValidator($validator); + $question->setMaxAttempts(2); + + $dialog->setInputStream($this->getInputStream("\nblack\n")); + $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); + try { + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($error, $e->getMessage()); + } + } + + /** + * @dataProvider simpleAnswerProvider + */ + public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'My environment 1', + 'My environment 2', + 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function simpleAnswerProvider() + { + return array( + array(0, 'My environment 1'), + array(1, 'My environment 2'), + array(2, 'My environment 3'), + array('My environment 1', 'My environment 1'), + array('My environment 2', 'My environment 2'), + array('My environment 3', 'My environment 3'), + ); + } + + /** + * @dataProvider mixedKeysChoiceListAnswerProvider + */ + public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) + { + $possibleChoices = array( + '0' => 'No environment', + '1' => 'My environment 1', + 'env_2' => 'My environment 2', + 3 => 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function mixedKeysChoiceListAnswerProvider() + { + return array( + array('0', '0'), + array('No environment', '0'), + array('1', '1'), + array('env_2', 'env_2'), + array(3, '3'), + array('My environment 1', '1'), + ); + } + + /** + * @dataProvider answerProvider + */ + public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'env_1' => 'My environment 1', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3. + */ + public function testAmbiguousChoiceFromChoicelist() + { + $possibleChoices = array( + 'env_1' => 'My first environment', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("My environment\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + } + + public function answerProvider() + { + return array( + array('env_1', 'env_1'), + array('env_2', 'env_2'), + array('env_3', 'env_3'), + array('My environment 1', 'env_1'), + ); + } + + public function testNoInteraction() + { + $dialog = new QuestionHelper(); + $question = new Question('Do you have a job?', 'not yet'); + $this->assertEquals('not yet', $dialog->ask($this->createInputInterfaceMock(false), $this->createOutputInterface(), $question)); + } + + /** + * @requires function mb_strwidth + */ + public function testChoiceOutputFormattingQuestionForUtf8Keys() + { + $question = 'Lorem ipsum?'; + $possibleChoices = array( + 'foo' => 'foo', + 'żółw' => 'bar', + 'łabądź' => 'baz', + ); + $outputShown = array( + $question, + ' [foo ] foo', + ' [żółw ] bar', + ' [łabądź] baz', + ); + $output = $this->getMock('\Symfony\Component\Console\Output\OutputInterface'); + $output->method('getFormatter')->willReturn(new OutputFormatter()); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown)); + + $question = new ChoiceQuestion($question, $possibleChoices, 'foo'); + $dialog->ask($this->createInputInterfaceMock(), $output, $question); + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fwrite($stream, $input); + rewind($stream); + + return $stream; + } + + protected function createOutputInterface() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + protected function createInputInterfaceMock($interactive = true) + { + $mock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $mock->expects($this->any()) + ->method('isInteractive') + ->will($this->returnValue($interactive)); + + return $mock; + } + + private function hasSttyAvailable() + { + exec('stty 2>&1', $output, $exitcode); + + return $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Tests/Helper/TableStyleTest.php b/vendor/symfony/console/Tests/Helper/TableStyleTest.php new file mode 100644 index 0000000..587d841 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/TableStyleTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\TableStyle; + +class TableStyleTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH). + */ + public function testSetPadTypeWithInvalidType() + { + $style = new TableStyle(); + $style->setPadType('TEST'); + } +} diff --git a/vendor/symfony/console/Tests/Helper/TableTest.php b/vendor/symfony/console/Tests/Helper/TableTest.php new file mode 100644 index 0000000..d917d69 --- /dev/null +++ b/vendor/symfony/console/Tests/Helper/TableTest.php @@ -0,0 +1,639 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableStyle; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Output\StreamOutput; + +class TableTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'r+'); + } + + protected function tearDown() + { + fclose($this->stream); + $this->stream = null; + } + + /** + * @dataProvider testRenderProvider + */ + public function testRender($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRows($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->addRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setStyle($style) + ; + foreach ($rows as $row) { + $table->addRow($row); + } + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderProvider() + { + $books = array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ); + + return array( + array( + array('ISBN', 'Title', 'Author'), + $books, + 'default', +<< array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-700', 'Divine Com', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + 'default', +<<
99921-58-10-700 | Divine Com | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++----------------------------------+----------------------+-----------------+ + +TABLE + ), + 'Cell with colspan' => array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + new TableSeparator(), + array(new TableCell('Divine Comedy(Dante Alighieri)', array('colspan' => 3))), + new TableSeparator(), + array( + new TableCell('Arduino: A Quick-Start Guide', array('colspan' => 2)), + 'Mark Schmidt', + ), + new TableSeparator(), + array( + '9971-5-0210-0', + new TableCell("A Tale of \nTwo Cities", array('colspan' => 2)), + ), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3)), + 'Divine Comedy', + 'Dante Alighieri', + ), + array('A Tale of Two Cities', 'Charles Dickens'), + array("The Lord of \nthe Rings", "J. R. \nR. Tolkien"), + new TableSeparator(), + array('80-902734-1-6', new TableCell("And Then \nThere \nWere None", array('rowspan' => 3)), 'Agatha Christie'), + array('80-902734-1-7', 'Test'), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 2)), + ), + array('J. R. R. Tolkien'), + array('J. R. R'), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + new TableSeparator(), + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + new TableCell("Dante \nAlighieri", array('rowspan' => 2, 'colspan' => 1)), + ), + ), + 'default', +<<
array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + ), + 'default', +<<
array( + array('ISBN', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)), + 'Dante Alighieri', + ), + array(new TableSeparator()), + array('Charles Dickens'), + ), + 'default', +<<
array( + array( + array(new TableCell('Main title', array('colspan' => 3))), + array('ISBN', 'Title', 'Author'), + ), + array(), + 'default', +<<
array( + array(), + array( + array( + new TableCell('1', array('colspan' => 3)), + new TableCell('2', array('colspan' => 2)), + new TableCell('3', array('colspan' => 2)), + new TableCell('4', array('colspan' => 2)), + ), + ), + 'default', +<<
getOutputStream()); + $table + ->setHeaders(array('■■')) + ->setRows(array(array(1234))) + ->setStyle('default') + ; + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testStyle() + { + $style = new TableStyle(); + $style + ->setHorizontalBorderChar('.') + ->setVerticalBorderChar('.') + ->setCrossingChar('.') + ; + + Table::setStyleDefinition('dotfull', $style); + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array(array('Bar'))) + ->setStyle('dotfull'); + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRowSeparator() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array( + array('Bar1'), + new TableSeparator(), + array('Bar2'), + new TableSeparator(), + array('Bar3'), + )); + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + + $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works'); + } + + public function testRenderMultiCalls() + { + $table = new Table($output = $this->getOutputStream()); + $table->setRows(array( + array(new TableCell('foo', array('colspan' => 2))), + )); + $table->render(); + $table->render(); + $table->render(); + + $expected = +<<
assertEquals($expected, $this->getOutputContent($output)); + } + + public function testColumnStyle() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), + )); + + $style = new TableStyle(); + $style->setPadType(STR_PAD_LEFT); + $table->setColumnStyle(3, $style); + + $table->render(); + + $expected = + <<
assertEquals($expected, $this->getOutputContent($output)); + } + + protected function getOutputStream() + { + return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false); + } + + protected function getOutputContent(StreamOutput $output) + { + rewind($output->getStream()); + + return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream())); + } +} diff --git a/vendor/symfony/console/Tests/Input/ArgvInputTest.php b/vendor/symfony/console/Tests/Input/ArgvInputTest.php new file mode 100644 index 0000000..dd217ed --- /dev/null +++ b/vendor/symfony/console/Tests/Input/ArgvInputTest.php @@ -0,0 +1,336 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArgvInputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $_SERVER['argv'] = array('cli.php', 'foo'); + $input = new ArgvInput(); + $r = new \ReflectionObject($input); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + + $this->assertEquals(array('foo'), $p->getValue($input), '__construct() automatically get its input from the argv server variable'); + } + + public function testParseArguments() + { + $input = new ArgvInput(array('cli.php', 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() is stateless'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArgvInput($input); + $input->bind(new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('cli.php', '--foo'), + array(new InputOption('foo')), + array('foo' => true), + '->parse() parses long options without a value', + ), + array( + array('cli.php', '--foo=bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a = separator)', + ), + array( + array('cli.php', '--foo', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f'), + array(new InputOption('foo', 'f')), + array('foo' => true), + '->parse() parses short options without a value', + ), + array( + array('cli.php', '-fbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with no separator)', + ), + array( + array('cli.php', '-f', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f', ''), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value', + ), + array( + array('cli.php', '-f', '', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value followed by an argument', + ), + array( + array('cli.php', '-f', '', '-b'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => '', 'bar' => true), + '->parse() parses short options with an optional empty value followed by an option', + ), + array( + array('cli.php', '-f', '-b', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => null, 'bar' => true), + '->parse() parses short options with an optional value which is not present', + ), + array( + array('cli.php', '-fb'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b')), + array('foo' => true, 'bar' => true), + '->parse() parses short options when they are aggregated as a single one', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has a required value', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => 'bbar', 'bar' => null), + '->parse() parses short options when they are aggregated as a single one and one of them takes a value', + ), + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testInvalidInput($argv, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('RuntimeException', $expectedExceptionMessage); + + $input = new ArgvInput($argv); + $input->bind($definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('cli.php', '--foo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-ffoo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "-o" option does not exist.', + ), + array( + array('cli.php', '--foo=bar'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "--foo" option does not accept a value.', + ), + array( + array('cli.php', 'foo', 'bar'), + new InputDefinition(), + 'Too many arguments.', + ), + array( + array('cli.php', '--foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(), + 'The "-f" option does not exist.', + ), + array( + array('cli.php', '-1'), + new InputDefinition(array(new InputArgument('number'))), + 'The "-1" option does not exist.', + ), + ); + } + + public function testParseArrayArgument() + { + $input = new ArgvInput(array('cli.php', 'foo', 'bar', 'baz', 'bat')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz', 'bat')), $input->getArguments(), '->parse() parses array arguments'); + } + + public function testParseArrayOption() + { + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', 'baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertSame(array('name' => array('foo', 'bar', null)), $input->getOptions(), '->parse() parses empty array options as null ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', '--anotherOption')); + $input->bind(new InputDefinition(array( + new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('anotherOption', null, InputOption::VALUE_NONE), + ))); + $this->assertSame(array('name' => array('foo', 'bar', null), 'anotherOption' => true), $input->getOptions(), '->parse() parses empty array options as null ("--option value" syntax)'); + } + + public function testParseNegativeNumberAfterDoubleDash() + { + $input = new ArgvInput(array('cli.php', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number')))); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + + $input = new ArgvInput(array('cli.php', '-f', 'bar', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence'); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + } + + public function testParseEmptyStringArgument() + { + $input = new ArgvInput(array('cli.php', '-f', 'bar', '')); + $input->bind(new InputDefinition(array(new InputArgument('empty'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + + $this->assertEquals(array('empty' => ''), $input->getArguments(), '->parse() parses empty string arguments'); + } + + public function testGetFirstArgument() + { + $input = new ArgvInput(array('cli.php', '-fbbar')); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null when there is no arguments'); + + $input = new ArgvInput(array('cli.php', '-fbbar', 'foo')); + $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + } + + public function testHasParameterOption() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', 'foo')); + $this->assertFalse($input->hasParameterOption('--foo'), '->hasParameterOption() returns false if the given short option is not in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo=bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given option with provided value is in the raw input'); + } + + public function testHasParameterOptionOnlyOptions() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertTrue($input->hasParameterOption('-f', true), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo', '--', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo', true), '->hasParameterOption() returns true if the given long option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo=bar', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo', true), '->hasParameterOption() returns true if the given long option with provided value is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--', '--foo')); + $this->assertFalse($input->hasParameterOption('--foo', true), '->hasParameterOption() returns false if the given option is in the raw input but after an end of options signal'); + } + + public function testToString() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertEquals('-f foo', (string) $input); + + $input = new ArgvInput(array('cli.php', '-f', '--bar=foo', 'a b c d', "A\nB'C")); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } + + /** + * @dataProvider provideGetParameterOptionValues + */ + public function testGetParameterOptionEqualSign($argv, $key, $onlyParams, $expected) + { + $input = new ArgvInput($argv); + $this->assertEquals($expected, $input->getParameterOption($key, false, $onlyParams), '->getParameterOption() returns the expected value'); + } + + public function provideGetParameterOptionValues() + { + return array( + array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), '--env', false, 'dev'), + array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), false, '1'), + array(array('app/console', 'foo:bar', '--env=dev', '', '--en=1'), array('--en'), false, '1'), + array(array('app/console', 'foo:bar', '--env', 'val'), '--env', false, 'val'), + array(array('app/console', 'foo:bar', '--env', 'val', '--dummy'), '--env', false, 'val'), + array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', false, 'dev'), + array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', true, false), + ); + } + + public function testParseSingleDashAsArgument() + { + $input = new ArgvInput(array('cli.php', '-')); + $input->bind(new InputDefinition(array(new InputArgument('file')))); + $this->assertEquals(array('file' => '-'), $input->getArguments(), '->parse() parses single dash as an argument'); + } +} diff --git a/vendor/symfony/console/Tests/Input/ArrayInputTest.php b/vendor/symfony/console/Tests/Input/ArrayInputTest.php new file mode 100644 index 0000000..afb9913 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/ArrayInputTest.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArrayInputTest extends \PHPUnit_Framework_TestCase +{ + public function testGetFirstArgument() + { + $input = new ArrayInput(array()); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null if no argument were passed'); + $input = new ArrayInput(array('name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + $input = new ArrayInput(array('--foo' => 'bar', 'name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + } + + public function testHasParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + $this->assertFalse($input->hasParameterOption('--bar'), '->hasParameterOption() returns false if an option is not present in the passed parameters'); + + $input = new ArrayInput(array('--foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + + $input = new ArrayInput(array('--foo', '--', '--bar')); + $this->assertTrue($input->hasParameterOption('--bar'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + $this->assertFalse($input->hasParameterOption('--bar', true), '->hasParameterOption() returns false if an option is present in the passed parameters after an end of options signal'); + } + + public function testGetParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + $this->assertFalse($input->getParameterOption('--bar'), '->getParameterOption() returns the default if an option is not present in the passed parameters'); + + $input = new ArrayInput(array('Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + + $input = new ArrayInput(array('--foo', '--', '--bar' => 'woop')); + $this->assertEquals('woop', $input->getParameterOption('--bar'), '->getParameterOption() returns the correct value if an option is present in the passed parameters'); + $this->assertFalse($input->getParameterOption('--bar', false, true), '->getParameterOption() returns false if an option is present in the passed parameters after an end of options signal'); + } + + public function testParseArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArrayInput($input, new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('--foo' => 'bar'), + array(new InputOption('foo')), + array('foo' => 'bar'), + '->parse() parses long options', + ), + array( + array('--foo' => 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'bar'), + '->parse() parses long options with a default value', + ), + array( + array('--foo' => null), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'default'), + '->parse() parses long options with a default value', + ), + array( + array('-f' => 'bar'), + array(new InputOption('foo', 'f')), + array('foo' => 'bar'), + '->parse() parses short options', + ), + array( + array('--' => null, '-f' => 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'default'), + '->parse() does not parse opts after an end of options signal', + ), + array( + array('--' => null), + array(), + array(), + '->parse() does not choke on end of options signal', + ), + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testParseInvalidInput($parameters, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage); + + new ArrayInput($parameters, $definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('foo' => 'foo'), + new InputDefinition(array(new InputArgument('name'))), + 'The "foo" argument does not exist.', + ), + array( + array('--foo' => null), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('--foo' => 'foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('-o' => 'foo'), + new InputDefinition(), + 'The "-o" option does not exist.', + ), + ); + } + + public function testToString() + { + $input = new ArrayInput(array('-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C")); + $this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputArgumentTest.php b/vendor/symfony/console/Tests/Input/InputArgumentTest.php new file mode 100644 index 0000000..cfb37cd --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputArgumentTest.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputArgument; + +class InputArgumentTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $argument = new InputArgument('foo'); + $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); + } + + public function testModes() + { + $argument = new InputArgument('foo'); + $this->assertFalse($argument->isRequired(), '__construct() gives a "InputArgument::OPTIONAL" mode by default'); + + $argument = new InputArgument('foo', null); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $this->assertTrue($argument->isRequired(), '__construct() can take "InputArgument::REQUIRED" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Argument mode "%s" is not valid.', $mode)); + + new InputArgument('foo', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + public function testIsArray() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isArray(), '->isArray() returns false if the argument can not be an array'); + } + + public function testGetDescription() + { + $argument = new InputArgument('foo', null, 'Some description'); + $this->assertEquals('Some description', $argument->getDescription(), '->getDescription() return the message description'); + } + + public function testGetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $this->assertEquals('default', $argument->getDefault(), '->getDefault() return the default value'); + } + + public function testSetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $argument->setDefault(null); + $this->assertNull($argument->getDefault(), '->setDefault() can reset the default value by passing null'); + $argument->setDefault('another'); + $this->assertEquals('another', $argument->getDefault(), '->setDefault() changes the default value'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $argument->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $argument->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value except for InputArgument::OPTIONAL mode. + */ + public function testSetDefaultWithRequiredArgument() + { + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $argument->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array argument must be an array. + */ + public function testSetDefaultWithArrayArgument() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $argument->setDefault('default'); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputDefinitionTest.php b/vendor/symfony/console/Tests/Input/InputDefinitionTest.php new file mode 100644 index 0000000..2b28014 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputDefinitionTest.php @@ -0,0 +1,402 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputDefinitionTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixtures; + + protected $foo, $bar, $foo1, $foo2; + + public static function setUpBeforeClass() + { + self::$fixtures = __DIR__.'/../Fixtures/'; + } + + public function testConstructorArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getArguments(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument'); + } + + public function testConstructorOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getOptions(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument'); + } + + public function testSetArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->setArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->setArguments() sets the array of InputArgument objects'); + $definition->setArguments(array($this->bar)); + + $this->assertEquals(array('bar' => $this->bar), $definition->getArguments(), '->setArguments() clears all InputArgument objects'); + } + + public function testAddArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArguments() adds an array of InputArgument objects'); + $definition->addArguments(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects'); + } + + public function testAddArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + $definition->addArgument($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An argument with name "foo" already exists. + */ + public function testArgumentsMustHaveDifferentNames() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo1); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add an argument after an array argument. + */ + public function testArrayArgumentHasToBeLast() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument(new InputArgument('fooarray', InputArgument::IS_ARRAY)); + $definition->addArgument(new InputArgument('anotherbar')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add a required argument after an optional one. + */ + public function testRequiredArgumentCannotFollowAnOptionalOne() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo2); + } + + public function testGetArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "bar" argument does not exist. + */ + public function testGetInvalidArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $definition->getArgument('bar'); + } + + public function testHasArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + + $this->assertTrue($definition->hasArgument('foo'), '->hasArgument() returns true if a InputArgument exists for the given name'); + $this->assertFalse($definition->hasArgument('bar'), '->hasArgument() returns false if a InputArgument exists for the given name'); + } + + public function testGetArgumentRequiredCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + } + + public function testGetArgumentCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + } + + public function testGetArgumentDefaults() + { + $definition = new InputDefinition(array( + new InputArgument('foo1', InputArgument::OPTIONAL), + new InputArgument('foo2', InputArgument::OPTIONAL, '', 'default'), + new InputArgument('foo3', InputArgument::OPTIONAL | InputArgument::IS_ARRAY), + // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo1' => null, 'foo2' => 'default', 'foo3' => array()), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + + $definition = new InputDefinition(array( + new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo4' => array(1, 2)), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + } + + public function testSetOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->setOptions() sets the array of InputOption objects'); + $definition->setOptions(array($this->bar)); + $this->assertEquals(array('bar' => $this->bar), $definition->getOptions(), '->setOptions() clears all InputOption objects'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-f" option does not exist. + */ + public function testSetOptionsClearsOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->setOptions(array($this->bar)); + $definition->getOptionForShortcut('f'); + } + + public function testAddOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOptions() adds an array of InputOption objects'); + $definition->addOptions(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOptions() does not clear existing InputOption objects'); + } + + public function testAddOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOption() adds a InputOption object'); + $definition->addOption($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOption() adds a InputOption object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option named "foo" already exists. + */ + public function testAddDuplicateOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo2); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "f" already exists. + */ + public function testAddDuplicateShortcutOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo1); + } + + public function testGetOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testGetInvalidOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOption('bar'); + } + + public function testHasOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasOption('foo'), '->hasOption() returns true if a InputOption exists for the given name'); + $this->assertFalse($definition->hasOption('bar'), '->hasOption() returns false if a InputOption exists for the given name'); + } + + public function testHasShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasShortcut('f'), '->hasShortcut() returns true if a InputOption exists for the given shortcut'); + $this->assertFalse($definition->hasShortcut('b'), '->hasShortcut() returns false if a InputOption exists for the given shortcut'); + } + + public function testGetOptionForShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + public function testGetOptionForMultiShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->multi)); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('m'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('mmm'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-l" option does not exist. + */ + public function testGetOptionForInvalidShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOptionForShortcut('l'); + } + + public function testGetOptionDefaults() + { + $definition = new InputDefinition(array( + new InputOption('foo1', null, InputOption::VALUE_NONE), + new InputOption('foo2', null, InputOption::VALUE_REQUIRED), + new InputOption('foo3', null, InputOption::VALUE_REQUIRED, '', 'default'), + new InputOption('foo4', null, InputOption::VALUE_OPTIONAL), + new InputOption('foo5', null, InputOption::VALUE_OPTIONAL, '', 'default'), + new InputOption('foo6', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('foo7', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', array(1, 2)), + )); + $defaults = array( + 'foo1' => false, + 'foo2' => null, + 'foo3' => 'default', + 'foo4' => null, + 'foo5' => 'default', + 'foo6' => array(), + 'foo7' => array(1, 2), + ); + $this->assertSame($defaults, $definition->getOptionDefaults(), '->getOptionDefaults() returns the default values for all options'); + } + + /** + * @dataProvider getGetSynopsisData + */ + public function testGetSynopsis(InputDefinition $definition, $expectedSynopsis, $message = null) + { + $this->assertEquals($expectedSynopsis, $definition->getSynopsis(), $message ? '->getSynopsis() '.$message : ''); + } + + public function getGetSynopsisData() + { + return array( + array(new InputDefinition(array(new InputOption('foo'))), '[--foo]', 'puts optional options in square brackets'), + array(new InputDefinition(array(new InputOption('foo', 'f'))), '[-f|--foo]', 'separates shortcut with a pipe'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), '[-f|--foo FOO]', 'uses shortcut as value placeholder'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))), '[-f|--foo [FOO]]', 'puts optional values in square brackets'), + + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED))), '', 'puts arguments in angle brackets'), + array(new InputDefinition(array(new InputArgument('foo'))), '[]', 'puts optional arguments in square brackets'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::IS_ARRAY))), '[]...', 'uses an ellipsis for array arguments'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY))), ' ()...', 'uses parenthesis and ellipsis for required array arguments'), + + array(new InputDefinition(array(new InputOption('foo'), new InputArgument('foo', InputArgument::REQUIRED))), '[--foo] [--] ', 'puts [--] between options and arguments'), + ); + } + + public function testGetShortSynopsis() + { + $definition = new InputDefinition(array(new InputOption('foo'), new InputOption('bar'), new InputArgument('cat'))); + $this->assertEquals('[options] [--] []', $definition->getSynopsis(true), '->getSynopsis(true) groups options in [options]'); + } + + protected function initializeArguments() + { + $this->foo = new InputArgument('foo'); + $this->bar = new InputArgument('bar'); + $this->foo1 = new InputArgument('foo'); + $this->foo2 = new InputArgument('foo2', InputArgument::REQUIRED); + } + + protected function initializeOptions() + { + $this->foo = new InputOption('foo', 'f'); + $this->bar = new InputOption('bar', 'b'); + $this->foo1 = new InputOption('fooBis', 'f'); + $this->foo2 = new InputOption('foo', 'p'); + $this->multi = new InputOption('multi', 'm|mm|mmm'); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputOptionTest.php b/vendor/symfony/console/Tests/Input/InputOptionTest.php new file mode 100644 index 0000000..53ce1df --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputOptionTest.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputOption; + +class InputOptionTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $option = new InputOption('foo'); + $this->assertEquals('foo', $option->getName(), '__construct() takes a name as its first argument'); + $option = new InputOption('--foo'); + $this->assertEquals('foo', $option->getName(), '__construct() removes the leading -- of the option name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value. + */ + public function testArrayModeWithoutValue() + { + new InputOption('foo', 'f', InputOption::VALUE_IS_ARRAY); + } + + public function testShortcut() + { + $option = new InputOption('foo', 'f'); + $this->assertEquals('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument'); + $option = new InputOption('foo', '-f|-ff|fff'); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo', array('f', 'ff', '-fff')); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo'); + $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default'); + } + + public function testModes() + { + $option = new InputOption('foo', 'f'); + $this->assertFalse($option->acceptValue(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueRequired(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueOptional(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + + $option = new InputOption('foo', 'f', null); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_REQUIRED); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertTrue($option->isValueRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Option mode "%s" is not valid.', $mode)); + + new InputOption('foo', 'f', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEmptyNameIsInvalid() + { + new InputOption(''); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDoubleDashNameIsInvalid() + { + new InputOption('--'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSingleDashOptionIsInvalid() + { + new InputOption('foo', '-'); + } + + public function testIsArray() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertTrue($option->isArray(), '->isArray() returns true if the option can be an array'); + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->isArray(), '->isArray() returns false if the option can not be an array'); + } + + public function testGetDescription() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $this->assertEquals('Some description', $option->getDescription(), '->getDescription() returns the description message'); + } + + public function testGetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED); + $this->assertNull($option->getDefault(), '->getDefault() returns null if no default value is configured'); + + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertEquals(array(), $option->getDefault(), '->getDefault() returns an empty array if option is an array'); + + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value'); + } + + public function testSetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $option->setDefault(null); + $this->assertNull($option->getDefault(), '->setDefault() can reset the default value by passing null'); + $option->setDefault('another'); + $this->assertEquals('another', $option->getDefault(), '->setDefault() changes the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY); + $option->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $option->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value when using InputOption::VALUE_NONE mode. + */ + public function testDefaultValueWithValueNoneMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $option->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array option must be an array. + */ + public function testDefaultValueWithIsArrayMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $option->setDefault('default'); + } + + public function testEquals() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', null, 'Alternative description'); + $this->assertTrue($option->equals($option2)); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description', true); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('bar', 'f', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', '', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $this->assertFalse($option->equals($option2)); + } +} diff --git a/vendor/symfony/console/Tests/Input/InputTest.php b/vendor/symfony/console/Tests/Input/InputTest.php new file mode 100644 index 0000000..eb1c661 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/InputTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument'); + } + + public function testOptions() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name')))); + $this->assertEquals('foo', $input->getOption('name'), '->getOption() returns the value for the given option'); + + $input->setOption('name', 'bar'); + $this->assertEquals('bar', $input->getOption('name'), '->setOption() sets the value for a given option'); + $this->assertEquals(array('name' => 'bar'), $input->getOptions(), '->getOptions() returns all option values'); + + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getOptions(), '->getOptions() returns all option values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testSetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->setOption('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testGetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->getOption('foo'); + } + + public function testArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument'); + + $input->setArgument('name', 'bar'); + $this->assertEquals('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument'); + $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->getArguments() returns all argument values'); + + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getArguments(), '->getArguments() returns all argument values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testSetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->setArgument('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testGetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->getArgument('foo'); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingArguments() + { + $input = new ArrayInput(array()); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + $input->validate(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingRequiredArguments() + { + $input = new ArrayInput(array('bar' => 'baz')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED), new InputArgument('bar', InputArgument::OPTIONAL)))); + $input->validate(); + } + + public function testValidate() + { + $input = new ArrayInput(array('name' => 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + + $this->assertNull($input->validate()); + } + + public function testSetGetInteractive() + { + $input = new ArrayInput(array()); + $this->assertTrue($input->isInteractive(), '->isInteractive() returns whether the input should be interactive or not'); + $input->setInteractive(false); + $this->assertFalse($input->isInteractive(), '->setInteractive() changes the interactive flag'); + } +} diff --git a/vendor/symfony/console/Tests/Input/StringInputTest.php b/vendor/symfony/console/Tests/Input/StringInputTest.php new file mode 100644 index 0000000..7059cf0 --- /dev/null +++ b/vendor/symfony/console/Tests/Input/StringInputTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\StringInput; + +class StringInputTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTokenizeData + */ + public function testTokenize($input, $tokens, $message) + { + $input = new StringInput($input); + $r = new \ReflectionClass('Symfony\Component\Console\Input\ArgvInput'); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + $this->assertEquals($tokens, $p->getValue($input), $message); + } + + public function testInputOptionWithGivenString() + { + $definition = new InputDefinition( + array(new InputOption('foo', null, InputOption::VALUE_REQUIRED)) + ); + + // call to bind + $input = new StringInput('--foo=bar'); + $input->bind($definition); + $this->assertEquals('bar', $input->getOption('foo')); + } + + public function getTokenizeData() + { + return array( + array('', array(), '->tokenize() parses an empty string'), + array('foo', array('foo'), '->tokenize() parses arguments'), + array(' foo bar ', array('foo', 'bar'), '->tokenize() ignores whitespaces between arguments'), + array('"quoted"', array('quoted'), '->tokenize() parses quoted arguments'), + array("'quoted'", array('quoted'), '->tokenize() parses quoted arguments'), + array("'a\rb\nc\td'", array("a\rb\nc\td"), '->tokenize() parses whitespace chars in strings'), + array("'a'\r'b'\n'c'\t'd'", array('a', 'b', 'c', 'd'), '->tokenize() parses whitespace chars between args as spaces'), + array('\"quoted\"', array('"quoted"'), '->tokenize() parses escaped-quoted arguments'), + array("\'quoted\'", array('\'quoted\''), '->tokenize() parses escaped-quoted arguments'), + array('-a', array('-a'), '->tokenize() parses short options'), + array('-azc', array('-azc'), '->tokenize() parses aggregated short options'), + array('-awithavalue', array('-awithavalue'), '->tokenize() parses short options with a value'), + array('-a"foo bar"', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a"foo bar""foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'\'foo bar\'', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'"foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('--long-option', array('--long-option'), '->tokenize() parses long options'), + array('--long-option=foo', array('--long-option=foo'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar"', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar""another"', array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('--long-option=\'foo bar\'', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar''another'", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar'\"another\"", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('foo -a -ffoo --long bar', array('foo', '-a', '-ffoo', '--long', 'bar'), '->tokenize() parses when several arguments and options'), + ); + } + + public function testToString() + { + $input = new StringInput('-f foo'); + $this->assertEquals('-f foo', (string) $input); + + $input = new StringInput('-f --bar=foo "a b c d"'); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input); + + $input = new StringInput('-f --bar=foo \'a b c d\' '."'A\nB\\'C'"); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php new file mode 100644 index 0000000..c5eca2c --- /dev/null +++ b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Logger; + +use Psr\Log\Test\LoggerInterfaceTest; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Logger\ConsoleLogger; +use Symfony\Component\Console\Tests\Fixtures\DummyOutput; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console logger test. + * + * @author Kévin Dunglas + */ +class ConsoleLoggerTest extends LoggerInterfaceTest +{ + /** + * @var DummyOutput + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function getLogger() + { + $this->output = new DummyOutput(OutputInterface::VERBOSITY_VERBOSE); + + return new ConsoleLogger($this->output, array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, + LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL, + LogLevel::DEBUG => OutputInterface::VERBOSITY_NORMAL, + )); + } + + /** + * {@inheritdoc} + */ + public function getLogs() + { + return $this->output->getLogs(); + } +} diff --git a/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php b/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php new file mode 100644 index 0000000..1afbbb6 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\Output; + +class ConsoleOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertSame($output->getFormatter(), $output->getErrorOutput()->getFormatter(), '__construct() takes a formatter or null as the third argument'); + } +} diff --git a/vendor/symfony/console/Tests/Output/NullOutputTest.php b/vendor/symfony/console/Tests/Output/NullOutputTest.php new file mode 100644 index 0000000..b20ae4e --- /dev/null +++ b/vendor/symfony/console/Tests/Output/NullOutputTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; + +class NullOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new NullOutput(); + + ob_start(); + $output->write('foo'); + $buffer = ob_get_clean(); + + $this->assertSame('', $buffer, '->write() does nothing (at least nothing is printed)'); + $this->assertFalse($output->isDecorated(), '->isDecorated() returns false'); + } + + public function testVerbosity() + { + $output = new NullOutput(); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() returns VERBOSITY_QUIET for NullOutput by default'); + + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput'); + } +} diff --git a/vendor/symfony/console/Tests/Output/OutputTest.php b/vendor/symfony/console/Tests/Output/OutputTest.php new file mode 100644 index 0000000..45e6ddc --- /dev/null +++ b/vendor/symfony/console/Tests/Output/OutputTest.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new TestOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + public function testSetIsDecorated() + { + $output = new TestOutput(); + $output->setDecorated(true); + $this->assertTrue($output->isDecorated(), 'setDecorated() sets the decorated flag'); + } + + public function testSetGetVerbosity() + { + $output = new TestOutput(); + $output->setVerbosity(Output::VERBOSITY_QUIET); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '->setVerbosity() sets the verbosity'); + + $this->assertTrue($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_NORMAL); + $this->assertFalse($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_DEBUG); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertTrue($output->isDebug()); + } + + public function testWriteWithVerbosityQuiet() + { + $output = new TestOutput(Output::VERBOSITY_QUIET); + $output->writeln('foo'); + $this->assertEquals('', $output->output, '->writeln() outputs nothing if verbosity is set to VERBOSITY_QUIET'); + } + + public function testWriteAnArrayOfMessages() + { + $output = new TestOutput(); + $output->writeln(array('foo', 'bar')); + $this->assertEquals("foo\nbar\n", $output->output, '->writeln() can take an array of messages to output'); + } + + /** + * @dataProvider provideWriteArguments + */ + public function testWriteRawMessage($message, $type, $expectedOutput) + { + $output = new TestOutput(); + $output->writeln($message, $type); + $this->assertEquals($expectedOutput, $output->output); + } + + public function provideWriteArguments() + { + return array( + array('foo', Output::OUTPUT_RAW, "foo\n"), + array('foo', Output::OUTPUT_PLAIN, "foo\n"), + ); + } + + public function testWriteWithDecorationTurnedOff() + { + $output = new TestOutput(); + $output->setDecorated(false); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() strips decoration tags if decoration is set to false'); + } + + public function testWriteDecoratedMessage() + { + $fooStyle = new OutputFormatterStyle('yellow', 'red', array('blink')); + $output = new TestOutput(); + $output->getFormatter()->setStyle('FOO', $fooStyle); + $output->setDecorated(true); + $output->writeln('foo'); + $this->assertEquals("\033[33;41;5mfoo\033[39;49;25m\n", $output->output, '->writeln() decorates the output'); + } + + public function testWriteWithInvalidStyle() + { + $output = new TestOutput(); + + $output->clear(); + $output->write('foo'); + $this->assertEquals('foo', $output->output, '->write() do nothing when a style does not exist'); + + $output->clear(); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() do nothing when a style does not exist'); + } + + /** + * @dataProvider verbosityProvider + */ + public function testWriteWithVerbosityOption($verbosity, $expected, $msg) + { + $output = new TestOutput(); + + $output->setVerbosity($verbosity); + $output->clear(); + $output->write('1', false); + $output->write('2', false, Output::VERBOSITY_QUIET); + $output->write('3', false, Output::VERBOSITY_NORMAL); + $output->write('4', false, Output::VERBOSITY_VERBOSE); + $output->write('5', false, Output::VERBOSITY_VERY_VERBOSE); + $output->write('6', false, Output::VERBOSITY_DEBUG); + $this->assertEquals($expected, $output->output, $msg); + } + + public function verbosityProvider() + { + return array( + array(Output::VERBOSITY_QUIET, '2', '->write() in QUIET mode only outputs when an explicit QUIET verbosity is passed'), + array(Output::VERBOSITY_NORMAL, '123', '->write() in NORMAL mode outputs anything below an explicit VERBOSE verbosity'), + array(Output::VERBOSITY_VERBOSE, '1234', '->write() in VERBOSE mode outputs anything below an explicit VERY_VERBOSE verbosity'), + array(Output::VERBOSITY_VERY_VERBOSE, '12345', '->write() in VERY_VERBOSE mode outputs anything below an explicit DEBUG verbosity'), + array(Output::VERBOSITY_DEBUG, '123456', '->write() in DEBUG mode outputs everything'), + ); + } +} + +class TestOutput extends Output +{ + public $output = ''; + + public function clear() + { + $this->output = ''; + } + + protected function doWrite($message, $newline) + { + $this->output .= $message.($newline ? "\n" : ''); + } +} diff --git a/vendor/symfony/console/Tests/Output/StreamOutputTest.php b/vendor/symfony/console/Tests/Output/StreamOutputTest.php new file mode 100644 index 0000000..2fd4f61 --- /dev/null +++ b/vendor/symfony/console/Tests/Output/StreamOutputTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\StreamOutput; + +class StreamOutputTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'a', false); + } + + protected function tearDown() + { + $this->stream = null; + } + + public function testConstructor() + { + $output = new StreamOutput($this->stream, Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The StreamOutput class needs a stream as its first argument. + */ + public function testStreamIsRequired() + { + new StreamOutput('foo'); + } + + public function testGetStream() + { + $output = new StreamOutput($this->stream); + $this->assertEquals($this->stream, $output->getStream(), '->getStream() returns the current stream'); + } + + public function testDoWrite() + { + $output = new StreamOutput($this->stream); + $output->writeln('foo'); + rewind($output->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($output->getStream()), '->doWrite() writes to the stream'); + } +} diff --git a/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php b/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php new file mode 100644 index 0000000..6bf6412 --- /dev/null +++ b/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Style; + +use PHPUnit_Framework_TestCase; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Console\Tester\CommandTester; + +class SymfonyStyleTest extends PHPUnit_Framework_TestCase +{ + /** @var Command */ + protected $command; + /** @var CommandTester */ + protected $tester; + + protected function setUp() + { + $this->command = new Command('sfstyle'); + $this->tester = new CommandTester($this->command); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + /** + * @dataProvider inputCommandToOutputFilesProvider + */ + public function testOutputs($inputCommandFilepath, $outputFilepath) + { + $code = require $inputCommandFilepath; + $this->command->setCode($code); + $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); + $this->assertStringEqualsFile($outputFilepath, $this->tester->getDisplay(true)); + } + + public function inputCommandToOutputFilesProvider() + { + $baseDir = __DIR__.'/../Fixtures/Style/SymfonyStyle'; + + return array_map(null, glob($baseDir.'/command/command_*.php'), glob($baseDir.'/output/output_*.txt')); + } + + public function testLongWordsBlockWrapping() + { + $word = 'Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon'; + $wordLength = strlen($word); + $maxLineLength = SymfonyStyle::MAX_LINE_LENGTH - 3; + + $this->command->setCode(function (InputInterface $input, OutputInterface $output) use ($word) { + $sfStyle = new SymfonyStyleWithForcedLineLength($input, $output); + $sfStyle->block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false); + }); + + $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); + $expectedCount = (int) ceil($wordLength / ($maxLineLength)) + (int) ($wordLength > $maxLineLength - 5); + $this->assertSame($expectedCount, substr_count($this->tester->getDisplay(true), ' § ')); + } +} + +/** + * Use this class in tests to force the line length + * and ensure a consistent output for expectations. + */ +class SymfonyStyleWithForcedLineLength extends SymfonyStyle +{ + public function __construct(InputInterface $input, OutputInterface $output) + { + parent::__construct($input, $output); + + $ref = new \ReflectionProperty(get_parent_class($this), 'lineLength'); + $ref->setAccessible(true); + $ref->setValue($this, 120); + } +} diff --git a/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php b/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php new file mode 100644 index 0000000..a8389dd --- /dev/null +++ b/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\ApplicationTester; + +class ApplicationTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $application; + protected $tester; + + protected function setUp() + { + $this->application = new Application(); + $this->application->setAutoExit(false); + $this->application->register('foo') + ->addArgument('foo') + ->setCode(function ($input, $output) { $output->writeln('foo'); }) + ; + + $this->tester = new ApplicationTester($this->application); + $this->tester->run(array('command' => 'foo', 'foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->application = null; + $this->tester = null; + } + + public function testRun() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } +} diff --git a/vendor/symfony/console/Tests/Tester/CommandTesterTest.php b/vendor/symfony/console/Tests/Tester/CommandTesterTest.php new file mode 100644 index 0000000..b54c00e --- /dev/null +++ b/vendor/symfony/console/Tests/Tester/CommandTesterTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $command; + protected $tester; + + protected function setUp() + { + $this->command = new Command('foo'); + $this->command->addArgument('command'); + $this->command->addArgument('foo'); + $this->command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $this->tester = new CommandTester($this->command); + $this->tester->execute(array('foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + public function testExecute() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } + + public function testCommandFromApplication() + { + $application = new Application(); + $application->setAutoExit(false); + + $command = new Command('foo'); + $command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $application->add($command); + + $tester = new CommandTester($application->find('foo')); + + // check that there is no need to pass the command name here + $this->assertEquals(0, $tester->execute(array())); + } +} diff --git a/vendor/symfony/console/composer.json b/vendor/symfony/console/composer.json new file mode 100644 index 0000000..1a316e4 --- /dev/null +++ b/vendor/symfony/console/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Symfony Console Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "psr/log": "~1.0" + }, + "suggest": { + "symfony/event-dispatcher": "", + "symfony/process": "", + "psr/log": "For using the console logger" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Console\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/console/phpunit.xml.dist b/vendor/symfony/console/phpunit.xml.dist new file mode 100644 index 0000000..ae0dcbe --- /dev/null +++ b/vendor/symfony/console/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/css-selector/.gitignore b/vendor/symfony/css-selector/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/css-selector/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/css-selector/CHANGELOG.md b/vendor/symfony/css-selector/CHANGELOG.md new file mode 100644 index 0000000..4061ff2 --- /dev/null +++ b/vendor/symfony/css-selector/CHANGELOG.md @@ -0,0 +1,13 @@ +CHANGELOG +========= + +2.8.0 +----- + + * Added the `CssSelectorConverter` class as a non-static API for the component. + * Deprecated the `CssSelector` static API of the component. + +2.1.0 +----- + + * none diff --git a/vendor/symfony/css-selector/CssSelectorConverter.php b/vendor/symfony/css-selector/CssSelectorConverter.php new file mode 100644 index 0000000..e31ac19 --- /dev/null +++ b/vendor/symfony/css-selector/CssSelectorConverter.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector; + +use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser; +use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser; +use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser; +use Symfony\Component\CssSelector\Parser\Shortcut\HashParser; +use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension; +use Symfony\Component\CssSelector\XPath\Translator; + +/** + * CssSelectorConverter is the main entry point of the component and can convert CSS + * selectors to XPath expressions. + * + * @author Christophe Coevoet + */ +class CssSelectorConverter +{ + private $translator; + + /** + * @param bool $html Whether HTML support should be enabled. Disable it for XML documents. + */ + public function __construct($html = true) + { + $this->translator = new Translator(); + + if ($html) { + $this->translator->registerExtension(new HtmlExtension($this->translator)); + } + + $this->translator + ->registerParserShortcut(new EmptyStringParser()) + ->registerParserShortcut(new ElementParser()) + ->registerParserShortcut(new ClassParser()) + ->registerParserShortcut(new HashParser()) + ; + } + + /** + * Translates a CSS expression to its XPath equivalent. + * + * Optionally, a prefix can be added to the resulting XPath + * expression with the $prefix parameter. + * + * @param string $cssExpr The CSS expression. + * @param string $prefix An optional prefix for the XPath expression. + * + * @return string + */ + public function toXPath($cssExpr, $prefix = 'descendant-or-self::') + { + return $this->translator->cssToXPath($cssExpr, $prefix); + } +} diff --git a/vendor/symfony/css-selector/Exception/ExceptionInterface.php b/vendor/symfony/css-selector/Exception/ExceptionInterface.php new file mode 100644 index 0000000..e4c5ae1 --- /dev/null +++ b/vendor/symfony/css-selector/Exception/ExceptionInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +/** + * Interface for exceptions. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/css-selector/Exception/ExpressionErrorException.php b/vendor/symfony/css-selector/Exception/ExpressionErrorException.php new file mode 100644 index 0000000..fd5deea --- /dev/null +++ b/vendor/symfony/css-selector/Exception/ExpressionErrorException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +/** + * ParseException is thrown when a CSS selector syntax is not valid. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class ExpressionErrorException extends ParseException +{ +} diff --git a/vendor/symfony/css-selector/Exception/InternalErrorException.php b/vendor/symfony/css-selector/Exception/InternalErrorException.php new file mode 100644 index 0000000..e60e5ed --- /dev/null +++ b/vendor/symfony/css-selector/Exception/InternalErrorException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +/** + * ParseException is thrown when a CSS selector syntax is not valid. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class InternalErrorException extends ParseException +{ +} diff --git a/vendor/symfony/css-selector/Exception/ParseException.php b/vendor/symfony/css-selector/Exception/ParseException.php new file mode 100644 index 0000000..3b0b0ee --- /dev/null +++ b/vendor/symfony/css-selector/Exception/ParseException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +/** + * ParseException is thrown when a CSS selector syntax is not valid. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Fabien Potencier + */ +class ParseException extends \Exception implements ExceptionInterface +{ +} diff --git a/vendor/symfony/css-selector/Exception/SyntaxErrorException.php b/vendor/symfony/css-selector/Exception/SyntaxErrorException.php new file mode 100644 index 0000000..418bc30 --- /dev/null +++ b/vendor/symfony/css-selector/Exception/SyntaxErrorException.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +use Symfony\Component\CssSelector\Parser\Token; + +/** + * ParseException is thrown when a CSS selector syntax is not valid. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class SyntaxErrorException extends ParseException +{ + /** + * @param string $expectedValue + * @param Token $foundToken + * + * @return SyntaxErrorException + */ + public static function unexpectedToken($expectedValue, Token $foundToken) + { + return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken)); + } + + /** + * @param string $pseudoElement + * @param string $unexpectedLocation + * + * @return SyntaxErrorException + */ + public static function pseudoElementFound($pseudoElement, $unexpectedLocation) + { + return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation)); + } + + /** + * @param int $position + * + * @return SyntaxErrorException + */ + public static function unclosedString($position) + { + return new self(sprintf('Unclosed/invalid string at %s.', $position)); + } + + /** + * @return SyntaxErrorException + */ + public static function nestedNot() + { + return new self('Got nested ::not().'); + } + + /** + * @return SyntaxErrorException + */ + public static function stringAsFunctionArgument() + { + return new self('String not allowed as function argument.'); + } +} diff --git a/vendor/symfony/css-selector/LICENSE b/vendor/symfony/css-selector/LICENSE new file mode 100644 index 0000000..12a7453 --- /dev/null +++ b/vendor/symfony/css-selector/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/css-selector/Node/AbstractNode.php b/vendor/symfony/css-selector/Node/AbstractNode.php new file mode 100644 index 0000000..7477e91 --- /dev/null +++ b/vendor/symfony/css-selector/Node/AbstractNode.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Abstract base node class. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +abstract class AbstractNode implements NodeInterface +{ + /** + * @var string + */ + private $nodeName; + + /** + * @return string + */ + public function getNodeName() + { + if (null === $this->nodeName) { + $this->nodeName = preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', get_called_class()); + } + + return $this->nodeName; + } +} diff --git a/vendor/symfony/css-selector/Node/AttributeNode.php b/vendor/symfony/css-selector/Node/AttributeNode.php new file mode 100644 index 0000000..af872b7 --- /dev/null +++ b/vendor/symfony/css-selector/Node/AttributeNode.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "[| ]" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class AttributeNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $namespace; + + /** + * @var string + */ + private $attribute; + + /** + * @var string + */ + private $operator; + + /** + * @var string + */ + private $value; + + /** + * @param NodeInterface $selector + * @param string $namespace + * @param string $attribute + * @param string $operator + * @param string $value + */ + public function __construct(NodeInterface $selector, $namespace, $attribute, $operator, $value) + { + $this->selector = $selector; + $this->namespace = $namespace; + $this->attribute = $attribute; + $this->operator = $operator; + $this->value = $value; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * @return string + */ + public function getAttribute() + { + return $this->attribute; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->operator; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $attribute = $this->namespace ? $this->namespace.'|'.$this->attribute : $this->attribute; + + return 'exists' === $this->operator + ? sprintf('%s[%s[%s]]', $this->getNodeName(), $this->selector, $attribute) + : sprintf("%s[%s[%s %s '%s']]", $this->getNodeName(), $this->selector, $attribute, $this->operator, $this->value); + } +} diff --git a/vendor/symfony/css-selector/Node/ClassNode.php b/vendor/symfony/css-selector/Node/ClassNode.php new file mode 100644 index 0000000..f965e77 --- /dev/null +++ b/vendor/symfony/css-selector/Node/ClassNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "." node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class ClassNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $name; + + /** + * @param NodeInterface $selector + * @param string $name + */ + public function __construct(NodeInterface $selector, $name) + { + $this->selector = $selector; + $this->name = $name; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name); + } +} diff --git a/vendor/symfony/css-selector/Node/CombinedSelectorNode.php b/vendor/symfony/css-selector/Node/CombinedSelectorNode.php new file mode 100644 index 0000000..39f6599 --- /dev/null +++ b/vendor/symfony/css-selector/Node/CombinedSelectorNode.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a combined node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class CombinedSelectorNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $combinator; + + /** + * @var NodeInterface + */ + private $subSelector; + + /** + * @param NodeInterface $selector + * @param string $combinator + * @param NodeInterface $subSelector + */ + public function __construct(NodeInterface $selector, $combinator, NodeInterface $subSelector) + { + $this->selector = $selector; + $this->combinator = $combinator; + $this->subSelector = $subSelector; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getCombinator() + { + return $this->combinator; + } + + /** + * @return NodeInterface + */ + public function getSubSelector() + { + return $this->subSelector; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $combinator = ' ' === $this->combinator ? '' : $this->combinator; + + return sprintf('%s[%s %s %s]', $this->getNodeName(), $this->selector, $combinator, $this->subSelector); + } +} diff --git a/vendor/symfony/css-selector/Node/ElementNode.php b/vendor/symfony/css-selector/Node/ElementNode.php new file mode 100644 index 0000000..06e343e --- /dev/null +++ b/vendor/symfony/css-selector/Node/ElementNode.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "|" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class ElementNode extends AbstractNode +{ + /** + * @var string|null + */ + private $namespace; + + /** + * @var string|null + */ + private $element; + + /** + * @param string|null $namespace + * @param string|null $element + */ + public function __construct($namespace = null, $element = null) + { + $this->namespace = $namespace; + $this->element = $element; + } + + /** + * @return null|string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * @return null|string + */ + public function getElement() + { + return $this->element; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return new Specificity(0, 0, $this->element ? 1 : 0); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $element = $this->element ?: '*'; + + return sprintf('%s[%s]', $this->getNodeName(), $this->namespace ? $this->namespace.'|'.$element : $element); + } +} diff --git a/vendor/symfony/css-selector/Node/FunctionNode.php b/vendor/symfony/css-selector/Node/FunctionNode.php new file mode 100644 index 0000000..612f348 --- /dev/null +++ b/vendor/symfony/css-selector/Node/FunctionNode.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +use Symfony\Component\CssSelector\Parser\Token; + +/** + * Represents a ":()" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class FunctionNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $name; + + /** + * @var Token[] + */ + private $arguments; + + /** + * @param NodeInterface $selector + * @param string $name + * @param Token[] $arguments + */ + public function __construct(NodeInterface $selector, $name, array $arguments = array()) + { + $this->selector = $selector; + $this->name = strtolower($name); + $this->arguments = $arguments; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return Token[] + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $arguments = implode(', ', array_map(function (Token $token) { + return "'".$token->getValue()."'"; + }, $this->arguments)); + + return sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : ''); + } +} diff --git a/vendor/symfony/css-selector/Node/HashNode.php b/vendor/symfony/css-selector/Node/HashNode.php new file mode 100644 index 0000000..20db465 --- /dev/null +++ b/vendor/symfony/css-selector/Node/HashNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "#" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class HashNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $id; + + /** + * @param NodeInterface $selector + * @param string $id + */ + public function __construct(NodeInterface $selector, $id) + { + $this->selector = $selector; + $this->id = $id; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id); + } +} diff --git a/vendor/symfony/css-selector/Node/NegationNode.php b/vendor/symfony/css-selector/Node/NegationNode.php new file mode 100644 index 0000000..4b5aa22 --- /dev/null +++ b/vendor/symfony/css-selector/Node/NegationNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a ":not()" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class NegationNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var NodeInterface + */ + private $subSelector; + + /** + * @param NodeInterface $selector + * @param NodeInterface $subSelector + */ + public function __construct(NodeInterface $selector, NodeInterface $subSelector) + { + $this->selector = $selector; + $this->subSelector = $subSelector; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return NodeInterface + */ + public function getSubSelector() + { + return $this->subSelector; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector); + } +} diff --git a/vendor/symfony/css-selector/Node/NodeInterface.php b/vendor/symfony/css-selector/Node/NodeInterface.php new file mode 100644 index 0000000..d919e20 --- /dev/null +++ b/vendor/symfony/css-selector/Node/NodeInterface.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Interface for nodes. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface NodeInterface +{ + /** + * Returns node's name. + * + * @return string + */ + public function getNodeName(); + + /** + * Returns node's specificity. + * + * @return Specificity + */ + public function getSpecificity(); + + /** + * Returns node's string representation. + * + * @return string + */ + public function __toString(); +} diff --git a/vendor/symfony/css-selector/Node/PseudoNode.php b/vendor/symfony/css-selector/Node/PseudoNode.php new file mode 100644 index 0000000..c23ddd5 --- /dev/null +++ b/vendor/symfony/css-selector/Node/PseudoNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a ":" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class PseudoNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $identifier; + + /** + * @param NodeInterface $selector + * @param string $identifier + */ + public function __construct(NodeInterface $selector, $identifier) + { + $this->selector = $selector; + $this->identifier = strtolower($identifier); + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier); + } +} diff --git a/vendor/symfony/css-selector/Node/SelectorNode.php b/vendor/symfony/css-selector/Node/SelectorNode.php new file mode 100644 index 0000000..729e091 --- /dev/null +++ b/vendor/symfony/css-selector/Node/SelectorNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "(::|:)" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class SelectorNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $tree; + + /** + * @var null|string + */ + private $pseudoElement; + + /** + * @param NodeInterface $tree + * @param null|string $pseudoElement + */ + public function __construct(NodeInterface $tree, $pseudoElement = null) + { + $this->tree = $tree; + $this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null; + } + + /** + * @return NodeInterface + */ + public function getTree() + { + return $this->tree; + } + + /** + * @return null|string + */ + public function getPseudoElement() + { + return $this->pseudoElement; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : ''); + } +} diff --git a/vendor/symfony/css-selector/Node/Specificity.php b/vendor/symfony/css-selector/Node/Specificity.php new file mode 100644 index 0000000..a24b4fd --- /dev/null +++ b/vendor/symfony/css-selector/Node/Specificity.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a node specificity. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @see http://www.w3.org/TR/selectors/#specificity + * + * @author Jean-François Simon + * + * @internal + */ +class Specificity +{ + const A_FACTOR = 100; + const B_FACTOR = 10; + const C_FACTOR = 1; + + /** + * @var int + */ + private $a; + + /** + * @var int + */ + private $b; + + /** + * @var int + */ + private $c; + + /** + * Constructor. + * + * @param int $a + * @param int $b + * @param int $c + */ + public function __construct($a, $b, $c) + { + $this->a = $a; + $this->b = $b; + $this->c = $c; + } + + /** + * @param Specificity $specificity + * + * @return Specificity + */ + public function plus(Specificity $specificity) + { + return new self($this->a + $specificity->a, $this->b + $specificity->b, $this->c + $specificity->c); + } + + /** + * Returns global specificity value. + * + * @return int + */ + public function getValue() + { + return $this->a * self::A_FACTOR + $this->b * self::B_FACTOR + $this->c * self::C_FACTOR; + } + + /** + * Returns -1 if the object specificity is lower than the argument, + * 0 if they are equal, and 1 if the argument is lower. + * + * @param Specificity $specificity + * + * @return int + */ + public function compareTo(Specificity $specificity) + { + if ($this->a !== $specificity->a) { + return $this->a > $specificity->a ? 1 : -1; + } + + if ($this->b !== $specificity->b) { + return $this->b > $specificity->b ? 1 : -1; + } + + if ($this->c !== $specificity->c) { + return $this->c > $specificity->c ? 1 : -1; + } + + return 0; + } +} diff --git a/vendor/symfony/css-selector/Parser/Handler/CommentHandler.php b/vendor/symfony/css-selector/Parser/Handler/CommentHandler.php new file mode 100644 index 0000000..a29775c --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Handler/CommentHandler.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class CommentHandler implements HandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + if ('/*' !== $reader->getSubstring(2)) { + return false; + } + + $offset = $reader->getOffset('*/'); + + if (false === $offset) { + $reader->moveToEnd(); + } else { + $reader->moveForward($offset + 2); + } + + return true; + } +} diff --git a/vendor/symfony/css-selector/Parser/Handler/HandlerInterface.php b/vendor/symfony/css-selector/Parser/Handler/HandlerInterface.php new file mode 100644 index 0000000..a1297c8 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Handler/HandlerInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * CSS selector handler interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface HandlerInterface +{ + /** + * @param Reader $reader + * @param TokenStream $stream + * + * @return bool + */ + public function handle(Reader $reader, TokenStream $stream); +} diff --git a/vendor/symfony/css-selector/Parser/Handler/HashHandler.php b/vendor/symfony/css-selector/Parser/Handler/HashHandler.php new file mode 100644 index 0000000..f74bda5 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Handler/HashHandler.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class HashHandler implements HandlerInterface +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @var TokenizerEscaping + */ + private $escaping; + + /** + * @param TokenizerPatterns $patterns + * @param TokenizerEscaping $escaping + */ + public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) + { + $this->patterns = $patterns; + $this->escaping = $escaping; + } + + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $match = $reader->findPattern($this->patterns->getHashPattern()); + + if (!$match) { + return false; + } + + $value = $this->escaping->escapeUnicode($match[1]); + $stream->push(new Token(Token::TYPE_HASH, $value, $reader->getPosition())); + $reader->moveForward(strlen($match[0])); + + return true; + } +} diff --git a/vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.php b/vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.php new file mode 100644 index 0000000..358c7c1 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class IdentifierHandler implements HandlerInterface +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @var TokenizerEscaping + */ + private $escaping; + + /** + * @param TokenizerPatterns $patterns + * @param TokenizerEscaping $escaping + */ + public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) + { + $this->patterns = $patterns; + $this->escaping = $escaping; + } + + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $match = $reader->findPattern($this->patterns->getIdentifierPattern()); + + if (!$match) { + return false; + } + + $value = $this->escaping->escapeUnicode($match[0]); + $stream->push(new Token(Token::TYPE_IDENTIFIER, $value, $reader->getPosition())); + $reader->moveForward(strlen($match[0])); + + return true; + } +} diff --git a/vendor/symfony/css-selector/Parser/Handler/NumberHandler.php b/vendor/symfony/css-selector/Parser/Handler/NumberHandler.php new file mode 100644 index 0000000..4ea5c48 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Handler/NumberHandler.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class NumberHandler implements HandlerInterface +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @param TokenizerPatterns $patterns + */ + public function __construct(TokenizerPatterns $patterns) + { + $this->patterns = $patterns; + } + + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $match = $reader->findPattern($this->patterns->getNumberPattern()); + + if (!$match) { + return false; + } + + $stream->push(new Token(Token::TYPE_NUMBER, $match[0], $reader->getPosition())); + $reader->moveForward(strlen($match[0])); + + return true; + } +} diff --git a/vendor/symfony/css-selector/Parser/Handler/StringHandler.php b/vendor/symfony/css-selector/Parser/Handler/StringHandler.php new file mode 100644 index 0000000..4205296 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Handler/StringHandler.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Exception\InternalErrorException; +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class StringHandler implements HandlerInterface +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @var TokenizerEscaping + */ + private $escaping; + + /** + * @param TokenizerPatterns $patterns + * @param TokenizerEscaping $escaping + */ + public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) + { + $this->patterns = $patterns; + $this->escaping = $escaping; + } + + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $quote = $reader->getSubstring(1); + + if (!in_array($quote, array("'", '"'))) { + return false; + } + + $reader->moveForward(1); + $match = $reader->findPattern($this->patterns->getQuotedStringPattern($quote)); + + if (!$match) { + throw new InternalErrorException(sprintf('Should have found at least an empty match at %s.', $reader->getPosition())); + } + + // check unclosed strings + if (strlen($match[0]) === $reader->getRemainingLength()) { + throw SyntaxErrorException::unclosedString($reader->getPosition() - 1); + } + + // check quotes pairs validity + if ($quote !== $reader->getSubstring(1, strlen($match[0]))) { + throw SyntaxErrorException::unclosedString($reader->getPosition() - 1); + } + + $string = $this->escaping->escapeUnicodeAndNewLine($match[0]); + $stream->push(new Token(Token::TYPE_STRING, $string, $reader->getPosition())); + $reader->moveForward(strlen($match[0]) + 1); + + return true; + } +} diff --git a/vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.php b/vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.php new file mode 100644 index 0000000..4c2d335 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * CSS selector whitespace handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class WhitespaceHandler implements HandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $match = $reader->findPattern('~^[ \t\r\n\f]+~'); + + if (false === $match) { + return false; + } + + $stream->push(new Token(Token::TYPE_WHITESPACE, $match[0], $reader->getPosition())); + $reader->moveForward(strlen($match[0])); + + return true; + } +} diff --git a/vendor/symfony/css-selector/Parser/Parser.php b/vendor/symfony/css-selector/Parser/Parser.php new file mode 100644 index 0000000..f94aea3 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Parser.php @@ -0,0 +1,401 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; +use Symfony\Component\CssSelector\Node; +use Symfony\Component\CssSelector\Parser\Tokenizer\Tokenizer; + +/** + * CSS selector parser. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Parser implements ParserInterface +{ + /** + * @var Tokenizer + */ + private $tokenizer; + + /** + * Constructor. + * + * @param null|Tokenizer $tokenizer + */ + public function __construct(Tokenizer $tokenizer = null) + { + $this->tokenizer = $tokenizer ?: new Tokenizer(); + } + + /** + * {@inheritdoc} + */ + public function parse($source) + { + $reader = new Reader($source); + $stream = $this->tokenizer->tokenize($reader); + + return $this->parseSelectorList($stream); + } + + /** + * Parses the arguments for ":nth-child()" and friends. + * + * @param Token[] $tokens + * + * @throws SyntaxErrorException + * + * @return array + */ + public static function parseSeries(array $tokens) + { + foreach ($tokens as $token) { + if ($token->isString()) { + throw SyntaxErrorException::stringAsFunctionArgument(); + } + } + + $joined = trim(implode('', array_map(function (Token $token) { + return $token->getValue(); + }, $tokens))); + + $int = function ($string) { + if (!is_numeric($string)) { + throw SyntaxErrorException::stringAsFunctionArgument(); + } + + return (int) $string; + }; + + switch (true) { + case 'odd' === $joined: + return array(2, 1); + case 'even' === $joined: + return array(2, 0); + case 'n' === $joined: + return array(1, 0); + case false === strpos($joined, 'n'): + return array(0, $int($joined)); + } + + $split = explode('n', $joined); + $first = isset($split[0]) ? $split[0] : null; + + return array( + $first ? ('-' === $first || '+' === $first ? $int($first.'1') : $int($first)) : 1, + isset($split[1]) && $split[1] ? $int($split[1]) : 0, + ); + } + + /** + * Parses selector nodes. + * + * @param TokenStream $stream + * + * @return array + */ + private function parseSelectorList(TokenStream $stream) + { + $stream->skipWhitespace(); + $selectors = array(); + + while (true) { + $selectors[] = $this->parserSelectorNode($stream); + + if ($stream->getPeek()->isDelimiter(array(','))) { + $stream->getNext(); + $stream->skipWhitespace(); + } else { + break; + } + } + + return $selectors; + } + + /** + * Parses next selector or combined node. + * + * @param TokenStream $stream + * + * @throws SyntaxErrorException + * + * @return Node\SelectorNode + */ + private function parserSelectorNode(TokenStream $stream) + { + list($result, $pseudoElement) = $this->parseSimpleSelector($stream); + + while (true) { + $stream->skipWhitespace(); + $peek = $stream->getPeek(); + + if ($peek->isFileEnd() || $peek->isDelimiter(array(','))) { + break; + } + + if (null !== $pseudoElement) { + throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector'); + } + + if ($peek->isDelimiter(array('+', '>', '~'))) { + $combinator = $stream->getNext()->getValue(); + $stream->skipWhitespace(); + } else { + $combinator = ' '; + } + + list($nextSelector, $pseudoElement) = $this->parseSimpleSelector($stream); + $result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector); + } + + return new Node\SelectorNode($result, $pseudoElement); + } + + /** + * Parses next simple node (hash, class, pseudo, negation). + * + * @param TokenStream $stream + * @param bool $insideNegation + * + * @throws SyntaxErrorException + * + * @return array + */ + private function parseSimpleSelector(TokenStream $stream, $insideNegation = false) + { + $stream->skipWhitespace(); + + $selectorStart = count($stream->getUsed()); + $result = $this->parseElementNode($stream); + $pseudoElement = null; + + while (true) { + $peek = $stream->getPeek(); + if ($peek->isWhitespace() + || $peek->isFileEnd() + || $peek->isDelimiter(array(',', '+', '>', '~')) + || ($insideNegation && $peek->isDelimiter(array(')'))) + ) { + break; + } + + if (null !== $pseudoElement) { + throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector'); + } + + if ($peek->isHash()) { + $result = new Node\HashNode($result, $stream->getNext()->getValue()); + } elseif ($peek->isDelimiter(array('.'))) { + $stream->getNext(); + $result = new Node\ClassNode($result, $stream->getNextIdentifier()); + } elseif ($peek->isDelimiter(array('['))) { + $stream->getNext(); + $result = $this->parseAttributeNode($result, $stream); + } elseif ($peek->isDelimiter(array(':'))) { + $stream->getNext(); + + if ($stream->getPeek()->isDelimiter(array(':'))) { + $stream->getNext(); + $pseudoElement = $stream->getNextIdentifier(); + + continue; + } + + $identifier = $stream->getNextIdentifier(); + if (in_array(strtolower($identifier), array('first-line', 'first-letter', 'before', 'after'))) { + // Special case: CSS 2.1 pseudo-elements can have a single ':'. + // Any new pseudo-element must have two. + $pseudoElement = $identifier; + + continue; + } + + if (!$stream->getPeek()->isDelimiter(array('('))) { + $result = new Node\PseudoNode($result, $identifier); + + continue; + } + + $stream->getNext(); + $stream->skipWhitespace(); + + if ('not' === strtolower($identifier)) { + if ($insideNegation) { + throw SyntaxErrorException::nestedNot(); + } + + list($argument, $argumentPseudoElement) = $this->parseSimpleSelector($stream, true); + $next = $stream->getNext(); + + if (null !== $argumentPseudoElement) { + throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside ::not()'); + } + + if (!$next->isDelimiter(array(')'))) { + throw SyntaxErrorException::unexpectedToken('")"', $next); + } + + $result = new Node\NegationNode($result, $argument); + } else { + $arguments = array(); + $next = null; + + while (true) { + $stream->skipWhitespace(); + $next = $stream->getNext(); + + if ($next->isIdentifier() + || $next->isString() + || $next->isNumber() + || $next->isDelimiter(array('+', '-')) + ) { + $arguments[] = $next; + } elseif ($next->isDelimiter(array(')'))) { + break; + } else { + throw SyntaxErrorException::unexpectedToken('an argument', $next); + } + } + + if (empty($arguments)) { + throw SyntaxErrorException::unexpectedToken('at least one argument', $next); + } + + $result = new Node\FunctionNode($result, $identifier, $arguments); + } + } else { + throw SyntaxErrorException::unexpectedToken('selector', $peek); + } + } + + if (count($stream->getUsed()) === $selectorStart) { + throw SyntaxErrorException::unexpectedToken('selector', $stream->getPeek()); + } + + return array($result, $pseudoElement); + } + + /** + * Parses next element node. + * + * @param TokenStream $stream + * + * @return Node\ElementNode + */ + private function parseElementNode(TokenStream $stream) + { + $peek = $stream->getPeek(); + + if ($peek->isIdentifier() || $peek->isDelimiter(array('*'))) { + if ($peek->isIdentifier()) { + $namespace = $stream->getNext()->getValue(); + } else { + $stream->getNext(); + $namespace = null; + } + + if ($stream->getPeek()->isDelimiter(array('|'))) { + $stream->getNext(); + $element = $stream->getNextIdentifierOrStar(); + } else { + $element = $namespace; + $namespace = null; + } + } else { + $element = $namespace = null; + } + + return new Node\ElementNode($namespace, $element); + } + + /** + * Parses next attribute node. + * + * @param Node\NodeInterface $selector + * @param TokenStream $stream + * + * @throws SyntaxErrorException + * + * @return Node\AttributeNode + */ + private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream) + { + $stream->skipWhitespace(); + $attribute = $stream->getNextIdentifierOrStar(); + + if (null === $attribute && !$stream->getPeek()->isDelimiter(array('|'))) { + throw SyntaxErrorException::unexpectedToken('"|"', $stream->getPeek()); + } + + if ($stream->getPeek()->isDelimiter(array('|'))) { + $stream->getNext(); + + if ($stream->getPeek()->isDelimiter(array('='))) { + $namespace = null; + $stream->getNext(); + $operator = '|='; + } else { + $namespace = $attribute; + $attribute = $stream->getNextIdentifier(); + $operator = null; + } + } else { + $namespace = $operator = null; + } + + if (null === $operator) { + $stream->skipWhitespace(); + $next = $stream->getNext(); + + if ($next->isDelimiter(array(']'))) { + return new Node\AttributeNode($selector, $namespace, $attribute, 'exists', null); + } elseif ($next->isDelimiter(array('='))) { + $operator = '='; + } elseif ($next->isDelimiter(array('^', '$', '*', '~', '|', '!')) + && $stream->getPeek()->isDelimiter(array('=')) + ) { + $operator = $next->getValue().'='; + $stream->getNext(); + } else { + throw SyntaxErrorException::unexpectedToken('operator', $next); + } + } + + $stream->skipWhitespace(); + $value = $stream->getNext(); + + if ($value->isNumber()) { + // if the value is a number, it's casted into a string + $value = new Token(Token::TYPE_STRING, (string) $value->getValue(), $value->getPosition()); + } + + if (!($value->isIdentifier() || $value->isString())) { + throw SyntaxErrorException::unexpectedToken('string or identifier', $value); + } + + $stream->skipWhitespace(); + $next = $stream->getNext(); + + if (!$next->isDelimiter(array(']'))) { + throw SyntaxErrorException::unexpectedToken('"]"', $next); + } + + return new Node\AttributeNode($selector, $namespace, $attribute, $operator, $value->getValue()); + } +} diff --git a/vendor/symfony/css-selector/Parser/ParserInterface.php b/vendor/symfony/css-selector/Parser/ParserInterface.php new file mode 100644 index 0000000..c5af203 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/ParserInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +use Symfony\Component\CssSelector\Node\SelectorNode; + +/** + * CSS selector parser interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface ParserInterface +{ + /** + * Parses given selector source into an array of tokens. + * + * @param string $source + * + * @return SelectorNode[] + */ + public function parse($source); +} diff --git a/vendor/symfony/css-selector/Parser/Reader.php b/vendor/symfony/css-selector/Parser/Reader.php new file mode 100644 index 0000000..4113636 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Reader.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +/** + * CSS selector reader. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Reader +{ + /** + * @var string + */ + private $source; + + /** + * @var int + */ + private $length; + + /** + * @var int + */ + private $position = 0; + + /** + * @param string $source + */ + public function __construct($source) + { + $this->source = $source; + $this->length = strlen($source); + } + + /** + * @return bool + */ + public function isEOF() + { + return $this->position >= $this->length; + } + + /** + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * @return int + */ + public function getRemainingLength() + { + return $this->length - $this->position; + } + + /** + * @param int $length + * @param int $offset + * + * @return string + */ + public function getSubstring($length, $offset = 0) + { + return substr($this->source, $this->position + $offset, $length); + } + + /** + * @param string $string + * + * @return int + */ + public function getOffset($string) + { + $position = strpos($this->source, $string, $this->position); + + return false === $position ? false : $position - $this->position; + } + + /** + * @param string $pattern + * + * @return bool + */ + public function findPattern($pattern) + { + $source = substr($this->source, $this->position); + + if (preg_match($pattern, $source, $matches)) { + return $matches; + } + + return false; + } + + /** + * @param int $length + */ + public function moveForward($length) + { + $this->position += $length; + } + + /** + */ + public function moveToEnd() + { + $this->position = $this->length; + } +} diff --git a/vendor/symfony/css-selector/Parser/Shortcut/ClassParser.php b/vendor/symfony/css-selector/Parser/Shortcut/ClassParser.php new file mode 100644 index 0000000..c513de5 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Shortcut/ClassParser.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\ClassNode; +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * CSS selector class parser shortcut. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class ClassParser implements ParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($source) + { + // Matches an optional namespace, optional element, and required class + // $source = 'test|input.ab6bd_field'; + // $matches = array (size=4) + // 0 => string 'test|input.ab6bd_field' (length=22) + // 1 => string 'test' (length=4) + // 2 => string 'input' (length=5) + // 3 => string 'ab6bd_field' (length=11) + if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+\.([\w-]++)$/i', trim($source), $matches)) { + return array( + new SelectorNode(new ClassNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])), + ); + } + + return array(); + } +} diff --git a/vendor/symfony/css-selector/Parser/Shortcut/ElementParser.php b/vendor/symfony/css-selector/Parser/Shortcut/ElementParser.php new file mode 100644 index 0000000..c29f5e4 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Shortcut/ElementParser.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * CSS selector element parser shortcut. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class ElementParser implements ParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($source) + { + // Matches an optional namespace, required element or `*` + // $source = 'testns|testel'; + // $matches = array (size=3) + // 0 => string 'testns|testel' (length=13) + // 1 => string 'testns' (length=6) + // 2 => string 'testel' (length=6) + if (preg_match('/^(?:([a-z]++)\|)?([\w-]++|\*)$/i', trim($source), $matches)) { + return array(new SelectorNode(new ElementNode($matches[1] ?: null, $matches[2]))); + } + + return array(); + } +} diff --git a/vendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php b/vendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php new file mode 100644 index 0000000..016cf0a --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * CSS selector class parser shortcut. + * + * This shortcut ensure compatibility with previous version. + * - The parser fails to parse an empty string. + * - In the previous version, an empty string matches each tags. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class EmptyStringParser implements ParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($source) + { + // Matches an empty string + if ($source == '') { + return array(new SelectorNode(new ElementNode(null, '*'))); + } + + return array(); + } +} diff --git a/vendor/symfony/css-selector/Parser/Shortcut/HashParser.php b/vendor/symfony/css-selector/Parser/Shortcut/HashParser.php new file mode 100644 index 0000000..3f3883b --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Shortcut/HashParser.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\HashNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * CSS selector hash parser shortcut. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class HashParser implements ParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($source) + { + // Matches an optional namespace, optional element, and required id + // $source = 'test|input#ab6bd_field'; + // $matches = array (size=4) + // 0 => string 'test|input#ab6bd_field' (length=22) + // 1 => string 'test' (length=4) + // 2 => string 'input' (length=5) + // 3 => string 'ab6bd_field' (length=11) + if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+#([\w-]++)$/i', trim($source), $matches)) { + return array( + new SelectorNode(new HashNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])), + ); + } + + return array(); + } +} diff --git a/vendor/symfony/css-selector/Parser/Token.php b/vendor/symfony/css-selector/Parser/Token.php new file mode 100644 index 0000000..68fac59 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Token.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +/** + * CSS selector token. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Token +{ + const TYPE_FILE_END = 'eof'; + const TYPE_DELIMITER = 'delimiter'; + const TYPE_WHITESPACE = 'whitespace'; + const TYPE_IDENTIFIER = 'identifier'; + const TYPE_HASH = 'hash'; + const TYPE_NUMBER = 'number'; + const TYPE_STRING = 'string'; + + /** + * @var int + */ + private $type; + + /** + * @var string + */ + private $value; + + /** + * @var int + */ + private $position; + + /** + * @param int $type + * @param string $value + * @param int $position + */ + public function __construct($type, $value, $position) + { + $this->type = $type; + $this->value = $value; + $this->position = $position; + } + + /** + * @return int + */ + public function getType() + { + return $this->type; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * @return bool + */ + public function isFileEnd() + { + return self::TYPE_FILE_END === $this->type; + } + + /** + * @param array $values + * + * @return bool + */ + public function isDelimiter(array $values = array()) + { + if (self::TYPE_DELIMITER !== $this->type) { + return false; + } + + if (empty($values)) { + return true; + } + + return in_array($this->value, $values); + } + + /** + * @return bool + */ + public function isWhitespace() + { + return self::TYPE_WHITESPACE === $this->type; + } + + /** + * @return bool + */ + public function isIdentifier() + { + return self::TYPE_IDENTIFIER === $this->type; + } + + /** + * @return bool + */ + public function isHash() + { + return self::TYPE_HASH === $this->type; + } + + /** + * @return bool + */ + public function isNumber() + { + return self::TYPE_NUMBER === $this->type; + } + + /** + * @return bool + */ + public function isString() + { + return self::TYPE_STRING === $this->type; + } + + /** + * @return string + */ + public function __toString() + { + if ($this->value) { + return sprintf('<%s "%s" at %s>', $this->type, $this->value, $this->position); + } + + return sprintf('<%s at %s>', $this->type, $this->position); + } +} diff --git a/vendor/symfony/css-selector/Parser/TokenStream.php b/vendor/symfony/css-selector/Parser/TokenStream.php new file mode 100644 index 0000000..1ec727f --- /dev/null +++ b/vendor/symfony/css-selector/Parser/TokenStream.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +use Symfony\Component\CssSelector\Exception\InternalErrorException; +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; + +/** + * CSS selector token stream. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class TokenStream +{ + /** + * @var Token[] + */ + private $tokens = array(); + + /** + * @var bool + */ + private $frozen = false; + + /** + * @var Token[] + */ + private $used = array(); + + /** + * @var int + */ + private $cursor = 0; + + /** + * @var Token|null + */ + private $peeked = null; + + /** + * @var bool + */ + private $peeking = false; + + /** + * Pushes a token. + * + * @param Token $token + * + * @return TokenStream + */ + public function push(Token $token) + { + $this->tokens[] = $token; + + return $this; + } + + /** + * Freezes stream. + * + * @return TokenStream + */ + public function freeze() + { + $this->frozen = true; + + return $this; + } + + /** + * Returns next token. + * + * @throws InternalErrorException If there is no more token + * + * @return Token + */ + public function getNext() + { + if ($this->peeking) { + $this->peeking = false; + $this->used[] = $this->peeked; + + return $this->peeked; + } + + if (!isset($this->tokens[$this->cursor])) { + throw new InternalErrorException('Unexpected token stream end.'); + } + + return $this->tokens[$this->cursor++]; + } + + /** + * Returns peeked token. + * + * @return Token + */ + public function getPeek() + { + if (!$this->peeking) { + $this->peeked = $this->getNext(); + $this->peeking = true; + } + + return $this->peeked; + } + + /** + * Returns used tokens. + * + * @return Token[] + */ + public function getUsed() + { + return $this->used; + } + + /** + * Returns nex identifier token. + * + * @throws SyntaxErrorException If next token is not an identifier + * + * @return string The identifier token value + */ + public function getNextIdentifier() + { + $next = $this->getNext(); + + if (!$next->isIdentifier()) { + throw SyntaxErrorException::unexpectedToken('identifier', $next); + } + + return $next->getValue(); + } + + /** + * Returns nex identifier or star delimiter token. + * + * @throws SyntaxErrorException If next token is not an identifier or a star delimiter + * + * @return null|string The identifier token value or null if star found + */ + public function getNextIdentifierOrStar() + { + $next = $this->getNext(); + + if ($next->isIdentifier()) { + return $next->getValue(); + } + + if ($next->isDelimiter(array('*'))) { + return; + } + + throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next); + } + + /** + * Skips next whitespace if any. + */ + public function skipWhitespace() + { + $peek = $this->getPeek(); + + if ($peek->isWhitespace()) { + $this->getNext(); + } + } +} diff --git a/vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.php b/vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.php new file mode 100644 index 0000000..aa9fc50 --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Tokenizer; + +use Symfony\Component\CssSelector\Parser\Handler; +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * CSS selector tokenizer. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Tokenizer +{ + /** + * @var Handler\HandlerInterface[] + */ + private $handlers; + + /** + * Constructor. + */ + public function __construct() + { + $patterns = new TokenizerPatterns(); + $escaping = new TokenizerEscaping($patterns); + + $this->handlers = array( + new Handler\WhitespaceHandler(), + new Handler\IdentifierHandler($patterns, $escaping), + new Handler\HashHandler($patterns, $escaping), + new Handler\StringHandler($patterns, $escaping), + new Handler\NumberHandler($patterns), + new Handler\CommentHandler(), + ); + } + + /** + * Tokenize selector source code. + * + * @param Reader $reader + * + * @return TokenStream + */ + public function tokenize(Reader $reader) + { + $stream = new TokenStream(); + + while (!$reader->isEOF()) { + foreach ($this->handlers as $handler) { + if ($handler->handle($reader, $stream)) { + continue 2; + } + } + + $stream->push(new Token(Token::TYPE_DELIMITER, $reader->getSubstring(1), $reader->getPosition())); + $reader->moveForward(1); + } + + return $stream + ->push(new Token(Token::TYPE_FILE_END, null, $reader->getPosition())) + ->freeze(); + } +} diff --git a/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php b/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php new file mode 100644 index 0000000..af4c31e --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Tokenizer; + +/** + * CSS selector tokenizer escaping applier. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class TokenizerEscaping +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @param TokenizerPatterns $patterns + */ + public function __construct(TokenizerPatterns $patterns) + { + $this->patterns = $patterns; + } + + /** + * @param string $value + * + * @return string + */ + public function escapeUnicode($value) + { + $value = $this->replaceUnicodeSequences($value); + + return preg_replace($this->patterns->getSimpleEscapePattern(), '$1', $value); + } + + /** + * @param string $value + * + * @return string + */ + public function escapeUnicodeAndNewLine($value) + { + $value = preg_replace($this->patterns->getNewLineEscapePattern(), '', $value); + + return $this->escapeUnicode($value); + } + + /** + * @param string $value + * + * @return string + */ + private function replaceUnicodeSequences($value) + { + return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) { + $c = hexdec($match[1]); + + if (0x80 > $c %= 0x200000) { + return chr($c); + } + if (0x800 > $c) { + return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); + } + if (0x10000 > $c) { + return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); + } + }, $value); + } +} diff --git a/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php b/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php new file mode 100644 index 0000000..5b071cd --- /dev/null +++ b/vendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Tokenizer; + +/** + * CSS selector tokenizer patterns builder. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class TokenizerPatterns +{ + /** + * @var string + */ + private $unicodeEscapePattern; + + /** + * @var string + */ + private $simpleEscapePattern; + + /** + * @var string + */ + private $newLineEscapePattern; + + /** + * @var string + */ + private $escapePattern; + + /** + * @var string + */ + private $stringEscapePattern; + + /** + * @var string + */ + private $nonAsciiPattern; + + /** + * @var string + */ + private $nmCharPattern; + + /** + * @var string + */ + private $nmStartPattern; + + /** + * @var string + */ + private $identifierPattern; + + /** + * @var string + */ + private $hashPattern; + + /** + * @var string + */ + private $numberPattern; + + /** + * @var string + */ + private $quotedStringPattern; + + /** + * Constructor. + */ + public function __construct() + { + $this->unicodeEscapePattern = '\\\\([0-9a-f]{1,6})(?:\r\n|[ \n\r\t\f])?'; + $this->simpleEscapePattern = '\\\\(.)'; + $this->newLineEscapePattern = '\\\\(?:\n|\r\n|\r|\f)'; + $this->escapePattern = $this->unicodeEscapePattern.'|\\\\[^\n\r\f0-9a-f]'; + $this->stringEscapePattern = $this->newLineEscapePattern.'|'.$this->escapePattern; + $this->nonAsciiPattern = '[^\x00-\x7F]'; + $this->nmCharPattern = '[_a-z0-9-]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; + $this->nmStartPattern = '[_a-z]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; + $this->identifierPattern = '(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*'; + $this->hashPattern = '#((?:'.$this->nmCharPattern.')+)'; + $this->numberPattern = '[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)'; + $this->quotedStringPattern = '([^\n\r\f%s]|'.$this->stringEscapePattern.')*'; + } + + /** + * @return string + */ + public function getNewLineEscapePattern() + { + return '~^'.$this->newLineEscapePattern.'~'; + } + + /** + * @return string + */ + public function getSimpleEscapePattern() + { + return '~^'.$this->simpleEscapePattern.'~'; + } + + /** + * @return string + */ + public function getUnicodeEscapePattern() + { + return '~^'.$this->unicodeEscapePattern.'~i'; + } + + /** + * @return string + */ + public function getIdentifierPattern() + { + return '~^'.$this->identifierPattern.'~i'; + } + + /** + * @return string + */ + public function getHashPattern() + { + return '~^'.$this->hashPattern.'~i'; + } + + /** + * @return string + */ + public function getNumberPattern() + { + return '~^'.$this->numberPattern.'~'; + } + + /** + * @param string $quote + * + * @return string + */ + public function getQuotedStringPattern($quote) + { + return '~^'.sprintf($this->quotedStringPattern, $quote).'~i'; + } +} diff --git a/vendor/symfony/css-selector/README.md b/vendor/symfony/css-selector/README.md new file mode 100644 index 0000000..eef2885 --- /dev/null +++ b/vendor/symfony/css-selector/README.md @@ -0,0 +1,84 @@ +CssSelector Component +===================== + +CssSelector converts CSS selectors to XPath expressions. + +The component only goal is to convert CSS selectors to their XPath +equivalents: + +```php +use Symfony\Component\CssSelector\CssSelectorConverter; + +$converter = new CssSelectorConverter(); +print $converter->toXPath('div.item > h4 > a'); +``` + +HTML and XML are different +-------------------------- + +The `CssSelector` component comes with an `HTML` extension which is enabled by +default. If you need to use this component with `XML` documents, you have to +disable this `HTML` extension. That's because, `HTML` tag & attribute names are +always lower-cased, but case-sensitive in `XML`: + +```php +// disable `HTML` extension: +$converter = new CssSelectorConverter(false); +``` + +When the `HTML` extension is enabled, tag names are lower-cased, attribute +names are lower-cased, the following extra pseudo-classes are supported: +`checked`, `link`, `disabled`, `enabled`, `selected`, `invalid`, `hover`, +`visited`, and the `lang()` function is also added. + +Resources +--------- + +This component is a port of the Python cssselect library +[v0.7.1](https://github.com/SimonSapin/cssselect/releases/tag/v0.7.1), +which is distributed under the BSD license. + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/CssSelector/ + $ composer install + $ phpunit + +License +------- + +This component is a port of the Python cssselect library, +which is copyright Ian Bicking, https://github.com/SimonSapin/cssselect. + +Copyright (c) 2007-2012 Ian Bicking and contributors. See AUTHORS +for more details. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of Ian Bicking 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 IAN BICKING 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/vendor/symfony/css-selector/Tests/CssSelectorConverterTest.php b/vendor/symfony/css-selector/Tests/CssSelectorConverterTest.php new file mode 100644 index 0000000..624909b --- /dev/null +++ b/vendor/symfony/css-selector/Tests/CssSelectorConverterTest.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests; + +use Symfony\Component\CssSelector\CssSelectorConverter; + +class CssSelectorConverterTest extends \PHPUnit_Framework_TestCase +{ + public function testCssToXPath() + { + $converter = new CssSelectorConverter(); + + $this->assertEquals('descendant-or-self::*', $converter->toXPath('')); + $this->assertEquals('descendant-or-self::h1', $converter->toXPath('h1')); + $this->assertEquals("descendant-or-self::h1[@id = 'foo']", $converter->toXPath('h1#foo')); + $this->assertEquals("descendant-or-self::h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]", $converter->toXPath('h1.foo')); + $this->assertEquals('descendant-or-self::foo:h1', $converter->toXPath('foo|h1')); + $this->assertEquals('descendant-or-self::h1', $converter->toXPath('H1')); + } + + public function testCssToXPathXml() + { + $converter = new CssSelectorConverter(false); + + $this->assertEquals('descendant-or-self::H1', $converter->toXPath('H1')); + } + + /** + * @expectedException \Symfony\Component\CssSelector\Exception\ParseException + * @expectedExceptionMessage Expected identifier, but found. + */ + public function testParseExceptions() + { + $converter = new CssSelectorConverter(); + $converter->toXPath('h1:'); + } + + /** @dataProvider getCssToXPathWithoutPrefixTestData */ + public function testCssToXPathWithoutPrefix($css, $xpath) + { + $converter = new CssSelectorConverter(); + + $this->assertEquals($xpath, $converter->toXPath($css, ''), '->parse() parses an input string and returns a node'); + } + + public function getCssToXPathWithoutPrefixTestData() + { + return array( + array('h1', 'h1'), + array('foo|h1', 'foo:h1'), + array('h1, h2, h3', 'h1 | h2 | h3'), + array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]"), + array('h1 > p', 'h1/p'), + array('h1#foo', "h1[@id = 'foo']"), + array('h1.foo', "h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), + array('h1[class*="foo bar"]', "h1[@class and contains(@class, 'foo bar')]"), + array('h1[foo|class*="foo bar"]', "h1[@foo:class and contains(@foo:class, 'foo bar')]"), + array('h1[class]', 'h1[@class]'), + array('h1 .foo', "h1/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), + array('h1 #foo', "h1/descendant-or-self::*/*[@id = 'foo']"), + array('h1 [class*=foo]', "h1/descendant-or-self::*/*[@class and contains(@class, 'foo')]"), + array('div>.foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), + array('div > .foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/AbstractNodeTest.php b/vendor/symfony/css-selector/Tests/Node/AbstractNodeTest.php new file mode 100644 index 0000000..932e803 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/AbstractNodeTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\NodeInterface; + +abstract class AbstractNodeTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getToStringConversionTestData */ + public function testToStringConversion(NodeInterface $node, $representation) + { + $this->assertEquals($representation, (string) $node); + } + + /** @dataProvider getSpecificityValueTestData */ + public function testSpecificityValue(NodeInterface $node, $value) + { + $this->assertEquals($value, $node->getSpecificity()->getValue()); + } + + abstract public function getToStringConversionTestData(); + + abstract public function getSpecificityValueTestData(); +} diff --git a/vendor/symfony/css-selector/Tests/Node/AttributeNodeTest.php b/vendor/symfony/css-selector/Tests/Node/AttributeNodeTest.php new file mode 100644 index 0000000..1fd090f --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/AttributeNodeTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\AttributeNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class AttributeNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new AttributeNode(new ElementNode(), null, 'attribute', 'exists', null), 'Attribute[Element[*][attribute]]'), + array(new AttributeNode(new ElementNode(), null, 'attribute', '$=', 'value'), "Attribute[Element[*][attribute $= 'value']]"), + array(new AttributeNode(new ElementNode(), 'namespace', 'attribute', '$=', 'value'), "Attribute[Element[*][namespace|attribute $= 'value']]"), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new AttributeNode(new ElementNode(), null, 'attribute', 'exists', null), 10), + array(new AttributeNode(new ElementNode(null, 'element'), null, 'attribute', 'exists', null), 11), + array(new AttributeNode(new ElementNode(), null, 'attribute', '$=', 'value'), 10), + array(new AttributeNode(new ElementNode(), 'namespace', 'attribute', '$=', 'value'), 10), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/ClassNodeTest.php b/vendor/symfony/css-selector/Tests/Node/ClassNodeTest.php new file mode 100644 index 0000000..e0ab45a --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/ClassNodeTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ClassNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class ClassNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new ClassNode(new ElementNode(), 'class'), 'Class[Element[*].class]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new ClassNode(new ElementNode(), 'class'), 10), + array(new ClassNode(new ElementNode(null, 'element'), 'class'), 11), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/CombinedSelectorNodeTest.php b/vendor/symfony/css-selector/Tests/Node/CombinedSelectorNodeTest.php new file mode 100644 index 0000000..9547298 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/CombinedSelectorNodeTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\CombinedSelectorNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class CombinedSelectorNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new CombinedSelectorNode(new ElementNode(), '>', new ElementNode()), 'CombinedSelector[Element[*] > Element[*]]'), + array(new CombinedSelectorNode(new ElementNode(), ' ', new ElementNode()), 'CombinedSelector[Element[*] Element[*]]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new CombinedSelectorNode(new ElementNode(), '>', new ElementNode()), 0), + array(new CombinedSelectorNode(new ElementNode(null, 'element'), '>', new ElementNode()), 1), + array(new CombinedSelectorNode(new ElementNode(null, 'element'), '>', new ElementNode(null, 'element')), 2), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/ElementNodeTest.php b/vendor/symfony/css-selector/Tests/Node/ElementNodeTest.php new file mode 100644 index 0000000..6d24789 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/ElementNodeTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ElementNode; + +class ElementNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new ElementNode(), 'Element[*]'), + array(new ElementNode(null, 'element'), 'Element[element]'), + array(new ElementNode('namespace', 'element'), 'Element[namespace|element]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new ElementNode(), 0), + array(new ElementNode(null, 'element'), 1), + array(new ElementNode('namespace', 'element'), 1), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/FunctionNodeTest.php b/vendor/symfony/css-selector/Tests/Node/FunctionNodeTest.php new file mode 100644 index 0000000..ee3ce51 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/FunctionNodeTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\Parser\Token; + +class FunctionNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new FunctionNode(new ElementNode(), 'function'), 'Function[Element[*]:function()]'), + array(new FunctionNode(new ElementNode(), 'function', array( + new Token(Token::TYPE_IDENTIFIER, 'value', 0), + )), "Function[Element[*]:function(['value'])]"), + array(new FunctionNode(new ElementNode(), 'function', array( + new Token(Token::TYPE_STRING, 'value1', 0), + new Token(Token::TYPE_NUMBER, 'value2', 0), + )), "Function[Element[*]:function(['value1', 'value2'])]"), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new FunctionNode(new ElementNode(), 'function'), 10), + array(new FunctionNode(new ElementNode(), 'function', array( + new Token(Token::TYPE_IDENTIFIER, 'value', 0), + )), 10), + array(new FunctionNode(new ElementNode(), 'function', array( + new Token(Token::TYPE_STRING, 'value1', 0), + new Token(Token::TYPE_NUMBER, 'value2', 0), + )), 10), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/HashNodeTest.php b/vendor/symfony/css-selector/Tests/Node/HashNodeTest.php new file mode 100644 index 0000000..8554b22 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/HashNodeTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\HashNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class HashNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new HashNode(new ElementNode(), 'id'), 'Hash[Element[*]#id]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new HashNode(new ElementNode(), 'id'), 100), + array(new HashNode(new ElementNode(null, 'id'), 'class'), 101), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/NegationNodeTest.php b/vendor/symfony/css-selector/Tests/Node/NegationNodeTest.php new file mode 100644 index 0000000..edf4552 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/NegationNodeTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ClassNode; +use Symfony\Component\CssSelector\Node\NegationNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class NegationNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new NegationNode(new ElementNode(), new ClassNode(new ElementNode(), 'class')), 'Negation[Element[*]:not(Class[Element[*].class])]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new NegationNode(new ElementNode(), new ClassNode(new ElementNode(), 'class')), 10), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/PseudoNodeTest.php b/vendor/symfony/css-selector/Tests/Node/PseudoNodeTest.php new file mode 100644 index 0000000..bc57813 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/PseudoNodeTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\PseudoNode; + +class PseudoNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new PseudoNode(new ElementNode(), 'pseudo'), 'Pseudo[Element[*]:pseudo]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new PseudoNode(new ElementNode(), 'pseudo'), 10), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/SelectorNodeTest.php b/vendor/symfony/css-selector/Tests/Node/SelectorNodeTest.php new file mode 100644 index 0000000..5badf71 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/SelectorNodeTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\SelectorNode; + +class SelectorNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new SelectorNode(new ElementNode()), 'Selector[Element[*]]'), + array(new SelectorNode(new ElementNode(), 'pseudo'), 'Selector[Element[*]::pseudo]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new SelectorNode(new ElementNode()), 0), + array(new SelectorNode(new ElementNode(), 'pseudo'), 1), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Node/SpecificityTest.php b/vendor/symfony/css-selector/Tests/Node/SpecificityTest.php new file mode 100644 index 0000000..c34fe5f --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Node/SpecificityTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\Specificity; + +class SpecificityTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getValueTestData */ + public function testValue(Specificity $specificity, $value) + { + $this->assertEquals($value, $specificity->getValue()); + } + + /** @dataProvider getValueTestData */ + public function testPlusValue(Specificity $specificity, $value) + { + $this->assertEquals($value + 123, $specificity->plus(new Specificity(1, 2, 3))->getValue()); + } + + public function getValueTestData() + { + return array( + array(new Specificity(0, 0, 0), 0), + array(new Specificity(0, 0, 2), 2), + array(new Specificity(0, 3, 0), 30), + array(new Specificity(4, 0, 0), 400), + array(new Specificity(4, 3, 2), 432), + ); + } + + /** @dataProvider getCompareTestData */ + public function testCompareTo(Specificity $a, Specificity $b, $result) + { + $this->assertEquals($result, $a->compareTo($b)); + } + + public function getCompareTestData() + { + return array( + array(new Specificity(0, 0, 0), new Specificity(0, 0, 0), 0), + array(new Specificity(0, 0, 1), new Specificity(0, 0, 1), 0), + array(new Specificity(0, 0, 2), new Specificity(0, 0, 1), 1), + array(new Specificity(0, 0, 2), new Specificity(0, 0, 3), -1), + array(new Specificity(0, 4, 0), new Specificity(0, 4, 0), 0), + array(new Specificity(0, 6, 0), new Specificity(0, 5, 11), 1), + array(new Specificity(0, 7, 0), new Specificity(0, 8, 0), -1), + array(new Specificity(9, 0, 0), new Specificity(9, 0, 0), 0), + array(new Specificity(11, 0, 0), new Specificity(10, 11, 0), 1), + array(new Specificity(12, 11, 0), new Specificity(13, 0, 0), -1), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Handler/AbstractHandlerTest.php b/vendor/symfony/css-selector/Tests/Parser/Handler/AbstractHandlerTest.php new file mode 100644 index 0000000..57afa5a --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Handler/AbstractHandlerTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * @author Jean-François Simon + */ +abstract class AbstractHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getHandleValueTestData */ + public function testHandleValue($value, Token $expectedToken, $remainingContent) + { + $reader = new Reader($value); + $stream = new TokenStream(); + + $this->assertTrue($this->generateHandler()->handle($reader, $stream)); + $this->assertEquals($expectedToken, $stream->getNext()); + $this->assertRemainingContent($reader, $remainingContent); + } + + /** @dataProvider getDontHandleValueTestData */ + public function testDontHandleValue($value) + { + $reader = new Reader($value); + $stream = new TokenStream(); + + $this->assertFalse($this->generateHandler()->handle($reader, $stream)); + $this->assertStreamEmpty($stream); + $this->assertRemainingContent($reader, $value); + } + + abstract public function getHandleValueTestData(); + + abstract public function getDontHandleValueTestData(); + + abstract protected function generateHandler(); + + protected function assertStreamEmpty(TokenStream $stream) + { + $property = new \ReflectionProperty($stream, 'tokens'); + $property->setAccessible(true); + + $this->assertEquals(array(), $property->getValue($stream)); + } + + protected function assertRemainingContent(Reader $reader, $remainingContent) + { + if ('' === $remainingContent) { + $this->assertEquals(0, $reader->getRemainingLength()); + $this->assertTrue($reader->isEOF()); + } else { + $this->assertEquals(strlen($remainingContent), $reader->getRemainingLength()); + $this->assertEquals(0, $reader->getOffset($remainingContent)); + } + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Handler/CommentHandlerTest.php b/vendor/symfony/css-selector/Tests/Parser/Handler/CommentHandlerTest.php new file mode 100644 index 0000000..3961bf7 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Handler/CommentHandlerTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\CommentHandler; +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +class CommentHandlerTest extends AbstractHandlerTest +{ + /** @dataProvider getHandleValueTestData */ + public function testHandleValue($value, Token $unusedArgument, $remainingContent) + { + $reader = new Reader($value); + $stream = new TokenStream(); + + $this->assertTrue($this->generateHandler()->handle($reader, $stream)); + // comments are ignored (not pushed as token in stream) + $this->assertStreamEmpty($stream); + $this->assertRemainingContent($reader, $remainingContent); + } + + public function getHandleValueTestData() + { + return array( + // 2nd argument only exists for inherited method compatibility + array('/* comment */', new Token(null, null, null), ''), + array('/* comment */foo', new Token(null, null, null), 'foo'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('>'), + array('+'), + array(' '), + ); + } + + protected function generateHandler() + { + return new CommentHandler(); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Handler/HashHandlerTest.php b/vendor/symfony/css-selector/Tests/Parser/Handler/HashHandlerTest.php new file mode 100644 index 0000000..b7fa00a --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Handler/HashHandlerTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\HashHandler; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; + +class HashHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array('#id', new Token(Token::TYPE_HASH, 'id', 0), ''), + array('#123', new Token(Token::TYPE_HASH, '123', 0), ''), + + array('#id.class', new Token(Token::TYPE_HASH, 'id', 0), '.class'), + array('#id element', new Token(Token::TYPE_HASH, 'id', 0), ' element'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('id'), + array('123'), + array('<'), + array('<'), + array('#'), + ); + } + + protected function generateHandler() + { + $patterns = new TokenizerPatterns(); + + return new HashHandler($patterns, new TokenizerEscaping($patterns)); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Handler/IdentifierHandlerTest.php b/vendor/symfony/css-selector/Tests/Parser/Handler/IdentifierHandlerTest.php new file mode 100644 index 0000000..44d3574 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Handler/IdentifierHandlerTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\IdentifierHandler; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; + +class IdentifierHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array('foo', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), ''), + array('foo|bar', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '|bar'), + array('foo.class', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '.class'), + array('foo[attr]', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '[attr]'), + array('foo bar', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), ' bar'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('>'), + array('+'), + array(' '), + array('*|foo'), + array('/* comment */'), + ); + } + + protected function generateHandler() + { + $patterns = new TokenizerPatterns(); + + return new IdentifierHandler($patterns, new TokenizerEscaping($patterns)); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Handler/NumberHandlerTest.php b/vendor/symfony/css-selector/Tests/Parser/Handler/NumberHandlerTest.php new file mode 100644 index 0000000..675fd05 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Handler/NumberHandlerTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\NumberHandler; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +class NumberHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array('12', new Token(Token::TYPE_NUMBER, '12', 0), ''), + array('12.34', new Token(Token::TYPE_NUMBER, '12.34', 0), ''), + array('+12.34', new Token(Token::TYPE_NUMBER, '+12.34', 0), ''), + array('-12.34', new Token(Token::TYPE_NUMBER, '-12.34', 0), ''), + + array('12 arg', new Token(Token::TYPE_NUMBER, '12', 0), ' arg'), + array('12]', new Token(Token::TYPE_NUMBER, '12', 0), ']'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('hello'), + array('>'), + array('+'), + array(' '), + array('/* comment */'), + ); + } + + protected function generateHandler() + { + $patterns = new TokenizerPatterns(); + + return new NumberHandler($patterns); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Handler/StringHandlerTest.php b/vendor/symfony/css-selector/Tests/Parser/Handler/StringHandlerTest.php new file mode 100644 index 0000000..89eff8b --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Handler/StringHandlerTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\StringHandler; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; + +class StringHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array('"hello"', new Token(Token::TYPE_STRING, 'hello', 1), ''), + array('"1"', new Token(Token::TYPE_STRING, '1', 1), ''), + array('" "', new Token(Token::TYPE_STRING, ' ', 1), ''), + array('""', new Token(Token::TYPE_STRING, '', 1), ''), + array("'hello'", new Token(Token::TYPE_STRING, 'hello', 1), ''), + + array("'foo'bar", new Token(Token::TYPE_STRING, 'foo', 1), 'bar'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('hello'), + array('>'), + array('1'), + array(' '), + ); + } + + protected function generateHandler() + { + $patterns = new TokenizerPatterns(); + + return new StringHandler($patterns, new TokenizerEscaping($patterns)); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Handler/WhitespaceHandlerTest.php b/vendor/symfony/css-selector/Tests/Parser/Handler/WhitespaceHandlerTest.php new file mode 100644 index 0000000..f5f9e71 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Handler/WhitespaceHandlerTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\WhitespaceHandler; +use Symfony\Component\CssSelector\Parser\Token; + +class WhitespaceHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array(' ', new Token(Token::TYPE_WHITESPACE, ' ', 0), ''), + array("\n", new Token(Token::TYPE_WHITESPACE, "\n", 0), ''), + array("\t", new Token(Token::TYPE_WHITESPACE, "\t", 0), ''), + + array(' foo', new Token(Token::TYPE_WHITESPACE, ' ', 0), 'foo'), + array(' .foo', new Token(Token::TYPE_WHITESPACE, ' ', 0), '.foo'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('>'), + array('1'), + array('a'), + ); + } + + protected function generateHandler() + { + return new WhitespaceHandler(); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/ParserTest.php b/vendor/symfony/css-selector/Tests/Parser/ParserTest.php new file mode 100644 index 0000000..0454d9f --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/ParserTest.php @@ -0,0 +1,248 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser; + +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Parser; +use Symfony\Component\CssSelector\Parser\Token; + +class ParserTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getParserTestData */ + public function testParser($source, $representation) + { + $parser = new Parser(); + + $this->assertEquals($representation, array_map(function (SelectorNode $node) { + return (string) $node->getTree(); + }, $parser->parse($source))); + } + + /** @dataProvider getParserExceptionTestData */ + public function testParserException($source, $message) + { + $parser = new Parser(); + + try { + $parser->parse($source); + $this->fail('Parser should throw a SyntaxErrorException.'); + } catch (SyntaxErrorException $e) { + $this->assertEquals($message, $e->getMessage()); + } + } + + /** @dataProvider getPseudoElementsTestData */ + public function testPseudoElements($source, $element, $pseudo) + { + $parser = new Parser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($element, (string) $selector->getTree()); + $this->assertEquals($pseudo, (string) $selector->getPseudoElement()); + } + + /** @dataProvider getSpecificityTestData */ + public function testSpecificity($source, $value) + { + $parser = new Parser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($value, $selector->getSpecificity()->getValue()); + } + + /** @dataProvider getParseSeriesTestData */ + public function testParseSeries($series, $a, $b) + { + $parser = new Parser(); + $selectors = $parser->parse(sprintf(':nth-child(%s)', $series)); + $this->assertCount(1, $selectors); + + /** @var FunctionNode $function */ + $function = $selectors[0]->getTree(); + $this->assertEquals(array($a, $b), Parser::parseSeries($function->getArguments())); + } + + /** @dataProvider getParseSeriesExceptionTestData */ + public function testParseSeriesException($series) + { + $parser = new Parser(); + $selectors = $parser->parse(sprintf(':nth-child(%s)', $series)); + $this->assertCount(1, $selectors); + + /** @var FunctionNode $function */ + $function = $selectors[0]->getTree(); + $this->setExpectedException('Symfony\Component\CssSelector\Exception\SyntaxErrorException'); + Parser::parseSeries($function->getArguments()); + } + + public function getParserTestData() + { + return array( + array('*', array('Element[*]')), + array('*|*', array('Element[*]')), + array('*|foo', array('Element[foo]')), + array('foo|*', array('Element[foo|*]')), + array('foo|bar', array('Element[foo|bar]')), + array('#foo#bar', array('Hash[Hash[Element[*]#foo]#bar]')), + array('div>.foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array('div> .foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array('div >.foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array('div > .foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array("div \n> \t \t .foo", array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array('td.foo,.bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array('td.foo, .bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array("td.foo\t\r\n\f ,\t\r\n\f .bar", array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array('td.foo,.bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array('td.foo, .bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array("td.foo\t\r\n\f ,\t\r\n\f .bar", array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array('div, td.foo, div.bar span', array('Element[div]', 'Class[Element[td].foo]', 'CombinedSelector[Class[Element[div].bar] Element[span]]')), + array('div > p', array('CombinedSelector[Element[div] > Element[p]]')), + array('td:first', array('Pseudo[Element[td]:first]')), + array('td :first', array('CombinedSelector[Element[td] Pseudo[Element[*]:first]]')), + array('a[name]', array('Attribute[Element[a][name]]')), + array("a[ name\t]", array('Attribute[Element[a][name]]')), + array('a [name]', array('CombinedSelector[Element[a] Attribute[Element[*][name]]]')), + array('a[rel="include"]', array("Attribute[Element[a][rel = 'include']]")), + array('a[rel = include]', array("Attribute[Element[a][rel = 'include']]")), + array("a[hreflang |= 'en']", array("Attribute[Element[a][hreflang |= 'en']]")), + array('a[hreflang|=en]', array("Attribute[Element[a][hreflang |= 'en']]")), + array('div:nth-child(10)', array("Function[Element[div]:nth-child(['10'])]")), + array(':nth-child(2n+2)', array("Function[Element[*]:nth-child(['2', 'n', '+2'])]")), + array('div:nth-of-type(10)', array("Function[Element[div]:nth-of-type(['10'])]")), + array('div div:nth-of-type(10) .aclass', array("CombinedSelector[CombinedSelector[Element[div] Function[Element[div]:nth-of-type(['10'])]] Class[Element[*].aclass]]")), + array('label:only', array('Pseudo[Element[label]:only]')), + array('a:lang(fr)', array("Function[Element[a]:lang(['fr'])]")), + array('div:contains("foo")', array("Function[Element[div]:contains(['foo'])]")), + array('div#foobar', array('Hash[Element[div]#foobar]')), + array('div:not(div.foo)', array('Negation[Element[div]:not(Class[Element[div].foo])]')), + array('td ~ th', array('CombinedSelector[Element[td] ~ Element[th]]')), + array('.foo[data-bar][data-baz=0]', array("Attribute[Attribute[Class[Element[*].foo][data-bar]][data-baz = '0']]")), + ); + } + + public function getParserExceptionTestData() + { + return array( + array('attributes(href)/html/body/a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '(', 10))->getMessage()), + array('attributes(href)', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '(', 10))->getMessage()), + array('html/body/a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '/', 4))->getMessage()), + array(' ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 1))->getMessage()), + array('div, ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 5))->getMessage()), + array(' , div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, ',', 1))->getMessage()), + array('p, , div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, ',', 3))->getMessage()), + array('div > ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 6))->getMessage()), + array(' > div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '>', 2))->getMessage()), + array('foo|#bar', SyntaxErrorException::unexpectedToken('identifier or "*"', new Token(Token::TYPE_HASH, 'bar', 4))->getMessage()), + array('#.foo', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '#', 0))->getMessage()), + array('.#foo', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_HASH, 'foo', 1))->getMessage()), + array(':#foo', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_HASH, 'foo', 1))->getMessage()), + array('[*]', SyntaxErrorException::unexpectedToken('"|"', new Token(Token::TYPE_DELIMITER, ']', 2))->getMessage()), + array('[foo|]', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_DELIMITER, ']', 5))->getMessage()), + array('[#]', SyntaxErrorException::unexpectedToken('identifier or "*"', new Token(Token::TYPE_DELIMITER, '#', 1))->getMessage()), + array('[foo=#]', SyntaxErrorException::unexpectedToken('string or identifier', new Token(Token::TYPE_DELIMITER, '#', 5))->getMessage()), + array(':nth-child()', SyntaxErrorException::unexpectedToken('at least one argument', new Token(Token::TYPE_DELIMITER, ')', 11))->getMessage()), + array('[href]a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_IDENTIFIER, 'a', 6))->getMessage()), + array('[rel:stylesheet]', SyntaxErrorException::unexpectedToken('operator', new Token(Token::TYPE_DELIMITER, ':', 4))->getMessage()), + array('[rel=stylesheet', SyntaxErrorException::unexpectedToken('"]"', new Token(Token::TYPE_FILE_END, '', 15))->getMessage()), + array(':lang(fr', SyntaxErrorException::unexpectedToken('an argument', new Token(Token::TYPE_FILE_END, '', 8))->getMessage()), + array(':contains("foo', SyntaxErrorException::unclosedString(10)->getMessage()), + array('foo!', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '!', 3))->getMessage()), + ); + } + + public function getPseudoElementsTestData() + { + return array( + array('foo', 'Element[foo]', ''), + array('*', 'Element[*]', ''), + array(':empty', 'Pseudo[Element[*]:empty]', ''), + array(':BEfore', 'Element[*]', 'before'), + array(':aftER', 'Element[*]', 'after'), + array(':First-Line', 'Element[*]', 'first-line'), + array(':First-Letter', 'Element[*]', 'first-letter'), + array('::befoRE', 'Element[*]', 'before'), + array('::AFter', 'Element[*]', 'after'), + array('::firsT-linE', 'Element[*]', 'first-line'), + array('::firsT-letteR', 'Element[*]', 'first-letter'), + array('::Selection', 'Element[*]', 'selection'), + array('foo:after', 'Element[foo]', 'after'), + array('foo::selection', 'Element[foo]', 'selection'), + array('lorem#ipsum ~ a#b.c[href]:empty::selection', 'CombinedSelector[Hash[Element[lorem]#ipsum] ~ Pseudo[Attribute[Class[Hash[Element[a]#b].c][href]]:empty]]', 'selection'), + ); + } + + public function getSpecificityTestData() + { + return array( + array('*', 0), + array(' foo', 1), + array(':empty ', 10), + array(':before', 1), + array('*:before', 1), + array(':nth-child(2)', 10), + array('.bar', 10), + array('[baz]', 10), + array('[baz="4"]', 10), + array('[baz^="4"]', 10), + array('#lipsum', 100), + array(':not(*)', 0), + array(':not(foo)', 1), + array(':not(.foo)', 10), + array(':not([foo])', 10), + array(':not(:empty)', 10), + array(':not(#foo)', 100), + array('foo:empty', 11), + array('foo:before', 2), + array('foo::before', 2), + array('foo:empty::before', 12), + array('#lorem + foo#ipsum:first-child > bar:first-line', 213), + ); + } + + public function getParseSeriesTestData() + { + return array( + array('1n+3', 1, 3), + array('1n +3', 1, 3), + array('1n + 3', 1, 3), + array('1n+ 3', 1, 3), + array('1n-3', 1, -3), + array('1n -3', 1, -3), + array('1n - 3', 1, -3), + array('1n- 3', 1, -3), + array('n-5', 1, -5), + array('odd', 2, 1), + array('even', 2, 0), + array('3n', 3, 0), + array('n', 1, 0), + array('+n', 1, 0), + array('-n', -1, 0), + array('5', 0, 5), + ); + } + + public function getParseSeriesExceptionTestData() + { + return array( + array('foo'), + array('n+'), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/ReaderTest.php b/vendor/symfony/css-selector/Tests/Parser/ReaderTest.php new file mode 100644 index 0000000..03c054e --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/ReaderTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser; + +use Symfony\Component\CssSelector\Parser\Reader; + +class ReaderTest extends \PHPUnit_Framework_TestCase +{ + public function testIsEOF() + { + $reader = new Reader(''); + $this->assertTrue($reader->isEOF()); + + $reader = new Reader('hello'); + $this->assertFalse($reader->isEOF()); + + $this->assignPosition($reader, 2); + $this->assertFalse($reader->isEOF()); + + $this->assignPosition($reader, 5); + $this->assertTrue($reader->isEOF()); + } + + public function testGetRemainingLength() + { + $reader = new Reader('hello'); + $this->assertEquals(5, $reader->getRemainingLength()); + + $this->assignPosition($reader, 2); + $this->assertEquals(3, $reader->getRemainingLength()); + + $this->assignPosition($reader, 5); + $this->assertEquals(0, $reader->getRemainingLength()); + } + + public function testGetSubstring() + { + $reader = new Reader('hello'); + $this->assertEquals('he', $reader->getSubstring(2)); + $this->assertEquals('el', $reader->getSubstring(2, 1)); + + $this->assignPosition($reader, 2); + $this->assertEquals('ll', $reader->getSubstring(2)); + $this->assertEquals('lo', $reader->getSubstring(2, 1)); + } + + public function testGetOffset() + { + $reader = new Reader('hello'); + $this->assertEquals(2, $reader->getOffset('ll')); + $this->assertFalse($reader->getOffset('w')); + + $this->assignPosition($reader, 2); + $this->assertEquals(0, $reader->getOffset('ll')); + $this->assertFalse($reader->getOffset('he')); + } + + public function testFindPattern() + { + $reader = new Reader('hello'); + + $this->assertFalse($reader->findPattern('/world/')); + $this->assertEquals(array('hello', 'h'), $reader->findPattern('/^([a-z]).*/')); + + $this->assignPosition($reader, 2); + $this->assertFalse($reader->findPattern('/^h.*/')); + $this->assertEquals(array('llo'), $reader->findPattern('/^llo$/')); + } + + public function testMoveForward() + { + $reader = new Reader('hello'); + $this->assertEquals(0, $reader->getPosition()); + + $reader->moveForward(2); + $this->assertEquals(2, $reader->getPosition()); + } + + public function testToEnd() + { + $reader = new Reader('hello'); + $reader->moveToEnd(); + $this->assertTrue($reader->isEOF()); + } + + private function assignPosition(Reader $reader, $value) + { + $position = new \ReflectionProperty($reader, 'position'); + $position->setAccessible(true); + $position->setValue($reader, $value); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Shortcut/ClassParserTest.php b/vendor/symfony/css-selector/Tests/Parser/Shortcut/ClassParserTest.php new file mode 100644 index 0000000..6efdd67 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Shortcut/ClassParserTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser; + +/** + * @author Jean-François Simon + */ +class ClassParserTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getParseTestData */ + public function testParse($source, $representation) + { + $parser = new ClassParser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($representation, (string) $selector->getTree()); + } + + public function getParseTestData() + { + return array( + array('.testclass', 'Class[Element[*].testclass]'), + array('testel.testclass', 'Class[Element[testel].testclass]'), + array('testns|.testclass', 'Class[Element[testns|*].testclass]'), + array('testns|*.testclass', 'Class[Element[testns|*].testclass]'), + array('testns|testel.testclass', 'Class[Element[testns|testel].testclass]'), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Shortcut/ElementParserTest.php b/vendor/symfony/css-selector/Tests/Parser/Shortcut/ElementParserTest.php new file mode 100644 index 0000000..b30b5ee --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Shortcut/ElementParserTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser; + +/** + * @author Jean-François Simon + */ +class ElementParserTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getParseTestData */ + public function testParse($source, $representation) + { + $parser = new ElementParser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($representation, (string) $selector->getTree()); + } + + public function getParseTestData() + { + return array( + array('*', 'Element[*]'), + array('testel', 'Element[testel]'), + array('testns|*', 'Element[testns|*]'), + array('testns|testel', 'Element[testns|testel]'), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Shortcut/EmptyStringParserTest.php b/vendor/symfony/css-selector/Tests/Parser/Shortcut/EmptyStringParserTest.php new file mode 100644 index 0000000..b7c3539 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Shortcut/EmptyStringParserTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser; + +/** + * @author Jean-François Simon + */ +class EmptyStringParserTest extends \PHPUnit_Framework_TestCase +{ + public function testParse() + { + $parser = new EmptyStringParser(); + $selectors = $parser->parse(''); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals('Element[*]', (string) $selector->getTree()); + + $selectors = $parser->parse('this will produce an empty array'); + $this->assertCount(0, $selectors); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/Shortcut/HashParserTest.php b/vendor/symfony/css-selector/Tests/Parser/Shortcut/HashParserTest.php new file mode 100644 index 0000000..d2ce891 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/Shortcut/HashParserTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Shortcut\HashParser; + +/** + * @author Jean-François Simon + */ +class HashParserTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getParseTestData */ + public function testParse($source, $representation) + { + $parser = new HashParser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($representation, (string) $selector->getTree()); + } + + public function getParseTestData() + { + return array( + array('#testid', 'Hash[Element[*]#testid]'), + array('testel#testid', 'Hash[Element[testel]#testid]'), + array('testns|#testid', 'Hash[Element[testns|*]#testid]'), + array('testns|*#testid', 'Hash[Element[testns|*]#testid]'), + array('testns|testel#testid', 'Hash[Element[testns|testel]#testid]'), + ); + } +} diff --git a/vendor/symfony/css-selector/Tests/Parser/TokenStreamTest.php b/vendor/symfony/css-selector/Tests/Parser/TokenStreamTest.php new file mode 100644 index 0000000..8f3253a --- /dev/null +++ b/vendor/symfony/css-selector/Tests/Parser/TokenStreamTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser; + +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +class TokenStreamTest extends \PHPUnit_Framework_TestCase +{ + public function testGetNext() + { + $stream = new TokenStream(); + $stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + $stream->push($t2 = new Token(Token::TYPE_DELIMITER, '.', 2)); + $stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'title', 3)); + + $this->assertSame($t1, $stream->getNext()); + $this->assertSame($t2, $stream->getNext()); + $this->assertSame($t3, $stream->getNext()); + } + + public function testGetPeek() + { + $stream = new TokenStream(); + $stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + $stream->push($t2 = new Token(Token::TYPE_DELIMITER, '.', 2)); + $stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'title', 3)); + + $this->assertSame($t1, $stream->getPeek()); + $this->assertSame($t1, $stream->getNext()); + $this->assertSame($t2, $stream->getPeek()); + $this->assertSame($t2, $stream->getPeek()); + $this->assertSame($t2, $stream->getNext()); + } + + public function testGetNextIdentifier() + { + $stream = new TokenStream(); + $stream->push(new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + + $this->assertEquals('h1', $stream->getNextIdentifier()); + } + + public function testFailToGetNextIdentifier() + { + $this->setExpectedException('Symfony\Component\CssSelector\Exception\SyntaxErrorException'); + + $stream = new TokenStream(); + $stream->push(new Token(Token::TYPE_DELIMITER, '.', 2)); + $stream->getNextIdentifier(); + } + + public function testGetNextIdentifierOrStar() + { + $stream = new TokenStream(); + + $stream->push(new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + $this->assertEquals('h1', $stream->getNextIdentifierOrStar()); + + $stream->push(new Token(Token::TYPE_DELIMITER, '*', 0)); + $this->assertNull($stream->getNextIdentifierOrStar()); + } + + public function testFailToGetNextIdentifierOrStar() + { + $this->setExpectedException('Symfony\Component\CssSelector\Exception\SyntaxErrorException'); + + $stream = new TokenStream(); + $stream->push(new Token(Token::TYPE_DELIMITER, '.', 2)); + $stream->getNextIdentifierOrStar(); + } + + public function testSkipWhitespace() + { + $stream = new TokenStream(); + $stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + $stream->push($t2 = new Token(Token::TYPE_WHITESPACE, ' ', 2)); + $stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'h1', 3)); + + $stream->skipWhitespace(); + $this->assertSame($t1, $stream->getNext()); + + $stream->skipWhitespace(); + $this->assertSame($t3, $stream->getNext()); + } +} diff --git a/vendor/symfony/css-selector/Tests/XPath/Fixtures/ids.html b/vendor/symfony/css-selector/Tests/XPath/Fixtures/ids.html new file mode 100644 index 0000000..5799fad --- /dev/null +++ b/vendor/symfony/css-selector/Tests/XPath/Fixtures/ids.html @@ -0,0 +1,48 @@ + + + + +
+ + + + link +
    +
  1. content
  2. +
  3. +
    +
    +
  4. +
  5. +
  6. +
  7. +
  8. +
  9. +
+

+ hi there + guy + + + + + + + +

+ + +
+

+
    +
+ + + + +
+
+ diff --git a/vendor/symfony/css-selector/Tests/XPath/Fixtures/lang.xml b/vendor/symfony/css-selector/Tests/XPath/Fixtures/lang.xml new file mode 100644 index 0000000..14f8dbe --- /dev/null +++ b/vendor/symfony/css-selector/Tests/XPath/Fixtures/lang.xml @@ -0,0 +1,11 @@ + + a + b + c + d + e + f + + + + diff --git a/vendor/symfony/css-selector/Tests/XPath/Fixtures/shakespear.html b/vendor/symfony/css-selector/Tests/XPath/Fixtures/shakespear.html new file mode 100644 index 0000000..15d1ad3 --- /dev/null +++ b/vendor/symfony/css-selector/Tests/XPath/Fixtures/shakespear.html @@ -0,0 +1,308 @@ + + + + + + +
+
+

As You Like It

+
+ by William Shakespeare +
+
+

ACT I, SCENE III. A room in the palace.

+
+
Enter CELIA and ROSALIND
+
+
CELIA
+
+
Why, cousin! why, Rosalind! Cupid have mercy! not a word?
+
+
ROSALIND
+
+
Not one to throw at a dog.
+
+
CELIA
+
+
No, thy words are too precious to be cast away upon
+
curs; throw some of them at me; come, lame me with reasons.
+
+
ROSALIND
+
CELIA
+
+
But is all this for your father?
+
+
+
Then there were two cousins laid up; when the one
+
should be lamed with reasons and the other mad
+
without any.
+
+
ROSALIND
+
+
No, some of it is for my child's father. O, how
+
full of briers is this working-day world!
+
+
CELIA
+
+
They are but burs, cousin, thrown upon thee in
+
holiday foolery: if we walk not in the trodden
+
paths our very petticoats will catch them.
+
+
ROSALIND
+
+
I could shake them off my coat: these burs are in my heart.
+
+
CELIA
+
+
Hem them away.
+
+
ROSALIND
+
+
I would try, if I could cry 'hem' and have him.
+
+
CELIA
+
+
Come, come, wrestle with thy affections.
+
+
ROSALIND
+
+
O, they take the part of a better wrestler than myself!
+
+
CELIA
+
+
O, a good wish upon you! you will try in time, in
+
despite of a fall. But, turning these jests out of
+
service, let us talk in good earnest: is it
+
possible, on such a sudden, you should fall into so
+
strong a liking with old Sir Rowland's youngest son?
+
+
ROSALIND
+
+
The duke my father loved his father dearly.
+
+
CELIA
+
+
Doth it therefore ensue that you should love his son
+
dearly? By this kind of chase, I should hate him,
+
for my father hated his father dearly; yet I hate
+
not Orlando.
+
+
ROSALIND
+
+
No, faith, hate him not, for my sake.
+
+
CELIA
+
+
Why should I not? doth he not deserve well?
+
+
ROSALIND
+
+
Let me love him for that, and do you love him
+
because I do. Look, here comes the duke.
+
+
CELIA
+
+
With his eyes full of anger.
+
Enter DUKE FREDERICK, with Lords
+
+
DUKE FREDERICK
+
+
Mistress, dispatch you with your safest haste
+
And get you from our court.
+
+
ROSALIND
+
+
Me, uncle?
+
+
DUKE FREDERICK
+
+
You, cousin
+
Within these ten days if that thou be'st found
+
So near our public court as twenty miles,
+
Thou diest for it.
+
+
ROSALIND
+
+
I do beseech your grace,
+
Let me the knowledge of my fault bear with me:
+
If with myself I hold intelligence
+
Or have acquaintance with mine own desires,
+
If that I do not dream or be not frantic,--
+
As I do trust I am not--then, dear uncle,
+
Never so much as in a thought unborn
+
Did I offend your highness.
+
+
DUKE FREDERICK
+
+
Thus do all traitors:
+
If their purgation did consist in words,
+
They are as innocent as grace itself:
+
Let it suffice thee that I trust thee not.
+
+
ROSALIND
+
+
Yet your mistrust cannot make me a traitor:
+
Tell me whereon the likelihood depends.
+
+
DUKE FREDERICK
+
+
Thou art thy father's daughter; there's enough.
+
+
ROSALIND
+
+
So was I when your highness took his dukedom;
+
So was I when your highness banish'd him:
+
Treason is not inherited, my lord;
+
Or, if we did derive it from our friends,
+
What's that to me? my father was no traitor:
+
Then, good my liege, mistake me not so much
+
To think my poverty is treacherous.
+
+
CELIA
+
+
Dear sovereign, hear me speak.
+
+
DUKE FREDERICK
+
+
Ay, Celia; we stay'd her for your sake,
+
Else had she with her father ranged along.
+
+
CELIA
+
+
I did not then entreat to have her stay;
+
It was your pleasure and your own remorse:
+
I was too young that time to value her;
+
But now I know her: if she be a traitor,
+
Why so am I; we still have slept together,
+
Rose at an instant, learn'd, play'd, eat together,
+
And wheresoever we went, like Juno's swans,
+
Still we went coupled and inseparable.
+
+
DUKE FREDERICK
+
+
She is too subtle for thee; and her smoothness,
+
Her very silence and her patience
+
Speak to the people, and they pity her.
+
Thou art a fool: she robs thee of thy name;
+
And thou wilt show more bright and seem more virtuous
+
When she is gone. Then open not thy lips:
+
Firm and irrevocable is my doom
+
Which I have pass'd upon her; she is banish'd.
+
+
CELIA
+
+
Pronounce that sentence then on me, my liege:
+
I cannot live out of her company.
+
+
DUKE FREDERICK
+
+
You are a fool. You, niece, provide yourself:
+
If you outstay the time, upon mine honour,
+
And in the greatness of my word, you die.
+
Exeunt DUKE FREDERICK and Lords
+
+
CELIA
+
+
O my poor Rosalind, whither wilt thou go?
+
Wilt thou change fathers? I will give thee mine.
+
I charge thee, be not thou more grieved than I am.
+
+
ROSALIND
+
+
I have more cause.
+
+
CELIA
+
+
Thou hast not, cousin;
+
Prithee be cheerful: know'st thou not, the duke
+
Hath banish'd me, his daughter?
+
+
ROSALIND
+
+
That he hath not.
+
+
CELIA
+
+
No, hath not? Rosalind lacks then the love
+
Which teacheth thee that thou and I am one:
+
Shall we be sunder'd? shall we part, sweet girl?
+
No: let my father seek another heir.
+
Therefore devise with me how we may fly,
+
Whither to go and what to bear with us;
+
And do not seek to take your change upon you,
+
To bear your griefs yourself and leave me out;
+
For, by this heaven, now at our sorrows pale,
+
Say what thou canst, I'll go along with thee.
+
+
ROSALIND
+
+
Why, whither shall we go?
+
+
CELIA
+
+
To seek my uncle in the forest of Arden.
+
+
ROSALIND
+
+
Alas, what danger will it be to us,
+
Maids as we are, to travel forth so far!
+
Beauty provoketh thieves sooner than gold.
+
+
CELIA
+
+
I'll put myself in poor and mean attire
+
And with a kind of umber smirch my face;
+
The like do you: so shall we pass along
+
And never stir assailants.
+
+
ROSALIND
+
+
Were it not better,
+
Because that I am more than common tall,
+
That I did suit me all points like a man?
+
A gallant curtle-axe upon my thigh,
+
A boar-spear in my hand; and--in my heart
+
Lie there what hidden woman's fear there will--
+
We'll have a swashing and a martial outside,
+
As many other mannish cowards have
+
That do outface it with their semblances.
+
+
CELIA
+
+
What shall I call thee when thou art a man?
+
+
ROSALIND
+
+
I'll have no worse a name than Jove's own page;
+
And therefore look you call me Ganymede.
+
But what will you be call'd?
+
+
CELIA
+
+
Something that hath a reference to my state
+
No longer Celia, but Aliena.
+
+
ROSALIND
+
+
But, cousin, what if we assay'd to steal
+
The clownish fool out of your father's court?
+
Would he not be a comfort to our travel?
+
+
CELIA
+
+
He'll go along o'er the wide world with me;
+
Leave me alone to woo him. Let's away,
+
And get our jewels and our wealth together,
+
Devise the fittest time and safest way
+
To hide us from pursuit that will be made
+
After my flight. Now go we in content
+
To liberty and not to banishment.
+
Exeunt
+
+
+
+
+ + diff --git a/vendor/symfony/css-selector/Tests/XPath/TranslatorTest.php b/vendor/symfony/css-selector/Tests/XPath/TranslatorTest.php new file mode 100644 index 0000000..407458f --- /dev/null +++ b/vendor/symfony/css-selector/Tests/XPath/TranslatorTest.php @@ -0,0 +1,324 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\XPath; + +use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension; +use Symfony\Component\CssSelector\XPath\Translator; + +class TranslatorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getXpathLiteralTestData */ + public function testXpathLiteral($value, $literal) + { + $this->assertEquals($literal, Translator::getXpathLiteral($value)); + } + + /** @dataProvider getCssToXPathTestData */ + public function testCssToXPath($css, $xpath) + { + $translator = new Translator(); + $translator->registerExtension(new HtmlExtension($translator)); + $this->assertEquals($xpath, $translator->cssToXPath($css, '')); + } + + /** @dataProvider getXmlLangTestData */ + public function testXmlLang($css, array $elementsId) + { + $translator = new Translator(); + $document = new \SimpleXMLElement(file_get_contents(__DIR__.'/Fixtures/lang.xml')); + $elements = $document->xpath($translator->cssToXPath($css)); + $this->assertEquals(count($elementsId), count($elements)); + foreach ($elements as $element) { + $this->assertTrue(in_array($element->attributes()->id, $elementsId)); + } + } + + /** @dataProvider getHtmlIdsTestData */ + public function testHtmlIds($css, array $elementsId) + { + $translator = new Translator(); + $translator->registerExtension(new HtmlExtension($translator)); + $document = new \DOMDocument(); + $document->strictErrorChecking = false; + $internalErrors = libxml_use_internal_errors(true); + $document->loadHTMLFile(__DIR__.'/Fixtures/ids.html'); + $document = simplexml_import_dom($document); + $elements = $document->xpath($translator->cssToXPath($css)); + $this->assertCount(count($elementsId), $elementsId); + foreach ($elements as $element) { + if (null !== $element->attributes()->id) { + $this->assertTrue(in_array($element->attributes()->id, $elementsId)); + } + } + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + /** @dataProvider getHtmlShakespearTestData */ + public function testHtmlShakespear($css, $count) + { + $translator = new Translator(); + $translator->registerExtension(new HtmlExtension($translator)); + $document = new \DOMDocument(); + $document->strictErrorChecking = false; + $document->loadHTMLFile(__DIR__.'/Fixtures/shakespear.html'); + $document = simplexml_import_dom($document); + $bodies = $document->xpath('//body'); + $elements = $bodies[0]->xpath($translator->cssToXPath($css)); + $this->assertCount($count, $elements); + } + + public function getXpathLiteralTestData() + { + return array( + array('foo', "'foo'"), + array("foo's bar", '"foo\'s bar"'), + array("foo's \"middle\" bar", 'concat(\'foo\', "\'", \'s "middle" bar\')'), + array("foo's 'middle' \"bar\"", 'concat(\'foo\', "\'", \'s \', "\'", \'middle\', "\'", \' "bar"\')'), + ); + } + + public function getCssToXPathTestData() + { + return array( + array('*', '*'), + array('e', 'e'), + array('*|e', 'e'), + array('e|f', 'e:f'), + array('e[foo]', 'e[@foo]'), + array('e[foo|bar]', 'e[@foo:bar]'), + array('e[foo="bar"]', "e[@foo = 'bar']"), + array('e[foo~="bar"]', "e[@foo and contains(concat(' ', normalize-space(@foo), ' '), ' bar ')]"), + array('e[foo^="bar"]', "e[@foo and starts-with(@foo, 'bar')]"), + array('e[foo$="bar"]', "e[@foo and substring(@foo, string-length(@foo)-2) = 'bar']"), + array('e[foo*="bar"]', "e[@foo and contains(@foo, 'bar')]"), + array('e[hreflang|="en"]', "e[@hreflang and (@hreflang = 'en' or starts-with(@hreflang, 'en-'))]"), + array('e:nth-child(1)', "*/*[name() = 'e' and (position() = 1)]"), + array('e:nth-last-child(1)', "*/*[name() = 'e' and (position() = last() - 0)]"), + array('e:nth-last-child(2n+2)', "*/*[name() = 'e' and (last() - position() - 1 >= 0 and (last() - position() - 1) mod 2 = 0)]"), + array('e:nth-of-type(1)', '*/e[position() = 1]'), + array('e:nth-last-of-type(1)', '*/e[position() = last() - 0]'), + array('div e:nth-last-of-type(1) .aclass', "div/descendant-or-self::*/e[position() = last() - 0]/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' aclass ')]"), + array('e:first-child', "*/*[name() = 'e' and (position() = 1)]"), + array('e:last-child', "*/*[name() = 'e' and (position() = last())]"), + array('e:first-of-type', '*/e[position() = 1]'), + array('e:last-of-type', '*/e[position() = last()]'), + array('e:only-child', "*/*[name() = 'e' and (last() = 1)]"), + array('e:only-of-type', 'e[last() = 1]'), + array('e:empty', 'e[not(*) and not(string-length())]'), + array('e:EmPTY', 'e[not(*) and not(string-length())]'), + array('e:root', 'e[not(parent::*)]'), + array('e:hover', 'e[0]'), + array('e:contains("foo")', "e[contains(string(.), 'foo')]"), + array('e:ConTains(foo)', "e[contains(string(.), 'foo')]"), + array('e.warning', "e[@class and contains(concat(' ', normalize-space(@class), ' '), ' warning ')]"), + array('e#myid', "e[@id = 'myid']"), + array('e:not(:nth-child(odd))', 'e[not(position() - 1 >= 0 and (position() - 1) mod 2 = 0)]'), + array('e:nOT(*)', 'e[0]'), + array('e f', 'e/descendant-or-self::*/f'), + array('e > f', 'e/f'), + array('e + f', "e/following-sibling::*[name() = 'f' and (position() = 1)]"), + array('e ~ f', 'e/following-sibling::f'), + array('div#container p', "div[@id = 'container']/descendant-or-self::*/p"), + ); + } + + public function getXmlLangTestData() + { + return array( + array(':lang("EN")', array('first', 'second', 'third', 'fourth')), + array(':lang("en-us")', array('second', 'fourth')), + array(':lang(en-nz)', array('third')), + array(':lang(fr)', array('fifth')), + array(':lang(ru)', array('sixth')), + array(":lang('ZH')", array('eighth')), + array(':lang(de) :lang(zh)', array('eighth')), + array(':lang(en), :lang(zh)', array('first', 'second', 'third', 'fourth', 'eighth')), + array(':lang(es)', array()), + ); + } + + public function getHtmlIdsTestData() + { + return array( + array('div', array('outer-div', 'li-div', 'foobar-div')), + array('DIV', array('outer-div', 'li-div', 'foobar-div')), // case-insensitive in HTML + array('div div', array('li-div')), + array('div, div div', array('outer-div', 'li-div', 'foobar-div')), + array('a[name]', array('name-anchor')), + array('a[NAme]', array('name-anchor')), // case-insensitive in HTML: + array('a[rel]', array('tag-anchor', 'nofollow-anchor')), + array('a[rel="tag"]', array('tag-anchor')), + array('a[href*="localhost"]', array('tag-anchor')), + array('a[href*=""]', array()), + array('a[href^="http"]', array('tag-anchor', 'nofollow-anchor')), + array('a[href^="http:"]', array('tag-anchor')), + array('a[href^=""]', array()), + array('a[href$="org"]', array('nofollow-anchor')), + array('a[href$=""]', array()), + array('div[foobar~="bc"]', array('foobar-div')), + array('div[foobar~="cde"]', array('foobar-div')), + array('[foobar~="ab bc"]', array('foobar-div')), + array('[foobar~=""]', array()), + array('[foobar~=" \t"]', array()), + array('div[foobar~="cd"]', array()), + array('*[lang|="En"]', array('second-li')), + array('[lang|="En-us"]', array('second-li')), + // Attribute values are case sensitive + array('*[lang|="en"]', array()), + array('[lang|="en-US"]', array()), + array('*[lang|="e"]', array()), + // ... :lang() is not. + array(':lang("EN")', array('second-li', 'li-div')), + array('*:lang(en-US)', array('second-li', 'li-div')), + array(':lang("e")', array()), + array('li:nth-child(3)', array('third-li')), + array('li:nth-child(10)', array()), + array('li:nth-child(2n)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-child(even)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-child(2n+0)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-child(+2n+1)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')), + array('li:nth-child(odd)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')), + array('li:nth-child(2n+4)', array('fourth-li', 'sixth-li')), + array('li:nth-child(3n+1)', array('first-li', 'fourth-li', 'seventh-li')), + array('li:nth-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-child(n+3)', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-child(-n)', array()), + array('li:nth-child(-n-1)', array()), + array('li:nth-child(-n+1)', array('first-li')), + array('li:nth-child(-n+3)', array('first-li', 'second-li', 'third-li')), + array('li:nth-last-child(0)', array()), + array('li:nth-last-child(2n)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-last-child(even)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-last-child(2n+2)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-last-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-last-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-last-child(n-3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-last-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-last-child(n+3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li')), + array('li:nth-last-child(-n)', array()), + array('li:nth-last-child(-n-1)', array()), + array('li:nth-last-child(-n+1)', array('seventh-li')), + array('li:nth-last-child(-n+3)', array('fifth-li', 'sixth-li', 'seventh-li')), + array('ol:first-of-type', array('first-ol')), + array('ol:nth-child(1)', array('first-ol')), + array('ol:nth-of-type(2)', array('second-ol')), + array('ol:nth-last-of-type(1)', array('second-ol')), + array('span:only-child', array('foobar-span')), + array('li div:only-child', array('li-div')), + array('div *:only-child', array('li-div', 'foobar-span')), + array('p:only-of-type', array('paragraph')), + array('a:empty', array('name-anchor')), + array('a:EMpty', array('name-anchor')), + array('li:empty', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li')), + array(':root', array('html')), + array('html:root', array('html')), + array('li:root', array()), + array('* :root', array()), + array('*:contains("link")', array('html', 'outer-div', 'tag-anchor', 'nofollow-anchor')), + array(':CONtains("link")', array('html', 'outer-div', 'tag-anchor', 'nofollow-anchor')), + array('*:contains("LInk")', array()), // case sensitive + array('*:contains("e")', array('html', 'nil', 'outer-div', 'first-ol', 'first-li', 'paragraph', 'p-em')), + array('*:contains("E")', array()), // case-sensitive + array('.a', array('first-ol')), + array('.b', array('first-ol')), + array('*.a', array('first-ol')), + array('ol.a', array('first-ol')), + array('.c', array('first-ol', 'third-li', 'fourth-li')), + array('*.c', array('first-ol', 'third-li', 'fourth-li')), + array('ol *.c', array('third-li', 'fourth-li')), + array('ol li.c', array('third-li', 'fourth-li')), + array('li ~ li.c', array('third-li', 'fourth-li')), + array('ol > li.c', array('third-li', 'fourth-li')), + array('#first-li', array('first-li')), + array('li#first-li', array('first-li')), + array('*#first-li', array('first-li')), + array('li div', array('li-div')), + array('li > div', array('li-div')), + array('div div', array('li-div')), + array('div > div', array()), + array('div>.c', array('first-ol')), + array('div > .c', array('first-ol')), + array('div + div', array('foobar-div')), + array('a ~ a', array('tag-anchor', 'nofollow-anchor')), + array('a[rel="tag"] ~ a', array('nofollow-anchor')), + array('ol#first-ol li:last-child', array('seventh-li')), + array('ol#first-ol *:last-child', array('li-div', 'seventh-li')), + array('#outer-div:first-child', array('outer-div')), + array('#outer-div :first-child', array('name-anchor', 'first-li', 'li-div', 'p-b', 'checkbox-fieldset-disabled', 'area-href')), + array('a[href]', array('tag-anchor', 'nofollow-anchor')), + array(':not(*)', array()), + array('a:not([href])', array('name-anchor')), + array('ol :Not(li[class])', array('first-li', 'second-li', 'li-div', 'fifth-li', 'sixth-li', 'seventh-li')), + // HTML-specific + array(':link', array('link-href', 'tag-anchor', 'nofollow-anchor', 'area-href')), + array(':visited', array()), + array(':enabled', array('link-href', 'tag-anchor', 'nofollow-anchor', 'checkbox-unchecked', 'text-checked', 'checkbox-checked', 'area-href')), + array(':disabled', array('checkbox-disabled', 'checkbox-disabled-checked', 'fieldset', 'checkbox-fieldset-disabled')), + array(':checked', array('checkbox-checked', 'checkbox-disabled-checked')), + ); + } + + public function getHtmlShakespearTestData() + { + return array( + array('*', 246), + array('div:contains(CELIA)', 26), + array('div:only-child', 22), // ? + array('div:nth-child(even)', 106), + array('div:nth-child(2n)', 106), + array('div:nth-child(odd)', 137), + array('div:nth-child(2n+1)', 137), + array('div:nth-child(n)', 243), + array('div:last-child', 53), + array('div:first-child', 51), + array('div > div', 242), + array('div + div', 190), + array('div ~ div', 190), + array('body', 1), + array('body div', 243), + array('div', 243), + array('div div', 242), + array('div div div', 241), + array('div, div, div', 243), + array('div, a, span', 243), + array('.dialog', 51), + array('div.dialog', 51), + array('div .dialog', 51), + array('div.character, div.dialog', 99), + array('div.direction.dialog', 0), + array('div.dialog.direction', 0), + array('div.dialog.scene', 1), + array('div.scene.scene', 1), + array('div.scene .scene', 0), + array('div.direction .dialog ', 0), + array('div .dialog .direction', 4), + array('div.dialog .dialog .direction', 4), + array('#speech5', 1), + array('div#speech5', 1), + array('div #speech5', 1), + array('div.scene div.dialog', 49), + array('div#scene1 div.dialog div', 142), + array('#scene1 #speech1', 1), + array('div[class]', 103), + array('div[class=dialog]', 50), + array('div[class^=dia]', 51), + array('div[class$=log]', 50), + array('div[class*=sce]', 1), + array('div[class|=dialog]', 50), // ? Seems right + array('div[class!=madeup]', 243), // ? Seems right + array('div[class~=dialog]', 51), // ? Seems right + ); + } +} diff --git a/vendor/symfony/css-selector/XPath/Extension/AbstractExtension.php b/vendor/symfony/css-selector/XPath/Extension/AbstractExtension.php new file mode 100644 index 0000000..026ac06 --- /dev/null +++ b/vendor/symfony/css-selector/XPath/Extension/AbstractExtension.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +/** + * XPath expression translator abstract extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +abstract class AbstractExtension implements ExtensionInterface +{ + /** + * {@inheritdoc} + */ + public function getNodeTranslators() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getCombinationTranslators() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getFunctionTranslators() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getPseudoClassTranslators() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getAttributeMatchingTranslators() + { + return array(); + } +} diff --git a/vendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php b/vendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php new file mode 100644 index 0000000..6ace8b5 --- /dev/null +++ b/vendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\XPath\Translator; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator attribute extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class AttributeMatchingExtension extends AbstractExtension +{ + /** + * {@inheritdoc} + */ + public function getAttributeMatchingTranslators() + { + return array( + 'exists' => array($this, 'translateExists'), + '=' => array($this, 'translateEquals'), + '~=' => array($this, 'translateIncludes'), + '|=' => array($this, 'translateDashMatch'), + '^=' => array($this, 'translatePrefixMatch'), + '$=' => array($this, 'translateSuffixMatch'), + '*=' => array($this, 'translateSubstringMatch'), + '!=' => array($this, 'translateDifferent'), + ); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateExists(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($attribute); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateEquals(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value))); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateIncludes(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($value ? sprintf( + '%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)', + $attribute, + Translator::getXpathLiteral(' '.$value.' ') + ) : '0'); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateDashMatch(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition(sprintf( + '%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))', + $attribute, + Translator::getXpathLiteral($value), + Translator::getXpathLiteral($value.'-') + )); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translatePrefixMatch(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($value ? sprintf( + '%1$s and starts-with(%1$s, %2$s)', + $attribute, + Translator::getXpathLiteral($value) + ) : '0'); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateSuffixMatch(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($value ? sprintf( + '%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s', + $attribute, + strlen($value) - 1, + Translator::getXpathLiteral($value) + ) : '0'); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateSubstringMatch(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($value ? sprintf( + '%1$s and contains(%1$s, %2$s)', + $attribute, + Translator::getXpathLiteral($value) + ) : '0'); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateDifferent(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition(sprintf( + $value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s', + $attribute, + Translator::getXpathLiteral($value) + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'attribute-matching'; + } +} diff --git a/vendor/symfony/css-selector/XPath/Extension/CombinationExtension.php b/vendor/symfony/css-selector/XPath/Extension/CombinationExtension.php new file mode 100644 index 0000000..0d2d658 --- /dev/null +++ b/vendor/symfony/css-selector/XPath/Extension/CombinationExtension.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator combination extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class CombinationExtension extends AbstractExtension +{ + /** + * {@inheritdoc} + */ + public function getCombinationTranslators() + { + return array( + ' ' => array($this, 'translateDescendant'), + '>' => array($this, 'translateChild'), + '+' => array($this, 'translateDirectAdjacent'), + '~' => array($this, 'translateIndirectAdjacent'), + ); + } + + /** + * @param XPathExpr $xpath + * @param XPathExpr $combinedXpath + * + * @return XPathExpr + */ + public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath) + { + return $xpath->join('/descendant-or-self::*/', $combinedXpath); + } + + /** + * @param XPathExpr $xpath + * @param XPathExpr $combinedXpath + * + * @return XPathExpr + */ + public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath) + { + return $xpath->join('/', $combinedXpath); + } + + /** + * @param XPathExpr $xpath + * @param XPathExpr $combinedXpath + * + * @return XPathExpr + */ + public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath) + { + return $xpath + ->join('/following-sibling::', $combinedXpath) + ->addNameTest() + ->addCondition('position() = 1'); + } + + /** + * @param XPathExpr $xpath + * @param XPathExpr $combinedXpath + * + * @return XPathExpr + */ + public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath) + { + return $xpath->join('/following-sibling::', $combinedXpath); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'combination'; + } +} diff --git a/vendor/symfony/css-selector/XPath/Extension/ExtensionInterface.php b/vendor/symfony/css-selector/XPath/Extension/ExtensionInterface.php new file mode 100644 index 0000000..3607022 --- /dev/null +++ b/vendor/symfony/css-selector/XPath/Extension/ExtensionInterface.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +/** + * XPath expression translator extension interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface ExtensionInterface +{ + /** + * Returns node translators. + * + * These callables will receive the node as first argument and the translator as second argument. + * + * @return callable[] + */ + public function getNodeTranslators(); + + /** + * Returns combination translators. + * + * @return callable[] + */ + public function getCombinationTranslators(); + + /** + * Returns function translators. + * + * @return callable[] + */ + public function getFunctionTranslators(); + + /** + * Returns pseudo-class translators. + * + * @return callable[] + */ + public function getPseudoClassTranslators(); + + /** + * Returns attribute operation translators. + * + * @return callable[] + */ + public function getAttributeMatchingTranslators(); + + /** + * Returns extension name. + * + * @return string + */ + public function getName(); +} diff --git a/vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php b/vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php new file mode 100644 index 0000000..ea05523 --- /dev/null +++ b/vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php @@ -0,0 +1,211 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\Exception\ExpressionErrorException; +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\Parser\Parser; +use Symfony\Component\CssSelector\XPath\Translator; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator function extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class FunctionExtension extends AbstractExtension +{ + /** + * {@inheritdoc} + */ + public function getFunctionTranslators() + { + return array( + 'nth-child' => array($this, 'translateNthChild'), + 'nth-last-child' => array($this, 'translateNthLastChild'), + 'nth-of-type' => array($this, 'translateNthOfType'), + 'nth-last-of-type' => array($this, 'translateNthLastOfType'), + 'contains' => array($this, 'translateContains'), + 'lang' => array($this, 'translateLang'), + ); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * @param bool $last + * @param bool $addNameTest + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateNthChild(XPathExpr $xpath, FunctionNode $function, $last = false, $addNameTest = true) + { + try { + list($a, $b) = Parser::parseSeries($function->getArguments()); + } catch (SyntaxErrorException $e) { + throw new ExpressionErrorException(sprintf('Invalid series: %s', implode(', ', $function->getArguments())), 0, $e); + } + + $xpath->addStarPrefix(); + if ($addNameTest) { + $xpath->addNameTest(); + } + + if (0 === $a) { + return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b)); + } + + if ($a < 0) { + if ($b < 1) { + return $xpath->addCondition('false()'); + } + + $sign = '<='; + } else { + $sign = '>='; + } + + $expr = 'position()'; + + if ($last) { + $expr = 'last() - '.$expr; + --$b; + } + + if (0 !== $b) { + $expr .= ' - '.$b; + } + + $conditions = array(sprintf('%s %s 0', $expr, $sign)); + + if (1 !== $a && -1 !== $a) { + $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a); + } + + return $xpath->addCondition(implode(' and ', $conditions)); + + // todo: handle an+b, odd, even + // an+b means every-a, plus b, e.g., 2n+1 means odd + // 0n+b means b + // n+0 means a=1, i.e., all elements + // an means every a elements, i.e., 2n means even + // -n means -1n + // -1n+6 means elements 6 and previous + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + */ + public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function) + { + return $this->translateNthChild($xpath, $function, true); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + */ + public function translateNthOfType(XPathExpr $xpath, FunctionNode $function) + { + return $this->translateNthChild($xpath, $function, false, false); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function) + { + if ('*' === $xpath->getElement()) { + throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.'); + } + + return $this->translateNthChild($xpath, $function, true, false); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateContains(XPathExpr $xpath, FunctionNode $function) + { + $arguments = $function->getArguments(); + foreach ($arguments as $token) { + if (!($token->isString() || $token->isIdentifier())) { + throw new ExpressionErrorException( + 'Expected a single string or identifier for :contains(), got ' + .implode(', ', $arguments) + ); + } + } + + return $xpath->addCondition(sprintf( + 'contains(string(.), %s)', + Translator::getXpathLiteral($arguments[0]->getValue()) + )); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateLang(XPathExpr $xpath, FunctionNode $function) + { + $arguments = $function->getArguments(); + foreach ($arguments as $token) { + if (!($token->isString() || $token->isIdentifier())) { + throw new ExpressionErrorException( + 'Expected a single string or identifier for :lang(), got ' + .implode(', ', $arguments) + ); + } + } + + return $xpath->addCondition(sprintf( + 'lang(%s)', + Translator::getXpathLiteral($arguments[0]->getValue()) + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'function'; + } +} diff --git a/vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php b/vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php new file mode 100644 index 0000000..de6ce41 --- /dev/null +++ b/vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php @@ -0,0 +1,240 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\Exception\ExpressionErrorException; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\XPath\Translator; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator HTML extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class HtmlExtension extends AbstractExtension +{ + /** + * Constructor. + * + * @param Translator $translator + */ + public function __construct(Translator $translator) + { + $translator + ->getExtension('node') + ->setFlag(NodeExtension::ELEMENT_NAME_IN_LOWER_CASE, true) + ->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true); + } + + /** + * {@inheritdoc} + */ + public function getPseudoClassTranslators() + { + return array( + 'checked' => array($this, 'translateChecked'), + 'link' => array($this, 'translateLink'), + 'disabled' => array($this, 'translateDisabled'), + 'enabled' => array($this, 'translateEnabled'), + 'selected' => array($this, 'translateSelected'), + 'invalid' => array($this, 'translateInvalid'), + 'hover' => array($this, 'translateHover'), + 'visited' => array($this, 'translateVisited'), + ); + } + + /** + * {@inheritdoc} + */ + public function getFunctionTranslators() + { + return array( + 'lang' => array($this, 'translateLang'), + ); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateChecked(XPathExpr $xpath) + { + return $xpath->addCondition( + '(@checked ' + ."and (name(.) = 'input' or name(.) = 'command')" + ."and (@type = 'checkbox' or @type = 'radio'))" + ); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateLink(XPathExpr $xpath) + { + return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')"); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateDisabled(XPathExpr $xpath) + { + return $xpath->addCondition( + '(' + .'@disabled and' + .'(' + ."(name(.) = 'input' and @type != 'hidden')" + ." or name(.) = 'button'" + ." or name(.) = 'select'" + ." or name(.) = 'textarea'" + ." or name(.) = 'command'" + ." or name(.) = 'fieldset'" + ." or name(.) = 'optgroup'" + ." or name(.) = 'option'" + .')' + .') or (' + ."(name(.) = 'input' and @type != 'hidden')" + ." or name(.) = 'button'" + ." or name(.) = 'select'" + ." or name(.) = 'textarea'" + .')' + .' and ancestor::fieldset[@disabled]' + ); + // todo: in the second half, add "and is not a descendant of that fieldset element's first legend element child, if any." + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateEnabled(XPathExpr $xpath) + { + return $xpath->addCondition( + '(' + .'@href and (' + ."name(.) = 'a'" + ." or name(.) = 'link'" + ." or name(.) = 'area'" + .')' + .') or (' + .'(' + ."name(.) = 'command'" + ." or name(.) = 'fieldset'" + ." or name(.) = 'optgroup'" + .')' + .' and not(@disabled)' + .') or (' + .'(' + ."(name(.) = 'input' and @type != 'hidden')" + ." or name(.) = 'button'" + ." or name(.) = 'select'" + ." or name(.) = 'textarea'" + ." or name(.) = 'keygen'" + .')' + .' and not (@disabled or ancestor::fieldset[@disabled])' + .') or (' + ."name(.) = 'option' and not(" + .'@disabled or ancestor::optgroup[@disabled]' + .')' + .')' + ); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateLang(XPathExpr $xpath, FunctionNode $function) + { + $arguments = $function->getArguments(); + foreach ($arguments as $token) { + if (!($token->isString() || $token->isIdentifier())) { + throw new ExpressionErrorException( + 'Expected a single string or identifier for :lang(), got ' + .implode(', ', $arguments) + ); + } + } + + return $xpath->addCondition(sprintf( + 'ancestor-or-self::*[@lang][1][starts-with(concat(' + ."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')" + .', %s)]', + 'lang', + Translator::getXpathLiteral(strtolower($arguments[0]->getValue()).'-') + )); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateSelected(XPathExpr $xpath) + { + return $xpath->addCondition("(@selected and name(.) = 'option')"); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateInvalid(XPathExpr $xpath) + { + return $xpath->addCondition('0'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateHover(XPathExpr $xpath) + { + return $xpath->addCondition('0'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateVisited(XPathExpr $xpath) + { + return $xpath->addCondition('0'); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'html'; + } +} diff --git a/vendor/symfony/css-selector/XPath/Extension/NodeExtension.php b/vendor/symfony/css-selector/XPath/Extension/NodeExtension.php new file mode 100644 index 0000000..9d7f8fa --- /dev/null +++ b/vendor/symfony/css-selector/XPath/Extension/NodeExtension.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\Node; +use Symfony\Component\CssSelector\XPath\Translator; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator node extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class NodeExtension extends AbstractExtension +{ + const ELEMENT_NAME_IN_LOWER_CASE = 1; + const ATTRIBUTE_NAME_IN_LOWER_CASE = 2; + const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4; + + /** + * @var int + */ + private $flags; + + /** + * Constructor. + * + * @param int $flags + */ + public function __construct($flags = 0) + { + $this->flags = $flags; + } + + /** + * @param int $flag + * @param bool $on + * + * @return NodeExtension + */ + public function setFlag($flag, $on) + { + if ($on && !$this->hasFlag($flag)) { + $this->flags += $flag; + } + + if (!$on && $this->hasFlag($flag)) { + $this->flags -= $flag; + } + + return $this; + } + + /** + * @param int $flag + * + * @return bool + */ + public function hasFlag($flag) + { + return $this->flags & $flag; + } + + /** + * {@inheritdoc} + */ + public function getNodeTranslators() + { + return array( + 'Selector' => array($this, 'translateSelector'), + 'CombinedSelector' => array($this, 'translateCombinedSelector'), + 'Negation' => array($this, 'translateNegation'), + 'Function' => array($this, 'translateFunction'), + 'Pseudo' => array($this, 'translatePseudo'), + 'Attribute' => array($this, 'translateAttribute'), + 'Class' => array($this, 'translateClass'), + 'Hash' => array($this, 'translateHash'), + 'Element' => array($this, 'translateElement'), + ); + } + + /** + * @param Node\SelectorNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateSelector(Node\SelectorNode $node, Translator $translator) + { + return $translator->nodeToXPath($node->getTree()); + } + + /** + * @param Node\CombinedSelectorNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator) + { + return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector()); + } + + /** + * @param Node\NegationNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateNegation(Node\NegationNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + $subXpath = $translator->nodeToXPath($node->getSubSelector()); + $subXpath->addNameTest(); + + if ($subXpath->getCondition()) { + return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition())); + } + + return $xpath->addCondition('0'); + } + + /** + * @param Node\FunctionNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateFunction(Node\FunctionNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + + return $translator->addFunction($xpath, $node); + } + + /** + * @param Node\PseudoNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translatePseudo(Node\PseudoNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + + return $translator->addPseudoClass($xpath, $node->getIdentifier()); + } + + /** + * @param Node\AttributeNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateAttribute(Node\AttributeNode $node, Translator $translator) + { + $name = $node->getAttribute(); + $safe = $this->isSafeName($name); + + if ($this->hasFlag(self::ATTRIBUTE_NAME_IN_LOWER_CASE)) { + $name = strtolower($name); + } + + if ($node->getNamespace()) { + $name = sprintf('%s:%s', $node->getNamespace(), $name); + $safe = $safe && $this->isSafeName($node->getNamespace()); + } + + $attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name)); + $value = $node->getValue(); + $xpath = $translator->nodeToXPath($node->getSelector()); + + if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) { + $value = strtolower($value); + } + + return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value); + } + + /** + * @param Node\ClassNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateClass(Node\ClassNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + + return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName()); + } + + /** + * @param Node\HashNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateHash(Node\HashNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + + return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId()); + } + + /** + * @param Node\ElementNode $node + * + * @return XPathExpr + */ + public function translateElement(Node\ElementNode $node) + { + $element = $node->getElement(); + + if ($this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) { + $element = strtolower($element); + } + + if ($element) { + $safe = $this->isSafeName($element); + } else { + $element = '*'; + $safe = true; + } + + if ($node->getNamespace()) { + $element = sprintf('%s:%s', $node->getNamespace(), $element); + $safe = $safe && $this->isSafeName($node->getNamespace()); + } + + $xpath = new XPathExpr('', $element); + + if (!$safe) { + $xpath->addNameTest(); + } + + return $xpath; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'node'; + } + + /** + * Tests if given name is safe. + * + * @param string $name + * + * @return bool + */ + private function isSafeName($name) + { + return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name); + } +} diff --git a/vendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.php b/vendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.php new file mode 100644 index 0000000..1c8b217 --- /dev/null +++ b/vendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\Exception\ExpressionErrorException; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator pseudo-class extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class PseudoClassExtension extends AbstractExtension +{ + /** + * {@inheritdoc} + */ + public function getPseudoClassTranslators() + { + return array( + 'root' => array($this, 'translateRoot'), + 'first-child' => array($this, 'translateFirstChild'), + 'last-child' => array($this, 'translateLastChild'), + 'first-of-type' => array($this, 'translateFirstOfType'), + 'last-of-type' => array($this, 'translateLastOfType'), + 'only-child' => array($this, 'translateOnlyChild'), + 'only-of-type' => array($this, 'translateOnlyOfType'), + 'empty' => array($this, 'translateEmpty'), + ); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateRoot(XPathExpr $xpath) + { + return $xpath->addCondition('not(parent::*)'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateFirstChild(XPathExpr $xpath) + { + return $xpath + ->addStarPrefix() + ->addNameTest() + ->addCondition('position() = 1'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateLastChild(XPathExpr $xpath) + { + return $xpath + ->addStarPrefix() + ->addNameTest() + ->addCondition('position() = last()'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateFirstOfType(XPathExpr $xpath) + { + if ('*' === $xpath->getElement()) { + throw new ExpressionErrorException('"*:first-of-type" is not implemented.'); + } + + return $xpath + ->addStarPrefix() + ->addCondition('position() = 1'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateLastOfType(XPathExpr $xpath) + { + if ('*' === $xpath->getElement()) { + throw new ExpressionErrorException('"*:last-of-type" is not implemented.'); + } + + return $xpath + ->addStarPrefix() + ->addCondition('position() = last()'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateOnlyChild(XPathExpr $xpath) + { + return $xpath + ->addStarPrefix() + ->addNameTest() + ->addCondition('last() = 1'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateOnlyOfType(XPathExpr $xpath) + { + if ('*' === $xpath->getElement()) { + throw new ExpressionErrorException('"*:only-of-type" is not implemented.'); + } + + return $xpath->addCondition('last() = 1'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateEmpty(XPathExpr $xpath) + { + return $xpath->addCondition('not(*) and not(string-length())'); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pseudo-class'; + } +} diff --git a/vendor/symfony/css-selector/XPath/Translator.php b/vendor/symfony/css-selector/XPath/Translator.php new file mode 100644 index 0000000..8c021b3 --- /dev/null +++ b/vendor/symfony/css-selector/XPath/Translator.php @@ -0,0 +1,301 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath; + +use Symfony\Component\CssSelector\Exception\ExpressionErrorException; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\Node\NodeInterface; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Parser; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * XPath expression translator interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Translator implements TranslatorInterface +{ + /** + * @var ParserInterface + */ + private $mainParser; + + /** + * @var ParserInterface[] + */ + private $shortcutParsers = array(); + + /** + * @var Extension\ExtensionInterface + */ + private $extensions = array(); + + /** + * @var array + */ + private $nodeTranslators = array(); + + /** + * @var array + */ + private $combinationTranslators = array(); + + /** + * @var array + */ + private $functionTranslators = array(); + + /** + * @var array + */ + private $pseudoClassTranslators = array(); + + /** + * @var array + */ + private $attributeMatchingTranslators = array(); + + /** + * Constructor. + */ + public function __construct(ParserInterface $parser = null) + { + $this->mainParser = $parser ?: new Parser(); + + $this + ->registerExtension(new Extension\NodeExtension()) + ->registerExtension(new Extension\CombinationExtension()) + ->registerExtension(new Extension\FunctionExtension()) + ->registerExtension(new Extension\PseudoClassExtension()) + ->registerExtension(new Extension\AttributeMatchingExtension()) + ; + } + + /** + * @param string $element + * + * @return string + */ + public static function getXpathLiteral($element) + { + if (false === strpos($element, "'")) { + return "'".$element."'"; + } + + if (false === strpos($element, '"')) { + return '"'.$element.'"'; + } + + $string = $element; + $parts = array(); + while (true) { + if (false !== $pos = strpos($string, "'")) { + $parts[] = sprintf("'%s'", substr($string, 0, $pos)); + $parts[] = "\"'\""; + $string = substr($string, $pos + 1); + } else { + $parts[] = "'$string'"; + break; + } + } + + return sprintf('concat(%s)', implode($parts, ', ')); + } + + /** + * {@inheritdoc} + */ + public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::') + { + $selectors = $this->parseSelectors($cssExpr); + + /** @var SelectorNode $selector */ + foreach ($selectors as $index => $selector) { + if (null !== $selector->getPseudoElement()) { + throw new ExpressionErrorException('Pseudo-elements are not supported.'); + } + + $selectors[$index] = $this->selectorToXPath($selector, $prefix); + } + + return implode(' | ', $selectors); + } + + /** + * {@inheritdoc} + */ + public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::') + { + return ($prefix ?: '').$this->nodeToXPath($selector); + } + + /** + * Registers an extension. + * + * @param Extension\ExtensionInterface $extension + * + * @return Translator + */ + public function registerExtension(Extension\ExtensionInterface $extension) + { + $this->extensions[$extension->getName()] = $extension; + + $this->nodeTranslators = array_merge($this->nodeTranslators, $extension->getNodeTranslators()); + $this->combinationTranslators = array_merge($this->combinationTranslators, $extension->getCombinationTranslators()); + $this->functionTranslators = array_merge($this->functionTranslators, $extension->getFunctionTranslators()); + $this->pseudoClassTranslators = array_merge($this->pseudoClassTranslators, $extension->getPseudoClassTranslators()); + $this->attributeMatchingTranslators = array_merge($this->attributeMatchingTranslators, $extension->getAttributeMatchingTranslators()); + + return $this; + } + + /** + * @param string $name + * + * @return Extension\ExtensionInterface + * + * @throws ExpressionErrorException + */ + public function getExtension($name) + { + if (!isset($this->extensions[$name])) { + throw new ExpressionErrorException(sprintf('Extension "%s" not registered.', $name)); + } + + return $this->extensions[$name]; + } + + /** + * Registers a shortcut parser. + * + * @param ParserInterface $shortcut + * + * @return Translator + */ + public function registerParserShortcut(ParserInterface $shortcut) + { + $this->shortcutParsers[] = $shortcut; + + return $this; + } + + /** + * @param NodeInterface $node + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function nodeToXPath(NodeInterface $node) + { + if (!isset($this->nodeTranslators[$node->getNodeName()])) { + throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName())); + } + + return call_user_func($this->nodeTranslators[$node->getNodeName()], $node, $this); + } + + /** + * @param string $combiner + * @param NodeInterface $xpath + * @param NodeInterface $combinedXpath + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function addCombination($combiner, NodeInterface $xpath, NodeInterface $combinedXpath) + { + if (!isset($this->combinationTranslators[$combiner])) { + throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner)); + } + + return call_user_func($this->combinationTranslators[$combiner], $this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath)); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function addFunction(XPathExpr $xpath, FunctionNode $function) + { + if (!isset($this->functionTranslators[$function->getName()])) { + throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName())); + } + + return call_user_func($this->functionTranslators[$function->getName()], $xpath, $function); + } + + /** + * @param XPathExpr $xpath + * @param string $pseudoClass + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function addPseudoClass(XPathExpr $xpath, $pseudoClass) + { + if (!isset($this->pseudoClassTranslators[$pseudoClass])) { + throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass)); + } + + return call_user_func($this->pseudoClassTranslators[$pseudoClass], $xpath); + } + + /** + * @param XPathExpr $xpath + * @param string $operator + * @param string $attribute + * @param string $value + * + * @throws ExpressionErrorException + * + * @return XPathExpr + */ + public function addAttributeMatching(XPathExpr $xpath, $operator, $attribute, $value) + { + if (!isset($this->attributeMatchingTranslators[$operator])) { + throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator)); + } + + return call_user_func($this->attributeMatchingTranslators[$operator], $xpath, $attribute, $value); + } + + /** + * @param string $css + * + * @return SelectorNode[] + */ + private function parseSelectors($css) + { + foreach ($this->shortcutParsers as $shortcut) { + $tokens = $shortcut->parse($css); + + if (!empty($tokens)) { + return $tokens; + } + } + + return $this->mainParser->parse($css); + } +} diff --git a/vendor/symfony/css-selector/XPath/TranslatorInterface.php b/vendor/symfony/css-selector/XPath/TranslatorInterface.php new file mode 100644 index 0000000..0b5de83 --- /dev/null +++ b/vendor/symfony/css-selector/XPath/TranslatorInterface.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath; + +use Symfony\Component\CssSelector\Node\SelectorNode; + +/** + * XPath expression translator interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface TranslatorInterface +{ + /** + * Translates a CSS selector to an XPath expression. + * + * @param string $cssExpr + * @param string $prefix + * + * @return string + */ + public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::'); + + /** + * Translates a parsed selector node to an XPath expression. + * + * @param SelectorNode $selector + * @param string $prefix + * + * @return string + */ + public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::'); +} diff --git a/vendor/symfony/css-selector/XPath/XPathExpr.php b/vendor/symfony/css-selector/XPath/XPathExpr.php new file mode 100644 index 0000000..420ef3d --- /dev/null +++ b/vendor/symfony/css-selector/XPath/XPathExpr.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath; + +/** + * XPath expression translator interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class XPathExpr +{ + /** + * @var string + */ + private $path; + + /** + * @var string + */ + private $element; + + /** + * @var string + */ + private $condition; + + /** + * @param string $path + * @param string $element + * @param string $condition + * @param bool $starPrefix + */ + public function __construct($path = '', $element = '*', $condition = '', $starPrefix = false) + { + $this->path = $path; + $this->element = $element; + $this->condition = $condition; + + if ($starPrefix) { + $this->addStarPrefix(); + } + } + + /** + * @return string + */ + public function getElement() + { + return $this->element; + } + + /** + * @param $condition + * + * @return XPathExpr + */ + public function addCondition($condition) + { + $this->condition = $this->condition ? sprintf('%s and (%s)', $this->condition, $condition) : $condition; + + return $this; + } + + /** + * @return string + */ + public function getCondition() + { + return $this->condition; + } + + /** + * @return XPathExpr + */ + public function addNameTest() + { + if ('*' !== $this->element) { + $this->addCondition('name() = '.Translator::getXpathLiteral($this->element)); + $this->element = '*'; + } + + return $this; + } + + /** + * @return XPathExpr + */ + public function addStarPrefix() + { + $this->path .= '*/'; + + return $this; + } + + /** + * Joins another XPathExpr with a combiner. + * + * @param string $combiner + * @param XPathExpr $expr + * + * @return XPathExpr + */ + public function join($combiner, XPathExpr $expr) + { + $path = $this->__toString().$combiner; + + if ('*/' !== $expr->path) { + $path .= $expr->path; + } + + $this->path = $path; + $this->element = $expr->element; + $this->condition = $expr->condition; + + return $this; + } + + /** + * @return string + */ + public function __toString() + { + $path = $this->path.$this->element; + $condition = null === $this->condition || '' === $this->condition ? '' : '['.$this->condition.']'; + + return $path.$condition; + } +} diff --git a/vendor/symfony/css-selector/composer.json b/vendor/symfony/css-selector/composer.json new file mode 100644 index 0000000..340a363 --- /dev/null +++ b/vendor/symfony/css-selector/composer.json @@ -0,0 +1,37 @@ +{ + "name": "symfony/css-selector", + "type": "library", + "description": "Symfony CssSelector Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/css-selector/phpunit.xml.dist b/vendor/symfony/css-selector/phpunit.xml.dist new file mode 100644 index 0000000..14a320c --- /dev/null +++ b/vendor/symfony/css-selector/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/debug/.gitignore b/vendor/symfony/debug/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/debug/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/debug/BufferingLogger.php b/vendor/symfony/debug/BufferingLogger.php new file mode 100644 index 0000000..a2ed75b --- /dev/null +++ b/vendor/symfony/debug/BufferingLogger.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +use Psr\Log\AbstractLogger; + +/** + * A buffering logger that stacks logs for later. + * + * @author Nicolas Grekas + */ +class BufferingLogger extends AbstractLogger +{ + private $logs = array(); + + public function log($level, $message, array $context = array()) + { + $this->logs[] = array($level, $message, $context); + } + + public function cleanLogs() + { + $logs = $this->logs; + $this->logs = array(); + + return $logs; + } +} diff --git a/vendor/symfony/debug/CHANGELOG.md b/vendor/symfony/debug/CHANGELOG.md new file mode 100644 index 0000000..c81bccf --- /dev/null +++ b/vendor/symfony/debug/CHANGELOG.md @@ -0,0 +1,47 @@ +CHANGELOG +========= + +3.0.0 +----- + +* removed classes, methods and interfaces deprecated in 2.x + +2.8.0 +----- + +* added BufferingLogger for errors that happen before a proper logger is configured +* allow throwing from `__toString()` with `return trigger_error($e, E_USER_ERROR);` +* deprecate ExceptionHandler::createResponse + +2.7.0 +----- + +* added deprecations checking for parent interfaces/classes to DebugClassLoader +* added ZTS support to symfony_debug extension +* added symfony_debug_backtrace() to symfony_debug extension + to track the backtrace of fatal errors + +2.6.0 +----- + +* generalized ErrorHandler and ExceptionHandler, + with some new methods and others deprecated +* enhanced error messages for uncaught exceptions + +2.5.0 +----- + +* added ExceptionHandler::setHandler() +* added UndefinedMethodFatalErrorHandler +* deprecated DummyException + +2.4.0 +----- + + * added a DebugClassLoader able to wrap any autoloader providing a findFile method + * improved error messages for not found classes and functions + +2.3.0 +----- + + * added the component diff --git a/vendor/symfony/debug/Debug.php b/vendor/symfony/debug/Debug.php new file mode 100644 index 0000000..e26810c --- /dev/null +++ b/vendor/symfony/debug/Debug.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +/** + * Registers all the debug tools. + * + * @author Fabien Potencier + */ +class Debug +{ + private static $enabled = false; + + /** + * Enables the debug tools. + * + * This method registers an error handler and an exception handler. + * + * If the Symfony ClassLoader component is available, a special + * class loader is also registered. + * + * @param int $errorReportingLevel The level of error reporting you want + * @param bool $displayErrors Whether to display errors (for development) or just log them (for production) + */ + public static function enable($errorReportingLevel = E_ALL, $displayErrors = true) + { + if (static::$enabled) { + return; + } + + static::$enabled = true; + + if (null !== $errorReportingLevel) { + error_reporting($errorReportingLevel); + } else { + error_reporting(E_ALL); + } + + if ('cli' !== php_sapi_name()) { + ini_set('display_errors', 0); + ExceptionHandler::register(); + } elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) { + // CLI - display errors only if they're not already logged to STDERR + ini_set('display_errors', 1); + } + if ($displayErrors) { + ErrorHandler::register(new ErrorHandler(new BufferingLogger())); + } else { + ErrorHandler::register()->throwAt(0, true); + } + + DebugClassLoader::enable(); + } +} diff --git a/vendor/symfony/debug/DebugClassLoader.php b/vendor/symfony/debug/DebugClassLoader.php new file mode 100644 index 0000000..0e660f0 --- /dev/null +++ b/vendor/symfony/debug/DebugClassLoader.php @@ -0,0 +1,295 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +/** + * Autoloader checking if the class is really defined in the file found. + * + * The ClassLoader will wrap all registered autoloaders + * and will throw an exception if a file is found but does + * not declare the class. + * + * @author Fabien Potencier + * @author Christophe Coevoet + * @author Nicolas Grekas + */ +class DebugClassLoader +{ + private $classLoader; + private $isFinder; + private static $caseCheck; + private static $deprecated = array(); + private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null'); + private static $darwinCache = array('/' => array('/', array())); + + /** + * Constructor. + * + * @param callable $classLoader A class loader + */ + public function __construct(callable $classLoader) + { + $this->classLoader = $classLoader; + $this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile'); + + if (!isset(self::$caseCheck)) { + self::$caseCheck = false !== stripos(PHP_OS, 'win') ? (false !== stripos(PHP_OS, 'darwin') ? 2 : 1) : 0; + } + } + + /** + * Gets the wrapped class loader. + * + * @return callable The wrapped class loader + */ + public function getClassLoader() + { + return $this->classLoader; + } + + /** + * Wraps all autoloaders. + */ + public static function enable() + { + // Ensures we don't hit https://bugs.php.net/42098 + class_exists('Symfony\Component\Debug\ErrorHandler'); + class_exists('Psr\Log\LogLevel'); + + if (!is_array($functions = spl_autoload_functions())) { + return; + } + + foreach ($functions as $function) { + spl_autoload_unregister($function); + } + + foreach ($functions as $function) { + if (!is_array($function) || !$function[0] instanceof self) { + $function = array(new static($function), 'loadClass'); + } + + spl_autoload_register($function); + } + } + + /** + * Disables the wrapping. + */ + public static function disable() + { + if (!is_array($functions = spl_autoload_functions())) { + return; + } + + foreach ($functions as $function) { + spl_autoload_unregister($function); + } + + foreach ($functions as $function) { + if (is_array($function) && $function[0] instanceof self) { + $function = $function[0]->getClassLoader(); + } + + spl_autoload_register($function); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + * + * @throws \RuntimeException + */ + public function loadClass($class) + { + ErrorHandler::stackErrors(); + + try { + if ($this->isFinder) { + if ($file = $this->classLoader[0]->findFile($class)) { + require_once $file; + } + } else { + call_user_func($this->classLoader, $class); + $file = false; + } + } finally { + ErrorHandler::unstackErrors(); + } + + $exists = class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); + + if ('\\' === $class[0]) { + $class = substr($class, 1); + } + + if ($exists) { + $refl = new \ReflectionClass($class); + $name = $refl->getName(); + + if ($name !== $class && 0 === strcasecmp($name, $class)) { + throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name)); + } + + if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) { + @trigger_error(sprintf('%s uses a reserved class name (%s) that will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED); + } elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) { + self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]); + } else { + if (2 > $len = 1 + (strpos($name, '\\', 1 + strpos($name, '\\')) ?: strpos($name, '_'))) { + $len = 0; + $ns = ''; + } else { + switch ($ns = substr($name, 0, $len)) { + case 'Symfony\Bridge\\': + case 'Symfony\Bundle\\': + case 'Symfony\Component\\': + $ns = 'Symfony\\'; + $len = strlen($ns); + break; + } + } + $parent = $refl->getParentClass(); + + if (!$parent || strncmp($ns, $parent->name, $len)) { + if ($parent && isset(self::$deprecated[$parent->name]) && strncmp($ns, $parent->name, $len)) { + @trigger_error(sprintf('The %s class extends %s that is deprecated %s', $name, $parent->name, self::$deprecated[$parent->name]), E_USER_DEPRECATED); + } + + $parentInterfaces = array(); + $deprecatedInterfaces = array(); + if ($parent) { + foreach ($parent->getInterfaceNames() as $interface) { + $parentInterfaces[$interface] = 1; + } + } + + foreach ($refl->getInterfaceNames() as $interface) { + if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len)) { + $deprecatedInterfaces[] = $interface; + } + foreach (class_implements($interface) as $interface) { + $parentInterfaces[$interface] = 1; + } + } + + foreach ($deprecatedInterfaces as $interface) { + if (!isset($parentInterfaces[$interface])) { + @trigger_error(sprintf('The %s %s %s that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED); + } + } + } + } + } + + if ($file) { + if (!$exists) { + if (false !== strpos($class, '/')) { + throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); + } + + throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); + } + if (self::$caseCheck) { + $real = explode('\\', $class.strrchr($file, '.')); + $tail = explode(DIRECTORY_SEPARATOR, str_replace('/', DIRECTORY_SEPARATOR, $file)); + + $i = count($tail) - 1; + $j = count($real) - 1; + + while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) { + --$i; + --$j; + } + + array_splice($tail, 0, $i + 1); + } + if (self::$caseCheck && $tail) { + $tail = DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $tail); + $tailLen = strlen($tail); + $real = $refl->getFileName(); + + if (2 === self::$caseCheck) { + // realpath() on MacOSX doesn't normalize the case of characters + + $i = 1 + strrpos($real, '/'); + $file = substr($real, $i); + $real = substr($real, 0, $i); + + if (isset(self::$darwinCache[$real])) { + $kDir = $real; + } else { + $kDir = strtolower($real); + + if (isset(self::$darwinCache[$kDir])) { + $real = self::$darwinCache[$kDir][0]; + } else { + $dir = getcwd(); + chdir($real); + $real = getcwd().'/'; + chdir($dir); + + $dir = $real; + $k = $kDir; + $i = strlen($dir) - 1; + while (!isset(self::$darwinCache[$k])) { + self::$darwinCache[$k] = array($dir, array()); + self::$darwinCache[$dir] = &self::$darwinCache[$k]; + + while ('/' !== $dir[--$i]) { + } + $k = substr($k, 0, ++$i); + $dir = substr($dir, 0, $i--); + } + } + } + + $dirFiles = self::$darwinCache[$kDir][1]; + + if (isset($dirFiles[$file])) { + $kFile = $file; + } else { + $kFile = strtolower($file); + + if (!isset($dirFiles[$kFile])) { + foreach (scandir($real, 2) as $f) { + if ('.' !== $f[0]) { + $dirFiles[$f] = $f; + if ($f === $file) { + $kFile = $k = $file; + } elseif ($f !== $k = strtolower($f)) { + $dirFiles[$k] = $f; + } + } + } + self::$darwinCache[$kDir][1] = $dirFiles; + } + } + + $real .= $dirFiles[$kFile]; + } + + if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) + && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) + ) { + throw new \RuntimeException(sprintf('Case mismatch between class and real file names: %s vs %s in %s', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1))); + } + } + + return true; + } + } +} diff --git a/vendor/symfony/debug/ErrorHandler.php b/vendor/symfony/debug/ErrorHandler.php new file mode 100644 index 0000000..20e81da --- /dev/null +++ b/vendor/symfony/debug/ErrorHandler.php @@ -0,0 +1,662 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +use Psr\Log\LogLevel; +use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\Exception\ContextErrorException; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\Exception\FatalThrowableError; +use Symfony\Component\Debug\Exception\OutOfMemoryException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; + +/** + * A generic ErrorHandler for the PHP engine. + * + * Provides five bit fields that control how errors are handled: + * - thrownErrors: errors thrown as \ErrorException + * - loggedErrors: logged errors, when not @-silenced + * - scopedErrors: errors thrown or logged with their local context + * - tracedErrors: errors logged with their stack trace, only once for repeated errors + * - screamedErrors: never @-silenced errors + * + * Each error level can be logged by a dedicated PSR-3 logger object. + * Screaming only applies to logging. + * Throwing takes precedence over logging. + * Uncaught exceptions are logged as E_ERROR. + * E_DEPRECATED and E_USER_DEPRECATED levels never throw. + * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw. + * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so. + * As errors have a performance cost, repeated errors are all logged, so that the developer + * can see them and weight them as more important to fix than others of the same level. + * + * @author Nicolas Grekas + */ +class ErrorHandler +{ + private $levels = array( + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'User Deprecated', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Runtime Notice', + E_WARNING => 'Warning', + E_USER_WARNING => 'User Warning', + E_COMPILE_WARNING => 'Compile Warning', + E_CORE_WARNING => 'Core Warning', + E_USER_ERROR => 'User Error', + E_RECOVERABLE_ERROR => 'Catchable Fatal Error', + E_COMPILE_ERROR => 'Compile Error', + E_PARSE => 'Parse Error', + E_ERROR => 'Error', + E_CORE_ERROR => 'Core Error', + ); + + private $loggers = array( + E_DEPRECATED => array(null, LogLevel::INFO), + E_USER_DEPRECATED => array(null, LogLevel::INFO), + E_NOTICE => array(null, LogLevel::WARNING), + E_USER_NOTICE => array(null, LogLevel::WARNING), + E_STRICT => array(null, LogLevel::WARNING), + E_WARNING => array(null, LogLevel::WARNING), + E_USER_WARNING => array(null, LogLevel::WARNING), + E_COMPILE_WARNING => array(null, LogLevel::WARNING), + E_CORE_WARNING => array(null, LogLevel::WARNING), + E_USER_ERROR => array(null, LogLevel::CRITICAL), + E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL), + E_COMPILE_ERROR => array(null, LogLevel::CRITICAL), + E_PARSE => array(null, LogLevel::CRITICAL), + E_ERROR => array(null, LogLevel::CRITICAL), + E_CORE_ERROR => array(null, LogLevel::CRITICAL), + ); + + private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED + private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED + private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE + private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE + private $loggedErrors = 0; + + private $loggedTraces = array(); + private $isRecursive = 0; + private $isRoot = false; + private $exceptionHandler; + private $bootstrappingLogger; + + private static $reservedMemory; + private static $stackedErrors = array(); + private static $stackedErrorLevels = array(); + private static $toStringException = null; + + /** + * Registers the error handler. + * + * @param self|null $handler The handler to register + * @param bool $replace Whether to replace or not any existing handler + * + * @return self The registered error handler + */ + public static function register(self $handler = null, $replace = true) + { + if (null === self::$reservedMemory) { + self::$reservedMemory = str_repeat('x', 10240); + register_shutdown_function(__CLASS__.'::handleFatalError'); + } + + if ($handlerIsNew = null === $handler) { + $handler = new static(); + } + + if (null === $prev = set_error_handler(array($handler, 'handleError'))) { + restore_error_handler(); + // Specifying the error types earlier would expose us to https://bugs.php.net/63206 + set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors); + $handler->isRoot = true; + } + + if ($handlerIsNew && is_array($prev) && $prev[0] instanceof self) { + $handler = $prev[0]; + $replace = false; + } + if ($replace || !$prev) { + $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException'))); + } else { + restore_error_handler(); + } + + $handler->throwAt(E_ALL & $handler->thrownErrors, true); + + return $handler; + } + + public function __construct(BufferingLogger $bootstrappingLogger = null) + { + if ($bootstrappingLogger) { + $this->bootstrappingLogger = $bootstrappingLogger; + $this->setDefaultLogger($bootstrappingLogger); + } + } + + /** + * Sets a logger to non assigned errors levels. + * + * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels + * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants + * @param bool $replace Whether to replace or not any existing logger + */ + public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false) + { + $loggers = array(); + + if (is_array($levels)) { + foreach ($levels as $type => $logLevel) { + if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { + $loggers[$type] = array($logger, $logLevel); + } + } + } else { + if (null === $levels) { + $levels = E_ALL; + } + foreach ($this->loggers as $type => $log) { + if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) { + $log[0] = $logger; + $loggers[$type] = $log; + } + } + } + + $this->setLoggers($loggers); + } + + /** + * Sets a logger for each error level. + * + * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map + * + * @return array The previous map + * + * @throws \InvalidArgumentException + */ + public function setLoggers(array $loggers) + { + $prevLogged = $this->loggedErrors; + $prev = $this->loggers; + $flush = array(); + + foreach ($loggers as $type => $log) { + if (!isset($prev[$type])) { + throw new \InvalidArgumentException('Unknown error type: '.$type); + } + if (!is_array($log)) { + $log = array($log); + } elseif (!array_key_exists(0, $log)) { + throw new \InvalidArgumentException('No logger provided'); + } + if (null === $log[0]) { + $this->loggedErrors &= ~$type; + } elseif ($log[0] instanceof LoggerInterface) { + $this->loggedErrors |= $type; + } else { + throw new \InvalidArgumentException('Invalid logger provided'); + } + $this->loggers[$type] = $log + $prev[$type]; + + if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) { + $flush[$type] = $type; + } + } + $this->reRegister($prevLogged | $this->thrownErrors); + + if ($flush) { + foreach ($this->bootstrappingLogger->cleanLogs() as $log) { + $type = $log[2]['type']; + if (!isset($flush[$type])) { + $this->bootstrappingLogger->log($log[0], $log[1], $log[2]); + } elseif ($this->loggers[$type][0]) { + $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]); + } + } + } + + return $prev; + } + + /** + * Sets a user exception handler. + * + * @param callable $handler A handler that will be called on Exception + * + * @return callable|null The previous exception handler + */ + public function setExceptionHandler(callable $handler = null) + { + $prev = $this->exceptionHandler; + $this->exceptionHandler = $handler; + + return $prev; + } + + /** + * Sets the PHP error levels that throw an exception when a PHP error occurs. + * + * @param int $levels A bit field of E_* constants for thrown errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function throwAt($levels, $replace = false) + { + $prev = $this->thrownErrors; + $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED; + if (!$replace) { + $this->thrownErrors |= $prev; + } + $this->reRegister($prev | $this->loggedErrors); + + return $prev; + } + + /** + * Sets the PHP error levels for which local variables are preserved. + * + * @param int $levels A bit field of E_* constants for scoped errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function scopeAt($levels, $replace = false) + { + $prev = $this->scopedErrors; + $this->scopedErrors = (int) $levels; + if (!$replace) { + $this->scopedErrors |= $prev; + } + + return $prev; + } + + /** + * Sets the PHP error levels for which the stack trace is preserved. + * + * @param int $levels A bit field of E_* constants for traced errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function traceAt($levels, $replace = false) + { + $prev = $this->tracedErrors; + $this->tracedErrors = (int) $levels; + if (!$replace) { + $this->tracedErrors |= $prev; + } + + return $prev; + } + + /** + * Sets the error levels where the @-operator is ignored. + * + * @param int $levels A bit field of E_* constants for screamed errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function screamAt($levels, $replace = false) + { + $prev = $this->screamedErrors; + $this->screamedErrors = (int) $levels; + if (!$replace) { + $this->screamedErrors |= $prev; + } + + return $prev; + } + + /** + * Re-registers as a PHP error handler if levels changed. + */ + private function reRegister($prev) + { + if ($prev !== $this->thrownErrors | $this->loggedErrors) { + $handler = set_error_handler('var_dump'); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + if ($handler === $this) { + restore_error_handler(); + if ($this->isRoot) { + set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors); + } else { + set_error_handler(array($this, 'handleError')); + } + } + } + } + + /** + * Handles errors by filtering then logging them according to the configured bit fields. + * + * @param int $type One of the E_* constants + * @param string $file + * @param int $line + * @param array $context + * + * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself. + * + * @throws \ErrorException When $this->thrownErrors requests so + * + * @internal + */ + public function handleError($type, $message, $file, $line, array $context, array $backtrace = null) + { + $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; + $log = $this->loggedErrors & $type; + $throw = $this->thrownErrors & $type & $level; + $type &= $level | $this->screamedErrors; + + if (!$type || (!$log && !$throw)) { + return $type && $log; + } + + if (null !== $backtrace && $type & E_ERROR) { + // E_ERROR fatal errors are triggered on HHVM when + // hhvm.error_handling.call_user_handler_on_fatals=1 + // which is the way to get their backtrace. + $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace')); + + return true; + } + + if ($throw) { + if (null !== self::$toStringException) { + $throw = self::$toStringException; + self::$toStringException = null; + } elseif (($this->scopedErrors & $type) && class_exists(ContextErrorException::class)) { + $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context); + } else { + $throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line); + } + + if (E_USER_ERROR & $type) { + $backtrace = $backtrace ?: $throw->getTrace(); + + for ($i = 1; isset($backtrace[$i]); ++$i) { + if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function']) + && '__toString' === $backtrace[$i]['function'] + && '->' === $backtrace[$i]['type'] + && !isset($backtrace[$i - 1]['class']) + && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) + ) { + // Here, we know trigger_error() has been called from __toString(). + // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead. + // A small convention allows working around the limitation: + // given a caught $e exception in __toString(), quitting the method with + // `return trigger_error($e, E_USER_ERROR);` allows this error handler + // to make $e get through the __toString() barrier. + + foreach ($context as $e) { + if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) { + if (1 === $i) { + // On HHVM + $throw = $e; + break; + } + self::$toStringException = $e; + + return true; + } + } + + if (1 < $i) { + // On PHP (not on HHVM), display the original error message instead of the default one. + $this->handleException($throw); + + // Stop the process by giving back the error to the native handler. + return false; + } + } + } + } + + throw $throw; + } + + // For duplicated errors, log the trace only once + $e = md5("{$type}/{$line}/{$file}\x00{$message}", true); + $trace = true; + + if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) { + $trace = false; + } else { + $this->loggedTraces[$e] = 1; + } + + $e = compact('type', 'file', 'line', 'level'); + + if ($type & $level) { + if ($this->scopedErrors & $type) { + $e['scope_vars'] = $context; + if ($trace) { + $e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT); + } + } elseif ($trace) { + if (null === $backtrace) { + $e['stack'] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } else { + foreach ($backtrace as &$frame) { + unset($frame['args'], $frame); + } + $e['stack'] = $backtrace; + } + } + } + + if ($this->isRecursive) { + $log = 0; + } elseif (self::$stackedErrorLevels) { + self::$stackedErrors[] = array($this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e); + } else { + try { + $this->isRecursive = true; + $this->loggers[$type][0]->log(($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e); + } finally { + $this->isRecursive = false; + } + } + + return $type && $log; + } + + /** + * Handles an exception by logging then forwarding it to another handler. + * + * @param \Exception|\Throwable $exception An exception to handle + * @param array $error An array as returned by error_get_last() + * + * @internal + */ + public function handleException($exception, array $error = null) + { + if (!$exception instanceof \Exception) { + $exception = new FatalThrowableError($exception); + } + $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR; + + if ($this->loggedErrors & $type) { + $e = array( + 'type' => $type, + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'level' => error_reporting(), + 'stack' => $exception->getTrace(), + ); + if ($exception instanceof FatalErrorException) { + if ($exception instanceof FatalThrowableError) { + $error = array( + 'type' => $type, + 'message' => $message = $exception->getMessage(), + 'file' => $e['file'], + 'line' => $e['line'], + ); + } else { + $message = 'Fatal '.$exception->getMessage(); + } + } elseif ($exception instanceof \ErrorException) { + $message = 'Uncaught '.$exception->getMessage(); + if ($exception instanceof ContextErrorException) { + $e['context'] = $exception->getContext(); + } + } else { + $message = 'Uncaught Exception: '.$exception->getMessage(); + } + if ($this->loggedErrors & $e['type']) { + $this->loggers[$e['type']][0]->log($this->loggers[$e['type']][1], $message, $e); + } + } + if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { + foreach ($this->getFatalErrorHandlers() as $handler) { + if ($e = $handler->handleError($error, $exception)) { + $exception = $e; + break; + } + } + } + if (empty($this->exceptionHandler)) { + throw $exception; // Give back $exception to the native handler + } + try { + call_user_func($this->exceptionHandler, $exception); + } catch (\Exception $handlerException) { + } catch (\Throwable $handlerException) { + } + if (isset($handlerException)) { + $this->exceptionHandler = null; + $this->handleException($handlerException); + } + } + + /** + * Shutdown registered function for handling PHP fatal errors. + * + * @param array $error An array as returned by error_get_last() + * + * @internal + */ + public static function handleFatalError(array $error = null) + { + if (null === self::$reservedMemory) { + return; + } + + self::$reservedMemory = null; + + $handler = set_error_handler('var_dump'); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + + if (!$handler instanceof self) { + return; + } + + if (null === $error) { + $error = error_get_last(); + } + + try { + while (self::$stackedErrorLevels) { + static::unstackErrors(); + } + } catch (\Exception $exception) { + // Handled below + } + + if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) { + // Let's not throw anymore but keep logging + $handler->throwAt(0, true); + $trace = isset($error['backtrace']) ? $error['backtrace'] : null; + + if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { + $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace); + } else { + $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace); + } + } elseif (!isset($exception)) { + return; + } + + try { + $handler->handleException($exception, $error); + } catch (FatalErrorException $e) { + // Ignore this re-throw + } + } + + /** + * Configures the error handler for delayed handling. + * Ensures also that non-catchable fatal errors are never silenced. + * + * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724 + * PHP has a compile stage where it behaves unusually. To workaround it, + * we plug an error handler that only stacks errors for later. + * + * The most important feature of this is to prevent + * autoloading until unstackErrors() is called. + */ + public static function stackErrors() + { + self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); + } + + /** + * Unstacks stacked errors and forwards to the logger. + */ + public static function unstackErrors() + { + $level = array_pop(self::$stackedErrorLevels); + + if (null !== $level) { + $e = error_reporting($level); + if ($e !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) { + // If the user changed the error level, do not overwrite it + error_reporting($e); + } + } + + if (empty(self::$stackedErrorLevels)) { + $errors = self::$stackedErrors; + self::$stackedErrors = array(); + + foreach ($errors as $e) { + $e[0]->log($e[1], $e[2], $e[3]); + } + } + } + + /** + * Gets the fatal error handlers. + * + * Override this method if you want to define more fatal error handlers. + * + * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface + */ + protected function getFatalErrorHandlers() + { + return array( + new UndefinedFunctionFatalErrorHandler(), + new UndefinedMethodFatalErrorHandler(), + new ClassNotFoundFatalErrorHandler(), + ); + } +} diff --git a/vendor/symfony/debug/Exception/ClassNotFoundException.php b/vendor/symfony/debug/Exception/ClassNotFoundException.php new file mode 100644 index 0000000..b91bf46 --- /dev/null +++ b/vendor/symfony/debug/Exception/ClassNotFoundException.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Class (or Trait or Interface) Not Found Exception. + * + * @author Konstanton Myakshin + */ +class ClassNotFoundException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/vendor/symfony/debug/Exception/ContextErrorException.php b/vendor/symfony/debug/Exception/ContextErrorException.php new file mode 100644 index 0000000..54f0198 --- /dev/null +++ b/vendor/symfony/debug/Exception/ContextErrorException.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Error Exception with Variable Context. + * + * @author Christian Sciberras + */ +class ContextErrorException extends \ErrorException +{ + private $context = array(); + + public function __construct($message, $code, $severity, $filename, $lineno, $context = array()) + { + parent::__construct($message, $code, $severity, $filename, $lineno); + $this->context = $context; + } + + /** + * @return array Array of variables that existed when the exception occurred + */ + public function getContext() + { + return $this->context; + } +} diff --git a/vendor/symfony/debug/Exception/FatalErrorException.php b/vendor/symfony/debug/Exception/FatalErrorException.php new file mode 100644 index 0000000..f24a54e --- /dev/null +++ b/vendor/symfony/debug/Exception/FatalErrorException.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Fatal Error Exception. + * + * @author Konstanton Myakshin + */ +class FatalErrorException extends \ErrorException +{ + public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true, array $trace = null) + { + parent::__construct($message, $code, $severity, $filename, $lineno); + + if (null !== $trace) { + if (!$traceArgs) { + foreach ($trace as &$frame) { + unset($frame['args'], $frame['this'], $frame); + } + } + + $this->setTrace($trace); + } elseif (null !== $traceOffset) { + if (function_exists('xdebug_get_function_stack')) { + $trace = xdebug_get_function_stack(); + if (0 < $traceOffset) { + array_splice($trace, -$traceOffset); + } + + foreach ($trace as &$frame) { + if (!isset($frame['type'])) { + // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695 + if (isset($frame['class'])) { + $frame['type'] = '::'; + } + } elseif ('dynamic' === $frame['type']) { + $frame['type'] = '->'; + } elseif ('static' === $frame['type']) { + $frame['type'] = '::'; + } + + // XDebug also has a different name for the parameters array + if (!$traceArgs) { + unset($frame['params'], $frame['args']); + } elseif (isset($frame['params']) && !isset($frame['args'])) { + $frame['args'] = $frame['params']; + unset($frame['params']); + } + } + + unset($frame); + $trace = array_reverse($trace); + } elseif (function_exists('symfony_debug_backtrace')) { + $trace = symfony_debug_backtrace(); + if (0 < $traceOffset) { + array_splice($trace, 0, $traceOffset); + } + } else { + $trace = array(); + } + + $this->setTrace($trace); + } + } + + protected function setTrace($trace) + { + $traceReflector = new \ReflectionProperty('Exception', 'trace'); + $traceReflector->setAccessible(true); + $traceReflector->setValue($this, $trace); + } +} diff --git a/vendor/symfony/debug/Exception/FatalThrowableError.php b/vendor/symfony/debug/Exception/FatalThrowableError.php new file mode 100644 index 0000000..6ff5ecd --- /dev/null +++ b/vendor/symfony/debug/Exception/FatalThrowableError.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Fatal Throwable Error. + * + * @author Nicolas Grekas + */ +class FatalThrowableError extends FatalErrorException +{ + public function __construct(\Throwable $e) + { + if ($e instanceof \ParseError) { + $message = 'Parse error: '.$e->getMessage(); + $severity = E_PARSE; + } elseif ($e instanceof \TypeError) { + $message = 'Type error: '.$e->getMessage(); + $severity = E_RECOVERABLE_ERROR; + } else { + $message = 'Fatal error: '.$e->getMessage(); + $severity = E_ERROR; + } + + \ErrorException::__construct( + $message, + $e->getCode(), + $severity, + $e->getFile(), + $e->getLine() + ); + + $this->setTrace($e->getTrace()); + } +} diff --git a/vendor/symfony/debug/Exception/FlattenException.php b/vendor/symfony/debug/Exception/FlattenException.php new file mode 100644 index 0000000..5802c50 --- /dev/null +++ b/vendor/symfony/debug/Exception/FlattenException.php @@ -0,0 +1,256 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; + +/** + * FlattenException wraps a PHP Exception to be able to serialize it. + * + * Basically, this class removes all objects from the trace. + * + * @author Fabien Potencier + */ +class FlattenException +{ + private $message; + private $code; + private $previous; + private $trace; + private $class; + private $statusCode; + private $headers; + private $file; + private $line; + + public static function create(\Exception $exception, $statusCode = null, array $headers = array()) + { + $e = new static(); + $e->setMessage($exception->getMessage()); + $e->setCode($exception->getCode()); + + if ($exception instanceof HttpExceptionInterface) { + $statusCode = $exception->getStatusCode(); + $headers = array_merge($headers, $exception->getHeaders()); + } + + if (null === $statusCode) { + $statusCode = 500; + } + + $e->setStatusCode($statusCode); + $e->setHeaders($headers); + $e->setTraceFromException($exception); + $e->setClass(get_class($exception)); + $e->setFile($exception->getFile()); + $e->setLine($exception->getLine()); + + $previous = $exception->getPrevious(); + + if ($previous instanceof \Exception) { + $e->setPrevious(static::create($previous)); + } elseif ($previous instanceof \Throwable) { + $e->setPrevious(static::create(new FatalThrowableError($previous))); + } + + return $e; + } + + public function toArray() + { + $exceptions = array(); + foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) { + $exceptions[] = array( + 'message' => $exception->getMessage(), + 'class' => $exception->getClass(), + 'trace' => $exception->getTrace(), + ); + } + + return $exceptions; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function setStatusCode($code) + { + $this->statusCode = $code; + } + + public function getHeaders() + { + return $this->headers; + } + + public function setHeaders(array $headers) + { + $this->headers = $headers; + } + + public function getClass() + { + return $this->class; + } + + public function setClass($class) + { + $this->class = $class; + } + + public function getFile() + { + return $this->file; + } + + public function setFile($file) + { + $this->file = $file; + } + + public function getLine() + { + return $this->line; + } + + public function setLine($line) + { + $this->line = $line; + } + + public function getMessage() + { + return $this->message; + } + + public function setMessage($message) + { + $this->message = $message; + } + + public function getCode() + { + return $this->code; + } + + public function setCode($code) + { + $this->code = $code; + } + + public function getPrevious() + { + return $this->previous; + } + + public function setPrevious(FlattenException $previous) + { + $this->previous = $previous; + } + + public function getAllPrevious() + { + $exceptions = array(); + $e = $this; + while ($e = $e->getPrevious()) { + $exceptions[] = $e; + } + + return $exceptions; + } + + public function getTrace() + { + return $this->trace; + } + + public function setTraceFromException(\Exception $exception) + { + $this->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine()); + } + + public function setTrace($trace, $file, $line) + { + $this->trace = array(); + $this->trace[] = array( + 'namespace' => '', + 'short_class' => '', + 'class' => '', + 'type' => '', + 'function' => '', + 'file' => $file, + 'line' => $line, + 'args' => array(), + ); + foreach ($trace as $entry) { + $class = ''; + $namespace = ''; + if (isset($entry['class'])) { + $parts = explode('\\', $entry['class']); + $class = array_pop($parts); + $namespace = implode('\\', $parts); + } + + $this->trace[] = array( + 'namespace' => $namespace, + 'short_class' => $class, + 'class' => isset($entry['class']) ? $entry['class'] : '', + 'type' => isset($entry['type']) ? $entry['type'] : '', + 'function' => isset($entry['function']) ? $entry['function'] : null, + 'file' => isset($entry['file']) ? $entry['file'] : null, + 'line' => isset($entry['line']) ? $entry['line'] : null, + 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(), + ); + } + } + + private function flattenArgs($args, $level = 0, &$count = 0) + { + $result = array(); + foreach ($args as $key => $value) { + if (++$count > 1e4) { + return array('array', '*SKIPPED over 10000 entries*'); + } + if (is_object($value)) { + $result[$key] = array('object', get_class($value)); + } elseif (is_array($value)) { + if ($level > 10) { + $result[$key] = array('array', '*DEEP NESTED ARRAY*'); + } else { + $result[$key] = array('array', $this->flattenArgs($value, $level + 1, $count)); + } + } elseif (null === $value) { + $result[$key] = array('null', null); + } elseif (is_bool($value)) { + $result[$key] = array('boolean', $value); + } elseif (is_resource($value)) { + $result[$key] = array('resource', get_resource_type($value)); + } elseif ($value instanceof \__PHP_Incomplete_Class) { + // Special case of object, is_object will return false + $result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value)); + } else { + $result[$key] = array('string', (string) $value); + } + } + + return $result; + } + + private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) + { + $array = new \ArrayObject($value); + + return $array['__PHP_Incomplete_Class_Name']; + } +} diff --git a/vendor/symfony/debug/Exception/OutOfMemoryException.php b/vendor/symfony/debug/Exception/OutOfMemoryException.php new file mode 100644 index 0000000..fec1979 --- /dev/null +++ b/vendor/symfony/debug/Exception/OutOfMemoryException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Out of memory exception. + * + * @author Nicolas Grekas + */ +class OutOfMemoryException extends FatalErrorException +{ +} diff --git a/vendor/symfony/debug/Exception/UndefinedFunctionException.php b/vendor/symfony/debug/Exception/UndefinedFunctionException.php new file mode 100644 index 0000000..a66ae2a --- /dev/null +++ b/vendor/symfony/debug/Exception/UndefinedFunctionException.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Undefined Function Exception. + * + * @author Konstanton Myakshin + */ +class UndefinedFunctionException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/vendor/symfony/debug/Exception/UndefinedMethodException.php b/vendor/symfony/debug/Exception/UndefinedMethodException.php new file mode 100644 index 0000000..350dc31 --- /dev/null +++ b/vendor/symfony/debug/Exception/UndefinedMethodException.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Undefined Method Exception. + * + * @author Grégoire Pineau + */ +class UndefinedMethodException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/vendor/symfony/debug/ExceptionHandler.php b/vendor/symfony/debug/ExceptionHandler.php new file mode 100644 index 0000000..33132be --- /dev/null +++ b/vendor/symfony/debug/ExceptionHandler.php @@ -0,0 +1,414 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\Debug\Exception\OutOfMemoryException; + +/** + * ExceptionHandler converts an exception to a Response object. + * + * It is mostly useful in debug mode to replace the default PHP/XDebug + * output with something prettier and more useful. + * + * As this class is mainly used during Kernel boot, where nothing is yet + * available, the Response content is always HTML. + * + * @author Fabien Potencier + * @author Nicolas Grekas + */ +class ExceptionHandler +{ + private $debug; + private $charset; + private $handler; + private $caughtBuffer; + private $caughtLength; + private $fileLinkFormat; + + public function __construct($debug = true, $charset = null, $fileLinkFormat = null) + { + $this->debug = $debug; + $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + } + + /** + * Registers the exception handler. + * + * @param bool $debug Enable/disable debug mode, where the stack trace is displayed + * @param string|null $charset The charset used by exception messages + * @param string|null $fileLinkFormat The IDE link template + * + * @return ExceptionHandler The registered exception handler + */ + public static function register($debug = true, $charset = null, $fileLinkFormat = null) + { + $handler = new static($debug, $charset, $fileLinkFormat); + + $prev = set_exception_handler(array($handler, 'handle')); + if (is_array($prev) && $prev[0] instanceof ErrorHandler) { + restore_exception_handler(); + $prev[0]->setExceptionHandler(array($handler, 'handle')); + } + + return $handler; + } + + /** + * Sets a user exception handler. + * + * @param callable $handler An handler that will be called on Exception + * + * @return callable|null The previous exception handler if any + */ + public function setHandler(callable $handler = null) + { + $old = $this->handler; + $this->handler = $handler; + + return $old; + } + + /** + * Sets the format for links to source files. + * + * @param string $format The format for links to source files + * + * @return string The previous file link format. + */ + public function setFileLinkFormat($format) + { + $old = $this->fileLinkFormat; + $this->fileLinkFormat = $format; + + return $old; + } + + /** + * Sends a response for the given Exception. + * + * To be as fail-safe as possible, the exception is first handled + * by our simple exception handler, then by the user exception handler. + * The latter takes precedence and any output from the former is cancelled, + * if and only if nothing bad happens in this handling path. + */ + public function handle(\Exception $exception) + { + if (null === $this->handler || $exception instanceof OutOfMemoryException) { + $this->sendPhpResponse($exception); + + return; + } + + $caughtLength = $this->caughtLength = 0; + + ob_start(function ($buffer) { + $this->caughtBuffer = $buffer; + + return ''; + }); + + $this->sendPhpResponse($exception); + while (null === $this->caughtBuffer && ob_end_flush()) { + // Empty loop, everything is in the condition + } + if (isset($this->caughtBuffer[0])) { + ob_start(function ($buffer) { + if ($this->caughtLength) { + // use substr_replace() instead of substr() for mbstring overloading resistance + $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength); + if (isset($cleanBuffer[0])) { + $buffer = $cleanBuffer; + } + } + + return $buffer; + }); + + echo $this->caughtBuffer; + $caughtLength = ob_get_length(); + } + $this->caughtBuffer = null; + + try { + call_user_func($this->handler, $exception); + $this->caughtLength = $caughtLength; + } catch (\Exception $e) { + if (!$caughtLength) { + // All handlers failed. Let PHP handle that now. + throw $exception; + } + } + } + + /** + * Sends the error associated with the given Exception as a plain PHP response. + * + * This method uses plain PHP functions like header() and echo to output + * the response. + * + * @param \Exception|FlattenException $exception An \Exception or FlattenException instance + */ + public function sendPhpResponse($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + + if (!headers_sent()) { + header(sprintf('HTTP/1.0 %s', $exception->getStatusCode())); + foreach ($exception->getHeaders() as $name => $value) { + header($name.': '.$value, false); + } + header('Content-Type: text/html; charset='.$this->charset); + } + + echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); + } + + /** + * Gets the full HTML content associated with the given exception. + * + * @param \Exception|FlattenException $exception An \Exception or FlattenException instance + * + * @return string The HTML content as a string + */ + public function getHtml($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + + return $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); + } + + /** + * Gets the HTML content associated with the given exception. + * + * @param FlattenException $exception A FlattenException instance + * + * @return string The content as a string + */ + public function getContent(FlattenException $exception) + { + switch ($exception->getStatusCode()) { + case 404: + $title = 'Sorry, the page you are looking for could not be found.'; + break; + default: + $title = 'Whoops, looks like something went wrong.'; + } + + $content = ''; + if ($this->debug) { + try { + $count = count($exception->getAllPrevious()); + $total = $count + 1; + foreach ($exception->toArray() as $position => $e) { + $ind = $count - $position + 1; + $class = $this->formatClass($e['class']); + $message = nl2br($this->escapeHtml($e['message'])); + $content .= sprintf(<<<'EOF' +

+ %d/%d + %s%s: + %s +

+
+
    + +EOF + , $ind, $total, $class, $this->formatPath($e['trace'][0]['file'], $e['trace'][0]['line']), $message); + foreach ($e['trace'] as $trace) { + $content .= '
  1. '; + if ($trace['function']) { + $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); + } + if (isset($trace['file']) && isset($trace['line'])) { + $content .= $this->formatPath($trace['file'], $trace['line']); + } + $content .= "
  2. \n"; + } + + $content .= "
\n
\n"; + } + } catch (\Exception $e) { + // something nasty happened and we cannot throw an exception anymore + if ($this->debug) { + $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage())); + } else { + $title = 'Whoops, looks like something went wrong.'; + } + } + } + + return << +

$title

+ $content + +EOF; + } + + /** + * Gets the stylesheet associated with the given exception. + * + * @param FlattenException $exception A FlattenException instance + * + * @return string The stylesheet as a string + */ + public function getStylesheet(FlattenException $exception) + { + return <<<'EOF' + .sf-reset { font: 11px Verdana, Arial, sans-serif; color: #333 } + .sf-reset .clear { clear:both; height:0; font-size:0; line-height:0; } + .sf-reset .clear_fix:after { display:block; height:0; clear:both; visibility:hidden; } + .sf-reset .clear_fix { display:inline-block; } + .sf-reset * html .clear_fix { height:1%; } + .sf-reset .clear_fix { display:block; } + .sf-reset, .sf-reset .block { margin: auto } + .sf-reset abbr { border-bottom: 1px dotted #000; cursor: help; } + .sf-reset p { font-size:14px; line-height:20px; color:#868686; padding-bottom:20px } + .sf-reset strong { font-weight:bold; } + .sf-reset a { color:#6c6159; cursor: default; } + .sf-reset a img { border:none; } + .sf-reset a:hover { text-decoration:underline; } + .sf-reset em { font-style:italic; } + .sf-reset h1, .sf-reset h2 { font: 20px Georgia, "Times New Roman", Times, serif } + .sf-reset .exception_counter { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; float: left; display: block; } + .sf-reset .exception_title { margin-left: 3em; margin-bottom: 0.7em; display: block; } + .sf-reset .exception_message { margin-left: 3em; display: block; } + .sf-reset .traces li { font-size:12px; padding: 2px 4px; list-style-type:decimal; margin-left:20px; } + .sf-reset .block { background-color:#FFFFFF; padding:10px 28px; margin-bottom:20px; + -webkit-border-bottom-right-radius: 16px; + -webkit-border-bottom-left-radius: 16px; + -moz-border-radius-bottomright: 16px; + -moz-border-radius-bottomleft: 16px; + border-bottom-right-radius: 16px; + border-bottom-left-radius: 16px; + border-bottom:1px solid #ccc; + border-right:1px solid #ccc; + border-left:1px solid #ccc; + } + .sf-reset .block_exception { background-color:#ddd; color: #333; padding:20px; + -webkit-border-top-left-radius: 16px; + -webkit-border-top-right-radius: 16px; + -moz-border-radius-topleft: 16px; + -moz-border-radius-topright: 16px; + border-top-left-radius: 16px; + border-top-right-radius: 16px; + border-top:1px solid #ccc; + border-right:1px solid #ccc; + border-left:1px solid #ccc; + overflow: hidden; + word-wrap: break-word; + } + .sf-reset a { background:none; color:#868686; text-decoration:none; } + .sf-reset a:hover { background:none; color:#313131; text-decoration:underline; } + .sf-reset ol { padding: 10px 0; } + .sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + border: 1px solid #ccc; + } +EOF; + } + + private function decorate($content, $css) + { + return << + + + + + + + + $content + + +EOF; + } + + private function formatClass($class) + { + $parts = explode('\\', $class); + + return sprintf('%s', $class, array_pop($parts)); + } + + private function formatPath($path, $line) + { + $path = $this->escapeHtml($path); + $file = preg_match('#[^/\\\\]*$#', $path, $file) ? $file[0] : $path; + + if ($linkFormat = $this->fileLinkFormat) { + $link = strtr($this->escapeHtml($linkFormat), array('%f' => $path, '%l' => (int) $line)); + + return sprintf(' in %s line %d', $link, $file, $line); + } + + return sprintf(' in %s line %d', $path, $file, $line); + } + + /** + * Formats an array as a string. + * + * @param array $args The argument array + * + * @return string + */ + private function formatArgs(array $args) + { + $result = array(); + foreach ($args as $key => $item) { + if ('object' === $item[0]) { + $formattedValue = sprintf('object(%s)', $this->formatClass($item[1])); + } elseif ('array' === $item[0]) { + $formattedValue = sprintf('array(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); + } elseif ('string' === $item[0]) { + $formattedValue = sprintf("'%s'", $this->escapeHtml($item[1])); + } elseif ('null' === $item[0]) { + $formattedValue = 'null'; + } elseif ('boolean' === $item[0]) { + $formattedValue = ''.strtolower(var_export($item[1], true)).''; + } elseif ('resource' === $item[0]) { + $formattedValue = 'resource'; + } else { + $formattedValue = str_replace("\n", '', var_export($this->escapeHtml((string) $item[1]), true)); + } + + $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); + } + + return implode(', ', $result); + } + + /** + * HTML-encodes a string. + */ + private function escapeHtml($str) + { + return htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset); + } +} diff --git a/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php new file mode 100644 index 0000000..c48d0d3 --- /dev/null +++ b/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\ClassNotFoundException; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\DebugClassLoader; +use Composer\Autoload\ClassLoader as ComposerClassLoader; +use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; + +/** + * ErrorHandler for classes that do not exist. + * + * @author Fabien Potencier + */ +class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '\' not found'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + foreach (array('class', 'interface', 'trait') as $typeName) { + $prefix = ucfirst($typeName).' \''; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + continue; + } + + $fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { + $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); + $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); + $tail = ' for another namespace?'; + } else { + $className = $fullyQualifiedClassName; + $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); + $tail = '?'; + } + + if ($candidates = $this->getClassCandidates($className)) { + $tail = array_pop($candidates).'"?'; + if ($candidates) { + $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail; + } else { + $tail = ' for "'.$tail; + } + } + $message .= "\nDid you forget a \"use\" statement".$tail; + + return new ClassNotFoundException($message, $exception); + } + } + + /** + * Tries to guess the full namespace for a given class name. + * + * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer + * autoloader (that should cover all common cases). + * + * @param string $class A class name (without its namespace) + * + * @return array An array of possible fully qualified class names + */ + private function getClassCandidates($class) + { + if (!is_array($functions = spl_autoload_functions())) { + return array(); + } + + // find Symfony and Composer autoloaders + $classes = array(); + + foreach ($functions as $function) { + if (!is_array($function)) { + continue; + } + // get class loaders wrapped by DebugClassLoader + if ($function[0] instanceof DebugClassLoader) { + $function = $function[0]->getClassLoader(); + + if (!is_array($function)) { + continue; + } + } + + if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) { + foreach ($function[0]->getPrefixes() as $prefix => $paths) { + foreach ($paths as $path) { + $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); + } + } + } + if ($function[0] instanceof ComposerClassLoader) { + foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) { + foreach ($paths as $path) { + $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); + } + } + } + } + + return array_unique($classes); + } + + /** + * @param string $path + * @param string $class + * @param string $prefix + * + * @return array + */ + private function findClassInPath($path, $class, $prefix) + { + if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) { + return array(); + } + + $classes = array(); + $filename = $class.'.php'; + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) { + $classes[] = $class; + } + } + + return $classes; + } + + /** + * @param string $path + * @param string $file + * @param string $prefix + * + * @return string|null + */ + private function convertFileToClass($path, $file, $prefix) + { + $candidates = array( + // namespaced class + $namespacedClass = str_replace(array($path.DIRECTORY_SEPARATOR, '.php', '/'), array('', '', '\\'), $file), + // namespaced class (with target dir) + $prefix.$namespacedClass, + // namespaced class (with target dir and separator) + $prefix.'\\'.$namespacedClass, + // PEAR class + str_replace('\\', '_', $namespacedClass), + // PEAR class (with target dir) + str_replace('\\', '_', $prefix.$namespacedClass), + // PEAR class (with target dir and separator) + str_replace('\\', '_', $prefix.'\\'.$namespacedClass), + ); + + if ($prefix) { + $candidates = array_filter($candidates, function ($candidate) use ($prefix) {return 0 === strpos($candidate, $prefix);}); + } + + // We cannot use the autoloader here as most of them use require; but if the class + // is not found, the new autoloader call will require the file again leading to a + // "cannot redeclare class" error. + foreach ($candidates as $candidate) { + if ($this->classExists($candidate)) { + return $candidate; + } + } + + require_once $file; + + foreach ($candidates as $candidate) { + if ($this->classExists($candidate)) { + return $candidate; + } + } + } + + /** + * @param string $class + * + * @return bool + */ + private function classExists($class) + { + return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); + } +} diff --git a/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php b/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php new file mode 100644 index 0000000..6b87eb3 --- /dev/null +++ b/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * Attempts to convert fatal errors to exceptions. + * + * @author Fabien Potencier + */ +interface FatalErrorHandlerInterface +{ + /** + * Attempts to convert an error into an exception. + * + * @param array $error An array as returned by error_get_last() + * @param FatalErrorException $exception A FatalErrorException instance + * + * @return FatalErrorException|null A FatalErrorException instance if the class is able to convert the error, null otherwise + */ + public function handleError(array $error, FatalErrorException $exception); +} diff --git a/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php b/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php new file mode 100644 index 0000000..c6f391a --- /dev/null +++ b/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\UndefinedFunctionException; +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * ErrorHandler for undefined functions. + * + * @author Fabien Potencier + */ +class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '()'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + $prefix = 'Call to undefined function '; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + return; + } + + $fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { + $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); + $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); + } else { + $functionName = $fullyQualifiedFunctionName; + $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName); + } + + $candidates = array(); + foreach (get_defined_functions() as $type => $definedFunctionNames) { + foreach ($definedFunctionNames as $definedFunctionName) { + if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) { + $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1); + } else { + $definedFunctionNameBasename = $definedFunctionName; + } + + if ($definedFunctionNameBasename === $functionName) { + $candidates[] = '\\'.$definedFunctionName; + } + } + } + + if ($candidates) { + sort($candidates); + $last = array_pop($candidates).'"?'; + if ($candidates) { + $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; + } else { + $candidates = '"'.$last; + } + $message .= "\nDid you mean to call ".$candidates; + } + + return new UndefinedFunctionException($message, $exception); + } +} diff --git a/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php b/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php new file mode 100644 index 0000000..f734d6b --- /dev/null +++ b/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\Exception\UndefinedMethodException; + +/** + * ErrorHandler for undefined methods. + * + * @author Grégoire Pineau + */ +class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches); + if (!$matches) { + return; + } + + $className = $matches[1]; + $methodName = $matches[2]; + + $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className); + + $candidates = array(); + foreach (get_class_methods($className) as $definedMethodName) { + $lev = levenshtein($methodName, $definedMethodName); + if ($lev <= strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) { + $candidates[] = $definedMethodName; + } + } + + if ($candidates) { + sort($candidates); + $last = array_pop($candidates).'"?'; + if ($candidates) { + $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; + } else { + $candidates = '"'.$last; + } + $message .= "\nDid you mean to call ".$candidates; + } + + return new UndefinedMethodException($message, $exception); + } +} diff --git a/vendor/symfony/debug/LICENSE b/vendor/symfony/debug/LICENSE new file mode 100644 index 0000000..12a7453 --- /dev/null +++ b/vendor/symfony/debug/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/debug/README.md b/vendor/symfony/debug/README.md new file mode 100644 index 0000000..eec5e1f --- /dev/null +++ b/vendor/symfony/debug/README.md @@ -0,0 +1,42 @@ +Debug Component +=============== + +Debug provides tools to make debugging easier. + +Enabling all debug tools is as easy as calling the `enable()` method on the +main `Debug` class: + +```php +use Symfony\Component\Debug\Debug; + +Debug::enable(); +``` + +You can also use the tools individually: + +```php +use Symfony\Component\Debug\DebugClassLoader; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\ExceptionHandler; + +if ('cli' !== php_sapi_name()) { + ini_set('display_errors', 0); + ExceptionHandler::register(); +} elseif (!ini_get('log_errors') || ini_get('error_log')) { + ini_set('display_errors', 1); +} +ErrorHandler::register(); +DebugClassLoader::enable(); +``` + +This component can optionally take advantage of the features of the HttpKernel +component. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Debug/ + $ composer install + $ phpunit diff --git a/vendor/symfony/debug/Resources/ext/README.md b/vendor/symfony/debug/Resources/ext/README.md new file mode 100644 index 0000000..25dccf0 --- /dev/null +++ b/vendor/symfony/debug/Resources/ext/README.md @@ -0,0 +1,134 @@ +Symfony Debug Extension for PHP 5 +================================= + +This extension publishes several functions to help building powerful debugging tools. +It is compatible with PHP 5.3, 5.4, 5.5 and 5.6; with ZTS and non-ZTS modes. +It is not required thus not provided for PHP 7. + +symfony_zval_info() +------------------- + +- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP, +- does work with references, preventing memory copying. + +Its behavior is about the same as: + +```php + gettype($array[$key]), + 'zval_hash' => /* hashed memory address of $array[$key] */, + 'zval_refcount' => /* internal zval refcount of $array[$key] */, + 'zval_isref' => /* is_ref status of $array[$key] */, + ); + + switch ($info['type']) { + case 'object': + $info += array( + 'object_class' => get_class($array[$key]), + 'object_refcount' => /* internal object refcount of $array[$key] */, + 'object_hash' => spl_object_hash($array[$key]), + 'object_handle' => /* internal object handle $array[$key] */, + ); + break; + + case 'resource': + $info += array( + 'resource_handle' => (int) $array[$key], + 'resource_type' => get_resource_type($array[$key]), + 'resource_refcount' => /* internal resource refcount of $array[$key] */, + ); + break; + + case 'array': + $info += array( + 'array_count' => count($array[$key]), + ); + break; + + case 'string': + $info += array( + 'strlen' => strlen($array[$key]), + ); + break; + } + + return $info; +} +``` + +symfony_debug_backtrace() +------------------------- + +This function works like debug_backtrace(), except that it can fetch the full backtrace in case of fatal errors: + +```php +function foo() { fatal(); } +function bar() { foo(); } + +function sd() { var_dump(symfony_debug_backtrace()); } + +register_shutdown_function('sd'); + +bar(); + +/* Will output +Fatal error: Call to undefined function fatal() in foo.php on line 42 +array(3) { + [0]=> + array(2) { + ["function"]=> + string(2) "sd" + ["args"]=> + array(0) { + } + } + [1]=> + array(4) { + ["file"]=> + string(7) "foo.php" + ["line"]=> + int(1) + ["function"]=> + string(3) "foo" + ["args"]=> + array(0) { + } + } + [2]=> + array(4) { + ["file"]=> + string(102) "foo.php" + ["line"]=> + int(2) + ["function"]=> + string(3) "bar" + ["args"]=> + array(0) { + } + } +} +*/ +``` + +Usage +----- + +To enable the extension from source, run: + +``` + phpize + ./configure + make + sudo make install +``` diff --git a/vendor/symfony/debug/Resources/ext/config.m4 b/vendor/symfony/debug/Resources/ext/config.m4 new file mode 100644 index 0000000..3c56047 --- /dev/null +++ b/vendor/symfony/debug/Resources/ext/config.m4 @@ -0,0 +1,63 @@ +dnl $Id$ +dnl config.m4 for extension symfony_debug + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. This file will not work +dnl without editing. + +dnl If your extension references something external, use with: + +dnl PHP_ARG_WITH(symfony_debug, for symfony_debug support, +dnl Make sure that the comment is aligned: +dnl [ --with-symfony_debug Include symfony_debug support]) + +dnl Otherwise use enable: + +PHP_ARG_ENABLE(symfony_debug, whether to enable symfony_debug support, +dnl Make sure that the comment is aligned: +[ --enable-symfony_debug Enable symfony_debug support]) + +if test "$PHP_SYMFONY_DEBUG" != "no"; then + dnl Write more examples of tests here... + + dnl # --with-symfony_debug -> check with-path + dnl SEARCH_PATH="/usr/local /usr" # you might want to change this + dnl SEARCH_FOR="/include/symfony_debug.h" # you most likely want to change this + dnl if test -r $PHP_SYMFONY_DEBUG/$SEARCH_FOR; then # path given as parameter + dnl SYMFONY_DEBUG_DIR=$PHP_SYMFONY_DEBUG + dnl else # search default path list + dnl AC_MSG_CHECKING([for symfony_debug files in default path]) + dnl for i in $SEARCH_PATH ; do + dnl if test -r $i/$SEARCH_FOR; then + dnl SYMFONY_DEBUG_DIR=$i + dnl AC_MSG_RESULT(found in $i) + dnl fi + dnl done + dnl fi + dnl + dnl if test -z "$SYMFONY_DEBUG_DIR"; then + dnl AC_MSG_RESULT([not found]) + dnl AC_MSG_ERROR([Please reinstall the symfony_debug distribution]) + dnl fi + + dnl # --with-symfony_debug -> add include path + dnl PHP_ADD_INCLUDE($SYMFONY_DEBUG_DIR/include) + + dnl # --with-symfony_debug -> check for lib and symbol presence + dnl LIBNAME=symfony_debug # you may want to change this + dnl LIBSYMBOL=symfony_debug # you most likely want to change this + + dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + dnl [ + dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SYMFONY_DEBUG_DIR/lib, SYMFONY_DEBUG_SHARED_LIBADD) + dnl AC_DEFINE(HAVE_SYMFONY_DEBUGLIB,1,[ ]) + dnl ],[ + dnl AC_MSG_ERROR([wrong symfony_debug lib version or lib not found]) + dnl ],[ + dnl -L$SYMFONY_DEBUG_DIR/lib -lm + dnl ]) + dnl + dnl PHP_SUBST(SYMFONY_DEBUG_SHARED_LIBADD) + + PHP_NEW_EXTENSION(symfony_debug, symfony_debug.c, $ext_shared) +fi diff --git a/vendor/symfony/debug/Resources/ext/config.w32 b/vendor/symfony/debug/Resources/ext/config.w32 new file mode 100644 index 0000000..487e691 --- /dev/null +++ b/vendor/symfony/debug/Resources/ext/config.w32 @@ -0,0 +1,13 @@ +// $Id$ +// vim:ft=javascript + +// If your extension references something external, use ARG_WITH +// ARG_WITH("symfony_debug", "for symfony_debug support", "no"); + +// Otherwise, use ARG_ENABLE +// ARG_ENABLE("symfony_debug", "enable symfony_debug support", "no"); + +if (PHP_SYMFONY_DEBUG != "no") { + EXTENSION("symfony_debug", "symfony_debug.c"); +} + diff --git a/vendor/symfony/debug/Resources/ext/php_symfony_debug.h b/vendor/symfony/debug/Resources/ext/php_symfony_debug.h new file mode 100644 index 0000000..26d0e8c --- /dev/null +++ b/vendor/symfony/debug/Resources/ext/php_symfony_debug.h @@ -0,0 +1,60 @@ +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#ifndef PHP_SYMFONY_DEBUG_H +#define PHP_SYMFONY_DEBUG_H + +extern zend_module_entry symfony_debug_module_entry; +#define phpext_symfony_debug_ptr &symfony_debug_module_entry + +#define PHP_SYMFONY_DEBUG_VERSION "2.7" + +#ifdef PHP_WIN32 +# define PHP_SYMFONY_DEBUG_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_SYMFONY_DEBUG_API __attribute__ ((visibility("default"))) +#else +# define PHP_SYMFONY_DEBUG_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +ZEND_BEGIN_MODULE_GLOBALS(symfony_debug) + intptr_t req_rand_init; + void (*old_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); + zval *debug_bt; +ZEND_END_MODULE_GLOBALS(symfony_debug) + +PHP_MINIT_FUNCTION(symfony_debug); +PHP_MSHUTDOWN_FUNCTION(symfony_debug); +PHP_RINIT_FUNCTION(symfony_debug); +PHP_RSHUTDOWN_FUNCTION(symfony_debug); +PHP_MINFO_FUNCTION(symfony_debug); +PHP_GINIT_FUNCTION(symfony_debug); +PHP_GSHUTDOWN_FUNCTION(symfony_debug); + +PHP_FUNCTION(symfony_zval_info); +PHP_FUNCTION(symfony_debug_backtrace); + +static char *_symfony_debug_memory_address_hash(void * TSRMLS_DC); +static const char *_symfony_debug_zval_type(zval *); +static const char* _symfony_debug_get_resource_type(long TSRMLS_DC); +static int _symfony_debug_get_resource_refcount(long TSRMLS_DC); + +void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); + +#ifdef ZTS +#define SYMFONY_DEBUG_G(v) TSRMG(symfony_debug_globals_id, zend_symfony_debug_globals *, v) +#else +#define SYMFONY_DEBUG_G(v) (symfony_debug_globals.v) +#endif + +#endif /* PHP_SYMFONY_DEBUG_H */ diff --git a/vendor/symfony/debug/Resources/ext/symfony_debug.c b/vendor/symfony/debug/Resources/ext/symfony_debug.c new file mode 100644 index 0000000..0d7cb60 --- /dev/null +++ b/vendor/symfony/debug/Resources/ext/symfony_debug.c @@ -0,0 +1,283 @@ +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#ifdef ZTS +#include "TSRM.h" +#endif +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_symfony_debug.h" +#include "ext/standard/php_rand.h" +#include "ext/standard/php_lcg.h" +#include "ext/spl/php_spl.h" +#include "Zend/zend_gc.h" +#include "Zend/zend_builtin_functions.h" +#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ +#include "ext/standard/php_array.h" +#include "Zend/zend_interfaces.h" +#include "SAPI.h" + +#define IS_PHP_53 ZEND_EXTENSION_API_NO == 220090626 + +ZEND_DECLARE_MODULE_GLOBALS(symfony_debug) + +ZEND_BEGIN_ARG_INFO_EX(symfony_zval_arginfo, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, array, 0) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +const zend_function_entry symfony_debug_functions[] = { + PHP_FE(symfony_zval_info, symfony_zval_arginfo) + PHP_FE(symfony_debug_backtrace, NULL) + PHP_FE_END +}; + +PHP_FUNCTION(symfony_debug_backtrace) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } +#if IS_PHP_53 + zend_fetch_debug_backtrace(return_value, 1, 0 TSRMLS_CC); +#else + zend_fetch_debug_backtrace(return_value, 1, 0, 0 TSRMLS_CC); +#endif + + if (!SYMFONY_DEBUG_G(debug_bt)) { + return; + } + + php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(SYMFONY_DEBUG_G(debug_bt)), 0 TSRMLS_CC); +} + +PHP_FUNCTION(symfony_zval_info) +{ + zval *key = NULL, *arg = NULL; + zval **data = NULL; + HashTable *array = NULL; + long options = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zh|l", &key, &array, &options) == FAILURE) { + return; + } + + switch (Z_TYPE_P(key)) { + case IS_STRING: + if (zend_symtable_find(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&data) == FAILURE) { + return; + } + break; + case IS_LONG: + if (zend_hash_index_find(array, Z_LVAL_P(key), (void **)&data)) { + return; + } + break; + } + + arg = *data; + + array_init(return_value); + + add_assoc_string(return_value, "type", (char *)_symfony_debug_zval_type(arg), 1); + add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg TSRMLS_CC), 16, 0); + add_assoc_long(return_value, "zval_refcount", Z_REFCOUNT_P(arg)); + add_assoc_bool(return_value, "zval_isref", (zend_bool)Z_ISREF_P(arg)); + + if (Z_TYPE_P(arg) == IS_OBJECT) { + char hash[33] = {0}; + + php_spl_object_hash(arg, (char *)hash TSRMLS_CC); + add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1); + add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount); + add_assoc_string(return_value, "object_hash", hash, 1); + add_assoc_long(return_value, "object_handle", Z_OBJ_HANDLE_P(arg)); + } else if (Z_TYPE_P(arg) == IS_ARRAY) { + add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg))); + } else if(Z_TYPE_P(arg) == IS_RESOURCE) { + add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg)); + add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg) TSRMLS_CC), 1); + add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg) TSRMLS_CC)); + } else if (Z_TYPE_P(arg) == IS_STRING) { + add_assoc_long(return_value, "strlen", Z_STRLEN_P(arg)); + } +} + +void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) +{ + TSRMLS_FETCH(); + zval *retval; + + switch (type) { + case E_ERROR: + case E_PARSE: + case E_CORE_ERROR: + case E_CORE_WARNING: + case E_COMPILE_ERROR: + case E_COMPILE_WARNING: + ALLOC_INIT_ZVAL(retval); +#if IS_PHP_53 + zend_fetch_debug_backtrace(retval, 1, 0 TSRMLS_CC); +#else + zend_fetch_debug_backtrace(retval, 1, 0, 0 TSRMLS_CC); +#endif + SYMFONY_DEBUG_G(debug_bt) = retval; + } + + SYMFONY_DEBUG_G(old_error_cb)(type, error_filename, error_lineno, format, args); +} + +static const char* _symfony_debug_get_resource_type(long rsid TSRMLS_DC) +{ + const char *res_type; + res_type = zend_rsrc_list_get_rsrc_type(rsid TSRMLS_CC); + + if (!res_type) { + return "Unknown"; + } + + return res_type; +} + +static int _symfony_debug_get_resource_refcount(long rsid TSRMLS_DC) +{ + zend_rsrc_list_entry *le; + + if (zend_hash_index_find(&EG(regular_list), rsid, (void **) &le)==SUCCESS) { + return le->refcount; + } + + return 0; +} + +static char *_symfony_debug_memory_address_hash(void *address TSRMLS_DC) +{ + char *result = NULL; + intptr_t address_rand; + + if (!SYMFONY_DEBUG_G(req_rand_init)) { + if (!BG(mt_rand_is_seeded)) { + php_mt_srand(GENERATE_SEED() TSRMLS_CC); + } + SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand(TSRMLS_C); + } + + address_rand = (intptr_t)address ^ SYMFONY_DEBUG_G(req_rand_init); + + spprintf(&result, 17, "%016zx", address_rand); + + return result; +} + +static const char *_symfony_debug_zval_type(zval *zv) +{ + switch (Z_TYPE_P(zv)) { + case IS_NULL: + return "NULL"; + break; + + case IS_BOOL: + return "boolean"; + break; + + case IS_LONG: + return "integer"; + break; + + case IS_DOUBLE: + return "double"; + break; + + case IS_STRING: + return "string"; + break; + + case IS_ARRAY: + return "array"; + break; + + case IS_OBJECT: + return "object"; + + case IS_RESOURCE: + return "resource"; + + default: + return "unknown type"; + } +} + +zend_module_entry symfony_debug_module_entry = { + STANDARD_MODULE_HEADER, + "symfony_debug", + symfony_debug_functions, + PHP_MINIT(symfony_debug), + PHP_MSHUTDOWN(symfony_debug), + PHP_RINIT(symfony_debug), + PHP_RSHUTDOWN(symfony_debug), + PHP_MINFO(symfony_debug), + PHP_SYMFONY_DEBUG_VERSION, + PHP_MODULE_GLOBALS(symfony_debug), + PHP_GINIT(symfony_debug), + PHP_GSHUTDOWN(symfony_debug), + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; + +#ifdef COMPILE_DL_SYMFONY_DEBUG +ZEND_GET_MODULE(symfony_debug) +#endif + +PHP_GINIT_FUNCTION(symfony_debug) +{ + memset(symfony_debug_globals, 0 , sizeof(*symfony_debug_globals)); +} + +PHP_GSHUTDOWN_FUNCTION(symfony_debug) +{ + +} + +PHP_MINIT_FUNCTION(symfony_debug) +{ + SYMFONY_DEBUG_G(old_error_cb) = zend_error_cb; + zend_error_cb = symfony_debug_error_cb; + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(symfony_debug) +{ + zend_error_cb = SYMFONY_DEBUG_G(old_error_cb); + + return SUCCESS; +} + +PHP_RINIT_FUNCTION(symfony_debug) +{ + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(symfony_debug) +{ + return SUCCESS; +} + +PHP_MINFO_FUNCTION(symfony_debug) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "Symfony Debug support", "enabled"); + php_info_print_table_header(2, "Symfony Debug version", PHP_SYMFONY_DEBUG_VERSION); + php_info_print_table_end(); +} diff --git a/vendor/symfony/debug/Resources/ext/tests/001.phpt b/vendor/symfony/debug/Resources/ext/tests/001.phpt new file mode 100644 index 0000000..4d41417 --- /dev/null +++ b/vendor/symfony/debug/Resources/ext/tests/001.phpt @@ -0,0 +1,151 @@ +--TEST-- +Test symfony_zval_info API +--SKIPIF-- + +--FILE-- + $int, + 'float' => $float, + 'str' => $str, + 'object' => $object, + 'array' => $array, + 'resource' => $resource, + 'null' => $null, + 'bool' => $bool, + 'refcount' => &$refcount2); + +var_dump(symfony_zval_info('int', $var)); +var_dump(symfony_zval_info('float', $var)); +var_dump(symfony_zval_info('str', $var)); +var_dump(symfony_zval_info('object', $var)); +var_dump(symfony_zval_info('array', $var)); +var_dump(symfony_zval_info('resource', $var)); +var_dump(symfony_zval_info('null', $var)); +var_dump(symfony_zval_info('bool', $var)); + +var_dump(symfony_zval_info('refcount', $var)); +var_dump(symfony_zval_info('not-exist', $var)); +?> +--EXPECTF-- +array(4) { + ["type"]=> + string(7) "integer" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(4) { + ["type"]=> + string(6) "double" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(5) { + ["type"]=> + string(6) "string" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["strlen"]=> + int(6) +} +array(8) { + ["type"]=> + string(6) "object" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["object_class"]=> + string(8) "stdClass" + ["object_refcount"]=> + int(1) + ["object_hash"]=> + string(32) "%s" + ["object_handle"]=> + int(%d) +} +array(5) { + ["type"]=> + string(5) "array" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["array_count"]=> + int(2) +} +array(7) { + ["type"]=> + string(8) "resource" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["resource_handle"]=> + int(%d) + ["resource_type"]=> + string(6) "stream" + ["resource_refcount"]=> + int(1) +} +array(4) { + ["type"]=> + string(4) "NULL" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(4) { + ["type"]=> + string(7) "boolean" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(4) { + ["type"]=> + string(7) "integer" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(3) + ["zval_isref"]=> + bool(true) +} +NULL diff --git a/vendor/symfony/debug/Resources/ext/tests/002.phpt b/vendor/symfony/debug/Resources/ext/tests/002.phpt new file mode 100644 index 0000000..ebe2f32 --- /dev/null +++ b/vendor/symfony/debug/Resources/ext/tests/002.phpt @@ -0,0 +1,64 @@ +--TEST-- +Test symfony_debug_backtrace in case of fatal error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Call to undefined function notexist() in %s on line %d +Array +( + [0] => Array + ( + [function] => bt + [args] => Array + ( + ) + + ) + + [1] => Array + ( + [file] => %s + [line] => %d + [function] => foo + [args] => Array + ( + ) + + ) + + [2] => Array + ( + [file] => %s + [line] => %d + [function] => bar + [args] => Array + ( + ) + + ) + +) diff --git a/vendor/symfony/debug/Resources/ext/tests/002_1.phpt b/vendor/symfony/debug/Resources/ext/tests/002_1.phpt new file mode 100644 index 0000000..4d52dbf --- /dev/null +++ b/vendor/symfony/debug/Resources/ext/tests/002_1.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test symfony_debug_backtrace in case of non fatal error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Array +( + [0] => Array + ( + [file] => %s + [line] => %d + [function] => bt + [args] => Array + ( + ) + + ) + + [1] => Array + ( + [file] => %s + [line] => %d + [function] => bar + [args] => Array + ( + ) + + ) + +) diff --git a/vendor/symfony/debug/Resources/ext/tests/003.phpt b/vendor/symfony/debug/Resources/ext/tests/003.phpt new file mode 100644 index 0000000..a363333 --- /dev/null +++ b/vendor/symfony/debug/Resources/ext/tests/003.phpt @@ -0,0 +1,85 @@ +--TEST-- +Test ErrorHandler in case of fatal error +--SKIPIF-- + +--FILE-- +setExceptionHandler('print_r'); + +if (function_exists('xdebug_disable')) { + xdebug_disable(); +} + +bar(); +?> +--EXPECTF-- +Fatal error: Call to undefined function Symfony\Component\Debug\notexist() in %s on line %d +Symfony\Component\Debug\Exception\UndefinedFunctionException Object +( + [message:protected] => Attempted to call function "notexist" from namespace "Symfony\Component\Debug". + [string:Exception:private] => + [code:protected] => 0 + [file:protected] => %s + [line:protected] => %d + [trace:Exception:private] => Array + ( + [0] => Array + ( +%A [function] => Symfony\Component\Debug\foo +%A [args] => Array + ( + ) + + ) + + [1] => Array + ( +%A [function] => Symfony\Component\Debug\bar +%A [args] => Array + ( + ) + + ) +%A + ) + + [previous:Exception:private] => + [severity:protected] => 1 +) diff --git a/vendor/symfony/debug/Tests/DebugClassLoaderTest.php b/vendor/symfony/debug/Tests/DebugClassLoaderTest.php new file mode 100644 index 0000000..5c226f4 --- /dev/null +++ b/vendor/symfony/debug/Tests/DebugClassLoaderTest.php @@ -0,0 +1,316 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Symfony\Component\Debug\DebugClassLoader; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\Exception\ContextErrorException; + +class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var int Error reporting level before running tests. + */ + private $errorReporting; + + private $loader; + + protected function setUp() + { + $this->errorReporting = error_reporting(E_ALL); + $this->loader = new ClassLoader(); + spl_autoload_register(array($this->loader, 'loadClass'), true, true); + DebugClassLoader::enable(); + } + + protected function tearDown() + { + DebugClassLoader::disable(); + spl_autoload_unregister(array($this->loader, 'loadClass')); + error_reporting($this->errorReporting); + } + + public function testIdempotence() + { + DebugClassLoader::enable(); + + $functions = spl_autoload_functions(); + foreach ($functions as $function) { + if (is_array($function) && $function[0] instanceof DebugClassLoader) { + $reflClass = new \ReflectionClass($function[0]); + $reflProp = $reflClass->getProperty('classLoader'); + $reflProp->setAccessible(true); + + $this->assertNotInstanceOf('Symfony\Component\Debug\DebugClassLoader', $reflProp->getValue($function[0])); + + return; + } + } + + $this->fail('DebugClassLoader did not register'); + } + + public function testUnsilencing() + { + if (PHP_VERSION_ID >= 70000) { + $this->markTestSkipped('PHP7 throws exceptions, unsilencing is not required anymore.'); + } + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM is not handled in this test case.'); + } + + ob_start(); + + $this->iniSet('log_errors', 0); + $this->iniSet('display_errors', 1); + + // See below: this will fail with parse error + // but this should not be @-silenced. + @class_exists(__NAMESPACE__.'\TestingUnsilencing', true); + + $output = ob_get_clean(); + + $this->assertStringMatchesFormat('%aParse error%a', $output); + } + + public function testStacking() + { + // the ContextErrorException must not be loaded to test the workaround + // for https://bugs.php.net/65322. + if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) { + $this->markTestSkipped('The ContextErrorException class is already loaded.'); + } + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM is not handled in this test case.'); + } + + ErrorHandler::register(); + + try { + // Trigger autoloading + E_STRICT at compile time + // which in turn triggers $errorHandler->handle() + // that again triggers autoloading for ContextErrorException. + // Error stacking works around the bug above and everything is fine. + + eval(' + namespace '.__NAMESPACE__.'; + class ChildTestingStacking extends TestingStacking { function foo($bar) {} } + '); + $this->fail('ContextErrorException expected'); + } catch (\ErrorException $exception) { + // if an exception is thrown, the test passed + $this->assertStringStartsWith(__FILE__, $exception->getFile()); + if (PHP_VERSION_ID < 70000) { + $this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage()); + $this->assertEquals(E_STRICT, $exception->getSeverity()); + } else { + $this->assertRegExp('/^Warning: Declaration/', $exception->getMessage()); + $this->assertEquals(E_WARNING, $exception->getSeverity()); + } + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + /** + * @expectedException \RuntimeException + */ + public function testNameCaseMismatch() + { + class_exists(__NAMESPACE__.'\TestingCaseMismatch', true); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Case mismatch between class and real file names + */ + public function testFileCaseMismatch() + { + if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) { + $this->markTestSkipped('Can only be run on case insensitive filesystems'); + } + + class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true); + } + + /** + * @expectedException \RuntimeException + */ + public function testPsr4CaseMismatch() + { + class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true); + } + + public function testNotPsr0() + { + $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0', true)); + } + + public function testNotPsr0Bis() + { + $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0bis', true)); + } + + public function testClassAlias() + { + $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\ClassAlias', true)); + } + + /** + * @dataProvider provideDeprecatedSuper + */ + public function testDeprecatedSuper($class, $super, $type) + { + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_DEPRECATED); + + class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_DEPRECATED, + 'message' => 'The Test\Symfony\Component\Debug\Tests\\'.$class.' class '.$type.' Symfony\Component\Debug\Tests\Fixtures\\'.$super.' that is deprecated but this is a test deprecation notice.', + ); + + $this->assertSame($xError, $lastError); + } + + public function provideDeprecatedSuper() + { + return array( + array('DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'), + array('DeprecatedParentClass', 'DeprecatedClass', 'extends'), + ); + } + + public function testInterfaceExtendsDeprecatedInterface() + { + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Test\\'.__NAMESPACE__.'\\NonDeprecatedInterfaceClass', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_NOTICE, + 'message' => '', + ); + + $this->assertSame($xError, $lastError); + } + + public function testDeprecatedSuperInSameNamespace() + { + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_NOTICE, + 'message' => '', + ); + + $this->assertSame($xError, $lastError); + } + + public function testReservedForPhp7() + { + if (PHP_VERSION_ID >= 70000) { + $this->markTestSkipped('PHP7 already prevents using reserved names.'); + } + + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Test\\'.__NAMESPACE__.'\\Float', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_DEPRECATED, + 'message' => 'Test\Symfony\Component\Debug\Tests\Float uses a reserved class name (Float) that will break on PHP 7 and higher', + ); + + $this->assertSame($xError, $lastError); + } +} + +class ClassLoader +{ + public function loadClass($class) + { + } + + public function getClassMap() + { + return array(__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php'); + } + + public function findFile($class) + { + $fixtureDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR; + + if (__NAMESPACE__.'\TestingUnsilencing' === $class) { + eval('-- parse error --'); + } elseif (__NAMESPACE__.'\TestingStacking' === $class) { + eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }'); + } elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) { + eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}'); + } elseif (__NAMESPACE__.'\Fixtures\CaseMismatch' === $class) { + return $fixtureDir.'CaseMismatch.php'; + } elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) { + return $fixtureDir.'psr4'.DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php'; + } elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) { + return $fixtureDir.'reallyNotPsr0.php'; + } elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) { + return $fixtureDir.'notPsr0Bis.php'; + } elseif (__NAMESPACE__.'\Fixtures\DeprecatedInterface' === $class) { + return $fixtureDir.'DeprecatedInterface.php'; + } elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) { + eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); + } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); + } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedInterfaceClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}'); + } elseif ('Test\\'.__NAMESPACE__.'\NonDeprecatedInterfaceClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}'); + } elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class Float {}'); + } + } +} diff --git a/vendor/symfony/debug/Tests/ErrorHandlerTest.php b/vendor/symfony/debug/Tests/ErrorHandlerTest.php new file mode 100644 index 0000000..e2f52fd --- /dev/null +++ b/vendor/symfony/debug/Tests/ErrorHandlerTest.php @@ -0,0 +1,491 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Psr\Log\LogLevel; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\BufferingLogger; +use Symfony\Component\Debug\Exception\ContextErrorException; + +/** + * ErrorHandlerTest. + * + * @author Robert Schönthal + * @author Nicolas Grekas + */ +class ErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testRegister() + { + $handler = ErrorHandler::register(); + + try { + $this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler); + $this->assertSame($handler, ErrorHandler::register()); + + $newHandler = new ErrorHandler(); + + $this->assertSame($newHandler, ErrorHandler::register($newHandler, false)); + $h = set_error_handler('var_dump'); + restore_error_handler(); + $this->assertSame(array($handler, 'handleError'), $h); + + try { + $this->assertSame($newHandler, ErrorHandler::register($newHandler, true)); + $h = set_error_handler('var_dump'); + restore_error_handler(); + $this->assertSame(array($newHandler, 'handleError'), $h); + } catch (\Exception $e) { + } + + restore_error_handler(); + restore_exception_handler(); + + if (isset($e)) { + throw $e; + } + } catch (\Exception $e) { + } + + restore_error_handler(); + restore_exception_handler(); + + if (isset($e)) { + throw $e; + } + } + + public function testNotice() + { + ErrorHandler::register(); + + try { + self::triggerNotice($this); + $this->fail('ContextErrorException expected'); + } catch (ContextErrorException $exception) { + // if an exception is thrown, the test passed + $this->assertEquals(E_NOTICE, $exception->getSeverity()); + $this->assertEquals(__FILE__, $exception->getFile()); + $this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage()); + $this->assertArrayHasKey('foobar', $exception->getContext()); + + $trace = $exception->getTrace(); + $this->assertEquals(__FILE__, $trace[0]['file']); + $this->assertEquals('Symfony\Component\Debug\ErrorHandler', $trace[0]['class']); + $this->assertEquals('handleError', $trace[0]['function']); + $this->assertEquals('->', $trace[0]['type']); + + $this->assertEquals(__FILE__, $trace[1]['file']); + $this->assertEquals(__CLASS__, $trace[1]['class']); + $this->assertEquals('triggerNotice', $trace[1]['function']); + $this->assertEquals('::', $trace[1]['type']); + + $this->assertEquals(__FILE__, $trace[1]['file']); + $this->assertEquals(__CLASS__, $trace[2]['class']); + $this->assertEquals(__FUNCTION__, $trace[2]['function']); + $this->assertEquals('->', $trace[2]['type']); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + // dummy function to test trace in error handler. + private static function triggerNotice($that) + { + // dummy variable to check for in error handler. + $foobar = 123; + $that->assertSame('', $foo.$foo.$bar); + } + + public function testConstruct() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(3, true); + $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0)); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testDefaultLogger() + { + try { + $handler = ErrorHandler::register(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $handler->setDefaultLogger($logger, E_NOTICE); + $handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL)); + + $loggers = array( + E_DEPRECATED => array(null, LogLevel::INFO), + E_USER_DEPRECATED => array(null, LogLevel::INFO), + E_NOTICE => array($logger, LogLevel::WARNING), + E_USER_NOTICE => array($logger, LogLevel::CRITICAL), + E_STRICT => array(null, LogLevel::WARNING), + E_WARNING => array(null, LogLevel::WARNING), + E_USER_WARNING => array(null, LogLevel::WARNING), + E_COMPILE_WARNING => array(null, LogLevel::WARNING), + E_CORE_WARNING => array(null, LogLevel::WARNING), + E_USER_ERROR => array(null, LogLevel::CRITICAL), + E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL), + E_COMPILE_ERROR => array(null, LogLevel::CRITICAL), + E_PARSE => array(null, LogLevel::CRITICAL), + E_ERROR => array(null, LogLevel::CRITICAL), + E_CORE_ERROR => array(null, LogLevel::CRITICAL), + ); + $this->assertSame($loggers, $handler->setLoggers(array())); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testHandleError() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(0, true); + $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(3, true); + $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(3, true); + try { + $handler->handleError(4, 'foo', 'foo.php', 12, array()); + } catch (\ErrorException $e) { + $this->assertSame('Parse Error: foo', $e->getMessage()); + $this->assertSame(4, $e->getSeverity()); + $this->assertSame('foo.php', $e->getFile()); + $this->assertSame(12, $e->getLine()); + } + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(E_USER_DEPRECATED, true); + $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(E_DEPRECATED, true); + $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $warnArgCheck = function ($logLevel, $message, $context) { + $this->assertEquals('info', $logLevel); + $this->assertEquals('foo', $message); + $this->assertArrayHasKey('type', $context); + $this->assertEquals($context['type'], E_USER_DEPRECATED); + $this->assertArrayHasKey('stack', $context); + $this->assertInternalType('array', $context['stack']); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($warnArgCheck)) + ; + + $handler = ErrorHandler::register(); + $handler->setDefaultLogger($logger, E_USER_DEPRECATED); + $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals('Undefined variable: undefVar', $message); + $this->assertArrayHasKey('type', $context); + $this->assertEquals($context['type'], E_NOTICE); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler = ErrorHandler::register(); + $handler->setDefaultLogger($logger, E_NOTICE); + $handler->screamAt(E_NOTICE); + unset($undefVar); + @$undefVar++; + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + public function testHandleUserError() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(0, true); + + $e = null; + $x = new \Exception('Foo'); + + try { + $f = new Fixtures\ToStringThrower($x); + $f .= ''; // Trigger $f->__toString() + } catch (\Exception $e) { + } + + $this->assertSame($x, $e); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testHandleDeprecation() + { + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals(LogLevel::INFO, $level); + $this->assertArrayHasKey('level', $context); + $this->assertEquals(E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED, $context['level']); + $this->assertArrayHasKey('stack', $context); + }; + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler = new ErrorHandler(); + $handler->setDefaultLogger($logger); + @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array()); + } + + public function testHandleException() + { + try { + $handler = ErrorHandler::register(); + + $exception = new \Exception('foo'); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals('Uncaught Exception: foo', $message); + $this->assertArrayHasKey('type', $context); + $this->assertEquals($context['type'], E_ERROR); + }; + + $logger + ->expects($this->exactly(2)) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler->setDefaultLogger($logger, E_ERROR); + + try { + $handler->handleException($exception); + $this->fail('Exception expected'); + } catch (\Exception $e) { + $this->assertSame($exception, $e); + } + + $handler->setExceptionHandler(function ($e) use ($exception) { + $this->assertSame($exception, $e); + }); + + $handler->handleException($exception); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testErrorStacking() + { + try { + $handler = ErrorHandler::register(); + $handler->screamAt(E_USER_WARNING); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logger + ->expects($this->exactly(2)) + ->method('log') + ->withConsecutive( + array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')), + array($this->equalTo(LogLevel::DEBUG), $this->equalTo('Silenced warning')) + ) + ; + + $handler->setDefaultLogger($logger, array(E_USER_WARNING => LogLevel::WARNING)); + + ErrorHandler::stackErrors(); + @trigger_error('Silenced warning', E_USER_WARNING); + $logger->log(LogLevel::WARNING, 'Dummy log'); + ErrorHandler::unstackErrors(); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testBootstrappingLogger() + { + $bootLogger = new BufferingLogger(); + $handler = new ErrorHandler($bootLogger); + + $loggers = array( + E_DEPRECATED => array($bootLogger, LogLevel::INFO), + E_USER_DEPRECATED => array($bootLogger, LogLevel::INFO), + E_NOTICE => array($bootLogger, LogLevel::WARNING), + E_USER_NOTICE => array($bootLogger, LogLevel::WARNING), + E_STRICT => array($bootLogger, LogLevel::WARNING), + E_WARNING => array($bootLogger, LogLevel::WARNING), + E_USER_WARNING => array($bootLogger, LogLevel::WARNING), + E_COMPILE_WARNING => array($bootLogger, LogLevel::WARNING), + E_CORE_WARNING => array($bootLogger, LogLevel::WARNING), + E_USER_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_RECOVERABLE_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_COMPILE_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_PARSE => array($bootLogger, LogLevel::CRITICAL), + E_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_CORE_ERROR => array($bootLogger, LogLevel::CRITICAL), + ); + + $this->assertSame($loggers, $handler->setLoggers(array())); + + $handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array()); + $expectedLog = array(LogLevel::INFO, 'Foo message', array('type' => E_DEPRECATED, 'file' => __FILE__, 'line' => 123, 'level' => error_reporting())); + + $logs = $bootLogger->cleanLogs(); + unset($logs[0][2]['stack']); + + $this->assertSame(array($expectedLog), $logs); + + $bootLogger->log($expectedLog[0], $expectedLog[1], $expectedLog[2]); + + $mockLogger = $this->getMock('Psr\Log\LoggerInterface'); + $mockLogger->expects($this->once()) + ->method('log') + ->with(LogLevel::WARNING, 'Foo message', $expectedLog[2]); + + $handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING))); + } + + public function testHandleFatalError() + { + try { + $handler = ErrorHandler::register(); + + $error = array( + 'type' => E_PARSE, + 'message' => 'foo', + 'file' => 'bar', + 'line' => 123, + ); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals('Fatal Parse Error: foo', $message); + $this->assertArrayHasKey('type', $context); + $this->assertEquals($context['type'], E_PARSE); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler->setDefaultLogger($logger, E_PARSE); + + $handler->handleFatalError($error); + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + public function testHandleFatalErrorOnHHVM() + { + try { + $handler = ErrorHandler::register(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger + ->expects($this->once()) + ->method('log') + ->with( + $this->equalTo(LogLevel::CRITICAL), + $this->equalTo('Fatal Error: foo'), + $this->equalTo(array( + 'type' => 1, + 'file' => 'bar', + 'line' => 123, + 'level' => -1, + 'stack' => array(456), + )) + ) + ; + + $handler->setDefaultLogger($logger, E_ERROR); + + $error = array( + 'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors + 'message' => 'foo', + 'file' => 'bar', + 'line' => 123, + 'context' => array(123), + 'backtrace' => array(456), + ); + + call_user_func_array(array($handler, 'handleError'), $error); + $handler->handleFatalError($error); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } +} diff --git a/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php b/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php new file mode 100644 index 0000000..6c570e2 --- /dev/null +++ b/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php @@ -0,0 +1,270 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\Exception; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\GoneHttpException; +use Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException; +use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; +use Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException; +use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; +use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; +use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; + +class FlattenExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testStatusCode() + { + $flattened = FlattenException::create(new \RuntimeException(), 403); + $this->assertEquals('403', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new \RuntimeException()); + $this->assertEquals('500', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new NotFoundHttpException()); + $this->assertEquals('404', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); + $this->assertEquals('401', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new BadRequestHttpException()); + $this->assertEquals('400', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new NotAcceptableHttpException()); + $this->assertEquals('406', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new ConflictHttpException()); + $this->assertEquals('409', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST'))); + $this->assertEquals('405', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new AccessDeniedHttpException()); + $this->assertEquals('403', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new GoneHttpException()); + $this->assertEquals('410', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new LengthRequiredHttpException()); + $this->assertEquals('411', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new PreconditionFailedHttpException()); + $this->assertEquals('412', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new PreconditionRequiredHttpException()); + $this->assertEquals('428', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new ServiceUnavailableHttpException()); + $this->assertEquals('503', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new TooManyRequestsHttpException()); + $this->assertEquals('429', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new UnsupportedMediaTypeHttpException()); + $this->assertEquals('415', $flattened->getStatusCode()); + } + + public function testHeadersForHttpException() + { + $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST'))); + $this->assertEquals(array('Allow' => 'POST'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); + $this->assertEquals(array('WWW-Authenticate' => 'Basic realm="My Realm"'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new ServiceUnavailableHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); + $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new ServiceUnavailableHttpException(120)); + $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders()); + + $flattened = FlattenException::create(new TooManyRequestsHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); + $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new TooManyRequestsHttpException(120)); + $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testFlattenHttpException(\Exception $exception, $statusCode) + { + $flattened = FlattenException::create($exception); + $flattened2 = FlattenException::create($exception); + + $flattened->setPrevious($flattened2); + + $this->assertEquals($exception->getMessage(), $flattened->getMessage(), 'The message is copied from the original exception.'); + $this->assertEquals($exception->getCode(), $flattened->getCode(), 'The code is copied from the original exception.'); + $this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception'); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testPrevious(\Exception $exception, $statusCode) + { + $flattened = FlattenException::create($exception); + $flattened2 = FlattenException::create($exception); + + $flattened->setPrevious($flattened2); + + $this->assertSame($flattened2, $flattened->getPrevious()); + + $this->assertSame(array($flattened2), $flattened->getAllPrevious()); + } + + /** + * @requires PHP 7.0 + */ + public function testPreviousError() + { + $exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42)); + + $flattened = FlattenException::create($exception)->getPrevious(); + + $this->assertEquals($flattened->getMessage(), 'Parse error: Oh noes!', 'The message is copied from the original exception.'); + $this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.'); + $this->assertEquals($flattened->getClass(), 'Symfony\Component\Debug\Exception\FatalThrowableError', 'The class is set to the class of the original exception'); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testLine(\Exception $exception) + { + $flattened = FlattenException::create($exception); + $this->assertSame($exception->getLine(), $flattened->getLine()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testFile(\Exception $exception) + { + $flattened = FlattenException::create($exception); + $this->assertSame($exception->getFile(), $flattened->getFile()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testToArray(\Exception $exception, $statusCode) + { + $flattened = FlattenException::create($exception); + $flattened->setTrace(array(), 'foo.php', 123); + + $this->assertEquals(array( + array( + 'message' => 'test', + 'class' => 'Exception', + 'trace' => array(array( + 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123, + 'args' => array(), + )), + ), + ), $flattened->toArray()); + } + + public function flattenDataProvider() + { + return array( + array(new \Exception('test', 123), 500), + ); + } + + public function testRecursionInArguments() + { + $a = array('foo', array(2, &$a)); + $exception = $this->createException($a); + + $flattened = FlattenException::create($exception); + $trace = $flattened->getTrace(); + $this->assertContains('*DEEP NESTED ARRAY*', serialize($trace)); + } + + public function testTooBigArray() + { + $a = array(); + for ($i = 0; $i < 20; ++$i) { + for ($j = 0; $j < 50; ++$j) { + for ($k = 0; $k < 10; ++$k) { + $a[$i][$j][$k] = 'value'; + } + } + } + $a[20] = 'value'; + $a[21] = 'value1'; + $exception = $this->createException($a); + + $flattened = FlattenException::create($exception); + $trace = $flattened->getTrace(); + $serializeTrace = serialize($trace); + + $this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace); + $this->assertNotContains('*value1*', $serializeTrace); + } + + private function createException($foo) + { + return new \Exception(); + } + + public function testSetTraceIncompleteClass() + { + $flattened = FlattenException::create(new \Exception('test', 123)); + $flattened->setTrace( + array( + array( + 'file' => __FILE__, + 'line' => 123, + 'function' => 'test', + 'args' => array( + unserialize('O:14:"BogusTestClass":0:{}'), + ), + ), + ), + 'foo.php', 123 + ); + + $this->assertEquals(array( + array( + 'message' => 'test', + 'class' => 'Exception', + 'trace' => array( + array( + 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', + 'file' => 'foo.php', 'line' => 123, + 'args' => array(), + ), + array( + 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => 'test', + 'file' => __FILE__, 'line' => 123, + 'args' => array( + array( + 'incomplete-object', 'BogusTestClass', + ), + ), + ), + ), + ), + ), $flattened->toArray()); + } +} diff --git a/vendor/symfony/debug/Tests/ExceptionHandlerTest.php b/vendor/symfony/debug/Tests/ExceptionHandlerTest.php new file mode 100644 index 0000000..1703da6 --- /dev/null +++ b/vendor/symfony/debug/Tests/ExceptionHandlerTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\Debug\Exception\OutOfMemoryException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; + +require_once __DIR__.'/HeaderMock.php'; + +class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + testHeader(); + } + + protected function tearDown() + { + testHeader(); + } + + public function testDebug() + { + $handler = new ExceptionHandler(false); + + ob_start(); + $handler->sendPhpResponse(new \RuntimeException('Foo')); + $response = ob_get_clean(); + + $this->assertContains('

Whoops, looks like something went wrong.

', $response); + $this->assertNotContains('

', $response); + + $handler = new ExceptionHandler(true); + + ob_start(); + $handler->sendPhpResponse(new \RuntimeException('Foo')); + $response = ob_get_clean(); + + $this->assertContains('

Whoops, looks like something went wrong.

', $response); + $this->assertContains('

', $response); + } + + public function testStatusCode() + { + $handler = new ExceptionHandler(false, 'iso8859-1'); + + ob_start(); + $handler->sendPhpResponse(new NotFoundHttpException('Foo')); + $response = ob_get_clean(); + + $this->assertContains('Sorry, the page you are looking for could not be found.', $response); + + $expectedHeaders = array( + array('HTTP/1.0 404', true, null), + array('Content-Type: text/html; charset=iso8859-1', true, null), + ); + + $this->assertSame($expectedHeaders, testHeader()); + } + + public function testHeaders() + { + $handler = new ExceptionHandler(false, 'iso8859-1'); + + ob_start(); + $handler->sendPhpResponse(new MethodNotAllowedHttpException(array('POST'))); + $response = ob_get_clean(); + + $expectedHeaders = array( + array('HTTP/1.0 405', true, null), + array('Allow: POST', false, null), + array('Content-Type: text/html; charset=iso8859-1', true, null), + ); + + $this->assertSame($expectedHeaders, testHeader()); + } + + public function testNestedExceptions() + { + $handler = new ExceptionHandler(true); + ob_start(); + $handler->sendPhpResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar'))); + $response = ob_get_clean(); + + $this->assertStringMatchesFormat('%AFoo%ABar%A', $response); + } + + public function testHandle() + { + $exception = new \Exception('foo'); + + $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse')); + $handler + ->expects($this->exactly(2)) + ->method('sendPhpResponse'); + + $handler->handle($exception); + + $handler->setHandler(function ($e) use ($exception) { + $this->assertSame($exception, $e); + }); + + $handler->handle($exception); + } + + public function testHandleOutOfMemoryException() + { + $exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__); + + $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse')); + $handler + ->expects($this->once()) + ->method('sendPhpResponse'); + + $handler->setHandler(function ($e) { + $this->fail('OutOfMemoryException should bypass the handler'); + }); + + $handler->handle($exception); + } +} diff --git a/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php new file mode 100644 index 0000000..360e6ed --- /dev/null +++ b/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; +use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; +use Symfony\Component\Debug\DebugClassLoader; +use Composer\Autoload\ClassLoader as ComposerClassLoader; + +class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + public static function setUpBeforeClass() + { + foreach (spl_autoload_functions() as $function) { + if (!is_array($function)) { + continue; + } + + // get class loaders wrapped by DebugClassLoader + if ($function[0] instanceof DebugClassLoader) { + $function = $function[0]->getClassLoader(); + } + + if ($function[0] instanceof ComposerClassLoader) { + $function[0]->add('Symfony_Component_Debug_Tests_Fixtures', dirname(dirname(dirname(dirname(dirname(__DIR__)))))); + break; + } + } + } + + /** + * @dataProvider provideClassNotFoundData + */ + public function testHandleClassNotFound($error, $translatedMessage, $autoloader = null) + { + if ($autoloader) { + // Unregister all autoloaders to ensure the custom provided + // autoloader is the only one to be used during the test run. + $autoloaders = spl_autoload_functions(); + array_map('spl_autoload_unregister', $autoloaders); + spl_autoload_register($autoloader); + } + + $handler = new ClassNotFoundFatalErrorHandler(); + + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + if ($autoloader) { + spl_autoload_unregister($autoloader); + array_map('spl_autoload_register', $autoloaders); + } + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); + $this->assertSame($translatedMessage, $exception->getMessage()); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideClassNotFoundData() + { + $prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception')); + + $symfonyAutoloader = new SymfonyClassLoader(); + $symfonyAutoloader->addPrefixes($prefixes); + + $debugClassLoader = new DebugClassLoader(array($symfonyAutoloader, 'loadClass')); + + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'WhizBangFactory\' not found', + ), + "Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found', + ), + "Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'PEARClass\' not found', + ), + "Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + array($symfonyAutoloader, 'loadClass'), + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + array($debugClassLoader, 'loadClass'), + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", + function ($className) { /* do nothing here */ }, + ), + ); + } + + public function testCannotRedeclareClass() + { + if (!file_exists(__DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP')) { + $this->markTestSkipped('Can only be run on case insensitive filesystems'); + } + + require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP'; + + $error = array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\RequiredTwice\' not found', + ); + + $handler = new ClassNotFoundFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); + } +} diff --git a/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php new file mode 100644 index 0000000..795b747 --- /dev/null +++ b/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; + +class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideUndefinedFunctionData + */ + public function testUndefinedFunction($error, $translatedMessage) + { + $handler = new UndefinedFunctionFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception); + // class names are case insensitive and PHP/HHVM do not return the same + $this->assertSame(strtolower($translatedMessage), strtolower($exception->getMessage())); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideUndefinedFunctionData() + { + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function test_namespaced_function()', + ), + "Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()', + ), + "Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function foo()', + ), + 'Attempted to call function "foo" from the global namespace.', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()', + ), + 'Attempted to call function "foo" from namespace "Foo\Bar\Baz".', + ), + ); + } +} + +function test_namespaced_function() +{ +} diff --git a/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php b/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php new file mode 100644 index 0000000..de7b21c --- /dev/null +++ b/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; + +class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideUndefinedMethodData + */ + public function testUndefinedMethod($error, $translatedMessage) + { + $handler = new UndefinedMethodFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedMethodException', $exception); + $this->assertSame($translatedMessage, $exception->getMessage()); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideUndefinedMethodData() + { + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined method SplObjectStorage::what()', + ), + 'Attempted to call an undefined method named "what" of class "SplObjectStorage".', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined method SplObjectStorage::walid()', + ), + "Attempted to call an undefined method named \"walid\" of class \"SplObjectStorage\".\nDid you mean to call \"valid\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined method SplObjectStorage::offsetFet()', + ), + "Attempted to call an undefined method named \"offsetFet\" of class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?", + ), + ); + } +} diff --git a/vendor/symfony/debug/Tests/Fixtures/ClassAlias.php b/vendor/symfony/debug/Tests/Fixtures/ClassAlias.php new file mode 100644 index 0000000..9d6dbaa --- /dev/null +++ b/vendor/symfony/debug/Tests/Fixtures/ClassAlias.php @@ -0,0 +1,3 @@ +exception = $e; + } + + public function __toString() + { + try { + throw $this->exception; + } catch (\Exception $e) { + // Using user_error() here is on purpose so we do not forget + // that this alias also should work alongside with trigger_error(). + return user_error($e, E_USER_ERROR); + } + } +} diff --git a/vendor/symfony/debug/Tests/Fixtures/casemismatch.php b/vendor/symfony/debug/Tests/Fixtures/casemismatch.php new file mode 100644 index 0000000..691d660 --- /dev/null +++ b/vendor/symfony/debug/Tests/Fixtures/casemismatch.php @@ -0,0 +1,7 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +function headers_sent() +{ + return false; +} + +function header($str, $replace = true, $status = null) +{ + Tests\testHeader($str, $replace, $status); +} + +namespace Symfony\Component\Debug\Tests; + +function testHeader() +{ + static $headers = array(); + + if (!$h = func_get_args()) { + $h = $headers; + $headers = array(); + + return $h; + } + + $headers[] = func_get_args(); +} diff --git a/vendor/symfony/debug/Tests/MockExceptionHandler.php b/vendor/symfony/debug/Tests/MockExceptionHandler.php new file mode 100644 index 0000000..a85d2d1 --- /dev/null +++ b/vendor/symfony/debug/Tests/MockExceptionHandler.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Symfony\Component\Debug\ExceptionHandler; + +class MockExceptionHandler extends Exceptionhandler +{ + public $e; + + public function handle(\Exception $e) + { + $this->e = $e; + } +} diff --git a/vendor/symfony/debug/composer.json b/vendor/symfony/debug/composer.json new file mode 100644 index 0000000..0eef83e --- /dev/null +++ b/vendor/symfony/debug/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony/debug", + "type": "library", + "description": "Symfony Debug Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/class-loader": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Debug\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/debug/phpunit.xml.dist b/vendor/symfony/debug/phpunit.xml.dist new file mode 100644 index 0000000..e99c4dd --- /dev/null +++ b/vendor/symfony/debug/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + ./Resources/ext/tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/dom-crawler/.gitignore b/vendor/symfony/dom-crawler/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/dom-crawler/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/dom-crawler/CHANGELOG.md b/vendor/symfony/dom-crawler/CHANGELOG.md new file mode 100644 index 0000000..48fd323 --- /dev/null +++ b/vendor/symfony/dom-crawler/CHANGELOG.md @@ -0,0 +1,45 @@ +CHANGELOG +========= + +2.5.0 +----- + +* [BC BREAK] The default value for checkbox and radio inputs without a value attribute have changed + from '1' to 'on' to match the HTML specification. +* [BC BREAK] The typehints on the `Link`, `Form` and `FormField` classes have been changed from + `\DOMNode` to `DOMElement`. Using any other type of `DOMNode` was triggering fatal errors in previous + versions. Code extending these classes will need to update the typehints when overwriting these methods. + +2.4.0 +----- + + * `Crawler::addXmlContent()` removes the default document namespace again if it's an only namespace. + * added support for automatic discovery and explicit registration of document + namespaces for `Crawler::filterXPath()` and `Crawler::filter()` + * improved content type guessing in `Crawler::addContent()` + * [BC BREAK] `Crawler::addXmlContent()` no longer removes the default document + namespace + +2.3.0 +----- + + * added Crawler::html() + * [BC BREAK] Crawler::each() and Crawler::reduce() now return Crawler instances instead of DomElement instances + * added schema relative URL support to links + * added support for HTML5 'form' attribute + +2.2.0 +----- + + * added a way to set raw path to the file in FileFormField - necessary for + simulating HTTP requests + +2.1.0 +----- + + * added support for the HTTP PATCH method + * refactored the Form class internals to support multi-dimensional fields + (the public API is backward compatible) + * added a way to get parsing errors for Crawler::addHtmlContent() and + Crawler::addXmlContent() via libxml functions + * added support for submitting a form without a submit button diff --git a/vendor/symfony/dom-crawler/Crawler.php b/vendor/symfony/dom-crawler/Crawler.php new file mode 100644 index 0000000..f43ee0f --- /dev/null +++ b/vendor/symfony/dom-crawler/Crawler.php @@ -0,0 +1,1035 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\CssSelector\CssSelectorConverter; + +/** + * Crawler eases navigation of a list of \DOMNode objects. + * + * @author Fabien Potencier + */ +class Crawler implements \Countable, \IteratorAggregate +{ + /** + * @var string The current URI + */ + protected $uri; + + /** + * @var string The default namespace prefix to be used with XPath and CSS expressions + */ + private $defaultNamespacePrefix = 'default'; + + /** + * @var array A map of manually registered namespaces + */ + private $namespaces = array(); + + /** + * @var string The base href value + */ + private $baseHref; + + /** + * @var \DOMDocument|null + */ + private $document; + + /** + * @var \DOMElement[] + */ + private $nodes = array(); + + /** + * Whether the Crawler contains HTML or XML content (used when converting CSS to XPath). + * + * @var bool + */ + private $isHtml = true; + + /** + * Constructor. + * + * @param mixed $node A Node to use as the base for the crawling + * @param string $currentUri The current URI + * @param string $baseHref The base href value + */ + public function __construct($node = null, $currentUri = null, $baseHref = null) + { + $this->uri = $currentUri; + $this->baseHref = $baseHref ?: $currentUri; + + $this->add($node); + } + + /** + * Removes all the nodes. + */ + public function clear() + { + $this->nodes = array(); + $this->document = null; + } + + /** + * Adds a node to the current list of nodes. + * + * This method uses the appropriate specialized add*() method based + * on the type of the argument. + * + * @param \DOMNodeList|\DOMNode|array|string|null $node A node + * + * @throws \InvalidArgumentException When node is not the expected type. + */ + public function add($node) + { + if ($node instanceof \DOMNodeList) { + $this->addNodeList($node); + } elseif ($node instanceof \DOMNode) { + $this->addNode($node); + } elseif (is_array($node)) { + $this->addNodes($node); + } elseif (is_string($node)) { + $this->addContent($node); + } elseif (null !== $node) { + throw new \InvalidArgumentException(sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', is_object($node) ? get_class($node) : gettype($node))); + } + } + + /** + * Adds HTML/XML content. + * + * If the charset is not set via the content type, it is assumed + * to be ISO-8859-1, which is the default charset defined by the + * HTTP 1.1 specification. + * + * @param string $content A string to parse as HTML/XML + * @param null|string $type The content type of the string + */ + public function addContent($content, $type = null) + { + if (empty($type)) { + $type = 0 === strpos($content, ']+charset *= *["\']?([a-zA-Z\-0-9_:.]+)/i', $content, $matches)) { + $charset = $matches[1]; + } + + if (null === $charset) { + $charset = 'ISO-8859-1'; + } + + if ('x' === $xmlMatches[1]) { + $this->addXmlContent($content, $charset); + } else { + $this->addHtmlContent($content, $charset); + } + } + + /** + * Adds an HTML content to the list of nodes. + * + * The libxml errors are disabled when the content is parsed. + * + * If you want to get parsing errors, be sure to enable + * internal errors via libxml_use_internal_errors(true) + * and then, get the errors via libxml_get_errors(). Be + * sure to clear errors with libxml_clear_errors() afterward. + * + * @param string $content The HTML content + * @param string $charset The charset + */ + public function addHtmlContent($content, $charset = 'UTF-8') + { + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + + $dom = new \DOMDocument('1.0', $charset); + $dom->validateOnParse = true; + + set_error_handler(function () {throw new \Exception();}); + + try { + // Convert charset to HTML-entities to work around bugs in DOMDocument::loadHTML() + $content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset); + } catch (\Exception $e) { + } + + restore_error_handler(); + + if ('' !== trim($content)) { + @$dom->loadHTML($content); + } + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + $this->addDocument($dom); + + $base = $this->filterRelativeXPath('descendant-or-self::base')->extract(array('href')); + + $baseHref = current($base); + if (count($base) && !empty($baseHref)) { + if ($this->baseHref) { + $linkNode = $dom->createElement('a'); + $linkNode->setAttribute('href', $baseHref); + $link = new Link($linkNode, $this->baseHref); + $this->baseHref = $link->getUri(); + } else { + $this->baseHref = $baseHref; + } + } + } + + /** + * Adds an XML content to the list of nodes. + * + * The libxml errors are disabled when the content is parsed. + * + * If you want to get parsing errors, be sure to enable + * internal errors via libxml_use_internal_errors(true) + * and then, get the errors via libxml_get_errors(). Be + * sure to clear errors with libxml_clear_errors() afterward. + * + * @param string $content The XML content + * @param string $charset The charset + */ + public function addXmlContent($content, $charset = 'UTF-8') + { + // remove the default namespace if it's the only namespace to make XPath expressions simpler + if (!preg_match('/xmlns:/', $content)) { + $content = str_replace('xmlns', 'ns', $content); + } + + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + + $dom = new \DOMDocument('1.0', $charset); + $dom->validateOnParse = true; + + if ('' !== trim($content)) { + @$dom->loadXML($content, LIBXML_NONET | (defined('LIBXML_PARSEHUGE') ? LIBXML_PARSEHUGE : 0)); + } + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + $this->addDocument($dom); + + $this->isHtml = false; + } + + /** + * Adds a \DOMDocument to the list of nodes. + * + * @param \DOMDocument $dom A \DOMDocument instance + */ + public function addDocument(\DOMDocument $dom) + { + if ($dom->documentElement) { + $this->addNode($dom->documentElement); + } + } + + /** + * Adds a \DOMNodeList to the list of nodes. + * + * @param \DOMNodeList $nodes A \DOMNodeList instance + */ + public function addNodeList(\DOMNodeList $nodes) + { + foreach ($nodes as $node) { + if ($node instanceof \DOMNode) { + $this->addNode($node); + } + } + } + + /** + * Adds an array of \DOMNode instances to the list of nodes. + * + * @param \DOMNode[] $nodes An array of \DOMNode instances + */ + public function addNodes(array $nodes) + { + foreach ($nodes as $node) { + $this->add($node); + } + } + + /** + * Adds a \DOMNode instance to the list of nodes. + * + * @param \DOMNode $node A \DOMNode instance + */ + public function addNode(\DOMNode $node) + { + if ($node instanceof \DOMDocument) { + $node = $node->documentElement; + } + + if (null !== $this->document && $this->document !== $node->ownerDocument) { + throw new \InvalidArgumentException('Attaching DOM nodes from multiple documents in the same crawler is forbidden.'); + } + + if (null === $this->document) { + $this->document = $node->ownerDocument; + } + + // Don't add duplicate nodes in the Crawler + if (in_array($node, $this->nodes, true)) { + return; + } + + $this->nodes[] = $node; + } + + /** + * Returns a node given its position in the node list. + * + * @param int $position The position + * + * @return Crawler A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist. + */ + public function eq($position) + { + if (isset($this->nodes[$position])) { + return $this->createSubCrawler($this->nodes[$position]); + } + + return $this->createSubCrawler(null); + } + + /** + * Calls an anonymous function on each node of the list. + * + * The anonymous function receives the position and the node wrapped + * in a Crawler instance as arguments. + * + * Example: + * + * $crawler->filter('h1')->each(function ($node, $i) { + * return $node->text(); + * }); + * + * @param \Closure $closure An anonymous function + * + * @return array An array of values returned by the anonymous function + */ + public function each(\Closure $closure) + { + $data = array(); + foreach ($this->nodes as $i => $node) { + $data[] = $closure($this->createSubCrawler($node), $i); + } + + return $data; + } + + /** + * Slices the list of nodes by $offset and $length. + * + * @param int $offset + * @param int $length + * + * @return Crawler A Crawler instance with the sliced nodes + */ + public function slice($offset = 0, $length = null) + { + return $this->createSubCrawler(array_slice($this->nodes, $offset, $length)); + } + + /** + * Reduces the list of nodes by calling an anonymous function. + * + * To remove a node from the list, the anonymous function must return false. + * + * @param \Closure $closure An anonymous function + * + * @return Crawler A Crawler instance with the selected nodes. + */ + public function reduce(\Closure $closure) + { + $nodes = array(); + foreach ($this->nodes as $i => $node) { + if (false !== $closure($this->createSubCrawler($node), $i)) { + $nodes[] = $node; + } + } + + return $this->createSubCrawler($nodes); + } + + /** + * Returns the first node of the current selection. + * + * @return Crawler A Crawler instance with the first selected node + */ + public function first() + { + return $this->eq(0); + } + + /** + * Returns the last node of the current selection. + * + * @return Crawler A Crawler instance with the last selected node + */ + public function last() + { + return $this->eq(count($this->nodes) - 1); + } + + /** + * Returns the siblings nodes of the current selection. + * + * @return Crawler A Crawler instance with the sibling nodes + * + * @throws \InvalidArgumentException When current node is empty + */ + public function siblings() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->createSubCrawler($this->sibling($this->getNode(0)->parentNode->firstChild)); + } + + /** + * Returns the next siblings nodes of the current selection. + * + * @return Crawler A Crawler instance with the next sibling nodes + * + * @throws \InvalidArgumentException When current node is empty + */ + public function nextAll() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->createSubCrawler($this->sibling($this->getNode(0))); + } + + /** + * Returns the previous sibling nodes of the current selection. + * + * @return Crawler A Crawler instance with the previous sibling nodes + * + * @throws \InvalidArgumentException + */ + public function previousAll() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->createSubCrawler($this->sibling($this->getNode(0), 'previousSibling')); + } + + /** + * Returns the parents nodes of the current selection. + * + * @return Crawler A Crawler instance with the parents nodes of the current selection + * + * @throws \InvalidArgumentException When current node is empty + */ + public function parents() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + $nodes = array(); + + while ($node = $node->parentNode) { + if (1 === $node->nodeType) { + $nodes[] = $node; + } + } + + return $this->createSubCrawler($nodes); + } + + /** + * Returns the children nodes of the current selection. + * + * @return Crawler A Crawler instance with the children nodes + * + * @throws \InvalidArgumentException When current node is empty + */ + public function children() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0)->firstChild; + + return $this->createSubCrawler($node ? $this->sibling($node) : array()); + } + + /** + * Returns the attribute value of the first node of the list. + * + * @param string $attribute The attribute name + * + * @return string|null The attribute value or null if the attribute does not exist + * + * @throws \InvalidArgumentException When current node is empty + */ + public function attr($attribute) + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + + return $node->hasAttribute($attribute) ? $node->getAttribute($attribute) : null; + } + + /** + * Returns the node name of the first node of the list. + * + * @return string The node name + * + * @throws \InvalidArgumentException When current node is empty + */ + public function nodeName() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->getNode(0)->nodeName; + } + + /** + * Returns the node value of the first node of the list. + * + * @return string The node value + * + * @throws \InvalidArgumentException When current node is empty + */ + public function text() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->getNode(0)->nodeValue; + } + + /** + * Returns the first node of the list as HTML. + * + * @return string The node html + * + * @throws \InvalidArgumentException When current node is empty + */ + public function html() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $html = ''; + foreach ($this->getNode(0)->childNodes as $child) { + $html .= $child->ownerDocument->saveHTML($child); + } + + return $html; + } + + /** + * Extracts information from the list of nodes. + * + * You can extract attributes or/and the node value (_text). + * + * Example: + * + * $crawler->filter('h1 a')->extract(array('_text', 'href')); + * + * @param array $attributes An array of attributes + * + * @return array An array of extracted values + */ + public function extract($attributes) + { + $attributes = (array) $attributes; + $count = count($attributes); + + $data = array(); + foreach ($this->nodes as $node) { + $elements = array(); + foreach ($attributes as $attribute) { + if ('_text' === $attribute) { + $elements[] = $node->nodeValue; + } else { + $elements[] = $node->getAttribute($attribute); + } + } + + $data[] = $count > 1 ? $elements : $elements[0]; + } + + return $data; + } + + /** + * Filters the list of nodes with an XPath expression. + * + * The XPath expression is evaluated in the context of the crawler, which + * is considered as a fake parent of the elements inside it. + * This means that a child selector "div" or "./div" will match only + * the div elements of the current crawler, not their children. + * + * @param string $xpath An XPath expression + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + */ + public function filterXPath($xpath) + { + $xpath = $this->relativize($xpath); + + // If we dropped all expressions in the XPath while preparing it, there would be no match + if ('' === $xpath) { + return $this->createSubCrawler(null); + } + + return $this->filterRelativeXPath($xpath); + } + + /** + * Filters the list of nodes with a CSS selector. + * + * This method only works if you have installed the CssSelector Symfony Component. + * + * @param string $selector A CSS selector + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + * + * @throws \RuntimeException if the CssSelector Component is not available + */ + public function filter($selector) + { + if (!class_exists('Symfony\\Component\\CssSelector\\CssSelectorConverter')) { + throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector 2.8+ is not installed (you can use filterXPath instead).'); + } + + $converter = new CssSelectorConverter($this->isHtml); + + // The CssSelector already prefixes the selector with descendant-or-self:: + return $this->filterRelativeXPath($converter->toXPath($selector)); + } + + /** + * Selects links by name or alt value for clickable images. + * + * @param string $value The link text + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + */ + public function selectLink($value) + { + $xpath = sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) ', static::xpathLiteral(' '.$value.' ')). + sprintf('or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)]]', static::xpathLiteral(' '.$value.' ')); + + return $this->filterRelativeXPath($xpath); + } + + /** + * Selects a button by name or alt value for images. + * + * @param string $value The button text + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + */ + public function selectButton($value) + { + $translate = 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")'; + $xpath = sprintf('descendant-or-self::input[((contains(%s, "submit") or contains(%s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', $translate, $translate, static::xpathLiteral(' '.$value.' ')). + sprintf('or (contains(%s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id=%s or @name=%s] ', $translate, static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)). + sprintf('| descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id=%s or @name=%s]', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)); + + return $this->filterRelativeXPath($xpath); + } + + /** + * Returns a Link object for the first node in the list. + * + * @param string $method The method for the link (get by default) + * + * @return Link A Link instance + * + * @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement + */ + public function link($method = 'get') + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + + if (!$node instanceof \DOMElement) { + throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_class($node))); + } + + return new Link($node, $this->baseHref, $method); + } + + /** + * Returns an array of Link objects for the nodes in the list. + * + * @return Link[] An array of Link instances + * + * @throws \InvalidArgumentException If the current node list contains non-DOMElement instances + */ + public function links() + { + $links = array(); + foreach ($this->nodes as $node) { + if (!$node instanceof \DOMElement) { + throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_class($node))); + } + + $links[] = new Link($node, $this->baseHref, 'get'); + } + + return $links; + } + + /** + * Returns a Form object for the first node in the list. + * + * @param array $values An array of values for the form fields + * @param string $method The method for the form + * + * @return Form A Form instance + * + * @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement + */ + public function form(array $values = null, $method = null) + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + + if (!$node instanceof \DOMElement) { + throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_class($node))); + } + + $form = new Form($node, $this->uri, $method, $this->baseHref); + + if (null !== $values) { + $form->setValues($values); + } + + return $form; + } + + /** + * Overloads a default namespace prefix to be used with XPath and CSS expressions. + * + * @param string $prefix + */ + public function setDefaultNamespacePrefix($prefix) + { + $this->defaultNamespacePrefix = $prefix; + } + + /** + * @param string $prefix + * @param string $namespace + */ + public function registerNamespace($prefix, $namespace) + { + $this->namespaces[$prefix] = $namespace; + } + + /** + * Converts string for XPath expressions. + * + * Escaped characters are: quotes (") and apostrophe ('). + * + * Examples: + * + * echo Crawler::xpathLiteral('foo " bar'); + * //prints 'foo " bar' + * + * echo Crawler::xpathLiteral("foo ' bar"); + * //prints "foo ' bar" + * + * echo Crawler::xpathLiteral('a\'b"c'); + * //prints concat('a', "'", 'b"c') + * + * + * @param string $s String to be escaped + * + * @return string Converted string + */ + public static function xpathLiteral($s) + { + if (false === strpos($s, "'")) { + return sprintf("'%s'", $s); + } + + if (false === strpos($s, '"')) { + return sprintf('"%s"', $s); + } + + $string = $s; + $parts = array(); + while (true) { + if (false !== $pos = strpos($string, "'")) { + $parts[] = sprintf("'%s'", substr($string, 0, $pos)); + $parts[] = "\"'\""; + $string = substr($string, $pos + 1); + } else { + $parts[] = "'$string'"; + break; + } + } + + return sprintf('concat(%s)', implode($parts, ', ')); + } + + /** + * Filters the list of nodes with an XPath expression. + * + * The XPath expression should already be processed to apply it in the context of each node. + * + * @param string $xpath + * + * @return Crawler + */ + private function filterRelativeXPath($xpath) + { + $prefixes = $this->findNamespacePrefixes($xpath); + + $crawler = $this->createSubCrawler(null); + + foreach ($this->nodes as $node) { + $domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes); + $crawler->add($domxpath->query($xpath, $node)); + } + + return $crawler; + } + + /** + * Make the XPath relative to the current context. + * + * The returned XPath will match elements matching the XPath inside the current crawler + * when running in the context of a node of the crawler. + * + * @param string $xpath + * + * @return string + */ + private function relativize($xpath) + { + $expressions = array(); + + $unionPattern = '/\|(?![^\[]*\])/'; + // An expression which will never match to replace expressions which cannot match in the crawler + // We cannot simply drop + $nonMatchingExpression = 'a[name() = "b"]'; + + // Split any unions into individual expressions. + foreach (preg_split($unionPattern, $xpath) as $expression) { + $expression = trim($expression); + $parenthesis = ''; + + // If the union is inside some braces, we need to preserve the opening braces and apply + // the change only inside it. + if (preg_match('/^[\(\s*]+/', $expression, $matches)) { + $parenthesis = $matches[0]; + $expression = substr($expression, strlen($parenthesis)); + } + + if (0 === strpos($expression, 'self::*/')) { + $expression = './'.substr($expression, 8); + } + + // add prefix before absolute element selector + if (empty($expression)) { + $expression = $nonMatchingExpression; + } elseif (0 === strpos($expression, '//')) { + $expression = 'descendant-or-self::'.substr($expression, 2); + } elseif (0 === strpos($expression, './/')) { + $expression = 'descendant-or-self::'.substr($expression, 3); + } elseif (0 === strpos($expression, './')) { + $expression = 'self::'.substr($expression, 2); + } elseif (0 === strpos($expression, 'child::')) { + $expression = 'self::'.substr($expression, 7); + } elseif ('/' === $expression[0] || '.' === $expression[0] || 0 === strpos($expression, 'self::')) { + $expression = $nonMatchingExpression; + } elseif (0 === strpos($expression, 'descendant::')) { + $expression = 'descendant-or-self::'.substr($expression, strlen('descendant::')); + } elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) { + // the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes) + $expression = $nonMatchingExpression; + } elseif (0 !== strpos($expression, 'descendant-or-self::')) { + $expression = 'self::'.$expression; + } + $expressions[] = $parenthesis.$expression; + } + + return implode(' | ', $expressions); + } + + /** + * @param int $position + * + * @return \DOMElement|null + */ + public function getNode($position) + { + if (isset($this->nodes[$position])) { + return $this->nodes[$position]; + } + } + + /** + * @return int + */ + public function count() + { + return count($this->nodes); + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->nodes); + } + + /** + * @param \DOMElement $node + * @param string $siblingDir + * + * @return array + */ + protected function sibling($node, $siblingDir = 'nextSibling') + { + $nodes = array(); + + do { + if ($node !== $this->getNode(0) && $node->nodeType === 1) { + $nodes[] = $node; + } + } while ($node = $node->$siblingDir); + + return $nodes; + } + + /** + * @param \DOMDocument $document + * @param array $prefixes + * + * @return \DOMXPath + * + * @throws \InvalidArgumentException + */ + private function createDOMXPath(\DOMDocument $document, array $prefixes = array()) + { + $domxpath = new \DOMXPath($document); + + foreach ($prefixes as $prefix) { + $namespace = $this->discoverNamespace($domxpath, $prefix); + if (null !== $namespace) { + $domxpath->registerNamespace($prefix, $namespace); + } + } + + return $domxpath; + } + + /** + * @param \DOMXPath $domxpath + * @param string $prefix + * + * @return string + * + * @throws \InvalidArgumentException + */ + private function discoverNamespace(\DOMXPath $domxpath, $prefix) + { + if (isset($this->namespaces[$prefix])) { + return $this->namespaces[$prefix]; + } + + // ask for one namespace, otherwise we'd get a collection with an item for each node + $namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', $this->defaultNamespacePrefix === $prefix ? '' : $prefix)); + + if ($node = $namespaces->item(0)) { + return $node->nodeValue; + } + } + + /** + * @param string $xpath + * + * @return array + */ + private function findNamespacePrefixes($xpath) + { + if (preg_match_all('/(?P[a-z_][a-z_0-9\-\.]*+):[^"\/:]/i', $xpath, $matches)) { + return array_unique($matches['prefix']); + } + + return array(); + } + + /** + * Creates a crawler for some subnodes. + * + * @param \DOMElement|\DOMElement[]|\DOMNodeList|null $nodes + * + * @return static + */ + private function createSubCrawler($nodes) + { + $crawler = new static($nodes, $this->uri, $this->baseHref); + $crawler->isHtml = $this->isHtml; + $crawler->document = $this->document; + + return $crawler; + } +} diff --git a/vendor/symfony/dom-crawler/Field/ChoiceFormField.php b/vendor/symfony/dom-crawler/Field/ChoiceFormField.php new file mode 100644 index 0000000..fcf510c --- /dev/null +++ b/vendor/symfony/dom-crawler/Field/ChoiceFormField.php @@ -0,0 +1,319 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * ChoiceFormField represents a choice form field. + * + * It is constructed from a HTML select tag, or a HTML checkbox, or radio inputs. + * + * @author Fabien Potencier + */ +class ChoiceFormField extends FormField +{ + /** + * @var string + */ + private $type; + /** + * @var bool + */ + private $multiple; + /** + * @var array + */ + private $options; + /** + * @var bool + */ + private $validationDisabled = false; + + /** + * Returns true if the field should be included in the submitted values. + * + * @return bool true if the field should be included in the submitted values, false otherwise + */ + public function hasValue() + { + // don't send a value for unchecked checkboxes + if (in_array($this->type, array('checkbox', 'radio')) && null === $this->value) { + return false; + } + + return true; + } + + /** + * Check if the current selected option is disabled. + * + * @return bool + */ + public function isDisabled() + { + foreach ($this->options as $option) { + if ($option['value'] == $this->value && $option['disabled']) { + return true; + } + } + + return false; + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + */ + public function select($value) + { + $this->setValue($value); + } + + /** + * Ticks a checkbox. + * + * @throws \LogicException When the type provided is not correct + */ + public function tick() + { + if ('checkbox' !== $this->type) { + throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); + } + + $this->setValue(true); + } + + /** + * Ticks a checkbox. + * + * @throws \LogicException When the type provided is not correct + */ + public function untick() + { + if ('checkbox' !== $this->type) { + throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); + } + + $this->setValue(false); + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + * + * @throws \InvalidArgumentException When value type provided is not correct + */ + public function setValue($value) + { + if ('checkbox' === $this->type && false === $value) { + // uncheck + $this->value = null; + } elseif ('checkbox' === $this->type && true === $value) { + // check + $this->value = $this->options[0]['value']; + } else { + if (is_array($value)) { + if (!$this->multiple) { + throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name)); + } + + foreach ($value as $v) { + if (!$this->containsOption($v, $this->options)) { + throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->availableOptionValues()))); + } + } + } elseif (!$this->containsOption($value, $this->options)) { + throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->availableOptionValues()))); + } + + if ($this->multiple) { + $value = (array) $value; + } + + if (is_array($value)) { + $this->value = $value; + } else { + parent::setValue($value); + } + } + } + + /** + * Adds a choice to the current ones. + * + * This method should only be used internally. + * + * @param \DOMElement $node + * + * @throws \LogicException When choice provided is not multiple nor radio + */ + public function addChoice(\DOMElement $node) + { + if (!$this->multiple && 'radio' !== $this->type) { + throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name)); + } + + $option = $this->buildOptionValue($node); + $this->options[] = $option; + + if ($node->hasAttribute('checked')) { + $this->value = $option['value']; + } + } + + /** + * Returns the type of the choice field (radio, select, or checkbox). + * + * @return string The type + */ + public function getType() + { + return $this->type; + } + + /** + * Returns true if the field accepts multiple values. + * + * @return bool true if the field accepts multiple values, false otherwise + */ + public function isMultiple() + { + return $this->multiple; + } + + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) { + throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName)); + } + + if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) { + throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type'))); + } + + $this->value = null; + $this->options = array(); + $this->multiple = false; + + if ('input' == $this->node->nodeName) { + $this->type = strtolower($this->node->getAttribute('type')); + $optionValue = $this->buildOptionValue($this->node); + $this->options[] = $optionValue; + + if ($this->node->hasAttribute('checked')) { + $this->value = $optionValue['value']; + } + } else { + $this->type = 'select'; + if ($this->node->hasAttribute('multiple')) { + $this->multiple = true; + $this->value = array(); + $this->name = str_replace('[]', '', $this->name); + } + + $found = false; + foreach ($this->xpath->query('descendant::option', $this->node) as $option) { + $optionValue = $this->buildOptionValue($option); + $this->options[] = $optionValue; + + if ($option->hasAttribute('selected')) { + $found = true; + if ($this->multiple) { + $this->value[] = $optionValue['value']; + } else { + $this->value = $optionValue['value']; + } + } + } + + // if no option is selected and if it is a simple select box, take the first option as the value + if (!$found && !$this->multiple && !empty($this->options)) { + $this->value = $this->options[0]['value']; + } + } + } + + /** + * Returns option value with associated disabled flag. + * + * @param \DOMElement $node + * + * @return array + */ + private function buildOptionValue(\DOMElement $node) + { + $option = array(); + + $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : 'on'; + $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue; + $option['disabled'] = $node->hasAttribute('disabled'); + + return $option; + } + + /** + * Checks whether given value is in the existing options. + * + * @param string $optionValue + * @param array $options + * + * @return bool + */ + public function containsOption($optionValue, $options) + { + if ($this->validationDisabled) { + return true; + } + + foreach ($options as $option) { + if ($option['value'] == $optionValue) { + return true; + } + } + + return false; + } + + /** + * Returns list of available field options. + * + * @return array + */ + public function availableOptionValues() + { + $values = array(); + + foreach ($this->options as $option) { + $values[] = $option['value']; + } + + return $values; + } + + /** + * Disables the internal validation of the field. + * + * @return self + */ + public function disableValidation() + { + $this->validationDisabled = true; + + return $this; + } +} diff --git a/vendor/symfony/dom-crawler/Field/FileFormField.php b/vendor/symfony/dom-crawler/Field/FileFormField.php new file mode 100644 index 0000000..0e0f943 --- /dev/null +++ b/vendor/symfony/dom-crawler/Field/FileFormField.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * FileFormField represents a file form field (an HTML file input tag). + * + * @author Fabien Potencier + */ +class FileFormField extends FormField +{ + /** + * Sets the PHP error code associated with the field. + * + * @param int $error The error code (one of UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, or UPLOAD_ERR_EXTENSION) + * + * @throws \InvalidArgumentException When error code doesn't exist + */ + public function setErrorCode($error) + { + $codes = array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION); + if (!in_array($error, $codes)) { + throw new \InvalidArgumentException(sprintf('The error code %s is not valid.', $error)); + } + + $this->value = array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0); + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + */ + public function upload($value) + { + $this->setValue($value); + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + */ + public function setValue($value) + { + if (null !== $value && is_readable($value)) { + $error = UPLOAD_ERR_OK; + $size = filesize($value); + $info = pathinfo($value); + $name = $info['basename']; + + // copy to a tmp location + $tmp = sys_get_temp_dir().'/'.sha1(uniqid(mt_rand(), true)); + if (array_key_exists('extension', $info)) { + $tmp .= '.'.$info['extension']; + } + if (is_file($tmp)) { + unlink($tmp); + } + copy($value, $tmp); + $value = $tmp; + } else { + $error = UPLOAD_ERR_NO_FILE; + $size = 0; + $name = ''; + $value = ''; + } + + $this->value = array('name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size); + } + + /** + * Sets path to the file as string for simulating HTTP request. + * + * @param string $path The path to the file + */ + public function setFilePath($path) + { + parent::setValue($path); + } + + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('input' !== $this->node->nodeName) { + throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName)); + } + + if ('file' !== strtolower($this->node->getAttribute('type'))) { + throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is %s).', $this->node->getAttribute('type'))); + } + + $this->setValue(null); + } +} diff --git a/vendor/symfony/dom-crawler/Field/FormField.php b/vendor/symfony/dom-crawler/Field/FormField.php new file mode 100644 index 0000000..a6b33de --- /dev/null +++ b/vendor/symfony/dom-crawler/Field/FormField.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * FormField is the abstract class for all form fields. + * + * @author Fabien Potencier + */ +abstract class FormField +{ + /** + * @var \DOMElement + */ + protected $node; + /** + * @var string + */ + protected $name; + /** + * @var string + */ + protected $value; + /** + * @var \DOMDocument + */ + protected $document; + /** + * @var \DOMXPath + */ + protected $xpath; + /** + * @var bool + */ + protected $disabled; + + /** + * Constructor. + * + * @param \DOMElement $node The node associated with this field + */ + public function __construct(\DOMElement $node) + { + $this->node = $node; + $this->name = $node->getAttribute('name'); + $this->xpath = new \DOMXPath($node->ownerDocument); + + $this->initialize(); + } + + /** + * Returns the name of the field. + * + * @return string The name of the field + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the field. + * + * @return string|array The value of the field + */ + public function getValue() + { + return $this->value; + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + */ + public function setValue($value) + { + $this->value = (string) $value; + } + + /** + * Returns true if the field should be included in the submitted values. + * + * @return bool true if the field should be included in the submitted values, false otherwise + */ + public function hasValue() + { + return true; + } + + /** + * Check if the current field is disabled. + * + * @return bool + */ + public function isDisabled() + { + return $this->node->hasAttribute('disabled'); + } + + /** + * Initializes the form field. + */ + abstract protected function initialize(); +} diff --git a/vendor/symfony/dom-crawler/Field/InputFormField.php b/vendor/symfony/dom-crawler/Field/InputFormField.php new file mode 100644 index 0000000..090913e --- /dev/null +++ b/vendor/symfony/dom-crawler/Field/InputFormField.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * InputFormField represents an input form field (an HTML input tag). + * + * For inputs with type of file, checkbox, or radio, there are other more + * specialized classes (cf. FileFormField and ChoiceFormField). + * + * @author Fabien Potencier + */ +class InputFormField extends FormField +{ + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('input' !== $this->node->nodeName && 'button' !== $this->node->nodeName) { + throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName)); + } + + if ('checkbox' === strtolower($this->node->getAttribute('type'))) { + throw new \LogicException('Checkboxes should be instances of ChoiceFormField.'); + } + + if ('file' === strtolower($this->node->getAttribute('type'))) { + throw new \LogicException('File inputs should be instances of FileFormField.'); + } + + $this->value = $this->node->getAttribute('value'); + } +} diff --git a/vendor/symfony/dom-crawler/Field/TextareaFormField.php b/vendor/symfony/dom-crawler/Field/TextareaFormField.php new file mode 100644 index 0000000..15526e1 --- /dev/null +++ b/vendor/symfony/dom-crawler/Field/TextareaFormField.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * TextareaFormField represents a textarea form field (an HTML textarea tag). + * + * @author Fabien Potencier + */ +class TextareaFormField extends FormField +{ + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('textarea' !== $this->node->nodeName) { + throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName)); + } + + $this->value = ''; + foreach ($this->node->childNodes as $node) { + $this->value .= $node->wholeText; + } + } +} diff --git a/vendor/symfony/dom-crawler/Form.php b/vendor/symfony/dom-crawler/Form.php new file mode 100644 index 0000000..0c7a3b2 --- /dev/null +++ b/vendor/symfony/dom-crawler/Form.php @@ -0,0 +1,471 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\DomCrawler\Field\ChoiceFormField; +use Symfony\Component\DomCrawler\Field\FormField; + +/** + * Form represents an HTML form. + * + * @author Fabien Potencier + */ +class Form extends Link implements \ArrayAccess +{ + /** + * @var \DOMElement + */ + private $button; + + /** + * @var FormFieldRegistry + */ + private $fields; + + /** + * @var string + */ + private $baseHref; + + /** + * Constructor. + * + * @param \DOMElement $node A \DOMElement instance + * @param string $currentUri The URI of the page where the form is embedded + * @param string $method The method to use for the link (if null, it defaults to the method defined by the form) + * @param string $baseHref The URI of the used for relative links, but not for empty action + * + * @throws \LogicException if the node is not a button inside a form tag + */ + public function __construct(\DOMElement $node, $currentUri, $method = null, $baseHref = null) + { + parent::__construct($node, $currentUri, $method); + $this->baseHref = $baseHref; + + $this->initialize(); + } + + /** + * Gets the form node associated with this form. + * + * @return \DOMElement A \DOMElement instance + */ + public function getFormNode() + { + return $this->node; + } + + /** + * Sets the value of the fields. + * + * @param array $values An array of field values + * + * @return Form + */ + public function setValues(array $values) + { + foreach ($values as $name => $value) { + $this->fields->set($name, $value); + } + + return $this; + } + + /** + * Gets the field values. + * + * The returned array does not include file fields (@see getFiles). + * + * @return array An array of field values. + */ + public function getValues() + { + $values = array(); + foreach ($this->fields->all() as $name => $field) { + if ($field->isDisabled()) { + continue; + } + + if (!$field instanceof Field\FileFormField && $field->hasValue()) { + $values[$name] = $field->getValue(); + } + } + + return $values; + } + + /** + * Gets the file field values. + * + * @return array An array of file field values. + */ + public function getFiles() + { + if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) { + return array(); + } + + $files = array(); + + foreach ($this->fields->all() as $name => $field) { + if ($field->isDisabled()) { + continue; + } + + if ($field instanceof Field\FileFormField) { + $files[$name] = $field->getValue(); + } + } + + return $files; + } + + /** + * Gets the field values as PHP. + * + * This method converts fields with the array notation + * (like foo[bar] to arrays) like PHP does. + * + * @return array An array of field values. + */ + public function getPhpValues() + { + $values = array(); + foreach ($this->getValues() as $name => $value) { + $qs = http_build_query(array($name => $value), '', '&'); + if (!empty($qs)) { + parse_str($qs, $expandedValue); + $varName = substr($name, 0, strlen(key($expandedValue))); + $values = array_replace_recursive($values, array($varName => current($expandedValue))); + } + } + + return $values; + } + + /** + * Gets the file field values as PHP. + * + * This method converts fields with the array notation + * (like foo[bar] to arrays) like PHP does. + * + * @return array An array of field values. + */ + public function getPhpFiles() + { + $values = array(); + foreach ($this->getFiles() as $name => $value) { + $qs = http_build_query(array($name => $value), '', '&'); + if (!empty($qs)) { + parse_str($qs, $expandedValue); + $varName = substr($name, 0, strlen(key($expandedValue))); + $values = array_replace_recursive($values, array($varName => current($expandedValue))); + } + } + + return $values; + } + + /** + * Gets the URI of the form. + * + * The returned URI is not the same as the form "action" attribute. + * This method merges the value if the method is GET to mimics + * browser behavior. + * + * @return string The URI + */ + public function getUri() + { + $uri = parent::getUri(); + + if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) { + $query = parse_url($uri, PHP_URL_QUERY); + $currentParameters = array(); + if ($query) { + parse_str($query, $currentParameters); + } + + $queryString = http_build_query(array_merge($currentParameters, $this->getValues()), null, '&'); + + $pos = strpos($uri, '?'); + $base = false === $pos ? $uri : substr($uri, 0, $pos); + $uri = rtrim($base.'?'.$queryString, '?'); + } + + return $uri; + } + + protected function getRawUri() + { + return $this->node->getAttribute('action'); + } + + /** + * Gets the form method. + * + * If no method is defined in the form, GET is returned. + * + * @return string The method + */ + public function getMethod() + { + if (null !== $this->method) { + return $this->method; + } + + return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET'; + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @return bool true if the field exists, false otherwise + */ + public function has($name) + { + return $this->fields->has($name); + } + + /** + * Removes a field from the form. + * + * @param string $name The field name + * + * @throws \InvalidArgumentException when the name is malformed + */ + public function remove($name) + { + $this->fields->remove($name); + } + + /** + * Gets a named field. + * + * @param string $name The field name + * + * @return FormField The field instance + * + * @throws \InvalidArgumentException When field is not present in this form + */ + public function get($name) + { + return $this->fields->get($name); + } + + /** + * Sets a named field. + * + * @param FormField $field The field + */ + public function set(FormField $field) + { + $this->fields->add($field); + } + + /** + * Gets all fields. + * + * @return FormField[] An array of fields + */ + public function all() + { + return $this->fields->all(); + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @return bool true if the field exists, false otherwise + */ + public function offsetExists($name) + { + return $this->has($name); + } + + /** + * Gets the value of a field. + * + * @param string $name The field name + * + * @return FormField The associated Field instance + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetGet($name) + { + return $this->fields->get($name); + } + + /** + * Sets the value of a field. + * + * @param string $name The field name + * @param string|array $value The value of the field + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetSet($name, $value) + { + $this->fields->set($name, $value); + } + + /** + * Removes a field from the form. + * + * @param string $name The field name + */ + public function offsetUnset($name) + { + $this->fields->remove($name); + } + + /** + * Disables validation. + * + * @return self + */ + public function disableValidation() + { + foreach ($this->fields->all() as $field) { + if ($field instanceof Field\ChoiceFormField) { + $field->disableValidation(); + } + } + + return $this; + } + + /** + * Sets the node for the form. + * + * Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself. + * + * @param \DOMElement $node A \DOMElement instance + * + * @throws \LogicException If given node is not a button or input or does not have a form ancestor + */ + protected function setNode(\DOMElement $node) + { + $this->button = $node; + if ('button' === $node->nodeName || ('input' === $node->nodeName && in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image')))) { + if ($node->hasAttribute('form')) { + // if the node has the HTML5-compliant 'form' attribute, use it + $formId = $node->getAttribute('form'); + $form = $node->ownerDocument->getElementById($formId); + if (null === $form) { + throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId)); + } + $this->node = $form; + + return; + } + // we loop until we find a form ancestor + do { + if (null === $node = $node->parentNode) { + throw new \LogicException('The selected node does not have a form ancestor.'); + } + } while ('form' !== $node->nodeName); + } elseif ('form' !== $node->nodeName) { + throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName)); + } + + $this->node = $node; + } + + /** + * Adds form elements related to this form. + * + * Creates an internal copy of the submitted 'button' element and + * the form node or the entire document depending on whether we need + * to find non-descendant elements through HTML5 'form' attribute. + */ + private function initialize() + { + $this->fields = new FormFieldRegistry(); + + $xpath = new \DOMXPath($this->node->ownerDocument); + + // add submitted button if it has a valid name + if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) { + if ('input' == $this->button->nodeName && 'image' == strtolower($this->button->getAttribute('type'))) { + $name = $this->button->getAttribute('name'); + $this->button->setAttribute('value', '0'); + + // temporarily change the name of the input node for the x coordinate + $this->button->setAttribute('name', $name.'.x'); + $this->set(new Field\InputFormField($this->button)); + + // temporarily change the name of the input node for the y coordinate + $this->button->setAttribute('name', $name.'.y'); + $this->set(new Field\InputFormField($this->button)); + + // restore the original name of the input node + $this->button->setAttribute('name', $name); + } else { + $this->set(new Field\InputFormField($this->button)); + } + } + + // find form elements corresponding to the current form + if ($this->node->hasAttribute('id')) { + // corresponding elements are either descendants or have a matching HTML5 form attribute + $formId = Crawler::xpathLiteral($this->node->getAttribute('id')); + + $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s] | //form[@id=%s]//input[not(@form)] | //form[@id=%s]//button[not(@form)] | //form[@id=%s]//textarea[not(@form)] | //form[@id=%s]//select[not(@form)]', $formId, $formId, $formId, $formId, $formId, $formId, $formId, $formId)); + foreach ($fieldNodes as $node) { + $this->addField($node); + } + } else { + // do the xpath query with $this->node as the context node, to only find descendant elements + // however, descendant elements with form attribute are not part of this form + $fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $this->node); + foreach ($fieldNodes as $node) { + $this->addField($node); + } + } + + if ($this->baseHref && '' !== $this->node->getAttribute('action')) { + $this->currentUri = $this->baseHref; + } + } + + private function addField(\DOMElement $node) + { + if (!$node->hasAttribute('name') || !$node->getAttribute('name')) { + return; + } + + $nodeName = $node->nodeName; + if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) { + $this->set(new Field\ChoiceFormField($node)); + } elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) { + // there may be other fields with the same name that are no choice + // fields already registered (see https://github.com/symfony/symfony/issues/11689) + if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) { + $this->get($node->getAttribute('name'))->addChoice($node); + } else { + $this->set(new Field\ChoiceFormField($node)); + } + } elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) { + $this->set(new Field\FileFormField($node)); + } elseif ('input' == $nodeName && !in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image'))) { + $this->set(new Field\InputFormField($node)); + } elseif ('textarea' == $nodeName) { + $this->set(new Field\TextareaFormField($node)); + } + } +} diff --git a/vendor/symfony/dom-crawler/FormFieldRegistry.php b/vendor/symfony/dom-crawler/FormFieldRegistry.php new file mode 100644 index 0000000..edb2788 --- /dev/null +++ b/vendor/symfony/dom-crawler/FormFieldRegistry.php @@ -0,0 +1,222 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\DomCrawler\Field\FormField; + +/** + * This is an internal class that must not be used directly. + */ +class FormFieldRegistry +{ + private $fields = array(); + + private $base; + + /** + * Adds a field to the registry. + * + * @param FormField $field The field + * + * @throws \InvalidArgumentException when the name is malformed + */ + public function add(FormField $field) + { + $segments = $this->getSegments($field->getName()); + + $target = &$this->fields; + while ($segments) { + if (!is_array($target)) { + $target = array(); + } + $path = array_shift($segments); + if ('' === $path) { + $target = &$target[]; + } else { + $target = &$target[$path]; + } + } + $target = $field; + } + + /** + * Removes a field and its children from the registry. + * + * @param string $name The fully qualified name of the base field + * + * @throws \InvalidArgumentException when the name is malformed + */ + public function remove($name) + { + $segments = $this->getSegments($name); + $target = &$this->fields; + while (count($segments) > 1) { + $path = array_shift($segments); + if (!array_key_exists($path, $target)) { + return; + } + $target = &$target[$path]; + } + unset($target[array_shift($segments)]); + } + + /** + * Returns the value of the field and its children. + * + * @param string $name The fully qualified name of the field + * + * @return mixed The value of the field + * + * @throws \InvalidArgumentException when the name is malformed + * @throws \InvalidArgumentException if the field does not exist + */ + public function &get($name) + { + $segments = $this->getSegments($name); + $target = &$this->fields; + while ($segments) { + $path = array_shift($segments); + if (!array_key_exists($path, $target)) { + throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path)); + } + $target = &$target[$path]; + } + + return $target; + } + + /** + * Tests whether the form has the given field. + * + * @param string $name The fully qualified name of the field + * + * @return bool Whether the form has the given field + */ + public function has($name) + { + try { + $this->get($name); + + return true; + } catch (\InvalidArgumentException $e) { + return false; + } + } + + /** + * Set the value of a field and its children. + * + * @param string $name The fully qualified name of the field + * @param mixed $value The value + * + * @throws \InvalidArgumentException when the name is malformed + * @throws \InvalidArgumentException if the field does not exist + */ + public function set($name, $value) + { + $target = &$this->get($name); + if ((!is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) { + $target->setValue($value); + } elseif (is_array($value)) { + $fields = self::create($name, $value); + foreach ($fields->all() as $k => $v) { + $this->set($k, $v); + } + } else { + throw new \InvalidArgumentException(sprintf('Cannot set value on a compound field "%s".', $name)); + } + } + + /** + * Returns the list of field with their value. + * + * @return FormField[] The list of fields as array((string) Fully qualified name => (mixed) value) + */ + public function all() + { + return $this->walk($this->fields, $this->base); + } + + /** + * Creates an instance of the class. + * + * This function is made private because it allows overriding the $base and + * the $values properties without any type checking. + * + * @param string $base The fully qualified name of the base field + * @param array $values The values of the fields + * + * @return FormFieldRegistry + */ + private static function create($base, array $values) + { + $registry = new static(); + $registry->base = $base; + $registry->fields = $values; + + return $registry; + } + + /** + * Transforms a PHP array in a list of fully qualified name / value. + * + * @param array $array The PHP array + * @param string $base The name of the base field + * @param array $output The initial values + * + * @return array The list of fields as array((string) Fully qualified name => (mixed) value) + */ + private function walk(array $array, $base = '', array &$output = array()) + { + foreach ($array as $k => $v) { + $path = empty($base) ? $k : sprintf('%s[%s]', $base, $k); + if (is_array($v)) { + $this->walk($v, $path, $output); + } else { + $output[$path] = $v; + } + } + + return $output; + } + + /** + * Splits a field name into segments as a web browser would do. + * + * + * getSegments('base[foo][3][]') = array('base', 'foo, '3', ''); + * + * + * @param string $name The name of the field + * + * @return string[] The list of segments + * + * @throws \InvalidArgumentException when the name is malformed + */ + private function getSegments($name) + { + if (preg_match('/^(?P[^[]+)(?P(\[.*)|$)/', $name, $m)) { + $segments = array($m['base']); + while (!empty($m['extra'])) { + if (preg_match('/^\[(?P.*?)\](?P.*)$/', $m['extra'], $m)) { + $segments[] = $m['segment']; + } else { + throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name)); + } + } + + return $segments; + } + + throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name)); + } +} diff --git a/vendor/symfony/dom-crawler/LICENSE b/vendor/symfony/dom-crawler/LICENSE new file mode 100644 index 0000000..12a7453 --- /dev/null +++ b/vendor/symfony/dom-crawler/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/dom-crawler/Link.php b/vendor/symfony/dom-crawler/Link.php new file mode 100644 index 0000000..ede0991 --- /dev/null +++ b/vendor/symfony/dom-crawler/Link.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +/** + * Link represents an HTML link (an HTML a, area or link tag). + * + * @author Fabien Potencier + */ +class Link +{ + /** + * @var \DOMElement + */ + protected $node; + + /** + * @var string The method to use for the link + */ + protected $method; + + /** + * @var string The URI of the page where the link is embedded (or the base href) + */ + protected $currentUri; + + /** + * Constructor. + * + * @param \DOMElement $node A \DOMElement instance + * @param string $currentUri The URI of the page where the link is embedded (or the base href) + * @param string $method The method to use for the link (get by default) + * + * @throws \InvalidArgumentException if the node is not a link + */ + public function __construct(\DOMElement $node, $currentUri, $method = 'GET') + { + if (!in_array(strtolower(substr($currentUri, 0, 4)), array('http', 'file'))) { + throw new \InvalidArgumentException(sprintf('Current URI must be an absolute URL ("%s").', $currentUri)); + } + + $this->setNode($node); + $this->method = $method ? strtoupper($method) : null; + $this->currentUri = $currentUri; + } + + /** + * Gets the node associated with this link. + * + * @return \DOMElement A \DOMElement instance + */ + public function getNode() + { + return $this->node; + } + + /** + * Gets the method associated with this link. + * + * @return string The method + */ + public function getMethod() + { + return $this->method; + } + + /** + * Gets the URI associated with this link. + * + * @return string The URI + */ + public function getUri() + { + $uri = trim($this->getRawUri()); + + // absolute URL? + if (null !== parse_url($uri, PHP_URL_SCHEME)) { + return $uri; + } + + // empty URI + if (!$uri) { + return $this->currentUri; + } + + // an anchor + if ('#' === $uri[0]) { + return $this->cleanupAnchor($this->currentUri).$uri; + } + + $baseUri = $this->cleanupUri($this->currentUri); + + if ('?' === $uri[0]) { + return $baseUri.$uri; + } + + // absolute URL with relative schema + if (0 === strpos($uri, '//')) { + return preg_replace('#^([^/]*)//.*$#', '$1', $baseUri).$uri; + } + + $baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUri); + + // absolute path + if ('/' === $uri[0]) { + return $baseUri.$uri; + } + + // relative path + $path = parse_url(substr($this->currentUri, strlen($baseUri)), PHP_URL_PATH); + $path = $this->canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri); + + return $baseUri.('' === $path || '/' !== $path[0] ? '/' : '').$path; + } + + /** + * Returns raw URI data. + * + * @return string + */ + protected function getRawUri() + { + return $this->node->getAttribute('href'); + } + + /** + * Returns the canonicalized URI path (see RFC 3986, section 5.2.4). + * + * @param string $path URI path + * + * @return string + */ + protected function canonicalizePath($path) + { + if ('' === $path || '/' === $path) { + return $path; + } + + if ('.' === substr($path, -1)) { + $path .= '/'; + } + + $output = array(); + + foreach (explode('/', $path) as $segment) { + if ('..' === $segment) { + array_pop($output); + } elseif ('.' !== $segment) { + $output[] = $segment; + } + } + + return implode('/', $output); + } + + /** + * Sets current \DOMElement instance. + * + * @param \DOMElement $node A \DOMElement instance + * + * @throws \LogicException If given node is not an anchor + */ + protected function setNode(\DOMElement $node) + { + if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) { + throw new \LogicException(sprintf('Unable to navigate from a "%s" tag.', $node->nodeName)); + } + + $this->node = $node; + } + + /** + * Removes the query string and the anchor from the given uri. + * + * @param string $uri The uri to clean + * + * @return string + */ + private function cleanupUri($uri) + { + return $this->cleanupQuery($this->cleanupAnchor($uri)); + } + + /** + * Remove the query string from the uri. + * + * @param string $uri + * + * @return string + */ + private function cleanupQuery($uri) + { + if (false !== $pos = strpos($uri, '?')) { + return substr($uri, 0, $pos); + } + + return $uri; + } + + /** + * Remove the anchor from the uri. + * + * @param string $uri + * + * @return string + */ + private function cleanupAnchor($uri) + { + if (false !== $pos = strpos($uri, '#')) { + return substr($uri, 0, $pos); + } + + return $uri; + } +} diff --git a/vendor/symfony/dom-crawler/README.md b/vendor/symfony/dom-crawler/README.md new file mode 100644 index 0000000..d2c8de5 --- /dev/null +++ b/vendor/symfony/dom-crawler/README.md @@ -0,0 +1,36 @@ +DomCrawler Component +==================== + +DomCrawler eases DOM navigation for HTML and XML documents. + +If you are familiar with jQuery, DomCrawler is a PHP equivalent: + +```php +use Symfony\Component\DomCrawler\Crawler; + +$crawler = new Crawler(); +$crawler->addContent('

Hello World!

'); + +print $crawler->filterXPath('descendant-or-self::body/p')->text(); +``` + +If you are also using the CssSelector component, you can use CSS Selectors +instead of XPath expressions: + +```php +use Symfony\Component\DomCrawler\Crawler; + +$crawler = new Crawler(); +$crawler->addContent('

Hello World!

'); + +print $crawler->filter('body > p')->text(); +``` + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/DomCrawler/ + $ composer install + $ phpunit diff --git a/vendor/symfony/dom-crawler/Tests/CrawlerTest.php b/vendor/symfony/dom-crawler/Tests/CrawlerTest.php new file mode 100644 index 0000000..dedc526 --- /dev/null +++ b/vendor/symfony/dom-crawler/Tests/CrawlerTest.php @@ -0,0 +1,1097 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Tests; + +use Symfony\Component\DomCrawler\Crawler; + +class CrawlerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $crawler = new Crawler(); + $this->assertCount(0, $crawler, '__construct() returns an empty crawler'); + + $doc = new \DOMDocument(); + $node = $doc->createElement('test'); + + $crawler = new Crawler($node); + $this->assertCount(1, $crawler, '__construct() takes a node as a first argument'); + } + + public function testAdd() + { + $crawler = new Crawler(); + $crawler->add($this->createDomDocument()); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMDocument'); + + $crawler = new Crawler(); + $crawler->add($this->createNodeList()); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNodeList'); + + $list = array(); + foreach ($this->createNodeList() as $node) { + $list[] = $node; + } + $crawler = new Crawler(); + $crawler->add($list); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from an array of nodes'); + + $crawler = new Crawler(); + $crawler->add($this->createNodeList()->item(0)); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNode'); + + $crawler = new Crawler(); + $crawler->add('Foo'); + $this->assertEquals('Foo', $crawler->filterXPath('//body')->text(), '->add() adds nodes from a string'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testAddInvalidType() + { + $crawler = new Crawler(); + $crawler->add(1); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Attaching DOM nodes from multiple documents in the same crawler is forbidden. + */ + public function testAddMultipleDocumentNode() + { + $crawler = $this->createTestCrawler(); + $crawler->addHtmlContent('
', 'UTF-8'); + } + + public function testAddHtmlContent() + { + $crawler = new Crawler(); + $crawler->addHtmlContent('
', 'UTF-8'); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addHtmlContent() adds nodes from an HTML string'); + } + + public function testAddHtmlContentWithBaseTag() + { + $crawler = new Crawler(); + + $crawler->addHtmlContent('', 'UTF-8'); + + $this->assertEquals('http://symfony.com', $crawler->filterXPath('//base')->attr('href'), '->addHtmlContent() adds nodes from an HTML string'); + $this->assertEquals('http://symfony.com/contact', $crawler->filterXPath('//a')->link()->getUri(), '->addHtmlContent() adds nodes from an HTML string'); + } + + /** + * @requires extension mbstring + */ + public function testAddHtmlContentCharset() + { + $crawler = new Crawler(); + $crawler->addHtmlContent('
Tiếng Việt', 'UTF-8'); + + $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); + } + + public function testAddHtmlContentInvalidBaseTag() + { + $crawler = new Crawler(null, 'http://symfony.com'); + + $crawler->addHtmlContent('', 'UTF-8'); + + $this->assertEquals('http://symfony.com/contact', current($crawler->filterXPath('//a')->links())->getUri(), '->addHtmlContent() correctly handles a non-existent base tag href attribute'); + } + + public function testAddHtmlContentUnsupportedCharset() + { + $crawler = new Crawler(); + $crawler->addHtmlContent(file_get_contents(__DIR__.'/Fixtures/windows-1250.html'), 'Windows-1250'); + + $this->assertEquals('Žťčýů', $crawler->filterXPath('//p')->text()); + } + + /** + * @requires extension mbstring + */ + public function testAddHtmlContentCharsetGbk() + { + $crawler = new Crawler(); + //gbk encode of

中文

+ $crawler->addHtmlContent(base64_decode('PGh0bWw+PHA+1tDOxDwvcD48L2h0bWw+'), 'gbk'); + + $this->assertEquals('中文', $crawler->filterXPath('//p')->text()); + } + + public function testAddHtmlContentWithErrors() + { + $internalErrors = libxml_use_internal_errors(true); + + $crawler = new Crawler(); + $crawler->addHtmlContent(<<<'EOF' + + + + + + + + +EOF + , 'UTF-8'); + + $errors = libxml_get_errors(); + $this->assertCount(1, $errors); + $this->assertEquals("Tag nav invalid\n", $errors[0]->message); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + public function testAddXmlContent() + { + $crawler = new Crawler(); + $crawler->addXmlContent('
', 'UTF-8'); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addXmlContent() adds nodes from an XML string'); + } + + public function testAddXmlContentCharset() + { + $crawler = new Crawler(); + $crawler->addXmlContent('
Tiếng Việt
', 'UTF-8'); + + $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); + } + + public function testAddXmlContentWithErrors() + { + $internalErrors = libxml_use_internal_errors(true); + + $crawler = new Crawler(); + $crawler->addXmlContent(<<<'EOF' + + + + + +
+ + +EOF + , 'UTF-8'); + + $this->assertTrue(count(libxml_get_errors()) > 1); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + public function testAddContent() + { + $crawler = new Crawler(); + $crawler->addContent('
', 'text/html; charset=UTF-8'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string'); + + $crawler = new Crawler(); + $crawler->addContent('
', 'text/html; charset=UTF-8; dir=RTL'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string with extended content type'); + + $crawler = new Crawler(); + $crawler->addContent('
'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() uses text/html as the default type'); + + $crawler = new Crawler(); + $crawler->addContent('
', 'text/xml; charset=UTF-8'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string'); + + $crawler = new Crawler(); + $crawler->addContent('
', 'text/xml'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string'); + + $crawler = new Crawler(); + $crawler->addContent('foo bar', 'text/plain'); + $this->assertCount(0, $crawler, '->addContent() does nothing if the type is not (x|ht)ml'); + + $crawler = new Crawler(); + $crawler->addContent('中文'); + $this->assertEquals('中文', $crawler->filterXPath('//span')->text(), '->addContent() guess wrong charset'); + + $crawler = new Crawler(); + $crawler->addContent(iconv('UTF-8', 'SJIS', '日本語')); + $this->assertEquals('日本語', $crawler->filterXPath('//body')->text(), '->addContent() can recognize "Shift_JIS" in html5 meta charset tag'); + } + + public function testAddDocument() + { + $crawler = new Crawler(); + $crawler->addDocument($this->createDomDocument()); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addDocument() adds nodes from a \DOMDocument'); + } + + public function testAddNodeList() + { + $crawler = new Crawler(); + $crawler->addNodeList($this->createNodeList()); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodeList() adds nodes from a \DOMNodeList'); + } + + public function testAddNodes() + { + $list = array(); + foreach ($this->createNodeList() as $node) { + $list[] = $node; + } + + $crawler = new Crawler(); + $crawler->addNodes($list); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodes() adds nodes from an array of nodes'); + } + + public function testAddNode() + { + $crawler = new Crawler(); + $crawler->addNode($this->createNodeList()->item(0)); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNode() adds nodes from a \DOMNode'); + } + + public function testClear() + { + $doc = new \DOMDocument(); + $node = $doc->createElement('test'); + + $crawler = new Crawler($node); + $crawler->clear(); + $this->assertCount(0, $crawler, '->clear() removes all the nodes from the crawler'); + } + + public function testEq() + { + $crawler = $this->createTestCrawler()->filterXPath('//li'); + $this->assertNotSame($crawler, $crawler->eq(0), '->eq() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->eq() returns a new instance of a crawler'); + + $this->assertEquals('Two', $crawler->eq(1)->text(), '->eq() returns the nth node of the list'); + $this->assertCount(0, $crawler->eq(100), '->eq() returns an empty crawler if the nth node does not exist'); + } + + public function testEach() + { + $data = $this->createTestCrawler()->filterXPath('//ul[1]/li')->each(function ($node, $i) { + return $i.'-'.$node->text(); + }); + + $this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes an anonymous function on each node of the list'); + } + + public function testIteration() + { + $crawler = $this->createTestCrawler()->filterXPath('//li'); + + $this->assertInstanceOf('Traversable', $crawler); + $this->assertContainsOnlyInstancesOf('DOMElement', iterator_to_array($crawler), 'Iterating a Crawler gives DOMElement instances'); + } + + public function testSlice() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + $this->assertNotSame($crawler->slice(), $crawler, '->slice() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler->slice(), '->slice() returns a new instance of a crawler'); + + $this->assertCount(3, $crawler->slice(), '->slice() does not slice the nodes in the list if any param is entered'); + $this->assertCount(1, $crawler->slice(1, 1), '->slice() slices the nodes in the list'); + } + + public function testReduce() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + $nodes = $crawler->reduce(function ($node, $i) { + return $i !== 1; + }); + $this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler'); + + $this->assertCount(2, $nodes, '->reduce() filters the nodes in the list'); + } + + public function testAttr() + { + $this->assertEquals('first', $this->createTestCrawler()->filterXPath('//li')->attr('class'), '->attr() returns the attribute of the first element of the node list'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->attr('class'); + $this->fail('->attr() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->attr() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testMissingAttrValueIsNull() + { + $crawler = new Crawler(); + $crawler->addContent('
', 'text/html; charset=UTF-8'); + $div = $crawler->filterXPath('//div'); + + $this->assertEquals('sample value', $div->attr('non-empty-attr'), '->attr() reads non-empty attributes correctly'); + $this->assertEquals('', $div->attr('empty-attr'), '->attr() reads empty attributes correctly'); + $this->assertNull($div->attr('missing-attr'), '->attr() reads missing attributes correctly'); + } + + public function testNodeName() + { + $this->assertEquals('li', $this->createTestCrawler()->filterXPath('//li')->nodeName(), '->nodeName() returns the node name of the first element of the node list'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->nodeName(); + $this->fail('->nodeName() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->nodeName() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testText() + { + $this->assertEquals('One', $this->createTestCrawler()->filterXPath('//li')->text(), '->text() returns the node value of the first element of the node list'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->text(); + $this->fail('->text() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->text() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testHtml() + { + $this->assertEquals('Bar', $this->createTestCrawler()->filterXPath('//a[5]')->html()); + $this->assertEquals('', trim($this->createTestCrawler()->filterXPath('//form[@id="FooFormId"]')->html())); + + try { + $this->createTestCrawler()->filterXPath('//ol')->html(); + $this->fail('->html() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->html() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testExtract() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + + $this->assertEquals(array('One', 'Two', 'Three'), $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list'); + $this->assertEquals(array(array('One', 'first'), array('Two', ''), array('Three', '')), $crawler->extract(array('_text', 'class')), '->extract() returns an array of extracted data from the node list'); + + $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty'); + } + + public function testFilterXpathComplexQueries() + { + $crawler = $this->createTestCrawler()->filterXPath('//body'); + + $this->assertCount(0, $crawler->filterXPath('/input')); + $this->assertCount(0, $crawler->filterXPath('/body')); + $this->assertCount(1, $crawler->filterXPath('./body')); + $this->assertCount(1, $crawler->filterXPath('.//body')); + $this->assertCount(5, $crawler->filterXPath('.//input')); + $this->assertCount(4, $crawler->filterXPath('//form')->filterXPath('//button | //input')); + $this->assertCount(1, $crawler->filterXPath('body')); + $this->assertCount(6, $crawler->filterXPath('//button | //input')); + $this->assertCount(1, $crawler->filterXPath('//body')); + $this->assertCount(1, $crawler->filterXPath('descendant-or-self::body')); + $this->assertCount(1, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('./div'), 'A child selection finds only the current div'); + $this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('descendant::div'), 'A descendant selector matches the current div and its child'); + $this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('//div'), 'A descendant selector matches the current div and its child'); + $this->assertCount(5, $crawler->filterXPath('(//a | //div)//img')); + $this->assertCount(7, $crawler->filterXPath('((//a | //div)//img | //ul)')); + $this->assertCount(7, $crawler->filterXPath('( ( //a | //div )//img | //ul )')); + } + + public function testFilterXPath() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->filterXPath('//li'), '->filterXPath() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filterXPath() returns a new instance of a crawler'); + + $crawler = $this->createTestCrawler()->filterXPath('//ul'); + $this->assertCount(6, $crawler->filterXPath('//li'), '->filterXPath() filters the node list with the XPath expression'); + + $crawler = $this->createTestCrawler(); + $this->assertCount(3, $crawler->filterXPath('//body')->filterXPath('//button')->parents(), '->filterXpath() preserves parents when chained'); + } + + public function testFilterRemovesDuplicates() + { + $crawler = $this->createTestCrawler()->filter('html, body')->filter('li'); + $this->assertCount(6, $crawler, 'The crawler removes duplicates when filtering.'); + } + + public function testFilterXPathWithDefaultNamespace() + { + $crawler = $this->createTestXmlCrawler()->filterXPath('//default:entry/default:id'); + $this->assertCount(1, $crawler, '->filterXPath() automatically registers a namespace'); + $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); + } + + public function testFilterXPathWithCustomDefaultNamespace() + { + $crawler = $this->createTestXmlCrawler(); + $crawler->setDefaultNamespacePrefix('x'); + $crawler = $crawler->filterXPath('//x:entry/x:id'); + + $this->assertCount(1, $crawler, '->filterXPath() lets to override the default namespace prefix'); + $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); + } + + public function testFilterXPathWithNamespace() + { + $crawler = $this->createTestXmlCrawler()->filterXPath('//yt:accessControl'); + $this->assertCount(2, $crawler, '->filterXPath() automatically registers a namespace'); + } + + public function testFilterXPathWithMultipleNamespaces() + { + $crawler = $this->createTestXmlCrawler()->filterXPath('//media:group/yt:aspectRatio'); + $this->assertCount(1, $crawler, '->filterXPath() automatically registers multiple namespaces'); + $this->assertSame('widescreen', $crawler->text()); + } + + public function testFilterXPathWithManuallyRegisteredNamespace() + { + $crawler = $this->createTestXmlCrawler(); + $crawler->registerNamespace('m', 'http://search.yahoo.com/mrss/'); + + $crawler = $crawler->filterXPath('//m:group/yt:aspectRatio'); + $this->assertCount(1, $crawler, '->filterXPath() uses manually registered namespace'); + $this->assertSame('widescreen', $crawler->text()); + } + + public function testFilterXPathWithAnUrl() + { + $crawler = $this->createTestXmlCrawler(); + + $crawler = $crawler->filterXPath('//media:category[@scheme="http://gdata.youtube.com/schemas/2007/categories.cat"]'); + $this->assertCount(1, $crawler); + $this->assertSame('Music', $crawler->text()); + } + + public function testFilterXPathWithFakeRoot() + { + $crawler = $this->createTestCrawler(); + $this->assertCount(0, $crawler->filterXPath('.'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + $this->assertCount(0, $crawler->filterXPath('self::*'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + $this->assertCount(0, $crawler->filterXPath('self::_root'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + } + + public function testFilterXPathWithAncestorAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('ancestor::*'), 'The fake root node has no ancestor nodes'); + } + + public function testFilterXPathWithAncestorOrSelfAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('ancestor-or-self::*'), 'The fake root node has no ancestor nodes'); + } + + public function testFilterXPathWithAttributeAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('attribute::*'), 'The fake root node has no attribute nodes'); + } + + public function testFilterXPathWithAttributeAxisAfterElementAxis() + { + $this->assertCount(3, $this->createTestCrawler()->filterXPath('//form/button/attribute::*'), '->filterXPath() handles attribute axes properly when they are preceded by an element filtering axis'); + } + + public function testFilterXPathWithChildAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]'); + + $this->assertCount(1, $crawler->filterXPath('child::div'), 'A child selection finds only the current div'); + } + + public function testFilterXPathWithFollowingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//a'); + + $this->assertCount(0, $crawler->filterXPath('following::div'), 'The fake root node has no following nodes'); + } + + public function testFilterXPathWithFollowingSiblingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//a'); + + $this->assertCount(0, $crawler->filterXPath('following-sibling::div'), 'The fake root node has no following nodes'); + } + + public function testFilterXPathWithNamespaceAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//button'); + + $this->assertCount(0, $crawler->filterXPath('namespace::*'), 'The fake root node has no namespace nodes'); + } + + public function testFilterXPathWithNamespaceAxisAfterElementAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]/namespace::*'); + + $this->assertCount(0, $crawler->filterXPath('namespace::*'), 'Namespace axes cannot be requested'); + } + + public function testFilterXPathWithParentAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//button'); + + $this->assertCount(0, $crawler->filterXPath('parent::*'), 'The fake root node has no parent nodes'); + } + + public function testFilterXPathWithPrecedingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('preceding::*'), 'The fake root node has no preceding nodes'); + } + + public function testFilterXPathWithPrecedingSiblingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('preceding-sibling::*'), 'The fake root node has no preceding nodes'); + } + + public function testFilterXPathWithSelfAxes() + { + $crawler = $this->createTestCrawler()->filterXPath('//a'); + + $this->assertCount(0, $crawler->filterXPath('self::a'), 'The fake root node has no "real" element name'); + $this->assertCount(0, $crawler->filterXPath('self::a/img'), 'The fake root node has no "real" element name'); + $this->assertCount(9, $crawler->filterXPath('self::*/a')); + } + + public function testFilter() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->filter('li'), '->filter() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filter() returns a new instance of a crawler'); + + $crawler = $this->createTestCrawler()->filter('ul'); + + $this->assertCount(6, $crawler->filter('li'), '->filter() filters the node list with the CSS selector'); + } + + public function testFilterWithDefaultNamespace() + { + $crawler = $this->createTestXmlCrawler()->filter('default|entry default|id'); + $this->assertCount(1, $crawler, '->filter() automatically registers namespaces'); + $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); + } + + public function testFilterWithNamespace() + { + $crawler = $this->createTestXmlCrawler()->filter('yt|accessControl'); + $this->assertCount(2, $crawler, '->filter() automatically registers namespaces'); + } + + public function testFilterWithMultipleNamespaces() + { + $crawler = $this->createTestXmlCrawler()->filter('media|group yt|aspectRatio'); + $this->assertCount(1, $crawler, '->filter() automatically registers namespaces'); + $this->assertSame('widescreen', $crawler->text()); + } + + public function testFilterWithDefaultNamespaceOnly() + { + $crawler = new Crawler(' + + + http://localhost/foo + weekly + 0.5 + 2012-11-16 + + + http://localhost/bar + weekly + 0.5 + 2012-11-16 + + + '); + + $this->assertEquals(2, $crawler->filter('url')->count()); + } + + public function testSelectLink() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->selectLink('Foo'), '->selectLink() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectLink() returns a new instance of a crawler'); + + $this->assertCount(1, $crawler->selectLink('Fabien\'s Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(1, $crawler->selectLink('Fabien\'s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); + + $this->assertCount(2, $crawler->selectLink('Fabien"s Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(2, $crawler->selectLink('Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); + + $this->assertCount(1, $crawler->selectLink('\' Fabien"s Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(1, $crawler->selectLink('\' Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); + + $this->assertCount(4, $crawler->selectLink('Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(4, $crawler->selectLink('Bar'), '->selectLink() selects links by the node values'); + } + + public function testSelectButton() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->selectButton('FooValue'), '->selectButton() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectButton() returns a new instance of a crawler'); + + $this->assertEquals(1, $crawler->selectButton('FooValue')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('FooName')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('FooId')->count(), '->selectButton() selects buttons'); + + $this->assertEquals(1, $crawler->selectButton('BarValue')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('BarName')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('BarId')->count(), '->selectButton() selects buttons'); + + $this->assertEquals(1, $crawler->selectButton('FooBarValue')->count(), '->selectButton() selects buttons with form attribute too'); + $this->assertEquals(1, $crawler->selectButton('FooBarName')->count(), '->selectButton() selects buttons with form attribute too'); + } + + public function testSelectButtonWithSingleQuotesInNameAttribute() + { + $html = <<<'HTML' + + + +
+
+ + + + +HTML; + + $crawler = new Crawler($html); + + $this->assertCount(1, $crawler->selectButton('Click \'Here\'')); + } + + public function testSelectButtonWithDoubleQuotesInNameAttribute() + { + $html = <<<'HTML' + + + +
+ Login +
+
+ + + + +HTML; + + $crawler = new Crawler($html); + + $this->assertCount(1, $crawler->selectButton('Click "Here"')); + } + + public function testLink() + { + $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $crawler->link(), '->link() returns a Link instance'); + + $this->assertEquals('POST', $crawler->link('post')->getMethod(), '->link() takes a method as its argument'); + + $crawler = $this->createTestCrawler('http://example.com/bar')->selectLink('GetLink'); + $this->assertEquals('http://example.com/bar?get=param', $crawler->link()->getUri(), '->link() returns a Link instance'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->link(); + $this->fail('->link() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->link() throws an \InvalidArgumentException if the node list is empty'); + } + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The selected node should be instance of DOMElement + */ + public function testInvalidLink() + { + $crawler = $this->createTestCrawler('http://example.com/bar/'); + $crawler->filterXPath('//li/text()')->link(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The selected node should be instance of DOMElement + */ + public function testInvalidLinks() + { + $crawler = $this->createTestCrawler('http://example.com/bar/'); + $crawler->filterXPath('//li/text()')->link(); + } + + public function testSelectLinkAndLinkFiltered() + { + $html = <<<'HTML' + + + +
+ Login +
+
+ + + + +HTML; + + $crawler = new Crawler($html); + $filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'login-form']"); + + $this->assertCount(0, $filtered->selectLink('Login')); + $this->assertCount(1, $filtered->selectButton('Submit')); + + $filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'action']"); + + $this->assertCount(1, $filtered->selectLink('Login')); + $this->assertCount(0, $filtered->selectButton('Submit')); + + $this->assertCount(1, $crawler->selectLink('Login')->selectLink('Login')); + $this->assertCount(1, $crawler->selectButton('Submit')->selectButton('Submit')); + } + + public function testChaining() + { + $crawler = new Crawler('
'); + + $this->assertEquals('a', $crawler->filterXPath('//div')->filterXPath('div')->filterXPath('div')->attr('name')); + } + + public function testLinks() + { + $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo'); + $this->assertInternalType('array', $crawler->links(), '->links() returns an array'); + + $this->assertCount(4, $crawler->links(), '->links() returns an array'); + $links = $crawler->links(); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $links[0], '->links() returns an array of Link instances'); + + $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty'); + } + + public function testForm() + { + $testCrawler = $this->createTestCrawler('http://example.com/bar/'); + $crawler = $testCrawler->selectButton('FooValue'); + $crawler2 = $testCrawler->selectButton('FooBarValue'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler2->form(), '->form() returns a Form instance'); + + $this->assertEquals($crawler->form()->getFormNode()->getAttribute('id'), $crawler2->form()->getFormNode()->getAttribute('id'), '->form() works on elements with form attribute'); + + $this->assertEquals(array('FooName' => 'FooBar', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument'); + $this->assertEquals(array('FooName' => 'FooValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form()->getValues(), '->getValues() returns correct form values'); + $this->assertEquals(array('FooBarName' => 'FooBarValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler2->form()->getValues(), '->getValues() returns correct form values'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->form(); + $this->fail('->form() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->form() throws an \InvalidArgumentException if the node list is empty'); + } + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The selected node should be instance of DOMElement + */ + public function testInvalidForm() + { + $crawler = $this->createTestCrawler('http://example.com/bar/'); + $crawler->filterXPath('//li/text()')->form(); + } + + public function testLast() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + $this->assertNotSame($crawler, $crawler->last(), '->last() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->last() returns a new instance of a crawler'); + + $this->assertEquals('Three', $crawler->last()->text()); + } + + public function testFirst() + { + $crawler = $this->createTestCrawler()->filterXPath('//li'); + $this->assertNotSame($crawler, $crawler->first(), '->first() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->first() returns a new instance of a crawler'); + + $this->assertEquals('One', $crawler->first()->text()); + } + + public function testSiblings() + { + $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1); + $this->assertNotSame($crawler, $crawler->siblings(), '->siblings() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->siblings() returns a new instance of a crawler'); + + $nodes = $crawler->siblings(); + $this->assertEquals(2, $nodes->count()); + $this->assertEquals('One', $nodes->eq(0)->text()); + $this->assertEquals('Three', $nodes->eq(1)->text()); + + $nodes = $this->createTestCrawler()->filterXPath('//li')->eq(0)->siblings(); + $this->assertEquals(2, $nodes->count()); + $this->assertEquals('Two', $nodes->eq(0)->text()); + $this->assertEquals('Three', $nodes->eq(1)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->siblings(); + $this->fail('->siblings() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->siblings() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testNextAll() + { + $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1); + $this->assertNotSame($crawler, $crawler->nextAll(), '->nextAll() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->nextAll() returns a new instance of a crawler'); + + $nodes = $crawler->nextAll(); + $this->assertEquals(1, $nodes->count()); + $this->assertEquals('Three', $nodes->eq(0)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->nextAll(); + $this->fail('->nextAll() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->nextAll() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testPreviousAll() + { + $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(2); + $this->assertNotSame($crawler, $crawler->previousAll(), '->previousAll() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->previousAll() returns a new instance of a crawler'); + + $nodes = $crawler->previousAll(); + $this->assertEquals(2, $nodes->count()); + $this->assertEquals('Two', $nodes->eq(0)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->previousAll(); + $this->fail('->previousAll() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->previousAll() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testChildren() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul'); + $this->assertNotSame($crawler, $crawler->children(), '->children() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->children() returns a new instance of a crawler'); + + $nodes = $crawler->children(); + $this->assertEquals(3, $nodes->count()); + $this->assertEquals('One', $nodes->eq(0)->text()); + $this->assertEquals('Two', $nodes->eq(1)->text()); + $this->assertEquals('Three', $nodes->eq(2)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->children(); + $this->fail('->children() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->children() throws an \InvalidArgumentException if the node list is empty'); + } + + try { + $crawler = new Crawler('

'); + $crawler->filter('p')->children(); + $this->assertTrue(true, '->children() does not trigger a notice if the node has no children'); + } catch (\PHPUnit_Framework_Error_Notice $e) { + $this->fail('->children() does not trigger a notice if the node has no children'); + } + } + + public function testParents() + { + $crawler = $this->createTestCrawler()->filterXPath('//li[1]'); + $this->assertNotSame($crawler, $crawler->parents(), '->parents() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->parents() returns a new instance of a crawler'); + + $nodes = $crawler->parents(); + $this->assertEquals(3, $nodes->count()); + + $nodes = $this->createTestCrawler()->filterXPath('//html')->parents(); + $this->assertEquals(0, $nodes->count()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->parents(); + $this->fail('->parents() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->parents() throws an \InvalidArgumentException if the node list is empty'); + } + } + + /** + * @dataProvider getBaseTagData + */ + public function testBaseTag($baseValue, $linkValue, $expectedUri, $currentUri = null, $description = null) + { + $crawler = new Crawler('', $currentUri); + $this->assertEquals($expectedUri, $crawler->filterXPath('//a')->link()->getUri(), $description); + } + + public function getBaseTagData() + { + return array( + array('http://base.com', 'link', 'http://base.com/link'), + array('//base.com', 'link', 'https://base.com/link', 'https://domain.com', ' tag can use a schema-less URL'), + array('path/', 'link', 'https://domain.com/path/link', 'https://domain.com', ' tag can set a path'), + array('http://base.com', '#', 'http://base.com#', 'http://domain.com/path/link', ' tag does work with links to an anchor'), + array('http://base.com', '', 'http://base.com', 'http://domain.com/path/link', ' tag does work with empty links'), + ); + } + + /** + * @dataProvider getBaseTagWithFormData + */ + public function testBaseTagWithForm($baseValue, $actionValue, $expectedUri, $currentUri = null, $description = null) + { + $crawler = new Crawler('
', + array('bar' => array('InputFormField', 'bar')), + ), + array( + 'appends the submitted button value but not other submit buttons', + ' + ', + array('foobar' => array('InputFormField', 'foobar')), + ), + array( + 'turns an image input into x and y fields', + '', + array('bar.x' => array('InputFormField', '0'), 'bar.y' => array('InputFormField', '0')), + ), + array( + 'returns textareas', + ' + ', + array('foo' => array('TextareaFormField', 'foo')), + ), + array( + 'returns inputs', + ' + ', + array('foo' => array('InputFormField', 'foo')), + ), + array( + 'returns checkboxes', + ' + ', + array('foo' => array('ChoiceFormField', 'foo')), + ), + array( + 'returns not-checked checkboxes', + ' + ', + array('foo' => array('ChoiceFormField', false)), + ), + array( + 'returns radio buttons', + ' + + ', + array('foo' => array('ChoiceFormField', 'bar')), + ), + array( + 'returns file inputs', + ' + ', + array('foo' => array('FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), + ), + ); + } + + public function testGetFormNode() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
'); + + $form = new Form($dom->getElementsByTagName('input')->item(0), 'http://example.com'); + + $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); + } + + public function testGetFormNodeFromNamedForm() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
'); + + $form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com'); + + $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); + } + + public function testGetMethod() + { + $form = $this->createForm('
'); + $this->assertEquals('GET', $form->getMethod(), '->getMethod() returns get if no method is defined'); + + $form = $this->createForm('
'); + $this->assertEquals('POST', $form->getMethod(), '->getMethod() returns the method attribute value of the form'); + + $form = $this->createForm('
', 'put'); + $this->assertEquals('PUT', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); + + $form = $this->createForm('
', 'delete'); + $this->assertEquals('DELETE', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); + + $form = $this->createForm('
', 'patch'); + $this->assertEquals('PATCH', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); + } + + public function testGetSetValue() + { + $form = $this->createForm('
'); + + $this->assertEquals('foo', $form['foo']->getValue(), '->offsetGet() returns the value of a form field'); + + $form['foo'] = 'bar'; + + $this->assertEquals('bar', $form['foo']->getValue(), '->offsetSet() changes the value of a form field'); + + try { + $form['foobar'] = 'bar'; + $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } + + try { + $form['foobar']; + $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } + } + + public function testSetValueOnMultiValuedFieldsWithMalformedName() + { + $form = $this->createForm('
'); + + try { + $form['foo[bar'] = 'bar'; + $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.'); + } + } + + public function testDisableValidation() + { + $form = $this->createForm('
+ + + + '); + + $form->disableValidation(); + + $form['foo[bar]']->select('foo'); + $form['foo[baz]']->select('bar'); + $this->assertEquals('foo', $form['foo[bar]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.'); + $this->assertEquals('bar', $form['foo[baz]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.'); + } + + public function testOffsetUnset() + { + $form = $this->createForm('
'); + unset($form['foo']); + $this->assertFalse(isset($form['foo']), '->offsetUnset() removes a field'); + } + + public function testOffsetExists() + { + $form = $this->createForm('
'); + + $this->assertTrue(isset($form['foo']), '->offsetExists() return true if the field exists'); + $this->assertFalse(isset($form['bar']), '->offsetExists() return false if the field does not exist'); + } + + public function testGetValues() + { + $form = $this->createForm('
'); + $this->assertEquals(array('foo[bar]' => 'foo', 'bar' => 'bar', 'baz' => array()), $form->getValues(), '->getValues() returns all form field values'); + + $form = $this->createForm('
'); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include not-checked checkboxes'); + + $form = $this->createForm('
'); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include file input fields'); + + $form = $this->createForm('
'); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include disabled fields'); + } + + public function testSetValues() + { + $form = $this->createForm('
'); + $form->setValues(array('foo' => false, 'bar' => 'foo')); + $this->assertEquals(array('bar' => 'foo'), $form->getValues(), '->setValues() sets the values of fields'); + } + + public function testMultiselectSetValues() + { + $form = $this->createForm('
'); + $form->setValues(array('multi' => array('foo', 'bar'))); + $this->assertEquals(array('multi' => array('foo', 'bar')), $form->getValues(), '->setValue() sets the values of select'); + } + + public function testGetPhpValues() + { + $form = $this->createForm('
'); + $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays'); + + $form = $this->createForm('
'); + $this->assertEquals(array('fo.o' => array('ba.r' => 'foo'), 'ba r' => 'bar'), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names'); + + $form = $this->createForm('
'); + $this->assertEquals(array('fo.o' => array('ba.r' => array('foo', 'ba.z' => 'bar'))), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names recursively'); + + $form = $this->createForm('
'); + $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), "->getPhpValues() doesn't return empty values"); + } + + public function testGetFiles() + { + $form = $this->createForm('
'); + $this->assertEquals(array(), $form->getFiles(), '->getFiles() returns an empty array if method is get'); + + $form = $this->createForm('
'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for POST'); + + $form = $this->createForm('
', 'put'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PUT'); + + $form = $this->createForm('
', 'delete'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for DELETE'); + + $form = $this->createForm('
', 'patch'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PATCH'); + + $form = $this->createForm('
'); + $this->assertEquals(array(), $form->getFiles(), '->getFiles() does not include disabled file fields'); + } + + public function testGetPhpFiles() + { + $form = $this->createForm('
'); + $this->assertEquals(array('foo' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays'); + + $form = $this->createForm('
'); + $this->assertEquals(array('f.o o' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names'); + + $form = $this->createForm('
'); + $this->assertEquals(array('f.o o' => array('bar' => array('ba.z' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0), array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names recursively'); + } + + /** + * @dataProvider provideGetUriValues + */ + public function testGetUri($message, $form, $values, $uri, $method = null) + { + $form = $this->createForm($form, $method); + $form->setValues($values); + + $this->assertEquals('http://example.com'.$uri, $form->getUri(), '->getUri() '.$message); + } + + public function testGetBaseUri() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
'); + + $nodes = $dom->getElementsByTagName('input'); + $form = new Form($nodes->item($nodes->length - 1), 'http://www.foo.com/'); + $this->assertEquals('http://www.foo.com/foo.php', $form->getUri()); + } + + public function testGetUriWithAnchor() + { + $form = $this->createForm('
', null, 'http://example.com/id/123'); + + $this->assertEquals('http://example.com/id/123#foo', $form->getUri()); + } + + public function testGetUriActionAbsolute() + { + $formHtml = '
'; + + $form = $this->createForm($formHtml); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + $form = $this->createForm($formHtml, null, 'https://login.foo.com'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + $form = $this->createForm($formHtml, null, 'https://login.foo.com/bar/'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + // The action URI haven't the same domain Host have an another domain as Host + $form = $this->createForm($formHtml, null, 'https://www.foo.com'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + $form = $this->createForm($formHtml, null, 'https://www.foo.com/bar/'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + } + + public function testGetUriAbsolute() + { + $form = $this->createForm('
', null, 'http://localhost/foo/'); + $this->assertEquals('http://localhost/foo/foo', $form->getUri(), '->getUri() returns absolute URIs'); + + $form = $this->createForm('
', null, 'http://localhost/foo/'); + $this->assertEquals('http://localhost/foo', $form->getUri(), '->getUri() returns absolute URIs'); + } + + public function testGetUriWithOnlyQueryString() + { + $form = $this->createForm('
', null, 'http://localhost/foo/bar'); + $this->assertEquals('http://localhost/foo/bar?get=param', $form->getUri(), '->getUri() returns absolute URIs only if the host has been defined in the constructor'); + } + + public function testGetUriWithoutAction() + { + $form = $this->createForm('
', null, 'http://localhost/foo/bar'); + $this->assertEquals('http://localhost/foo/bar', $form->getUri(), '->getUri() returns path if no action defined'); + } + + public function provideGetUriValues() + { + return array( + array( + 'returns the URI of the form', + '
', + array(), + '/foo', + ), + array( + 'appends the form values if the method is get', + '
', + array(), + '/foo?foo=foo', + ), + array( + 'appends the form values and merges the submitted values', + '
', + array('foo' => 'bar'), + '/foo?foo=bar', + ), + array( + 'does not append values if the method is post', + '
', + array(), + '/foo', + ), + array( + 'does not append values if the method is patch', + '
', + array(), + '/foo', + 'PUT', + ), + array( + 'does not append values if the method is delete', + '
', + array(), + '/foo', + 'DELETE', + ), + array( + 'does not append values if the method is put', + '
', + array(), + '/foo', + 'PATCH', + ), + array( + 'appends the form values to an existing query string', + '
', + array(), + '/foo?bar=bar&foo=foo', + ), + array( + 'replaces query values with the form values', + '
', + array(), + '/foo?bar=foo', + ), + array( + 'returns an empty URI if the action is empty', + '
', + array(), + '/', + ), + array( + 'appends the form values even if the action is empty', + '
', + array(), + '/?foo=foo', + ), + array( + 'chooses the path if the action attribute value is a sharp (#)', + '
', + array(), + '/#', + ), + ); + } + + public function testHas() + { + $form = $this->createForm('
'); + + $this->assertFalse($form->has('foo'), '->has() returns false if a field is not in the form'); + $this->assertTrue($form->has('bar'), '->has() returns true if a field is in the form'); + } + + public function testRemove() + { + $form = $this->createForm('
'); + $form->remove('bar'); + $this->assertFalse($form->has('bar'), '->remove() removes a field'); + } + + public function testGet() + { + $form = $this->createForm('
'); + + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $form->get('bar'), '->get() returns the field object associated with the given name'); + + try { + $form->get('foo'); + $this->fail('->get() throws an \InvalidArgumentException if the field does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->get() throws an \InvalidArgumentException if the field does not exist'); + } + } + + public function testAll() + { + $form = $this->createForm('
'); + + $fields = $form->all(); + $this->assertCount(1, $fields, '->all() return an array of form field objects'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $fields['bar'], '->all() return an array of form field objects'); + } + + public function testSubmitWithoutAFormButton() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + +
+ + + + '); + + $nodes = $dom->getElementsByTagName('form'); + $form = new Form($nodes->item(0), 'http://example.com'); + $this->assertSame($nodes->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); + } + + public function testTypeAttributeIsCaseInsensitive() + { + $form = $this->createForm('
'); + $this->assertTrue($form->has('example.x'), '->has() returns true if the image input was correctly turned into an x and a y fields'); + $this->assertTrue($form->has('example.y'), '->has() returns true if the image input was correctly turned into an x and a y fields'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryAddThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('[foo]')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryRemoveThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->remove('[foo]'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryGetThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->get('[foo]'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryGetThrowAnExceptionWhenTheFieldDoesNotExist() + { + $registry = new FormFieldRegistry(); + $registry->get('foo'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistrySetThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->set('[foo]', null); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistrySetThrowAnExceptionWhenTheFieldDoesNotExist() + { + $registry = new FormFieldRegistry(); + $registry->set('foo', null); + } + + public function testFormFieldRegistryHasReturnsTrueWhenTheFQNExists() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo[bar]')); + + $this->assertTrue($registry->has('foo')); + $this->assertTrue($registry->has('foo[bar]')); + $this->assertFalse($registry->has('bar')); + $this->assertFalse($registry->has('foo[foo]')); + } + + public function testFormRegistryFieldsCanBeRemoved() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo')); + $registry->remove('foo'); + $this->assertFalse($registry->has('foo')); + } + + public function testFormRegistrySupportsMultivaluedFields() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo[]')); + $registry->add($this->getFormFieldMock('foo[]')); + $registry->add($this->getFormFieldMock('bar[5]')); + $registry->add($this->getFormFieldMock('bar[]')); + $registry->add($this->getFormFieldMock('bar[baz]')); + + $this->assertEquals( + array('foo[0]', 'foo[1]', 'bar[5]', 'bar[6]', 'bar[baz]'), + array_keys($registry->all()) + ); + } + + public function testFormRegistrySetValues() + { + $registry = new FormFieldRegistry(); + $registry->add($f2 = $this->getFormFieldMock('foo[2]')); + $registry->add($f3 = $this->getFormFieldMock('foo[3]')); + $registry->add($fbb = $this->getFormFieldMock('foo[bar][baz]')); + + $f2 + ->expects($this->exactly(2)) + ->method('setValue') + ->with(2) + ; + + $f3 + ->expects($this->exactly(2)) + ->method('setValue') + ->with(3) + ; + + $fbb + ->expects($this->exactly(2)) + ->method('setValue') + ->with('fbb') + ; + + $registry->set('foo[2]', 2); + $registry->set('foo[3]', 3); + $registry->set('foo[bar][baz]', 'fbb'); + + $registry->set('foo', array( + 2 => 2, + 3 => 3, + 'bar' => array( + 'baz' => 'fbb', + ), + )); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Cannot set value on a compound field "foo[bar]". + */ + public function testFormRegistrySetValueOnCompoundField() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo[bar][baz]')); + + $registry->set('foo[bar]', 'fbb'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unreachable field "0" + */ + public function testFormRegistrySetArrayOnNotCompoundField() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('bar')); + + $registry->set('bar', array('baz')); + } + + public function testDifferentFieldTypesWithSameName() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + + +
+ + + + + + + + + + '); + $form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com'); + + $this->assertInstanceOf('Symfony\Component\DomCrawler\Field\ChoiceFormField', $form->get('option')); + } + + protected function getFormFieldMock($name, $value = null) + { + $field = $this + ->getMockBuilder('Symfony\\Component\\DomCrawler\\Field\\FormField') + ->setMethods(array('getName', 'getValue', 'setValue', 'initialize')) + ->disableOriginalConstructor() + ->getMock() + ; + + $field + ->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)) + ; + + $field + ->expects($this->any()) + ->method('getValue') + ->will($this->returnValue($value)) + ; + + return $field; + } + + protected function createForm($form, $method = null, $currentUri = null) + { + $dom = new \DOMDocument(); + $dom->loadHTML(''.$form.''); + + $xPath = new \DOMXPath($dom); + $nodes = $xPath->query('//input | //button'); + + if (null === $currentUri) { + $currentUri = 'http://example.com/'; + } + + return new Form($nodes->item($nodes->length - 1), $currentUri, $method); + } + + protected function createTestHtml5Form() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + +

Hello form

+
+
+ +
+ + + + +
+
+
+ + + +
+ +
+ +
+
+
+ + +
+ + + +
+
+
+ + + + +

+
  • a list
  • may be a good example
+EOT + , + ), + $this->parser->parse($yaml) + ); + } + + public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks() + { + $yaml = << +

A heading

+ +
    +
  • a list
  • +
  • may be a good example
  • +
+EOT; + + $this->assertSame( + array( + 'test' => <<A heading +
    +
  • a list
  • +
  • may be a good example
  • +
+EOT + , + ), + $this->parser->parse($yaml) + ); + } +} + +class B +{ + public $b = 'foo'; +} diff --git a/vendor/symfony/yaml/Tests/YamlTest.php b/vendor/symfony/yaml/Tests/YamlTest.php new file mode 100644 index 0000000..ee008cf --- /dev/null +++ b/vendor/symfony/yaml/Tests/YamlTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Tests; + +use Symfony\Component\Yaml\Yaml; + +class YamlTest extends \PHPUnit_Framework_TestCase +{ + public function testParseAndDump() + { + $data = array('lorem' => 'ipsum', 'dolor' => 'sit'); + $yml = Yaml::dump($data); + $parsed = Yaml::parse($yml); + $this->assertEquals($data, $parsed); + } +} diff --git a/vendor/symfony/yaml/Unescaper.php b/vendor/symfony/yaml/Unescaper.php new file mode 100644 index 0000000..6e4f1d7 --- /dev/null +++ b/vendor/symfony/yaml/Unescaper.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Unescaper encapsulates unescaping rules for single and double-quoted + * YAML strings. + * + * @author Matthew Lewinski + * + * @internal + */ +class Unescaper +{ + /** + * Regex fragment that matches an escaped character in a double quoted string. + */ + const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)'; + + /** + * Unescapes a single quoted string. + * + * @param string $value A single quoted string. + * + * @return string The unescaped string. + */ + public function unescapeSingleQuotedString($value) + { + return str_replace('\'\'', '\'', $value); + } + + /** + * Unescapes a double quoted string. + * + * @param string $value A double quoted string. + * + * @return string The unescaped string. + */ + public function unescapeDoubleQuotedString($value) + { + $callback = function ($match) { + return $this->unescapeCharacter($match[0]); + }; + + // evaluate the string + return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); + } + + /** + * Unescapes a character that was found in a double-quoted string. + * + * @param string $value An escaped character + * + * @return string The unescaped character + */ + private function unescapeCharacter($value) + { + switch ($value[1]) { + case '0': + return "\x0"; + case 'a': + return "\x7"; + case 'b': + return "\x8"; + case 't': + return "\t"; + case "\t": + return "\t"; + case 'n': + return "\n"; + case 'v': + return "\xB"; + case 'f': + return "\xC"; + case 'r': + return "\r"; + case 'e': + return "\x1B"; + case ' ': + return ' '; + case '"': + return '"'; + case '/': + return '/'; + case '\\': + return '\\'; + case 'N': + // U+0085 NEXT LINE + return "\xC2\x85"; + case '_': + // U+00A0 NO-BREAK SPACE + return "\xC2\xA0"; + case 'L': + // U+2028 LINE SEPARATOR + return "\xE2\x80\xA8"; + case 'P': + // U+2029 PARAGRAPH SEPARATOR + return "\xE2\x80\xA9"; + case 'x': + return self::utf8chr(hexdec(substr($value, 2, 2))); + case 'u': + return self::utf8chr(hexdec(substr($value, 2, 4))); + case 'U': + return self::utf8chr(hexdec(substr($value, 2, 8))); + default: + throw new ParseException(sprintf('Found unknown escape character "%s".', $value)); + } + } + + /** + * Get the UTF-8 character for the given code point. + * + * @param int $c The unicode code point + * + * @return string The corresponding UTF-8 character + */ + private static function utf8chr($c) + { + if (0x80 > $c %= 0x200000) { + return chr($c); + } + if (0x800 > $c) { + return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); + } + if (0x10000 > $c) { + return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); + } + + return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); + } +} diff --git a/vendor/symfony/yaml/Yaml.php b/vendor/symfony/yaml/Yaml.php new file mode 100644 index 0000000..426896b --- /dev/null +++ b/vendor/symfony/yaml/Yaml.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Yaml offers convenience methods to load and dump YAML. + * + * @author Fabien Potencier + */ +class Yaml +{ + /** + * Parses YAML into a PHP value. + * + * Usage: + * + * $array = Yaml::parse(file_get_contents('config.yml')); + * print_r($array); + * + * + * @param string $input A string containing YAML + * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise + * @param bool $objectSupport True if object support is enabled, false otherwise + * @param bool $objectForMap True if maps should return a stdClass instead of array() + * + * @return mixed The YAML converted to a PHP value + * + * @throws ParseException If the YAML is not valid + */ + public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) + { + $yaml = new Parser(); + + return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport, $objectForMap); + } + + /** + * Dumps a PHP array to a YAML string. + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param array $array PHP array + * @param int $inline The level where you switch to inline YAML + * @param int $indent The amount of spaces to use for indentation of nested nodes. + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return string A YAML string representing the original PHP array + */ + public static function dump($array, $inline = 2, $indent = 4, $exceptionOnInvalidType = false, $objectSupport = false) + { + $yaml = new Dumper(); + $yaml->setIndentation($indent); + + return $yaml->dump($array, $inline, 0, $exceptionOnInvalidType, $objectSupport); + } +} diff --git a/vendor/symfony/yaml/composer.json b/vendor/symfony/yaml/composer.json new file mode 100644 index 0000000..db1714c --- /dev/null +++ b/vendor/symfony/yaml/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/yaml", + "type": "library", + "description": "Symfony Yaml Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Yaml\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/yaml/phpunit.xml.dist b/vendor/symfony/yaml/phpunit.xml.dist new file mode 100644 index 0000000..6bdbea1 --- /dev/null +++ b/vendor/symfony/yaml/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/vlucas/phpdotenv/LICENSE.txt b/vendor/vlucas/phpdotenv/LICENSE.txt new file mode 100644 index 0000000..70ee47e --- /dev/null +++ b/vendor/vlucas/phpdotenv/LICENSE.txt @@ -0,0 +1,32 @@ +The BSD 3-Clause License +http://opensource.org/licenses/BSD-3-Clause + +Copyright (c) 2013, Vance Lucas +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 the Vance Lucas nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/vendor/vlucas/phpdotenv/composer.json b/vendor/vlucas/phpdotenv/composer.json new file mode 100644 index 0000000..816d910 --- /dev/null +++ b/vendor/vlucas/phpdotenv/composer.json @@ -0,0 +1,31 @@ +{ + "name": "vlucas/phpdotenv", + "type": "library", + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": ["env", "dotenv", "environment"], + "homepage": "http://github.com/vlucas/phpdotenv", + "license" : "BSD", + "authors" : [ + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "http://www.vancelucas.com" + } + ], + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "^4.8|^5.0" + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + } +} diff --git a/vendor/vlucas/phpdotenv/src/Dotenv.php b/vendor/vlucas/phpdotenv/src/Dotenv.php new file mode 100644 index 0000000..0737a70 --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Dotenv.php @@ -0,0 +1,87 @@ +filePath = $this->getFilePath($path, $file); + $this->loader = new Loader($this->filePath, $immutable = true); + } + + /** + * Load `.env` file in given directory. + * + * @return array + */ + public function load() + { + $this->loader = new Loader($this->filePath, $immutable = true); + + return $this->loader->load(); + } + + /** + * Load `.env` file in given directory. + * + * @return array + */ + public function overload() + { + $this->loader = new Loader($this->filePath, $immutable = false); + + return $this->loader->load(); + } + + /** + * Returns the full path to the file. + * + * @param string $path + * @param string $file + * + * @return string + */ + protected function getFilePath($path, $file) + { + if (!is_string($file)) { + $file = '.env'; + } + + $filePath = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file; + + return $filePath; + } + + /** + * Required ensures that the specified variables exist, and returns a new Validator object. + * + * @param string|string[] $variable + * + * @return \Dotenv\Validator + */ + public function required($variable) + { + return new Validator((array) $variable, $this->loader); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php b/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php new file mode 100644 index 0000000..781eeab --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php @@ -0,0 +1,11 @@ +filePath = $filePath; + $this->immutable = $immutable; + } + + /** + * Load `.env` file in given directory. + * + * @return array + */ + public function load() + { + $this->ensureFileIsReadable(); + + $filePath = $this->filePath; + $lines = $this->readLinesFromFile($filePath); + foreach ($lines as $line) { + if (!$this->isComment($line) && $this->looksLikeSetter($line)) { + $this->setEnvironmentVariable($line); + } + } + + return $lines; + } + + /** + * Ensures the given filePath is readable. + * + * @throws \Dotenv\Exception\InvalidPathException + * + * @return void + */ + protected function ensureFileIsReadable() + { + if (!is_readable($this->filePath) || !is_file($this->filePath)) { + throw new InvalidPathException(sprintf('Unable to read the environment file at %s.', $this->filePath)); + } + } + + /** + * Normalise the given environment variable. + * + * Takes value as passed in by developer and: + * - ensures we're dealing with a separate name and value, breaking apart the name string if needed, + * - cleaning the value of quotes, + * - cleaning the name of quotes, + * - resolving nested variables. + * + * @param string $name + * @param string $value + * + * @return array + */ + protected function normaliseEnvironmentVariable($name, $value) + { + list($name, $value) = $this->splitCompoundStringIntoParts($name, $value); + list($name, $value) = $this->sanitiseVariableName($name, $value); + list($name, $value) = $this->sanitiseVariableValue($name, $value); + + $value = $this->resolveNestedVariables($value); + + return array($name, $value); + } + + /** + * Process the runtime filters. + * + * Called from the `VariableFactory`, passed as a callback in `$this->loadFromFile()`. + * + * @param string $name + * @param string $value + * + * @return array + */ + public function processFilters($name, $value) + { + list($name, $value) = $this->splitCompoundStringIntoParts($name, $value); + list($name, $value) = $this->sanitiseVariableName($name, $value); + list($name, $value) = $this->sanitiseVariableValue($name, $value); + + return array($name, $value); + } + + /** + * Read lines from the file, auto detecting line endings. + * + * @param string $filePath + * + * @return array + */ + protected function readLinesFromFile($filePath) + { + // Read file into an array of lines with auto-detected line endings + $autodetect = ini_get('auto_detect_line_endings'); + ini_set('auto_detect_line_endings', '1'); + $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + ini_set('auto_detect_line_endings', $autodetect); + + return $lines; + } + + /** + * Determine if the line in the file is a comment, e.g. begins with a #. + * + * @param string $line + * + * @return bool + */ + protected function isComment($line) + { + return strpos(trim($line), '#') === 0; + } + + /** + * Determine if the given line looks like it's setting a variable. + * + * @param string $line + * + * @return bool + */ + protected function looksLikeSetter($line) + { + return strpos($line, '=') !== false; + } + + /** + * Split the compound string into parts. + * + * If the `$name` contains an `=` sign, then we split it into 2 parts, a `name` & `value` + * disregarding the `$value` passed in. + * + * @param string $name + * @param string $value + * + * @return array + */ + protected function splitCompoundStringIntoParts($name, $value) + { + if (strpos($name, '=') !== false) { + list($name, $value) = array_map('trim', explode('=', $name, 2)); + } + + return array($name, $value); + } + + /** + * Strips quotes from the environment variable value. + * + * @param string $name + * @param string $value + * + * @throws \Dotenv\Exception\InvalidFileException + * + * @return array + */ + protected function sanitiseVariableValue($name, $value) + { + $value = trim($value); + if (!$value) { + return array($name, $value); + } + + if ($this->beginsWithAQuote($value)) { // value starts with a quote + $quote = $value[0]; + $regexPattern = sprintf( + '/^ + %1$s # match a quote at the start of the value + ( # capturing sub-pattern used + (?: # we do not need to capture this + [^%1$s\\\\] # any character other than a quote or backslash + |\\\\\\\\ # or two backslashes together + |\\\\%1$s # or an escaped quote e.g \" + )* # as many characters that match the previous rules + ) # end of the capturing sub-pattern + %1$s # and the closing quote + .*$ # and discard any string after the closing quote + /mx', + $quote + ); + $value = preg_replace($regexPattern, '$1', $value); + $value = str_replace("\\$quote", $quote, $value); + $value = str_replace('\\\\', '\\', $value); + } else { + $parts = explode(' #', $value, 2); + $value = trim($parts[0]); + + // Unquoted values cannot contain whitespace + if (preg_match('/\s+/', $value) > 0) { + throw new InvalidFileException('Dotenv values containing spaces must be surrounded by quotes.'); + } + } + + return array($name, trim($value)); + } + + /** + * Resolve the nested variables. + * + * Look for {$varname} patterns in the variable value and replace with an existing + * environment variable. + * + * @param string $value + * + * @return mixed + */ + protected function resolveNestedVariables($value) + { + if (strpos($value, '$') !== false) { + $loader = $this; + $value = preg_replace_callback( + '/\${([a-zA-Z0-9_]+)}/', + function ($matchedPatterns) use ($loader) { + $nestedVariable = $loader->getEnvironmentVariable($matchedPatterns[1]); + if ($nestedVariable === null) { + return $matchedPatterns[0]; + } else { + return $nestedVariable; + } + }, + $value + ); + } + + return $value; + } + + /** + * Strips quotes and the optional leading "export " from the environment variable name. + * + * @param string $name + * @param string $value + * + * @return array + */ + protected function sanitiseVariableName($name, $value) + { + $name = trim(str_replace(array('export ', '\'', '"'), '', $name)); + + return array($name, $value); + } + + /** + * Determine if the given string begins with a quote. + * + * @param string $value + * + * @return bool + */ + protected function beginsWithAQuote($value) + { + return strpbrk($value[0], '"\'') !== false; + } + + /** + * Search the different places for environment variables and return first value found. + * + * @param string $name + * + * @return string + */ + public function getEnvironmentVariable($name) + { + switch (true) { + case array_key_exists($name, $_ENV): + return $_ENV[$name]; + case array_key_exists($name, $_SERVER): + return $_SERVER[$name]; + default: + $value = getenv($name); + return $value === false ? null : $value; // switch getenv default to null + } + } + + /** + * Set an environment variable. + * + * This is done using: + * - putenv, + * - $_ENV, + * - $_SERVER. + * + * The environment variable value is stripped of single and double quotes. + * + * @param string $name + * @param string|null $value + * + * @return void + */ + public function setEnvironmentVariable($name, $value = null) + { + list($name, $value) = $this->normaliseEnvironmentVariable($name, $value); + + // Don't overwrite existing environment variables if we're immutable + // Ruby's dotenv does this with `ENV[key] ||= value`. + if ($this->immutable && $this->getEnvironmentVariable($name) !== null) { + return; + } + + putenv("$name=$value"); + + $_ENV[$name] = $value; + $_SERVER[$name] = $value; + } + + /** + * Clear an environment variable. + * + * This is not (currently) used by Dotenv but is provided as a utility + * method for 3rd party code. + * + * This is done using: + * - putenv, + * - unset($_ENV, $_SERVER). + * + * @param string $name + * + * @see setEnvironmentVariable() + * + * @return void + */ + public function clearEnvironmentVariable($name) + { + // Don't clear anything if we're immutable. + if ($this->immutable) { + return; + } + + putenv($name); + + unset($_ENV[$name], $_SERVER[$name]); + } +} diff --git a/vendor/vlucas/phpdotenv/src/Validator.php b/vendor/vlucas/phpdotenv/src/Validator.php new file mode 100644 index 0000000..aa3cb0e --- /dev/null +++ b/vendor/vlucas/phpdotenv/src/Validator.php @@ -0,0 +1,130 @@ +variables = $variables; + $this->loader = $loader; + + $this->assertCallback( + function ($value) { + return $value !== null; + }, + 'is missing' + ); + } + + /** + * Assert that each variable is not empty. + * + * @return \Dotenv\Validator + */ + public function notEmpty() + { + return $this->assertCallback( + function ($value) { + return strlen(trim($value)) > 0; + }, + 'is empty' + ); + } + + /** + * Assert that each specified variable is an integer. + * + * @return \Dotenv\Validator + */ + public function isInteger() + { + return $this->assertCallback( + function ($value) { + return ctype_digit($value); + }, + 'is not an integer' + ); + } + + /** + * Assert that each variable is amongst the given choices. + * + * @param string[] $choices + * + * @return \Dotenv\Validator + */ + public function allowedValues(array $choices) + { + return $this->assertCallback( + function ($value) use ($choices) { + return in_array($value, $choices); + }, + 'is not an allowed value' + ); + } + + /** + * Assert that the callback returns true for each variable. + * + * @param callable $callback + * @param string $message + * + * @throws \Dotenv\Exception\InvalidCallbackException|\Dotenv\Exception\ValidationException + * + * @return \Dotenv\Validator + */ + protected function assertCallback($callback, $message = 'failed callback assertion') + { + if (!is_callable($callback)) { + throw new InvalidCallbackException('The provided callback must be callable.'); + } + + $variablesFailingAssertion = array(); + foreach ($this->variables as $variableName) { + $variableValue = $this->loader->getEnvironmentVariable($variableName); + if (call_user_func($callback, $variableValue) === false) { + $variablesFailingAssertion[] = $variableName." $message"; + } + } + + if (count($variablesFailingAssertion) > 0) { + throw new ValidationException(sprintf( + 'One or more environment variables failed assertions: %s.', + implode(', ', $variablesFailingAssertion) + )); + } + + return $this; + } +}