From faad290d2753aaf358efef09b53f498967e92e0b Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 18 Nov 2025 13:31:06 +0100 Subject: [PATCH 1/4] Replace textalk/websocket and iio/libmergepdf --- composer.json | 22 +- composer.lock | 625 ++-- vendor/autoload.php | 5 +- vendor/composer/InstalledVersions.php | 45 +- vendor/composer/autoload_classmap.php | 10 +- vendor/composer/autoload_psr4.php | 13 +- vendor/composer/autoload_static.php | 65 +- vendor/composer/installed.json | 660 +++-- vendor/composer/installed.php | 107 +- vendor/composer/platform_check.php | 9 +- vendor/cweagans/composer-patches/.gitignore | 1 - vendor/cweagans/composer-patches/LICENSE.md | 9 - vendor/cweagans/composer-patches/README.md | 205 -- .../cweagans/composer-patches/composer.json | 30 - .../cweagans/composer-patches/composer.lock | 2568 ----------------- .../composer-patches/phpunit.xml.dist | 18 - .../composer-patches/src/PatchEvent.php | 70 - .../composer-patches/src/PatchEvents.php | 30 - .../cweagans/composer-patches/src/Patches.php | 599 ---- .../composer-patches/tests/PatchEventTest.php | 39 - .../.github/ISSUE_TEMPLATE/bug_report.md | 37 - vendor/iio/libmergepdf/PATCHES.txt | 7 - vendor/iio/libmergepdf/composer.json | 35 - .../libmergepdf/src/Driver/DefaultDriver.php | 25 - .../src/Driver/DriverInterface.php | 13 - .../libmergepdf/src/Driver/Fpdi2Driver.php | 64 - .../libmergepdf/src/Driver/TcpdiDriver.php | 52 - vendor/iio/libmergepdf/src/Exception.php | 7 - vendor/iio/libmergepdf/src/Merger.php | 79 - vendor/iio/libmergepdf/src/Pages.php | 67 - vendor/iio/libmergepdf/src/PagesInterface.php | 11 - .../iio/libmergepdf/src/Source/FileSource.php | 50 - .../iio/libmergepdf/src/Source/RawSource.php | 45 - .../src/Source/SourceInterface.php | 25 - vendor/iio/libmergepdf/tcpdi/LICENSE | 191 -- vendor/iio/libmergepdf/tcpdi/README.md | 65 - .../pdf-merge/.github/workflows/ci.yml | 40 + vendor/karriere/pdf-merge/.gitignore | 7 + vendor/karriere/pdf-merge/CHANGELOG.md | 81 + vendor/karriere/pdf-merge/LICENSE | 188 ++ vendor/karriere/pdf-merge/README.md | 67 + vendor/karriere/pdf-merge/UPGRADE.md | 69 + vendor/karriere/pdf-merge/composer.json | 53 + vendor/karriere/pdf-merge/phpstan.neon | 5 + vendor/karriere/pdf-merge/phpunit.xml | 17 + vendor/karriere/pdf-merge/pint.json | 8 + .../pdf-merge/src/Config/FooterConfig.php | 33 + .../pdf-merge/src/Config/HeaderConfig.php | 54 + vendor/karriere/pdf-merge/src/Config/RGB.php | 18 + .../src/Exceptions/FileNotFoundException.php | 13 + .../Exceptions/NoFilesDefinedException.php | 13 + vendor/karriere/pdf-merge/src/PdfMerge.php | 122 + .../pdf-merge/tcpi}/fpdf_tpl.php | 304 +- .../pdf-merge/tcpi}/tcpdi.php | 238 +- .../pdf-merge/tcpi}/tcpdi_parser.php | 330 ++- .../karriere/pdf-merge/tests/PdfMergeTest.php | 93 + vendor/karriere/pdf-merge/tests/Pest.php | 22 + .../karriere/pdf-merge/tests/files/dummy.pdf | Bin 0 -> 16307 bytes .../pdf-merge/tests/files/dummy_landscape.pdf | Bin 0 -> 16893 bytes .../pdf-merge/tests/files/expected/output.pdf | Bin 0 -> 22256 bytes .../expected/output_mixed_orientation.pdf | Bin 0 -> 37218 bytes .../files/expected/output_with_header.pdf | Bin 0 -> 40836 bytes .../output_with_header_and_footer.pdf | Bin 0 -> 41067 bytes .../pdf-merge/tests/files/header_logo.jpg} | Bin vendor/phrity/comparison/composer.json | 34 + vendor/phrity/comparison/src/Comparable.php | 56 + vendor/phrity/comparison/src/Comparator.php | 200 ++ .../phrity/comparison/src/ComparisonTrait.php | 69 + vendor/phrity/comparison/src/Equalable.php | 22 + .../comparison/src/IncomparableException.php | 17 + vendor/phrity/http/LICENSE | 21 + vendor/phrity/http/composer.json | 40 + vendor/phrity/http/docu.json | 10 + vendor/phrity/http/src/HttpFactory.php | 175 ++ vendor/phrity/net-stream/composer.json | 38 + vendor/phrity/net-stream/src/Context.php | 214 ++ vendor/phrity/net-stream/src/SocketClient.php | 108 + vendor/phrity/net-stream/src/SocketServer.php | 173 ++ vendor/phrity/net-stream/src/SocketStream.php | 166 ++ vendor/phrity/net-stream/src/Stream.php | 317 ++ .../net-stream/src/StreamCollection.php | 215 ++ .../phrity/net-stream/src/StreamException.php | 84 + .../phrity/net-stream/src/StreamFactory.php | 133 + vendor/phrity/net-uri/composer.json | 37 + vendor/phrity/net-uri/src/Uri.php | 710 +++++ vendor/phrity/net-uri/src/UriFactory.php | 47 + vendor/phrity/util-errorhandler/composer.json | 34 + .../util-errorhandler/src/ErrorHandler.php | 131 + .../websocket/LICENSE.md} | 0 vendor/phrity/websocket/composer.json | 50 + vendor/phrity/websocket/docu.json | 102 + vendor/phrity/websocket/src/Client.php | 667 +++++ vendor/phrity/websocket/src/Connection.php | 435 +++ vendor/phrity/websocket/src/Constant.php | 17 + .../src/Exception/BadOpcodeException.php | 20 + .../src/Exception/BadUriException.php | 20 + .../src/Exception/ClientException.php | 16 + .../src/Exception/CloseException.php | 29 + .../Exception/ConnectionClosedException.php | 20 + .../Exception/ConnectionFailureException.php | 20 + .../Exception/ConnectionLevelInterface.php | 16 + .../Exception/ConnectionTimeoutException.php | 20 + .../websocket/src/Exception/Exception.php | 18 + .../src/Exception/ExceptionInterface.php | 19 + .../src/Exception/HandshakeException.php | 30 + .../src/Exception/MessageLevelInterface.php | 16 + .../src/Exception/ReconnectException.php | 30 + .../src/Exception/ServerException.php | 16 + vendor/phrity/websocket/src/Frame/Frame.php | 93 + .../websocket/src/Frame/FrameHandler.php | 212 ++ .../websocket/src/Http/DefaultHttpFactory.php | 72 + .../phrity/websocket/src/Http/HttpHandler.php | 140 + vendor/phrity/websocket/src/Http/Message.php | 185 ++ vendor/phrity/websocket/src/Http/Request.php | 137 + vendor/phrity/websocket/src/Http/Response.php | 150 + .../websocket/src/Http/ServerRequest.php | 153 + .../phrity/websocket/src/Message/Binary.php | 27 + vendor/phrity/websocket/src/Message/Close.php | 56 + .../phrity/websocket/src/Message/Message.php | 113 + .../websocket/src/Message/MessageHandler.php | 118 + vendor/phrity/websocket/src/Message/Ping.php | 17 + vendor/phrity/websocket/src/Message/Pong.php | 17 + vendor/phrity/websocket/src/Message/Text.php | 27 + .../websocket/src/Middleware/Callback.php | 100 + .../websocket/src/Middleware/CloseHandler.php | 73 + .../src/Middleware/CompressionExtension.php | 163 ++ .../CompressorInterface.php | 39 + .../DeflateCompressor.php | 236 ++ .../src/Middleware/FollowRedirect.php | 71 + .../src/Middleware/MiddlewareHandler.php | 172 ++ .../src/Middleware/MiddlewareInterface.php | 18 + .../websocket/src/Middleware/PingInterval.php | 67 + .../src/Middleware/PingResponder.php | 45 + .../ProcessHttpIncomingInterface.php | 20 + .../ProcessHttpOutgoingInterface.php | 24 + .../src/Middleware/ProcessHttpStack.php | 70 + .../Middleware/ProcessIncomingInterface.php | 20 + .../Middleware/ProcessOutgoingInterface.php | 25 + .../websocket/src/Middleware/ProcessStack.php | 73 + .../src/Middleware/ProcessTickInterface.php | 19 + .../src/Middleware/ProcessTickStack.php | 47 + .../src/Middleware/SubprotocolNegotiation.php | 113 + vendor/phrity/websocket/src/Server.php | 671 +++++ .../websocket/src/Trait/ListenerTrait.php | 126 + .../websocket/src/Trait/LoggerAwareTrait.php | 44 + .../websocket/src/Trait/OpcodeTrait.php | 25 + .../websocket/src/Trait/SendMethodsTrait.php | 74 + .../websocket/src/Trait/StringableTrait.php | 25 + vendor/psr/http-factory/LICENSE | 21 + vendor/psr/http-factory/README.md | 12 + vendor/psr/http-factory/composer.json | 38 + .../src/RequestFactoryInterface.php | 18 + .../src/ResponseFactoryInterface.php | 18 + .../src/ServerRequestFactoryInterface.php | 24 + .../src/StreamFactoryInterface.php | 45 + .../src/UploadedFileFactoryInterface.php | 34 + .../http-factory/src/UriFactoryInterface.php | 17 + vendor/psr/http-message/CHANGELOG.md | 36 + .../LICENSE.txt => psr/http-message/LICENSE} | 16 +- vendor/psr/http-message/README.md | 16 + vendor/psr/http-message/composer.json | 26 + .../psr/http-message/docs/PSR7-Interfaces.md | 130 + vendor/psr/http-message/docs/PSR7-Usage.md | 159 + .../psr/http-message/src/MessageInterface.php | 187 ++ .../psr/http-message/src/RequestInterface.php | 130 + .../http-message/src/ResponseInterface.php | 68 + .../src/ServerRequestInterface.php | 261 ++ .../psr/http-message/src/StreamInterface.php | 158 + .../src/UploadedFileInterface.php | 123 + vendor/psr/http-message/src/UriInterface.php | 324 +++ vendor/psr/log/Psr/Log/AbstractLogger.php | 128 - vendor/psr/log/Psr/Log/Test/DummyTest.php | 18 - .../log/Psr/Log/Test/LoggerInterfaceTest.php | 138 - vendor/psr/log/Psr/Log/Test/TestLogger.php | 147 - vendor/psr/log/composer.json | 6 +- vendor/psr/log/src/AbstractLogger.php | 15 + .../Log => src}/InvalidArgumentException.php | 0 vendor/psr/log/{Psr/Log => src}/LogLevel.php | 0 .../{Psr/Log => src}/LoggerAwareInterface.php | 6 +- .../log/{Psr/Log => src}/LoggerAwareTrait.php | 8 +- .../log/{Psr/Log => src}/LoggerInterface.php | 47 +- .../psr/log/{Psr/Log => src}/LoggerTrait.php | 64 +- .../psr/log/{Psr/Log => src}/NullLogger.php | 8 +- vendor/setasign/fpdi/README.md | 131 - vendor/setasign/fpdi/SECURITY.md | 5 - vendor/setasign/fpdi/composer.json | 51 - vendor/setasign/fpdi/src/FpdfTpl.php | 21 - vendor/setasign/fpdi/src/FpdfTplTrait.php | 473 --- vendor/setasign/fpdi/src/FpdfTrait.php | 192 -- vendor/setasign/fpdi/src/Fpdi.php | 34 - vendor/setasign/fpdi/src/FpdiException.php | 18 - vendor/setasign/fpdi/src/FpdiTrait.php | 655 ----- vendor/setasign/fpdi/src/GraphicsState.php | 97 - vendor/setasign/fpdi/src/Math/Matrix.php | 116 - vendor/setasign/fpdi/src/Math/Vector.php | 66 - .../CrossReference/AbstractReader.php | 95 - .../CrossReference/CrossReference.php | 326 --- .../CrossReferenceException.php | 79 - .../PdfParser/CrossReference/FixedReader.php | 200 -- .../PdfParser/CrossReference/LineReader.php | 168 -- .../CrossReference/ReaderInterface.php | 34 - .../fpdi/src/PdfParser/Filter/Ascii85.php | 102 - .../src/PdfParser/Filter/Ascii85Exception.php | 27 - .../fpdi/src/PdfParser/Filter/AsciiHex.php | 47 - .../src/PdfParser/Filter/FilterException.php | 23 - .../src/PdfParser/Filter/FilterInterface.php | 25 - .../fpdi/src/PdfParser/Filter/Flate.php | 77 - .../src/PdfParser/Filter/FlateException.php | 27 - .../fpdi/src/PdfParser/Filter/Lzw.php | 178 -- .../src/PdfParser/Filter/LzwException.php | 22 - .../setasign/fpdi/src/PdfParser/PdfParser.php | 435 --- .../fpdi/src/PdfParser/PdfParserException.php | 49 - .../fpdi/src/PdfParser/StreamReader.php | 477 --- .../setasign/fpdi/src/PdfParser/Tokenizer.php | 154 - .../fpdi/src/PdfParser/Type/PdfArray.php | 85 - .../fpdi/src/PdfParser/Type/PdfBoolean.php | 42 - .../fpdi/src/PdfParser/Type/PdfDictionary.php | 134 - .../fpdi/src/PdfParser/Type/PdfHexString.php | 77 - .../src/PdfParser/Type/PdfIndirectObject.php | 103 - .../Type/PdfIndirectObjectReference.php | 52 - .../fpdi/src/PdfParser/Type/PdfName.php | 82 - .../fpdi/src/PdfParser/Type/PdfNull.php | 19 - .../fpdi/src/PdfParser/Type/PdfNumeric.php | 43 - .../fpdi/src/PdfParser/Type/PdfStream.php | 352 --- .../fpdi/src/PdfParser/Type/PdfString.php | 202 -- .../fpdi/src/PdfParser/Type/PdfToken.php | 43 - .../fpdi/src/PdfParser/Type/PdfType.php | 106 - .../src/PdfParser/Type/PdfTypeException.php | 24 - .../src/PdfReader/DataStructure/Rectangle.php | 179 -- vendor/setasign/fpdi/src/PdfReader/Page.php | 420 --- .../fpdi/src/PdfReader/PageBoundaries.php | 94 - .../setasign/fpdi/src/PdfReader/PdfReader.php | 240 -- .../fpdi/src/PdfReader/PdfReaderException.php | 34 - vendor/setasign/fpdi/src/Tcpdf/Fpdi.php | 391 --- vendor/setasign/fpdi/src/TcpdfFpdi.php | 23 - vendor/setasign/fpdi/src/Tfpdf/FpdfTpl.php | 23 - vendor/setasign/fpdi/src/Tfpdf/Fpdi.php | 32 - vendor/setasign/fpdi/src/autoload.php | 21 - vendor/tecnickcom/tcpdf/CHANGELOG.TXT | 64 + vendor/tecnickcom/tcpdf/LICENSE.TXT | 2 +- vendor/tecnickcom/tcpdf/README.md | 6 +- vendor/tecnickcom/tcpdf/VERSION | 2 +- vendor/tecnickcom/tcpdf/composer.json | 12 +- .../examples/barcodes/example_1d_html.php | 57 - .../examples/barcodes/example_1d_png.php | 56 - .../examples/barcodes/example_1d_svg.php | 57 - .../examples/barcodes/example_1d_svgi.php | 57 - .../barcodes/example_2d_datamatrix_html.php | 57 - .../barcodes/example_2d_datamatrix_png.php | 56 - .../barcodes/example_2d_datamatrix_svg.php | 57 - .../barcodes/example_2d_datamatrix_svgi.php | 57 - .../barcodes/example_2d_pdf417_html.php | 57 - .../barcodes/example_2d_pdf417_png.php | 56 - .../barcodes/example_2d_pdf417_svg.php | 57 - .../barcodes/example_2d_pdf417_svgi.php | 57 - .../barcodes/example_2d_qrcode_html.php | 56 - .../barcodes/example_2d_qrcode_png.php | 56 - .../barcodes/example_2d_qrcode_svg.php | 56 - .../barcodes/example_2d_qrcode_svgi.php | 56 - .../barcodes/tcpdf_barcodes_1d_include.php | 46 - .../barcodes/tcpdf_barcodes_2d_include.php | 46 - .../examples/config/tcpdf_config_alt.php | 230 -- .../tcpdf/examples/data/cert/tcpdf.crt | 40 - .../tcpdf/examples/data/cert/tcpdf.fdf | Bin 1286 -> 0 bytes .../tcpdf/examples/data/cert/tcpdf.p12 | Bin 1749 -> 0 bytes .../tcpdf/examples/data/chapter_demo_1.txt | 19 - .../tcpdf/examples/data/chapter_demo_2.txt | 23 - .../tcpdf/examples/data/table_data_demo.txt | 15 - .../tcpdf/examples/data/utf8test.txt | 128 - .../tecnickcom/tcpdf/examples/example_001.php | 110 - .../tecnickcom/tcpdf/examples/example_002.php | 91 - .../tecnickcom/tcpdf/examples/example_003.php | 122 - .../tecnickcom/tcpdf/examples/example_004.php | 123 - .../tecnickcom/tcpdf/examples/example_005.php | 160 - .../tecnickcom/tcpdf/examples/example_006.php | 347 --- .../tecnickcom/tcpdf/examples/example_007.php | 117 - .../tecnickcom/tcpdf/examples/example_008.php | 99 - .../tecnickcom/tcpdf/examples/example_009.php | 148 - .../tecnickcom/tcpdf/examples/example_010.php | 152 - .../tecnickcom/tcpdf/examples/example_011.php | 141 - .../tecnickcom/tcpdf/examples/example_012.pdf | Bin 13214 -> 0 bytes .../tecnickcom/tcpdf/examples/example_012.php | 207 -- .../tecnickcom/tcpdf/examples/example_013.php | 231 -- .../tecnickcom/tcpdf/examples/example_014.php | 197 -- .../tecnickcom/tcpdf/examples/example_015.php | 164 -- .../tecnickcom/tcpdf/examples/example_016.php | 136 - .../tecnickcom/tcpdf/examples/example_017.php | 120 - .../tecnickcom/tcpdf/examples/example_018.php | 130 - .../tecnickcom/tcpdf/examples/example_019.php | 100 - .../tecnickcom/tcpdf/examples/example_020.php | 149 - .../tecnickcom/tcpdf/examples/example_021.php | 93 - .../tecnickcom/tcpdf/examples/example_022.php | 148 - .../tecnickcom/tcpdf/examples/example_023.php | 115 - .../tecnickcom/tcpdf/examples/example_024.php | 142 - .../tecnickcom/tcpdf/examples/example_025.php | 120 - .../tecnickcom/tcpdf/examples/example_026.php | 146 - .../tecnickcom/tcpdf/examples/example_027.php | 420 --- .../tecnickcom/tcpdf/examples/example_028.php | 140 - .../tecnickcom/tcpdf/examples/example_029.php | 126 - .../tecnickcom/tcpdf/examples/example_030.php | 190 -- .../tecnickcom/tcpdf/examples/example_031.php | 105 - .../tecnickcom/tcpdf/examples/example_032.php | 93 - .../tecnickcom/tcpdf/examples/example_033.php | 107 - .../tecnickcom/tcpdf/examples/example_034.php | 98 - .../tecnickcom/tcpdf/examples/example_035.php | 113 - .../tecnickcom/tcpdf/examples/example_036.php | 91 - .../tecnickcom/tcpdf/examples/example_037.php | 149 - .../tecnickcom/tcpdf/examples/example_038.php | 94 - .../tecnickcom/tcpdf/examples/example_039.php | 106 - .../tecnickcom/tcpdf/examples/example_040.php | 118 - .../tecnickcom/tcpdf/examples/example_041.php | 93 - .../tecnickcom/tcpdf/examples/example_042.php | 104 - .../tecnickcom/tcpdf/examples/example_043.php | 87 - .../tecnickcom/tcpdf/examples/example_044.php | 130 - .../tecnickcom/tcpdf/examples/example_045.php | 143 - .../tecnickcom/tcpdf/examples/example_046.php | 125 - .../tecnickcom/tcpdf/examples/example_047.php | 119 - .../tecnickcom/tcpdf/examples/example_048.php | 316 -- .../tecnickcom/tcpdf/examples/example_049.php | 128 - .../tecnickcom/tcpdf/examples/example_050.php | 212 -- .../tecnickcom/tcpdf/examples/example_051.php | 148 - .../tecnickcom/tcpdf/examples/example_052.php | 123 - .../tecnickcom/tcpdf/examples/example_053.php | 110 - .../tecnickcom/tcpdf/examples/example_054.php | 131 - .../tecnickcom/tcpdf/examples/example_055.php | 118 - .../tecnickcom/tcpdf/examples/example_056.php | 135 - .../tecnickcom/tcpdf/examples/example_057.php | 270 -- .../tecnickcom/tcpdf/examples/example_058.php | 97 - .../tecnickcom/tcpdf/examples/example_059.php | 193 -- .../tecnickcom/tcpdf/examples/example_060.php | 110 - .../tecnickcom/tcpdf/examples/example_061.php | 267 -- .../tecnickcom/tcpdf/examples/example_062.php | 142 - .../tecnickcom/tcpdf/examples/example_063.php | 133 - .../tecnickcom/tcpdf/examples/example_064.php | 178 -- .../tecnickcom/tcpdf/examples/example_065.php | 100 - .../tecnickcom/tcpdf/examples/example_066.php | 88 - .../tecnickcom/tcpdf/examples/example_067.php | 223 -- .../tcpdf/examples/images/_blank.png | Bin 137 -> 0 bytes .../tcpdf/examples/images/alpha.png | Bin 23862 -> 0 bytes .../tcpdf/examples/images/image_demo.jpg | Bin 573845 -> 0 bytes .../examples/images/image_with_alpha.png | Bin 243788 -> 0 bytes .../tecnickcom/tcpdf/examples/images/img.png | Bin 194711 -> 0 bytes .../tcpdf/examples/images/logo_example.gif | Bin 21022 -> 0 bytes .../tcpdf/examples/images/logo_example.jpg | Bin 32732 -> 0 bytes .../tcpdf/examples/images/logo_example.png | Bin 17829 -> 0 bytes .../tcpdf/examples/images/tcpdf_box.ai | 214 -- .../tcpdf/examples/images/tcpdf_box.svg | 69 - .../tcpdf/examples/images/tcpdf_cell.png | Bin 38367 -> 0 bytes .../tcpdf/examples/images/tcpdf_signature.png | Bin 67764 -> 0 bytes .../tcpdf/examples/images/testsvg.svg | 328 --- .../tecnickcom/tcpdf/examples/images/tux.svg | 1487 ---------- vendor/tecnickcom/tcpdf/examples/index.php | 117 - vendor/tecnickcom/tcpdf/examples/lang/afr.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/ara.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/aze.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/bel.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/bra.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/bul.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/cat.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/ces.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/chi.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/cym.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/dan.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/eng.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/est.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/eus.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/far.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/fra.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/ger.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/gle.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/glg.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/hat.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/heb.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/hrv.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/hun.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/hye.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/ind.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/ita.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/jpn.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/kat.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/kor.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/mkd.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/mlt.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/msa.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/nld.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/nob.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/pol.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/por.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/ron.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/rus.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/slv.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/spa.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/sqi.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/srp.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/swa.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/swe.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/ukr.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/urd.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/yid.php | 44 - vendor/tecnickcom/tcpdf/examples/lang/zho.php | 44 - .../tcpdf/examples/tcpdf_include.php | 49 - .../tecnickcom/tcpdf/include/tcpdf_colors.php | 24 +- .../tecnickcom/tcpdf/include/tcpdf_fonts.php | 49 +- .../tecnickcom/tcpdf/include/tcpdf_static.php | 102 +- vendor/tecnickcom/tcpdf/tcpdf.php | 223 +- vendor/tecnickcom/tcpdf/tcpdf_autoconfig.php | 16 +- vendor/tecnickcom/tcpdf/tcpdf_import.php | 104 - vendor/tecnickcom/tcpdf/tcpdf_parser.php | 815 ------ .../.github/ISSUE_TEMPLATE/bug_report.md | 21 - .../.github/ISSUE_TEMPLATE/feature_request.md | 14 - .../.github/ISSUE_TEMPLATE/other-issue.md | 10 - .../.github/workflows/acceptance.yml | 113 - vendor/textalk/websocket/.gitignore | 6 - vendor/textalk/websocket/Makefile | 32 - vendor/textalk/websocket/README.md | 67 - vendor/textalk/websocket/codestandard.xml | 10 - vendor/textalk/websocket/composer.json | 34 - vendor/textalk/websocket/docs/Changelog.md | 143 - vendor/textalk/websocket/docs/Client.md | 137 - vendor/textalk/websocket/docs/Contributing.md | 44 - vendor/textalk/websocket/docs/Examples.md | 98 - vendor/textalk/websocket/docs/Message.md | 60 - vendor/textalk/websocket/docs/Server.md | 136 - .../textalk/websocket/examples/echoserver.php | 87 - .../websocket/examples/random_client.php | 94 - .../websocket/examples/random_server.php | 93 - vendor/textalk/websocket/examples/send.php | 51 - .../websocket/lib/BadOpcodeException.php | 7 - .../textalk/websocket/lib/BadUriException.php | 7 - vendor/textalk/websocket/lib/Base.php | 486 ---- vendor/textalk/websocket/lib/Client.php | 226 -- .../websocket/lib/ConnectionException.php | 26 - vendor/textalk/websocket/lib/Exception.php | 7 - .../textalk/websocket/lib/Message/Binary.php | 8 - .../textalk/websocket/lib/Message/Close.php | 8 - .../textalk/websocket/lib/Message/Factory.php | 25 - .../textalk/websocket/lib/Message/Message.php | 53 - vendor/textalk/websocket/lib/Message/Ping.php | 8 - vendor/textalk/websocket/lib/Message/Pong.php | 8 - vendor/textalk/websocket/lib/Message/Text.php | 8 - vendor/textalk/websocket/lib/Server.php | 176 -- .../websocket/lib/TimeoutException.php | 7 - vendor/textalk/websocket/phpunit.xml.dist | 14 - vendor/textalk/websocket/tests/ClientTest.php | 458 --- .../textalk/websocket/tests/ExceptionTest.php | 51 - .../textalk/websocket/tests/MessageTest.php | 60 - vendor/textalk/websocket/tests/README.md | 29 - vendor/textalk/websocket/tests/ServerTest.php | 447 --- vendor/textalk/websocket/tests/bootstrap.php | 6 - .../textalk/websocket/tests/mock/EchoLog.php | 34 - .../websocket/tests/mock/MockSocket.php | 78 - .../websocket/tests/mock/mock-socket.php | 83 - .../websocket/tests/mock/payload.128.txt | 5 - .../websocket/tests/mock/payload.65536.txt | 1682 ----------- .../websocket/tests/scripts/client.close.json | 76 - .../tests/scripts/client.connect-authed.json | 58 - .../scripts/client.connect-bad-context.json | 7 - .../tests/scripts/client.connect-context.json | 58 - .../tests/scripts/client.connect-error.json | 23 - .../scripts/client.connect-extended.json | 58 - .../tests/scripts/client.connect-failed.json | 19 - .../client.connect-handshake-error.json | 65 - .../tests/scripts/client.connect-headers.json | 67 - .../scripts/client.connect-invalid-key.json | 49 - .../client.connect-invalid-upgrade.json | 49 - .../scripts/client.connect-persistent.json | 94 - .../tests/scripts/client.connect-timeout.json | 58 - .../tests/scripts/client.connect.json | 58 - .../tests/scripts/client.destruct.json | 23 - .../tests/scripts/client.reconnect.json | 99 - .../websocket/tests/scripts/close-remote.json | 55 - .../tests/scripts/config-timeout.json | 24 - .../websocket/tests/scripts/ping-pong.json | 150 - .../tests/scripts/receive-bad-opcode.json | 18 - .../tests/scripts/receive-broken-read.json | 58 - .../tests/scripts/receive-client-timeout.json | 50 - .../tests/scripts/receive-empty-read.json | 58 - .../tests/scripts/receive-fragmentation.json | 126 - .../tests/scripts/send-bad-opcode.json | 9 - .../tests/scripts/send-broken-write.json | 43 - .../tests/scripts/send-convenicance.json | 86 - .../tests/scripts/send-failed-write.json | 43 - .../tests/scripts/send-receive-128.json | 50 - .../tests/scripts/send-receive-65536.json | 113 - .../scripts/send-receive-multi-fragment.json | 112 - .../websocket/tests/scripts/send-receive.json | 50 - .../tests/scripts/server.accept-destruct.json | 315 -- .../scripts/server.accept-error-connect.json | 18 - .../scripts/server.accept-failed-connect.json | 14 - .../scripts/server.accept-failed-http.json | 265 -- .../scripts/server.accept-failed-ws-key.json | 265 -- .../tests/scripts/server.accept-timeout.json | 289 -- .../tests/scripts/server.accept.json | 287 -- .../websocket/tests/scripts/server.close.json | 70 - .../server.construct-error-socket-server.json | 28 - ...server.construct-failed-socket-server.json | 20 - .../tests/scripts/server.construct.json | 11 - 497 files changed, 13269 insertions(+), 38342 deletions(-) delete mode 100644 vendor/cweagans/composer-patches/.gitignore delete mode 100644 vendor/cweagans/composer-patches/LICENSE.md delete mode 100644 vendor/cweagans/composer-patches/README.md delete mode 100644 vendor/cweagans/composer-patches/composer.json delete mode 100644 vendor/cweagans/composer-patches/composer.lock delete mode 100644 vendor/cweagans/composer-patches/phpunit.xml.dist delete mode 100644 vendor/cweagans/composer-patches/src/PatchEvent.php delete mode 100644 vendor/cweagans/composer-patches/src/PatchEvents.php delete mode 100644 vendor/cweagans/composer-patches/src/Patches.php delete mode 100644 vendor/cweagans/composer-patches/tests/PatchEventTest.php delete mode 100644 vendor/iio/libmergepdf/.github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 vendor/iio/libmergepdf/PATCHES.txt delete mode 100644 vendor/iio/libmergepdf/composer.json delete mode 100644 vendor/iio/libmergepdf/src/Driver/DefaultDriver.php delete mode 100644 vendor/iio/libmergepdf/src/Driver/DriverInterface.php delete mode 100644 vendor/iio/libmergepdf/src/Driver/Fpdi2Driver.php delete mode 100644 vendor/iio/libmergepdf/src/Driver/TcpdiDriver.php delete mode 100644 vendor/iio/libmergepdf/src/Exception.php delete mode 100644 vendor/iio/libmergepdf/src/Merger.php delete mode 100644 vendor/iio/libmergepdf/src/Pages.php delete mode 100644 vendor/iio/libmergepdf/src/PagesInterface.php delete mode 100644 vendor/iio/libmergepdf/src/Source/FileSource.php delete mode 100644 vendor/iio/libmergepdf/src/Source/RawSource.php delete mode 100644 vendor/iio/libmergepdf/src/Source/SourceInterface.php delete mode 100644 vendor/iio/libmergepdf/tcpdi/LICENSE delete mode 100644 vendor/iio/libmergepdf/tcpdi/README.md create mode 100644 vendor/karriere/pdf-merge/.github/workflows/ci.yml create mode 100644 vendor/karriere/pdf-merge/.gitignore create mode 100644 vendor/karriere/pdf-merge/CHANGELOG.md create mode 100644 vendor/karriere/pdf-merge/LICENSE create mode 100644 vendor/karriere/pdf-merge/README.md create mode 100644 vendor/karriere/pdf-merge/UPGRADE.md create mode 100644 vendor/karriere/pdf-merge/composer.json create mode 100644 vendor/karriere/pdf-merge/phpstan.neon create mode 100644 vendor/karriere/pdf-merge/phpunit.xml create mode 100644 vendor/karriere/pdf-merge/pint.json create mode 100644 vendor/karriere/pdf-merge/src/Config/FooterConfig.php create mode 100644 vendor/karriere/pdf-merge/src/Config/HeaderConfig.php create mode 100644 vendor/karriere/pdf-merge/src/Config/RGB.php create mode 100644 vendor/karriere/pdf-merge/src/Exceptions/FileNotFoundException.php create mode 100644 vendor/karriere/pdf-merge/src/Exceptions/NoFilesDefinedException.php create mode 100644 vendor/karriere/pdf-merge/src/PdfMerge.php rename vendor/{iio/libmergepdf/tcpdi => karriere/pdf-merge/tcpi}/fpdf_tpl.php (71%) rename vendor/{iio/libmergepdf/tcpdi => karriere/pdf-merge/tcpi}/tcpdi.php (80%) rename vendor/{iio/libmergepdf/tcpdi => karriere/pdf-merge/tcpi}/tcpdi_parser.php (87%) create mode 100644 vendor/karriere/pdf-merge/tests/PdfMergeTest.php create mode 100644 vendor/karriere/pdf-merge/tests/Pest.php create mode 100644 vendor/karriere/pdf-merge/tests/files/dummy.pdf create mode 100644 vendor/karriere/pdf-merge/tests/files/dummy_landscape.pdf create mode 100644 vendor/karriere/pdf-merge/tests/files/expected/output.pdf create mode 100644 vendor/karriere/pdf-merge/tests/files/expected/output_mixed_orientation.pdf create mode 100644 vendor/karriere/pdf-merge/tests/files/expected/output_with_header.pdf create mode 100644 vendor/karriere/pdf-merge/tests/files/expected/output_with_header_and_footer.pdf rename vendor/{tecnickcom/tcpdf/examples/images/tcpdf_logo.jpg => karriere/pdf-merge/tests/files/header_logo.jpg} (100%) create mode 100644 vendor/phrity/comparison/composer.json create mode 100644 vendor/phrity/comparison/src/Comparable.php create mode 100644 vendor/phrity/comparison/src/Comparator.php create mode 100644 vendor/phrity/comparison/src/ComparisonTrait.php create mode 100644 vendor/phrity/comparison/src/Equalable.php create mode 100644 vendor/phrity/comparison/src/IncomparableException.php create mode 100644 vendor/phrity/http/LICENSE create mode 100644 vendor/phrity/http/composer.json create mode 100644 vendor/phrity/http/docu.json create mode 100644 vendor/phrity/http/src/HttpFactory.php create mode 100644 vendor/phrity/net-stream/composer.json create mode 100644 vendor/phrity/net-stream/src/Context.php create mode 100644 vendor/phrity/net-stream/src/SocketClient.php create mode 100644 vendor/phrity/net-stream/src/SocketServer.php create mode 100644 vendor/phrity/net-stream/src/SocketStream.php create mode 100644 vendor/phrity/net-stream/src/Stream.php create mode 100644 vendor/phrity/net-stream/src/StreamCollection.php create mode 100644 vendor/phrity/net-stream/src/StreamException.php create mode 100644 vendor/phrity/net-stream/src/StreamFactory.php create mode 100644 vendor/phrity/net-uri/composer.json create mode 100644 vendor/phrity/net-uri/src/Uri.php create mode 100644 vendor/phrity/net-uri/src/UriFactory.php create mode 100644 vendor/phrity/util-errorhandler/composer.json create mode 100644 vendor/phrity/util-errorhandler/src/ErrorHandler.php rename vendor/{textalk/websocket/COPYING.md => phrity/websocket/LICENSE.md} (100%) create mode 100644 vendor/phrity/websocket/composer.json create mode 100644 vendor/phrity/websocket/docu.json create mode 100644 vendor/phrity/websocket/src/Client.php create mode 100644 vendor/phrity/websocket/src/Connection.php create mode 100644 vendor/phrity/websocket/src/Constant.php create mode 100644 vendor/phrity/websocket/src/Exception/BadOpcodeException.php create mode 100644 vendor/phrity/websocket/src/Exception/BadUriException.php create mode 100644 vendor/phrity/websocket/src/Exception/ClientException.php create mode 100644 vendor/phrity/websocket/src/Exception/CloseException.php create mode 100644 vendor/phrity/websocket/src/Exception/ConnectionClosedException.php create mode 100644 vendor/phrity/websocket/src/Exception/ConnectionFailureException.php create mode 100644 vendor/phrity/websocket/src/Exception/ConnectionLevelInterface.php create mode 100644 vendor/phrity/websocket/src/Exception/ConnectionTimeoutException.php create mode 100644 vendor/phrity/websocket/src/Exception/Exception.php create mode 100644 vendor/phrity/websocket/src/Exception/ExceptionInterface.php create mode 100644 vendor/phrity/websocket/src/Exception/HandshakeException.php create mode 100644 vendor/phrity/websocket/src/Exception/MessageLevelInterface.php create mode 100644 vendor/phrity/websocket/src/Exception/ReconnectException.php create mode 100644 vendor/phrity/websocket/src/Exception/ServerException.php create mode 100644 vendor/phrity/websocket/src/Frame/Frame.php create mode 100644 vendor/phrity/websocket/src/Frame/FrameHandler.php create mode 100644 vendor/phrity/websocket/src/Http/DefaultHttpFactory.php create mode 100644 vendor/phrity/websocket/src/Http/HttpHandler.php create mode 100644 vendor/phrity/websocket/src/Http/Message.php create mode 100644 vendor/phrity/websocket/src/Http/Request.php create mode 100644 vendor/phrity/websocket/src/Http/Response.php create mode 100644 vendor/phrity/websocket/src/Http/ServerRequest.php create mode 100644 vendor/phrity/websocket/src/Message/Binary.php create mode 100644 vendor/phrity/websocket/src/Message/Close.php create mode 100644 vendor/phrity/websocket/src/Message/Message.php create mode 100644 vendor/phrity/websocket/src/Message/MessageHandler.php create mode 100644 vendor/phrity/websocket/src/Message/Ping.php create mode 100644 vendor/phrity/websocket/src/Message/Pong.php create mode 100644 vendor/phrity/websocket/src/Message/Text.php create mode 100644 vendor/phrity/websocket/src/Middleware/Callback.php create mode 100644 vendor/phrity/websocket/src/Middleware/CloseHandler.php create mode 100644 vendor/phrity/websocket/src/Middleware/CompressionExtension.php create mode 100644 vendor/phrity/websocket/src/Middleware/CompressionExtension/CompressorInterface.php create mode 100644 vendor/phrity/websocket/src/Middleware/CompressionExtension/DeflateCompressor.php create mode 100644 vendor/phrity/websocket/src/Middleware/FollowRedirect.php create mode 100644 vendor/phrity/websocket/src/Middleware/MiddlewareHandler.php create mode 100644 vendor/phrity/websocket/src/Middleware/MiddlewareInterface.php create mode 100644 vendor/phrity/websocket/src/Middleware/PingInterval.php create mode 100644 vendor/phrity/websocket/src/Middleware/PingResponder.php create mode 100644 vendor/phrity/websocket/src/Middleware/ProcessHttpIncomingInterface.php create mode 100644 vendor/phrity/websocket/src/Middleware/ProcessHttpOutgoingInterface.php create mode 100644 vendor/phrity/websocket/src/Middleware/ProcessHttpStack.php create mode 100644 vendor/phrity/websocket/src/Middleware/ProcessIncomingInterface.php create mode 100644 vendor/phrity/websocket/src/Middleware/ProcessOutgoingInterface.php create mode 100644 vendor/phrity/websocket/src/Middleware/ProcessStack.php create mode 100644 vendor/phrity/websocket/src/Middleware/ProcessTickInterface.php create mode 100644 vendor/phrity/websocket/src/Middleware/ProcessTickStack.php create mode 100644 vendor/phrity/websocket/src/Middleware/SubprotocolNegotiation.php create mode 100644 vendor/phrity/websocket/src/Server.php create mode 100644 vendor/phrity/websocket/src/Trait/ListenerTrait.php create mode 100644 vendor/phrity/websocket/src/Trait/LoggerAwareTrait.php create mode 100644 vendor/phrity/websocket/src/Trait/OpcodeTrait.php create mode 100644 vendor/phrity/websocket/src/Trait/SendMethodsTrait.php create mode 100644 vendor/phrity/websocket/src/Trait/StringableTrait.php create mode 100644 vendor/psr/http-factory/LICENSE create mode 100644 vendor/psr/http-factory/README.md create mode 100644 vendor/psr/http-factory/composer.json create mode 100644 vendor/psr/http-factory/src/RequestFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/ResponseFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/ServerRequestFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/StreamFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/UploadedFileFactoryInterface.php create mode 100644 vendor/psr/http-factory/src/UriFactoryInterface.php create mode 100644 vendor/psr/http-message/CHANGELOG.md rename vendor/{setasign/fpdi/LICENSE.txt => psr/http-message/LICENSE} (85%) create mode 100644 vendor/psr/http-message/README.md create mode 100644 vendor/psr/http-message/composer.json create mode 100644 vendor/psr/http-message/docs/PSR7-Interfaces.md create mode 100644 vendor/psr/http-message/docs/PSR7-Usage.md create mode 100644 vendor/psr/http-message/src/MessageInterface.php create mode 100644 vendor/psr/http-message/src/RequestInterface.php create mode 100644 vendor/psr/http-message/src/ResponseInterface.php create mode 100644 vendor/psr/http-message/src/ServerRequestInterface.php create mode 100644 vendor/psr/http-message/src/StreamInterface.php create mode 100644 vendor/psr/http-message/src/UploadedFileInterface.php create mode 100644 vendor/psr/http-message/src/UriInterface.php delete mode 100644 vendor/psr/log/Psr/Log/AbstractLogger.php delete mode 100644 vendor/psr/log/Psr/Log/Test/DummyTest.php delete mode 100644 vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php delete mode 100644 vendor/psr/log/Psr/Log/Test/TestLogger.php create mode 100644 vendor/psr/log/src/AbstractLogger.php rename vendor/psr/log/{Psr/Log => src}/InvalidArgumentException.php (100%) rename vendor/psr/log/{Psr/Log => src}/LogLevel.php (100%) rename vendor/psr/log/{Psr/Log => src}/LoggerAwareInterface.php (56%) rename vendor/psr/log/{Psr/Log => src}/LoggerAwareTrait.php (59%) rename vendor/psr/log/{Psr/Log => src}/LoggerInterface.php (63%) rename vendor/psr/log/{Psr/Log => src}/LoggerTrait.php (57%) rename vendor/psr/log/{Psr/Log => src}/NullLogger.php (74%) delete mode 100644 vendor/setasign/fpdi/README.md delete mode 100644 vendor/setasign/fpdi/SECURITY.md delete mode 100644 vendor/setasign/fpdi/composer.json delete mode 100644 vendor/setasign/fpdi/src/FpdfTpl.php delete mode 100644 vendor/setasign/fpdi/src/FpdfTplTrait.php delete mode 100644 vendor/setasign/fpdi/src/FpdfTrait.php delete mode 100644 vendor/setasign/fpdi/src/Fpdi.php delete mode 100644 vendor/setasign/fpdi/src/FpdiException.php delete mode 100644 vendor/setasign/fpdi/src/FpdiTrait.php delete mode 100644 vendor/setasign/fpdi/src/GraphicsState.php delete mode 100644 vendor/setasign/fpdi/src/Math/Matrix.php delete mode 100644 vendor/setasign/fpdi/src/Math/Vector.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/CrossReference/AbstractReader.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/CrossReference/CrossReference.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/CrossReference/CrossReferenceException.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/CrossReference/FixedReader.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/CrossReference/LineReader.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/CrossReference/ReaderInterface.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Filter/Ascii85.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Filter/Ascii85Exception.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Filter/AsciiHex.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Filter/FilterException.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Filter/FilterInterface.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Filter/Flate.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Filter/FlateException.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Filter/Lzw.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Filter/LzwException.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/PdfParser.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/PdfParserException.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/StreamReader.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Tokenizer.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfArray.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfBoolean.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfDictionary.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfHexString.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfIndirectObject.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfIndirectObjectReference.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfName.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfNull.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfNumeric.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfStream.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfString.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfToken.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfType.php delete mode 100644 vendor/setasign/fpdi/src/PdfParser/Type/PdfTypeException.php delete mode 100644 vendor/setasign/fpdi/src/PdfReader/DataStructure/Rectangle.php delete mode 100644 vendor/setasign/fpdi/src/PdfReader/Page.php delete mode 100644 vendor/setasign/fpdi/src/PdfReader/PageBoundaries.php delete mode 100644 vendor/setasign/fpdi/src/PdfReader/PdfReader.php delete mode 100644 vendor/setasign/fpdi/src/PdfReader/PdfReaderException.php delete mode 100644 vendor/setasign/fpdi/src/Tcpdf/Fpdi.php delete mode 100644 vendor/setasign/fpdi/src/TcpdfFpdi.php delete mode 100644 vendor/setasign/fpdi/src/Tfpdf/FpdfTpl.php delete mode 100644 vendor/setasign/fpdi/src/Tfpdf/Fpdi.php delete mode 100644 vendor/setasign/fpdi/src/autoload.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_html.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_png.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_svg.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_svgi.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_html.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_png.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_svg.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_svgi.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_html.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_png.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_svg.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_svgi.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_html.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_png.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_svg.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_svgi.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/tcpdf_barcodes_1d_include.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/barcodes/tcpdf_barcodes_2d_include.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/config/tcpdf_config_alt.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/data/cert/tcpdf.crt delete mode 100644 vendor/tecnickcom/tcpdf/examples/data/cert/tcpdf.fdf delete mode 100644 vendor/tecnickcom/tcpdf/examples/data/cert/tcpdf.p12 delete mode 100644 vendor/tecnickcom/tcpdf/examples/data/chapter_demo_1.txt delete mode 100644 vendor/tecnickcom/tcpdf/examples/data/chapter_demo_2.txt delete mode 100644 vendor/tecnickcom/tcpdf/examples/data/table_data_demo.txt delete mode 100644 vendor/tecnickcom/tcpdf/examples/data/utf8test.txt delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_001.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_002.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_003.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_004.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_005.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_006.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_007.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_008.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_009.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_010.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_011.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_012.pdf delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_012.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_013.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_014.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_015.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_016.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_017.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_018.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_019.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_020.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_021.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_022.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_023.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_024.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_025.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_026.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_027.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_028.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_029.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_030.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_031.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_032.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_033.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_034.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_035.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_036.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_037.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_038.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_039.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_040.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_041.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_042.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_043.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_044.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_045.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_046.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_047.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_048.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_049.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_050.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_051.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_052.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_053.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_054.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_055.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_056.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_057.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_058.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_059.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_060.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_061.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_062.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_063.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_064.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_065.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_066.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/example_067.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/_blank.png delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/alpha.png delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/image_demo.jpg delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/image_with_alpha.png delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/img.png delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/logo_example.gif delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/logo_example.jpg delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/logo_example.png delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/tcpdf_box.ai delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/tcpdf_box.svg delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/tcpdf_cell.png delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/tcpdf_signature.png delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/testsvg.svg delete mode 100644 vendor/tecnickcom/tcpdf/examples/images/tux.svg delete mode 100644 vendor/tecnickcom/tcpdf/examples/index.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/afr.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/ara.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/aze.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/bel.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/bra.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/bul.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/cat.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/ces.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/chi.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/cym.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/dan.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/eng.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/est.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/eus.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/far.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/fra.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/ger.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/gle.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/glg.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/hat.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/heb.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/hrv.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/hun.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/hye.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/ind.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/ita.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/jpn.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/kat.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/kor.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/mkd.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/mlt.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/msa.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/nld.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/nob.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/pol.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/por.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/ron.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/rus.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/slv.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/spa.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/sqi.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/srp.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/swa.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/swe.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/ukr.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/urd.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/yid.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/lang/zho.php delete mode 100644 vendor/tecnickcom/tcpdf/examples/tcpdf_include.php delete mode 100644 vendor/tecnickcom/tcpdf/tcpdf_import.php delete mode 100644 vendor/tecnickcom/tcpdf/tcpdf_parser.php delete mode 100644 vendor/textalk/websocket/.github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 vendor/textalk/websocket/.github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 vendor/textalk/websocket/.github/ISSUE_TEMPLATE/other-issue.md delete mode 100644 vendor/textalk/websocket/.github/workflows/acceptance.yml delete mode 100644 vendor/textalk/websocket/.gitignore delete mode 100644 vendor/textalk/websocket/Makefile delete mode 100644 vendor/textalk/websocket/README.md delete mode 100644 vendor/textalk/websocket/codestandard.xml delete mode 100644 vendor/textalk/websocket/composer.json delete mode 100644 vendor/textalk/websocket/docs/Changelog.md delete mode 100644 vendor/textalk/websocket/docs/Client.md delete mode 100644 vendor/textalk/websocket/docs/Contributing.md delete mode 100644 vendor/textalk/websocket/docs/Examples.md delete mode 100644 vendor/textalk/websocket/docs/Message.md delete mode 100644 vendor/textalk/websocket/docs/Server.md delete mode 100644 vendor/textalk/websocket/examples/echoserver.php delete mode 100644 vendor/textalk/websocket/examples/random_client.php delete mode 100644 vendor/textalk/websocket/examples/random_server.php delete mode 100644 vendor/textalk/websocket/examples/send.php delete mode 100644 vendor/textalk/websocket/lib/BadOpcodeException.php delete mode 100644 vendor/textalk/websocket/lib/BadUriException.php delete mode 100644 vendor/textalk/websocket/lib/Base.php delete mode 100644 vendor/textalk/websocket/lib/Client.php delete mode 100644 vendor/textalk/websocket/lib/ConnectionException.php delete mode 100644 vendor/textalk/websocket/lib/Exception.php delete mode 100644 vendor/textalk/websocket/lib/Message/Binary.php delete mode 100644 vendor/textalk/websocket/lib/Message/Close.php delete mode 100644 vendor/textalk/websocket/lib/Message/Factory.php delete mode 100644 vendor/textalk/websocket/lib/Message/Message.php delete mode 100644 vendor/textalk/websocket/lib/Message/Ping.php delete mode 100644 vendor/textalk/websocket/lib/Message/Pong.php delete mode 100644 vendor/textalk/websocket/lib/Message/Text.php delete mode 100644 vendor/textalk/websocket/lib/Server.php delete mode 100644 vendor/textalk/websocket/lib/TimeoutException.php delete mode 100644 vendor/textalk/websocket/phpunit.xml.dist delete mode 100644 vendor/textalk/websocket/tests/ClientTest.php delete mode 100644 vendor/textalk/websocket/tests/ExceptionTest.php delete mode 100644 vendor/textalk/websocket/tests/MessageTest.php delete mode 100644 vendor/textalk/websocket/tests/README.md delete mode 100644 vendor/textalk/websocket/tests/ServerTest.php delete mode 100644 vendor/textalk/websocket/tests/bootstrap.php delete mode 100644 vendor/textalk/websocket/tests/mock/EchoLog.php delete mode 100644 vendor/textalk/websocket/tests/mock/MockSocket.php delete mode 100644 vendor/textalk/websocket/tests/mock/mock-socket.php delete mode 100644 vendor/textalk/websocket/tests/mock/payload.128.txt delete mode 100644 vendor/textalk/websocket/tests/mock/payload.65536.txt delete mode 100644 vendor/textalk/websocket/tests/scripts/client.close.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-authed.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-bad-context.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-context.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-error.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-extended.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-failed.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-handshake-error.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-headers.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-invalid-key.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-invalid-upgrade.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-persistent.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect-timeout.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.connect.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.destruct.json delete mode 100644 vendor/textalk/websocket/tests/scripts/client.reconnect.json delete mode 100644 vendor/textalk/websocket/tests/scripts/close-remote.json delete mode 100644 vendor/textalk/websocket/tests/scripts/config-timeout.json delete mode 100644 vendor/textalk/websocket/tests/scripts/ping-pong.json delete mode 100644 vendor/textalk/websocket/tests/scripts/receive-bad-opcode.json delete mode 100644 vendor/textalk/websocket/tests/scripts/receive-broken-read.json delete mode 100644 vendor/textalk/websocket/tests/scripts/receive-client-timeout.json delete mode 100644 vendor/textalk/websocket/tests/scripts/receive-empty-read.json delete mode 100644 vendor/textalk/websocket/tests/scripts/receive-fragmentation.json delete mode 100644 vendor/textalk/websocket/tests/scripts/send-bad-opcode.json delete mode 100644 vendor/textalk/websocket/tests/scripts/send-broken-write.json delete mode 100644 vendor/textalk/websocket/tests/scripts/send-convenicance.json delete mode 100644 vendor/textalk/websocket/tests/scripts/send-failed-write.json delete mode 100644 vendor/textalk/websocket/tests/scripts/send-receive-128.json delete mode 100644 vendor/textalk/websocket/tests/scripts/send-receive-65536.json delete mode 100644 vendor/textalk/websocket/tests/scripts/send-receive-multi-fragment.json delete mode 100644 vendor/textalk/websocket/tests/scripts/send-receive.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.accept-destruct.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.accept-error-connect.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.accept-failed-connect.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.accept-failed-http.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.accept-failed-ws-key.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.accept-timeout.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.accept.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.close.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.construct-error-socket-server.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.construct-failed-socket-server.json delete mode 100644 vendor/textalk/websocket/tests/scripts/server.construct.json diff --git a/composer.json b/composer.json index 75694ad..6e9b396 100644 --- a/composer.json +++ b/composer.json @@ -1,24 +1,10 @@ { "require": { - "textalk/websocket": "^1.5", - "iio/libmergepdf": "^4.0", - "cweagans/composer-patches": "^1.7" + "php": ">=8.2", + "phrity/websocket": "^3.6", + "karriere/pdf-merge": "^3.3" }, "config": { - "sort-packages": true, - "platform": { - "php": "7.2.9" - }, - "allow-plugins": { - "cweagans/composer-patches": true - } - }, - "extra": { - "composer-exit-on-patch-failure": true, - "patches": { - "iio/libmergepdf": { - "Add PHP 8.2 support": "patches/iio-libmergepdf-support-php82.patch" - } - } + "sort-packages": true } } diff --git a/composer.lock b/composer.lock index 8de3495..a24e8f2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,140 +4,444 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c7d83fbb2e2e3ef3a2d8ac90883d26d6", + "content-hash": "5cffa2096256d965b5bbedcd5a7b515b", "packages": [ { - "name": "cweagans/composer-patches", - "version": "1.7.3", + "name": "karriere/pdf-merge", + "version": "v3.3.1", "source": { "type": "git", - "url": "https://github.com/cweagans/composer-patches.git", - "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db" + "url": "https://github.com/karriereat/pdf-merge.git", + "reference": "d524a9d261ea96b18bbbab685b5710a7a0fe3a6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweagans/composer-patches/zipball/e190d4466fe2b103a55467dfa83fc2fecfcaf2db", - "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "url": "https://api.github.com/repos/karriereat/pdf-merge/zipball/d524a9d261ea96b18bbbab685b5710a7a0fe3a6e", + "reference": "d524a9d261ea96b18bbbab685b5710a7a0fe3a6e", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3.0" + "php": "8.1.* | 8.2.* | 8.3.* | 8.4.*", + "tecnickcom/tcpdf": "^6.3" }, "require-dev": { - "composer/composer": "~1.0 || ~2.0", - "phpunit/phpunit": "~4.6" + "laravel/pint": "^1.5 | ^1.6", + "pestphp/pest": "^1.22", + "phpstan/phpstan": "^1.10" }, - "type": "composer-plugin", - "extra": { - "class": "cweagans\\Composer\\Patches" + "type": "library", + "autoload": { + "psr-4": { + "Karriere\\PdfMerge\\": "src/" + }, + "classmap": [ + "tcpi/fpdf_tpl.php", + "tcpi/tcpdi.php", + "tcpi/tcpdi_parser.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Alexander Lentner", + "email": "alexander.lentner@karriere.at", + "role": "Maintainer" + } + ], + "description": "A wrapper for the TCPDF class that provides an elegant API for merging PDFs", + "keywords": [ + "merge", + "pdf" + ], + "support": { + "issues": "https://github.com/karriereat/pdf-merge/issues", + "source": "https://github.com/karriereat/pdf-merge/tree/v3.3.1" + }, + "time": "2025-02-19T11:02:10+00:00" + }, + { + "name": "phrity/comparison", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-comparison.git", + "reference": "aedd44d59db08de7d6c31812d1490c22aab35c92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-comparison/zipball/aedd44d59db08de7d6c31812d1490c22aab35c92", + "reference": "aedd44d59db08de7d6c31812d1490c22aab35c92", + "shasum": "" }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", "autoload": { "psr-4": { - "cweagans\\Composer\\": "src" + "Phrity\\Comparison\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Cameron Eagans", - "email": "me@cweagans.net" + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" } ], - "description": "Provides a way to patch Composer packages.", + "description": "Interfaces and helper trait for comparing objects. Comparator for sort and filter applications.", + "homepage": "https://phrity.sirn.se/comparison", + "keywords": [ + "comparable", + "comparator", + "comparison", + "equalable", + "filter", + "sort" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-comparison/issues", + "source": "https://github.com/sirn-se/phrity-comparison/tree/1.4.0" + }, + "time": "2025-05-26T20:12:39+00:00" + }, + { + "name": "phrity/http", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-http.git", + "reference": "536e3e46e6220d171a59599ed1f4da9f6b6244fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-http/zipball/536e3e46e6220d171a59599ed1f4da9f6b6244fc", + "reference": "536e3e46e6220d171a59599ed1f4da9f6b6244fc", + "shasum": "" + }, + "require": { + "php": "^8.1", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 | ^2.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "robiningelbrecht/phpunit-coverage-tools": "^1.9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Phrity\\Http\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "Utilities and interfaces for handling HTTP.", + "homepage": "https://phrity.sirn.se/http", + "keywords": [ + "HTTP Factories", + "http", + "psr-17" + ], "support": { - "issues": "https://github.com/cweagans/composer-patches/issues", - "source": "https://github.com/cweagans/composer-patches/tree/1.7.3" + "issues": "https://github.com/sirn-se/phrity-http/issues", + "source": "https://github.com/sirn-se/phrity-http/tree/1.0.0" }, - "time": "2022-12-20T22:53:13+00:00" + "time": "2025-09-07T17:04:26+00:00" }, { - "name": "iio/libmergepdf", - "version": "4.0.4", + "name": "phrity/net-stream", + "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/hanneskod/libmergepdf.git", - "reference": "6613b978c08d00d559796ab510614243e4dd5dfb" + "url": "https://github.com/sirn-se/phrity-net-stream.git", + "reference": "c621bb3108a5a02bba64df2e5f0cd7ada02665b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hanneskod/libmergepdf/zipball/6613b978c08d00d559796ab510614243e4dd5dfb", - "reference": "6613b978c08d00d559796ab510614243e4dd5dfb", + "url": "https://api.github.com/repos/sirn-se/phrity-net-stream/zipball/c621bb3108a5a02bba64df2e5f0cd7ada02665b5", + "reference": "c621bb3108a5a02bba64df2e5f0cd7ada02665b5", "shasum": "" }, "require": { - "php": "^7.1||^8.0", - "setasign/fpdi": "^2", - "tecnickcom/tcpdf": "^6.2.22" + "php": "^8.1", + "phrity/util-errorhandler": "^1.1", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 | ^2.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "phrity/net-uri": "^2.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Phrity\\Net\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "Socket stream classes implementing PSR-7 Stream and PSR-17 StreamFactory", + "homepage": "https://phrity.sirn.se/net-stream", + "keywords": [ + "Socket", + "client", + "psr-17", + "psr-7", + "server", + "stream", + "stream factory" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-net-stream/issues", + "source": "https://github.com/sirn-se/phrity-net-stream/tree/2.3.1" + }, + "time": "2025-08-08T09:51:04+00:00" + }, + { + "name": "phrity/net-uri", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-net-uri.git", + "reference": "08de4cf07e439c4708f572249659f09198ac99f0" }, - "conflict": { - "rafikhaceb/tcpdi": "*", - "setasign/fpdf": "*" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-net-uri/zipball/08de4cf07e439c4708f572249659f09198ac99f0", + "reference": "08de4cf07e439c4708f572249659f09198ac99f0", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.1", + "phrity/comparison": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 | ^2.0" }, "require-dev": { - "phpunit/phpunit": "^7|^8", - "smalot/pdfparser": "~0.13" + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "phrity/util-errorhandler": "^1.1", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "ext-intl": "Enables IDN conversion for non-ASCII domains" }, "type": "library", "autoload": { "psr-4": { - "iio\\libmergepdf\\": "src/" - }, - "classmap": [ - "tcpdi/" - ] + "Phrity\\Net\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "WTFPL" + "MIT" ], "authors": [ { - "name": "Hannes Forsgård", - "email": "hannes.forsgard@fripost.org" + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" } ], - "description": "Library for merging multiple PDFs", - "homepage": "https://github.com/hanneskod/libmergepdf", + "description": "PSR-7 Uri and PSR-17 UriFactory implementation", + "homepage": "https://phrity.sirn.se/net-uri", "keywords": [ - "merge", - "pdf" + "psr-17", + "psr-7", + "uri", + "uri factory" ], "support": { - "issues": "https://github.com/hanneskod/libmergepdf/issues", - "source": "https://github.com/hanneskod/libmergepdf/tree/4.0.4" + "issues": "https://github.com/sirn-se/phrity-net-uri/issues", + "source": "https://github.com/sirn-se/phrity-net-uri/tree/2.2.0" }, - "time": "2020-12-07T12:18:49+00:00" + "time": "2025-05-25T13:05:13+00:00" }, { - "name": "psr/log", - "version": "1.1.4", + "name": "phrity/util-errorhandler", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "url": "https://github.com/sirn-se/phrity-util-errorhandler.git", + "reference": "9825f15ef9b4a93252ce53ca8962278832d834da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/sirn-se/phrity-util-errorhandler/zipball/9825f15ef9b4a93252ce53ca8962278832d834da", + "reference": "9825f15ef9b4a93252ce53ca8962278832d834da", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Phrity\\Util\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "Inline error handler; catch and resolve errors for code block.", + "homepage": "https://phrity.sirn.se/util-errorhandler", + "keywords": [ + "error", + "warning" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-util-errorhandler/issues", + "source": "https://github.com/sirn-se/phrity-util-errorhandler/tree/1.2.1" + }, + "time": "2025-08-08T09:48:45+00:00" + }, + { + "name": "phrity/websocket", + "version": "3.6.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/websocket-php.git", + "reference": "3f16b2564a230bbce716cccaff2f6156a60a8798" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/websocket-php/zipball/3f16b2564a230bbce716cccaff2f6156a60a8798", + "reference": "3f16b2564a230bbce716cccaff2f6156a60a8798", + "shasum": "" + }, + "require": { + "php": "^8.1", + "phrity/http": "^1.0", + "phrity/net-stream": "^2.3", + "phrity/net-uri": "^2.1", + "psr/http-message": "^1.1 | ^2.0", + "psr/log": "^1.0 | ^2.0 | ^3.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.0", + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "phrity/logger-console": "^1.0", + "phrity/net-mock": "^2.3", + "phrity/util-errorhandler": "^1.1", + "robiningelbrecht/phpunit-coverage-tools": "^1.9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "WebSocket client and server", + "homepage": "https://phrity.sirn.se/websocket", + "keywords": [ + "client", + "server", + "websocket" + ], + "support": { + "issues": "https://github.com/sirn-se/websocket-php/issues", + "source": "https://github.com/sirn-se/websocket-php/tree/3.6.0" + }, + "time": "2025-09-08T16:21:41+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -150,53 +454,48 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ - "log", + "factory", + "http", + "message", "psr", - "psr-3" + "psr-17", + "psr-7", + "request", + "response" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { - "name": "setasign/fpdi", - "version": "v2.6.4", + "name": "psr/http-message", + "version": "2.0", "source": { "type": "git", - "url": "https://github.com/Setasign/FPDI.git", - "reference": "4b53852fde2734ec6a07e458a085db627c60eada" + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDI/zipball/4b53852fde2734ec6a07e458a085db627c60eada", - "reference": "4b53852fde2734ec6a07e458a085db627c60eada", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "ext-zlib": "*", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "setasign/tfpdf": "<1.31" - }, - "require-dev": { - "phpunit/phpunit": "^7", - "setasign/fpdf": "~1.8.6", - "setasign/tfpdf": "~1.33", - "squizlabs/php_codesniffer": "^3.5", - "tecnickcom/tcpdf": "^6.8" - }, - "suggest": { - "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured." + "php": "^7.2 || ^8.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { - "setasign\\Fpdi\\": "src/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -205,47 +504,87 @@ ], "authors": [ { - "name": "Jan Slabon", - "email": "jan.slabon@setasign.com", - "homepage": "https://www.setasign.com" - }, - { - "name": "Maximilian Kresse", - "email": "maximilian.kresse@setasign.com", - "homepage": "https://www.setasign.com" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", - "homepage": "https://www.setasign.com/fpdi", + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "fpdf", - "fpdi", - "pdf" + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" ], "support": { - "issues": "https://github.com/Setasign/FPDI/issues", - "source": "https://github.com/Setasign/FPDI/tree/v2.6.4" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "funding": [ + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/setasign/fpdi", - "type": "tidelift" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "time": "2025-08-05T09:57:14+00:00" + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" }, { "name": "tecnickcom/tcpdf", - "version": "6.8.0", + "version": "6.10.0", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "14ffa0e308f5634aa2489568b4b90b24073b6731" + "reference": "ca5b6de294512145db96bcbc94e61696599c391d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/14ffa0e308f5634aa2489568b4b90b24073b6731", - "reference": "14ffa0e308f5634aa2489568b4b90b24073b6731", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/ca5b6de294512145db96bcbc94e61696599c391d", + "reference": "ca5b6de294512145db96bcbc94e61696599c391d", "shasum": "" }, "require": { @@ -258,8 +597,6 @@ "config", "include", "tcpdf.php", - "tcpdf_parser.php", - "tcpdf_import.php", "tcpdf_barcodes_1d.php", "tcpdf_barcodes_2d.php", "include/tcpdf_colors.php", @@ -297,64 +634,15 @@ ], "support": { "issues": "https://github.com/tecnickcom/TCPDF/issues", - "source": "https://github.com/tecnickcom/TCPDF/tree/6.8.0" + "source": "https://github.com/tecnickcom/TCPDF/tree/6.10.0" }, "funding": [ { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tcpdf%20project", + "url": "https://www.paypal.com/donate/?hosted_button_id=NZUEC5XS8MFBJ", "type": "custom" } ], - "time": "2024-12-23T13:34:57+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.8", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "d05dbaa97500176447ffb1f1800573f23085ab13" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/d05dbaa97500176447ffb1f1800573f23085ab13", - "reference": "d05dbaa97500176447ffb1f1800573f23085ab13", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.8" - }, - "time": "2022-04-26T06:28:24+00:00" + "time": "2025-05-27T18:02:28+00:00" } ], "packages-dev": [], @@ -363,10 +651,9 @@ "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": {}, - "platform-dev": {}, - "platform-overrides": { - "php": "7.2.9" + "platform": { + "php": ">=8.2" }, + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/vendor/autoload.php b/vendor/autoload.php index 7642d2c..5f84171 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -14,10 +14,7 @@ echo $err; } } - trigger_error( - $err, - E_USER_ERROR - ); + throw new RuntimeException($err); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index 51e734a..2052022 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -26,12 +26,23 @@ */ class InstalledVersions { + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + /** * @var mixed[]|null * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null */ private static $installed; + /** + * @var bool + */ + private static $installedIsLocalDir; + /** * @var bool|null */ @@ -309,6 +320,24 @@ public static function reload($data) { self::$installed = $data; self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; } /** @@ -322,19 +351,27 @@ private static function getInstalled() } $installed = array(); + $copiedLocalDir = false; if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ $required = require $vendorDir.'/composer/installed.php'; - $installed[] = self::$installedByVendor[$vendorDir] = $required; - if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $installed[count($installed) - 1]; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; } } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } } } @@ -350,7 +387,7 @@ private static function getInstalled() } } - if (self::$installed !== array()) { + if (self::$installed !== array() && !$copiedLocalDir) { $installed[] = self::$installed; } diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 9aed049..e598c22 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -8,8 +8,8 @@ return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Datamatrix' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/datamatrix.php', - 'FPDF' => $vendorDir . '/iio/libmergepdf/tcpdi/tcpdi.php', - 'FPDF_TPL' => $vendorDir . '/iio/libmergepdf/tcpdi/fpdf_tpl.php', + 'FPDF' => $vendorDir . '/karriere/pdf-merge/tcpi/tcpdi.php', + 'FPDF_TPL' => $vendorDir . '/karriere/pdf-merge/tcpi/fpdf_tpl.php', 'PDF417' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/pdf417.php', 'QRcode' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/qrcode.php', 'TCPDF' => $vendorDir . '/tecnickcom/tcpdf/tcpdf.php', @@ -20,9 +20,7 @@ 'TCPDF_FONTS' => $vendorDir . '/tecnickcom/tcpdf/include/tcpdf_fonts.php', 'TCPDF_FONT_DATA' => $vendorDir . '/tecnickcom/tcpdf/include/tcpdf_font_data.php', 'TCPDF_IMAGES' => $vendorDir . '/tecnickcom/tcpdf/include/tcpdf_images.php', - 'TCPDF_IMPORT' => $vendorDir . '/tecnickcom/tcpdf/tcpdf_import.php', - 'TCPDF_PARSER' => $vendorDir . '/tecnickcom/tcpdf/tcpdf_parser.php', 'TCPDF_STATIC' => $vendorDir . '/tecnickcom/tcpdf/include/tcpdf_static.php', - 'TCPDI' => $vendorDir . '/iio/libmergepdf/tcpdi/tcpdi.php', - 'tcpdi_parser' => $vendorDir . '/iio/libmergepdf/tcpdi/tcpdi_parser.php', + 'TCPDI' => $vendorDir . '/karriere/pdf-merge/tcpi/tcpdi.php', + 'tcpdi_parser' => $vendorDir . '/karriere/pdf-merge/tcpi/tcpdi_parser.php', ); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 0ddeaa4..6e32a62 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -6,9 +6,12 @@ $baseDir = dirname($vendorDir); return array( - 'setasign\\Fpdi\\' => array($vendorDir . '/setasign/fpdi/src'), - 'iio\\libmergepdf\\' => array($vendorDir . '/iio/libmergepdf/src'), - 'cweagans\\Composer\\' => array($vendorDir . '/cweagans/composer-patches/src'), - 'WebSocket\\' => array($vendorDir . '/textalk/websocket/lib'), - 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'WebSocket\\' => array($vendorDir . '/phrity/websocket/src'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/src'), + 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), + 'Phrity\\Util\\' => array($vendorDir . '/phrity/util-errorhandler/src'), + 'Phrity\\Net\\' => array($vendorDir . '/phrity/net-uri/src', $vendorDir . '/phrity/net-stream/src'), + 'Phrity\\Http\\' => array($vendorDir . '/phrity/http/src'), + 'Phrity\\Comparison\\' => array($vendorDir . '/phrity/comparison/src'), + 'Karriere\\PdfMerge\\' => array($vendorDir . '/karriere/pdf-merge/src'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 4680b7d..8281a73 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -7,18 +7,6 @@ class ComposerStaticInitff8ca5c94912b5ce3ac82b5c9f2b4776 { public static $prefixLengthsPsr4 = array ( - 's' => - array ( - 'setasign\\Fpdi\\' => 14, - ), - 'i' => - array ( - 'iio\\libmergepdf\\' => 16, - ), - 'c' => - array ( - 'cweagans\\Composer\\' => 18, - ), 'W' => array ( 'WebSocket\\' => 10, @@ -26,37 +14,60 @@ class ComposerStaticInitff8ca5c94912b5ce3ac82b5c9f2b4776 'P' => array ( 'Psr\\Log\\' => 8, + 'Psr\\Http\\Message\\' => 17, + 'Phrity\\Util\\' => 12, + 'Phrity\\Net\\' => 11, + 'Phrity\\Http\\' => 12, + 'Phrity\\Comparison\\' => 18, + ), + 'K' => + array ( + 'Karriere\\PdfMerge\\' => 18, ), ); public static $prefixDirsPsr4 = array ( - 'setasign\\Fpdi\\' => + 'WebSocket\\' => array ( - 0 => __DIR__ . '/..' . '/setasign/fpdi/src', + 0 => __DIR__ . '/..' . '/phrity/websocket/src', ), - 'iio\\libmergepdf\\' => + 'Psr\\Log\\' => array ( - 0 => __DIR__ . '/..' . '/iio/libmergepdf/src', + 0 => __DIR__ . '/..' . '/psr/log/src', ), - 'cweagans\\Composer\\' => + 'Psr\\Http\\Message\\' => array ( - 0 => __DIR__ . '/..' . '/cweagans/composer-patches/src', + 0 => __DIR__ . '/..' . '/psr/http-factory/src', + 1 => __DIR__ . '/..' . '/psr/http-message/src', ), - 'WebSocket\\' => + 'Phrity\\Util\\' => array ( - 0 => __DIR__ . '/..' . '/textalk/websocket/lib', + 0 => __DIR__ . '/..' . '/phrity/util-errorhandler/src', ), - 'Psr\\Log\\' => + 'Phrity\\Net\\' => + array ( + 0 => __DIR__ . '/..' . '/phrity/net-uri/src', + 1 => __DIR__ . '/..' . '/phrity/net-stream/src', + ), + 'Phrity\\Http\\' => + array ( + 0 => __DIR__ . '/..' . '/phrity/http/src', + ), + 'Phrity\\Comparison\\' => + array ( + 0 => __DIR__ . '/..' . '/phrity/comparison/src', + ), + 'Karriere\\PdfMerge\\' => array ( - 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + 0 => __DIR__ . '/..' . '/karriere/pdf-merge/src', ), ); public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'Datamatrix' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/datamatrix.php', - 'FPDF' => __DIR__ . '/..' . '/iio/libmergepdf/tcpdi/tcpdi.php', - 'FPDF_TPL' => __DIR__ . '/..' . '/iio/libmergepdf/tcpdi/fpdf_tpl.php', + 'FPDF' => __DIR__ . '/..' . '/karriere/pdf-merge/tcpi/tcpdi.php', + 'FPDF_TPL' => __DIR__ . '/..' . '/karriere/pdf-merge/tcpi/fpdf_tpl.php', 'PDF417' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/pdf417.php', 'QRcode' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/qrcode.php', 'TCPDF' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf.php', @@ -67,11 +78,9 @@ class ComposerStaticInitff8ca5c94912b5ce3ac82b5c9f2b4776 'TCPDF_FONTS' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/tcpdf_fonts.php', 'TCPDF_FONT_DATA' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/tcpdf_font_data.php', 'TCPDF_IMAGES' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/tcpdf_images.php', - 'TCPDF_IMPORT' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf_import.php', - 'TCPDF_PARSER' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf_parser.php', 'TCPDF_STATIC' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/tcpdf_static.php', - 'TCPDI' => __DIR__ . '/..' . '/iio/libmergepdf/tcpdi/tcpdi.php', - 'tcpdi_parser' => __DIR__ . '/..' . '/iio/libmergepdf/tcpdi/tcpdi_parser.php', + 'TCPDI' => __DIR__ . '/..' . '/karriere/pdf-merge/tcpi/tcpdi.php', + 'tcpdi_parser' => __DIR__ . '/..' . '/karriere/pdf-merge/tcpi/tcpdi_parser.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 0790686..6a2cf90 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1,147 +1,465 @@ { "packages": [ { - "name": "cweagans/composer-patches", - "version": "1.7.3", - "version_normalized": "1.7.3.0", + "name": "karriere/pdf-merge", + "version": "v3.3.1", + "version_normalized": "3.3.1.0", "source": { "type": "git", - "url": "https://github.com/cweagans/composer-patches.git", - "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db" + "url": "https://github.com/karriereat/pdf-merge.git", + "reference": "d524a9d261ea96b18bbbab685b5710a7a0fe3a6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweagans/composer-patches/zipball/e190d4466fe2b103a55467dfa83fc2fecfcaf2db", - "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "url": "https://api.github.com/repos/karriereat/pdf-merge/zipball/d524a9d261ea96b18bbbab685b5710a7a0fe3a6e", + "reference": "d524a9d261ea96b18bbbab685b5710a7a0fe3a6e", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3.0" + "php": "8.1.* | 8.2.* | 8.3.* | 8.4.*", + "tecnickcom/tcpdf": "^6.3" }, "require-dev": { - "composer/composer": "~1.0 || ~2.0", - "phpunit/phpunit": "~4.6" + "laravel/pint": "^1.5 | ^1.6", + "pestphp/pest": "^1.22", + "phpstan/phpstan": "^1.10" }, - "time": "2022-12-20T22:53:13+00:00", - "type": "composer-plugin", - "extra": { - "class": "cweagans\\Composer\\Patches" + "time": "2025-02-19T11:02:10+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Karriere\\PdfMerge\\": "src/" + }, + "classmap": [ + "tcpi/fpdf_tpl.php", + "tcpi/tcpdi.php", + "tcpi/tcpdi_parser.php" + ] }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Alexander Lentner", + "email": "alexander.lentner@karriere.at", + "role": "Maintainer" + } + ], + "description": "A wrapper for the TCPDF class that provides an elegant API for merging PDFs", + "keywords": [ + "merge", + "pdf" + ], + "support": { + "issues": "https://github.com/karriereat/pdf-merge/issues", + "source": "https://github.com/karriereat/pdf-merge/tree/v3.3.1" + }, + "install-path": "../karriere/pdf-merge" + }, + { + "name": "phrity/comparison", + "version": "1.4.0", + "version_normalized": "1.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-comparison.git", + "reference": "aedd44d59db08de7d6c31812d1490c22aab35c92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-comparison/zipball/aedd44d59db08de7d6c31812d1490c22aab35c92", + "reference": "aedd44d59db08de7d6c31812d1490c22aab35c92", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "time": "2025-05-26T20:12:39+00:00", + "type": "library", "installation-source": "dist", "autoload": { "psr-4": { - "cweagans\\Composer\\": "src" + "Phrity\\Comparison\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Cameron Eagans", - "email": "me@cweagans.net" + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" } ], - "description": "Provides a way to patch Composer packages.", + "description": "Interfaces and helper trait for comparing objects. Comparator for sort and filter applications.", + "homepage": "https://phrity.sirn.se/comparison", + "keywords": [ + "comparable", + "comparator", + "comparison", + "equalable", + "filter", + "sort" + ], "support": { - "issues": "https://github.com/cweagans/composer-patches/issues", - "source": "https://github.com/cweagans/composer-patches/tree/1.7.3" + "issues": "https://github.com/sirn-se/phrity-comparison/issues", + "source": "https://github.com/sirn-se/phrity-comparison/tree/1.4.0" }, - "install-path": "../cweagans/composer-patches" + "install-path": "../phrity/comparison" }, { - "name": "iio/libmergepdf", - "version": "4.0.4", - "version_normalized": "4.0.4.0", + "name": "phrity/http", + "version": "1.0.0", + "version_normalized": "1.0.0.0", "source": { "type": "git", - "url": "https://github.com/hanneskod/libmergepdf.git", - "reference": "6613b978c08d00d559796ab510614243e4dd5dfb" + "url": "https://github.com/sirn-se/phrity-http.git", + "reference": "536e3e46e6220d171a59599ed1f4da9f6b6244fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hanneskod/libmergepdf/zipball/6613b978c08d00d559796ab510614243e4dd5dfb", - "reference": "6613b978c08d00d559796ab510614243e4dd5dfb", + "url": "https://api.github.com/repos/sirn-se/phrity-http/zipball/536e3e46e6220d171a59599ed1f4da9f6b6244fc", + "reference": "536e3e46e6220d171a59599ed1f4da9f6b6244fc", "shasum": "" }, "require": { - "php": "^7.1||^8.0", - "setasign/fpdi": "^2", - "tecnickcom/tcpdf": "^6.2.22" + "php": "^8.1", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 | ^2.0" }, - "conflict": { - "rafikhaceb/tcpdi": "*", - "setasign/fpdf": "*" + "require-dev": { + "guzzlehttp/psr7": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "robiningelbrecht/phpunit-coverage-tools": "^1.9", + "squizlabs/php_codesniffer": "^3.5" + }, + "time": "2025-09-07T17:04:26+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Phrity\\Http\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "Utilities and interfaces for handling HTTP.", + "homepage": "https://phrity.sirn.se/http", + "keywords": [ + "HTTP Factories", + "http", + "psr-17" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-http/issues", + "source": "https://github.com/sirn-se/phrity-http/tree/1.0.0" + }, + "install-path": "../phrity/http" + }, + { + "name": "phrity/net-stream", + "version": "2.3.1", + "version_normalized": "2.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-net-stream.git", + "reference": "c621bb3108a5a02bba64df2e5f0cd7ada02665b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-net-stream/zipball/c621bb3108a5a02bba64df2e5f0cd7ada02665b5", + "reference": "c621bb3108a5a02bba64df2e5f0cd7ada02665b5", + "shasum": "" + }, + "require": { + "php": "^8.1", + "phrity/util-errorhandler": "^1.1", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 | ^2.0" }, "require-dev": { - "phpunit/phpunit": "^7|^8", - "smalot/pdfparser": "~0.13" + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "phrity/net-uri": "^2.0", + "squizlabs/php_codesniffer": "^3.5" }, - "time": "2020-12-07T12:18:49+00:00", + "time": "2025-08-08T09:51:04+00:00", "type": "library", - "extra": { - "patches_applied": { - "Add PHP 8.2 support": "patches/iio-libmergepdf-support-php82.patch" + "installation-source": "dist", + "autoload": { + "psr-4": { + "Phrity\\Net\\": "src/" } }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "Socket stream classes implementing PSR-7 Stream and PSR-17 StreamFactory", + "homepage": "https://phrity.sirn.se/net-stream", + "keywords": [ + "Socket", + "client", + "psr-17", + "psr-7", + "server", + "stream", + "stream factory" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-net-stream/issues", + "source": "https://github.com/sirn-se/phrity-net-stream/tree/2.3.1" + }, + "install-path": "../phrity/net-stream" + }, + { + "name": "phrity/net-uri", + "version": "2.2.0", + "version_normalized": "2.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-net-uri.git", + "reference": "08de4cf07e439c4708f572249659f09198ac99f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-net-uri/zipball/08de4cf07e439c4708f572249659f09198ac99f0", + "reference": "08de4cf07e439c4708f572249659f09198ac99f0", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.1", + "phrity/comparison": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 | ^2.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "phrity/util-errorhandler": "^1.1", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "ext-intl": "Enables IDN conversion for non-ASCII domains" + }, + "time": "2025-05-25T13:05:13+00:00", + "type": "library", "installation-source": "dist", "autoload": { "psr-4": { - "iio\\libmergepdf\\": "src/" - }, - "classmap": [ - "tcpdi/" - ] + "Phrity\\Net\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "WTFPL" + "MIT" ], "authors": [ { - "name": "Hannes Forsgård", - "email": "hannes.forsgard@fripost.org" + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" } ], - "description": "Library for merging multiple PDFs", - "homepage": "https://github.com/hanneskod/libmergepdf", + "description": "PSR-7 Uri and PSR-17 UriFactory implementation", + "homepage": "https://phrity.sirn.se/net-uri", "keywords": [ - "merge", - "pdf" + "psr-17", + "psr-7", + "uri", + "uri factory" ], - "install-path": "../iio/libmergepdf" + "support": { + "issues": "https://github.com/sirn-se/phrity-net-uri/issues", + "source": "https://github.com/sirn-se/phrity-net-uri/tree/2.2.0" + }, + "install-path": "../phrity/net-uri" }, { - "name": "psr/log", - "version": "1.1.4", - "version_normalized": "1.1.4.0", + "name": "phrity/util-errorhandler", + "version": "1.2.1", + "version_normalized": "1.2.1.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "url": "https://github.com/sirn-se/phrity-util-errorhandler.git", + "reference": "9825f15ef9b4a93252ce53ca8962278832d834da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/sirn-se/phrity-util-errorhandler/zipball/9825f15ef9b4a93252ce53ca8962278832d834da", + "reference": "9825f15ef9b4a93252ce53ca8962278832d834da", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^8.1" }, - "time": "2021-05-03T11:20:27+00:00", + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "time": "2025-08-08T09:48:45+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Phrity\\Util\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "Inline error handler; catch and resolve errors for code block.", + "homepage": "https://phrity.sirn.se/util-errorhandler", + "keywords": [ + "error", + "warning" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-util-errorhandler/issues", + "source": "https://github.com/sirn-se/phrity-util-errorhandler/tree/1.2.1" + }, + "install-path": "../phrity/util-errorhandler" + }, + { + "name": "phrity/websocket", + "version": "3.6.0", + "version_normalized": "3.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/websocket-php.git", + "reference": "3f16b2564a230bbce716cccaff2f6156a60a8798" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/websocket-php/zipball/3f16b2564a230bbce716cccaff2f6156a60a8798", + "reference": "3f16b2564a230bbce716cccaff2f6156a60a8798", + "shasum": "" + }, + "require": { + "php": "^8.1", + "phrity/http": "^1.0", + "phrity/net-stream": "^2.3", + "phrity/net-uri": "^2.1", + "psr/http-message": "^1.1 | ^2.0", + "psr/log": "^1.0 | ^2.0 | ^3.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.0", + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "phrity/logger-console": "^1.0", + "phrity/net-mock": "^2.3", + "phrity/util-errorhandler": "^1.1", + "robiningelbrecht/phpunit-coverage-tools": "^1.9", + "squizlabs/php_codesniffer": "^3.5" + }, + "time": "2025-09-08T16:21:41+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "WebSocket\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "WebSocket client and server", + "homepage": "https://phrity.sirn.se/websocket", + "keywords": [ + "client", + "server", + "websocket" + ], + "support": { + "issues": "https://github.com/sirn-se/websocket-php/issues", + "source": "https://github.com/sirn-se/websocket-php/tree/3.6.0" + }, + "install-path": "../phrity/websocket" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "time": "2024-04-15T12:06:14+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.0.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -154,53 +472,51 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ - "log", + "factory", + "http", + "message", "psr", - "psr-3" + "psr-17", + "psr-7", + "request", + "response" ], - "install-path": "../psr/log" + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "install-path": "../psr/http-factory" }, { - "name": "setasign/fpdi", - "version": "v2.6.0", - "version_normalized": "2.6.0.0", + "name": "psr/http-message", + "version": "2.0", + "version_normalized": "2.0.0.0", "source": { "type": "git", - "url": "https://github.com/Setasign/FPDI.git", - "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad" + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDI/zipball/a6db878129ec6c7e141316ee71872923e7f1b7ad", - "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "ext-zlib": "*", - "php": "^5.6 || ^7.0 || ^8.0" - }, - "conflict": { - "setasign/tfpdf": "<1.31" + "php": "^7.2 || ^8.0" }, - "require-dev": { - "phpunit/phpunit": "~5.7", - "setasign/fpdf": "~1.8.6", - "setasign/tfpdf": "~1.33", - "squizlabs/php_codesniffer": "^3.5", - "tecnickcom/tcpdf": "~6.2" - }, - "suggest": { - "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured." - }, - "time": "2023-12-11T16:03:32+00:00", + "time": "2023-04-04T09:54:51+00:00", "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "installation-source": "dist", "autoload": { "psr-4": { - "setasign\\Fpdi\\": "src/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -209,54 +525,98 @@ ], "authors": [ { - "name": "Jan Slabon", - "email": "jan.slabon@setasign.com", - "homepage": "https://www.setasign.com" - }, - { - "name": "Maximilian Kresse", - "email": "maximilian.kresse@setasign.com", - "homepage": "https://www.setasign.com" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", - "homepage": "https://www.setasign.com/fpdi", + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "fpdf", - "fpdi", - "pdf" + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" ], "support": { - "issues": "https://github.com/Setasign/FPDI/issues", - "source": "https://github.com/Setasign/FPDI/tree/v2.6.0" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "funding": [ + "install-path": "../psr/http-message" + }, + { + "name": "psr/log", + "version": "3.0.2", + "version_normalized": "3.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "time": "2024-09-11T13:17:53+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/setasign/fpdi", - "type": "tidelift" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "install-path": "../setasign/fpdi" + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "install-path": "../psr/log" }, { "name": "tecnickcom/tcpdf", - "version": "6.7.5", - "version_normalized": "6.7.5.0", + "version": "6.10.0", + "version_normalized": "6.10.0.0", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36" + "reference": "ca5b6de294512145db96bcbc94e61696599c391d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/951eabf0338ec2522bd0d5d9c79b08a3a3d36b36", - "reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/ca5b6de294512145db96bcbc94e61696599c391d", + "reference": "ca5b6de294512145db96bcbc94e61696599c391d", "shasum": "" }, "require": { - "php": ">=5.5.0" + "ext-curl": "*", + "php": ">=7.1.0" }, - "time": "2024-04-20T17:25:10+00:00", + "time": "2025-05-27T18:02:28+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -264,8 +624,6 @@ "config", "include", "tcpdf.php", - "tcpdf_parser.php", - "tcpdf_import.php", "tcpdf_barcodes_1d.php", "tcpdf_barcodes_2d.php", "include/tcpdf_colors.php", @@ -303,67 +661,15 @@ ], "support": { "issues": "https://github.com/tecnickcom/TCPDF/issues", - "source": "https://github.com/tecnickcom/TCPDF/tree/6.7.5" + "source": "https://github.com/tecnickcom/TCPDF/tree/6.10.0" }, "funding": [ { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tcpdf%20project", + "url": "https://www.paypal.com/donate/?hosted_button_id=NZUEC5XS8MFBJ", "type": "custom" } ], "install-path": "../tecnickcom/tcpdf" - }, - { - "name": "textalk/websocket", - "version": "1.5.8", - "version_normalized": "1.5.8.0", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "d05dbaa97500176447ffb1f1800573f23085ab13" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/d05dbaa97500176447ffb1f1800573f23085ab13", - "reference": "d05dbaa97500176447ffb1f1800573f23085ab13", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "time": "2022-04-26T06:28:24+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.8" - }, - "install-path": "../textalk/websocket" } ], "dev": true, diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 4a2afd8..88f772f 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -19,57 +19,102 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'cweagans/composer-patches' => array( - 'pretty_version' => '1.7.3', - 'version' => '1.7.3.0', - 'reference' => 'e190d4466fe2b103a55467dfa83fc2fecfcaf2db', - 'type' => 'composer-plugin', - 'install_path' => __DIR__ . '/../cweagans/composer-patches', + 'karriere/pdf-merge' => array( + 'pretty_version' => 'v3.3.1', + 'version' => '3.3.1.0', + 'reference' => 'd524a9d261ea96b18bbbab685b5710a7a0fe3a6e', + 'type' => 'library', + 'install_path' => __DIR__ . '/../karriere/pdf-merge', 'aliases' => array(), 'dev_requirement' => false, ), - 'iio/libmergepdf' => array( - 'pretty_version' => '4.0.4', - 'version' => '4.0.4.0', - 'reference' => '6613b978c08d00d559796ab510614243e4dd5dfb', + 'phrity/comparison' => array( + 'pretty_version' => '1.4.0', + 'version' => '1.4.0.0', + 'reference' => 'aedd44d59db08de7d6c31812d1490c22aab35c92', 'type' => 'library', - 'install_path' => __DIR__ . '/../iio/libmergepdf', + 'install_path' => __DIR__ . '/../phrity/comparison', 'aliases' => array(), 'dev_requirement' => false, ), - 'psr/log' => array( - 'pretty_version' => '1.1.4', - 'version' => '1.1.4.0', - 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11', + 'phrity/http' => array( + 'pretty_version' => '1.0.0', + 'version' => '1.0.0.0', + 'reference' => '536e3e46e6220d171a59599ed1f4da9f6b6244fc', 'type' => 'library', - 'install_path' => __DIR__ . '/../psr/log', + 'install_path' => __DIR__ . '/../phrity/http', 'aliases' => array(), 'dev_requirement' => false, ), - 'setasign/fpdi' => array( - 'pretty_version' => 'v2.6.0', - 'version' => '2.6.0.0', - 'reference' => 'a6db878129ec6c7e141316ee71872923e7f1b7ad', + 'phrity/net-stream' => array( + 'pretty_version' => '2.3.1', + 'version' => '2.3.1.0', + 'reference' => 'c621bb3108a5a02bba64df2e5f0cd7ada02665b5', 'type' => 'library', - 'install_path' => __DIR__ . '/../setasign/fpdi', + 'install_path' => __DIR__ . '/../phrity/net-stream', 'aliases' => array(), 'dev_requirement' => false, ), - 'tecnickcom/tcpdf' => array( - 'pretty_version' => '6.7.5', - 'version' => '6.7.5.0', - 'reference' => '951eabf0338ec2522bd0d5d9c79b08a3a3d36b36', + 'phrity/net-uri' => array( + 'pretty_version' => '2.2.0', + 'version' => '2.2.0.0', + 'reference' => '08de4cf07e439c4708f572249659f09198ac99f0', 'type' => 'library', - 'install_path' => __DIR__ . '/../tecnickcom/tcpdf', + 'install_path' => __DIR__ . '/../phrity/net-uri', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'phrity/util-errorhandler' => array( + 'pretty_version' => '1.2.1', + 'version' => '1.2.1.0', + 'reference' => '9825f15ef9b4a93252ce53ca8962278832d834da', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phrity/util-errorhandler', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'phrity/websocket' => array( + 'pretty_version' => '3.6.0', + 'version' => '3.6.0.0', + 'reference' => '3f16b2564a230bbce716cccaff2f6156a60a8798', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phrity/websocket', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/http-factory' => array( + 'pretty_version' => '1.1.0', + 'version' => '1.1.0.0', + 'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-factory', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/http-message' => array( + 'pretty_version' => '2.0', + 'version' => '2.0.0.0', + 'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-message', 'aliases' => array(), 'dev_requirement' => false, ), - 'textalk/websocket' => array( - 'pretty_version' => '1.5.8', - 'version' => '1.5.8.0', - 'reference' => 'd05dbaa97500176447ffb1f1800573f23085ab13', + 'psr/log' => array( + 'pretty_version' => '3.0.2', + 'version' => '3.0.2.0', + 'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3', 'type' => 'library', - 'install_path' => __DIR__ . '/../textalk/websocket', + 'install_path' => __DIR__ . '/../psr/log', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'tecnickcom/tcpdf' => array( + 'pretty_version' => '6.10.0', + 'version' => '6.10.0.0', + 'reference' => 'ca5b6de294512145db96bcbc94e61696599c391d', + 'type' => 'library', + 'install_path' => __DIR__ . '/../tecnickcom/tcpdf', 'aliases' => array(), 'dev_requirement' => false, ), diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php index 589e9e7..14bf88d 100644 --- a/vendor/composer/platform_check.php +++ b/vendor/composer/platform_check.php @@ -4,8 +4,8 @@ $issues = array(); -if (!(PHP_VERSION_ID >= 70200)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.'; +if (!(PHP_VERSION_ID >= 80200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.'; } if ($issues) { @@ -19,8 +19,7 @@ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; } } - trigger_error( - 'Composer detected issues in your platform: ' . implode(' ', $issues), - E_USER_ERROR + throw new \RuntimeException( + 'Composer detected issues in your platform: ' . implode(' ', $issues) ); } diff --git a/vendor/cweagans/composer-patches/.gitignore b/vendor/cweagans/composer-patches/.gitignore deleted file mode 100644 index 48b8bf9..0000000 --- a/vendor/cweagans/composer-patches/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vendor/ diff --git a/vendor/cweagans/composer-patches/LICENSE.md b/vendor/cweagans/composer-patches/LICENSE.md deleted file mode 100644 index d0dad3d..0000000 --- a/vendor/cweagans/composer-patches/LICENSE.md +++ /dev/null @@ -1,9 +0,0 @@ -Copyright 2013 Cameron Eagans - -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 the copyright holder 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/cweagans/composer-patches/README.md b/vendor/cweagans/composer-patches/README.md deleted file mode 100644 index 87f9dc0..0000000 --- a/vendor/cweagans/composer-patches/README.md +++ /dev/null @@ -1,205 +0,0 @@ -# composer-patches - -Simple patches plugin for Composer. Applies a patch from a local or remote file to any package required with composer. - -Note that the 1.x versions of Composer Patches are supported on a best-effort -basis due to the imminent release of 2.0.0. You may still be interested in -using 1.x if you need Composer to cooperate with earlier PHP versions. No new -features will be added to 1.x releases, but any security or bug fixes will -still be accepted. - -## Usage - -Example composer.json: - -```json -{ - "require": { - "cweagans/composer-patches": "~1.0", - "drupal/drupal": "~8.2" - }, - "config": { - "preferred-install": "source" - }, - "extra": { - "patches": { - "drupal/drupal": { - "Add startup configuration for PHP server": "https://www.drupal.org/files/issues/add_a_startup-1543858-30.patch" - } - } - } -} - -``` - -## Using an external patch file - -Instead of a patches key in your root composer.json, use a patches-file key. - -```json -{ - "require": { - "cweagans/composer-patches": "~1.0", - "drupal/drupal": "~8.2" - }, - "config": { - "preferred-install": "source" - }, - "extra": { - "patches-file": "local/path/to/your/composer.patches.json" - } -} - -``` - -Then your `composer.patches.json` should look like this: - -``` -{ - "patches": { - "vendor/project": { - "Patch title": "http://example.com/url/to/patch.patch" - } - } -} -``` - -## Allowing patches to be applied from dependencies - -If your project doesn't supply any patches of its own, but you still want to accept patches from dependencies, you must have the following in your composer file: - -```json -{ - "require": { - "cweagans/composer-patches": "^1.5.0" - }, - "extra": { - "enable-patching": true - } -} -``` - -If you do have a `patches` section in your composer file that defines your own set of patches then the `enable-patching` setting will be ignored and patches from dependencies will always be applied. - -## Ignoring patches - -There may be situations in which you want to ignore a patch supplied by a dependency. For example: - -- You use a different more recent version of a dependency, and now a patch isn't applying. -- You have a more up to date patch than the dependency, and want to use yours instead of theirs. -- A dependency's patch adds a feature to a project that you don't need. -- Your patches conflict with a dependency's patches. - -```json -{ - "require": { - "cweagans/composer-patches": "~1.0", - "drupal/drupal": "~8.2", - "drupal/lightning": "~8.1" - }, - "config": { - "preferred-install": "source" - }, - "extra": { - "patches": { - "drupal/drupal": { - "Add startup configuration for PHP server": "https://www.drupal.org/files/issues/add_a_startup-1543858-30.patch" - } - }, - "patches-ignore": { - "drupal/lightning": { - "drupal/panelizer": { - "This patch has known conflicts with our Quick Edit integration": "https://www.drupal.org/files/issues/2664682-49.patch" - } - } - } - } -} -``` - -## Allowing to force the patch level (-pX) - -Some situations require to force the patchLevel used to apply patches on a particular package. -Its useful for packages like drupal/core which packages only a subdir of the original upstream project on which patches are based. - -```json -{ - "extra": { - "patchLevel": { - "drupal/core": "-p2" - } - } -} -``` - -## Using patches from HTTP URLs - -Composer [blocks](https://getcomposer.org/doc/06-config.md#secure-http) you from downloading anything from HTTP URLs, you can disable this for your project by adding a `secure-http` setting in the config section of your `composer.json`. Note that the `config` section should be under the root of your `composer.json`. - -```json -{ - "config": { - "secure-http": false - } -} -``` - -However, it's always advised to setup HTTPS to prevent MITM code injection. - -## Patches containing modifications to composer.json files - -Because patching occurs _after_ Composer calculates dependencies and installs packages, changes to an underlying dependency's `composer.json` file introduced in a patch will have _no effect_ on installed packages. - -If you need to modify a dependency's `composer.json` or its underlying dependencies, you cannot use this plugin. Instead, you must do one of the following: -- Work to get the underlying issue resolved in the upstream package. -- Fork the package and [specify your fork as the package repository](https://getcomposer.org/doc/05-repositories.md#vcs) in your root `composer.json` -- Specify compatible package version requirements in your root `composer.json` - -## Error handling - -If a patch cannot be applied (hunk failed, different line endings, etc.) a message will be shown and the patch will be skipped. - -To enforce throwing an error and stopping package installation/update immediately, you have two available options: - -1. Add `"composer-exit-on-patch-failure": true` option to the `extra` section of your composer.json file. -1. Export `COMPOSER_EXIT_ON_PATCH_FAILURE=1` - -By default, failed patches are skipped. - -## Patches reporting - -When a patch is applied, the plugin writes a report-file `PATCHES.txt` to a patching directory (e.g. `./patch-me/PATCHES.txt`), -which contains a list of applied patches. - -If you want to avoid this behavior, add a specific key to the `extra` section: -```json -"extra": { - "composer-patches-skip-reporting": true -} -``` - -Or provide an environment variable `COMPOSER_PATCHES_SKIP_REPORTING` with a config. - -## Patching composer.json in dependencies - -This doesn't work like you'd want. By the time you're running `composer install`, -the metadata from your dependencies' composer.json has already been aggregated by -packagist (or whatever metadata repo you're using). Unfortunately, this means that -you cannot e.g. patch a dependency to be compatible with an earlier version of PHP -or change the framework version that a plugin depends on. - -@anotherjames over at @computerminds wrote an article about how to work around -that particular problem for a Drupal 8 -> Drupal 9 upgrade: - -[Apply Drupal 9 compatibility patches with Composer](https://www.computerminds.co.uk/articles/apply-drupal-9-compatibility-patches-composer) ([archive](https://web.archive.org/web/20210124171010/https://www.computerminds.co.uk/articles/apply-drupal-9-compatibility-patches-composer)) - -## Difference between this and netresearch/composer-patches-plugin - -- This plugin is much more simple to use and maintain -- This plugin doesn't require you to specify which package version you're patching -- This plugin is easy to use with Drupal modules (which don't use semantic versioning). -- This plugin will gather patches from all dependencies and apply them as if they were in the root composer.json - -## Credits - -A ton of this code is adapted or taken straight from https://github.com/jpstacey/composer-patcher, which is abandoned in favor of https://github.com/netresearch/composer-patches-plugin, which is (IMHO) overly complex and difficult to use. diff --git a/vendor/cweagans/composer-patches/composer.json b/vendor/cweagans/composer-patches/composer.json deleted file mode 100644 index 1565b02..0000000 --- a/vendor/cweagans/composer-patches/composer.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "cweagans/composer-patches", - "description": "Provides a way to patch Composer packages.", - "minimum-stability": "dev", - "license": "BSD-3-Clause", - "type": "composer-plugin", - "extra": { - "class": "cweagans\\Composer\\Patches" - }, - "authors": [ - { - "name": "Cameron Eagans", - "email": "me@cweagans.net" - } - ], - "require": { - "php": ">=5.3.0", - "composer-plugin-api": "^1.0 || ^2.0" - }, - "require-dev": { - "composer/composer": "~1.0 || ~2.0", - "phpunit/phpunit": "~4.6" - }, - "autoload": { - "psr-4": {"cweagans\\Composer\\": "src"} - }, - "autoload-dev": { - "psr-4": {"cweagans\\Composer\\Tests\\": "tests"} - } -} diff --git a/vendor/cweagans/composer-patches/composer.lock b/vendor/cweagans/composer-patches/composer.lock deleted file mode 100644 index d5fe177..0000000 --- a/vendor/cweagans/composer-patches/composer.lock +++ /dev/null @@ -1,2568 +0,0 @@ -{ - "_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#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "4a5c841252204815536a37cad51d347b", - "packages": [], - "packages-dev": [ - { - "name": "composer/ca-bundle", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/composer/ca-bundle.git", - "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd", - "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "ext-pcre": "*", - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", - "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\CaBundle\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", - "keywords": [ - "cabundle", - "cacert", - "certificate", - "ssl", - "tls" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-04-08T08:27:21+00:00" - }, - { - "name": "composer/composer", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/composer/composer.git", - "reference": "870fdc59dfcffe0bd2d43ca2de4235761d0dec7a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/870fdc59dfcffe0bd2d43ca2de4235761d0dec7a", - "reference": "870fdc59dfcffe0bd2d43ca2de4235761d0dec7a", - "shasum": "" - }, - "require": { - "composer/ca-bundle": "^1.0", - "composer/semver": "^3.0", - "composer/spdx-licenses": "^1.2", - "composer/xdebug-handler": "^1.1", - "justinrainbow/json-schema": "^5.2.10", - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0", - "react/promise": "^1.2 || ^2.7", - "seld/jsonlint": "^1.4", - "seld/phar-utils": "^1.0", - "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0", - "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0", - "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0", - "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0" - }, - "require-dev": { - "phpspec/prophecy": "^1.10", - "symfony/phpunit-bridge": "^4.2 || ^5.0" - }, - "suggest": { - "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", - "ext-zip": "Enabling the zip extension allows you to unzip archives", - "ext-zlib": "Allow gzip compression of HTTP requests" - }, - "bin": [ - "bin/composer" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\": "src/Composer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "https://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" - } - ], - "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", - "homepage": "https://getcomposer.org/", - "keywords": [ - "autoload", - "dependency", - "package" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-07-15T15:02:16+00:00" - }, - { - "name": "composer/semver", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "00915994bb1de62e750ae279669c9c5a57379957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/00915994bb1de62e750ae279669c9c5a57379957", - "reference": "00915994bb1de62e750ae279669c9c5a57379957", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^0.12.19", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-05-31T11:44:06+00:00" - }, - { - "name": "composer/spdx-licenses", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/composer/spdx-licenses.git", - "reference": "6946f785871e2314c60b4524851f3702ea4f2223" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/6946f785871e2314c60b4524851f3702ea4f2223", - "reference": "6946f785871e2314c60b4524851f3702ea4f2223", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Spdx\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "SPDX licenses list and validation library.", - "keywords": [ - "license", - "spdx", - "validator" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-07-15T15:35:07+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "1.4.2", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51", - "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-06-04T11:16:35+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "dev-master", - "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-14T21:17:01+00:00" - }, - { - "name": "justinrainbow/json-schema", - "version": "5.x-dev", - "source": { - "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", - "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" - }, - "bin": [ - "bin/validate-json" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } - ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", - "keywords": [ - "json", - "schema" - ], - "time": "2020-05-27T16:41:55+00: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-03T12:10:50+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "4f9b1eaf0a7da77c362f8d91cbc68ab1f4718d62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4f9b1eaf0a7da77c362f8d91cbc68ab1f4718d62", - "reference": "4f9b1eaf0a7da77c362f8d91cbc68ab1f4718d62", - "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.5.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-09-22T14:49:23+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "2.2.x-dev", - "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-06T15:47:00+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "dev-master", - "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-21T13:08:43+00:00" - }, - { - "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-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "dev-master", - "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-21T08:01:12+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "cab6c6fefee93d7b7c3a01292a0fe0884ea66644" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/cab6c6fefee93d7b7c3a01292a0fe0884ea66644", - "reference": "cab6c6fefee93d7b7c3a01292a0fe0884ea66644", - "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-23T14:46:55+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "4.8.x-dev", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "be067d6105286b74272facefc2697038f8807b77" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/264188ddf4d3586c80ea615f8ec8eaea34e652a1", - "reference": "be067d6105286b74272facefc2697038f8807b77", - "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": "2015-10-14T13:49:40+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "2.3.x-dev", - "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" - ], - "abandoned": true, - "time": "2015-10-02T06:51:40+00:00" - }, - { - "name": "psr/container", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "fc1bc363ecf887921e3897c7b1dad3587ae154eb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/fc1bc363ecf887921e3897c7b1dad3587ae154eb", - "reference": "fc1bc363ecf887921e3897c7b1dad3587ae154eb", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "time": "2019-10-04T14:07:35+00:00" - }, - { - "name": "psr/log", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "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", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2020-03-23T09:12:05+00:00" - }, - { - "name": "react/promise", - "version": "2.x-dev", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", - "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "keywords": [ - "promise", - "promises" - ], - "time": "2020-05-12T15:16:56+00:00" - }, - { - "name": "sebastian/comparator", - "version": "dev-master", - "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-26T15:48:44+00:00" - }, - { - "name": "sebastian/diff", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "6899b3e33bfbd386d88b5eea5f65f563e8793051" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6899b3e33bfbd386d88b5eea5f65f563e8793051", - "reference": "6899b3e33bfbd386d88b5eea5f65f563e8793051", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-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": "http://www.github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2015-06-22T14:15:55+00:00" - }, - { - "name": "sebastian/environment", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf", - "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44", - "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-08-03T06:14:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "f88f8936517d54ae6d589166810877fb2015d0a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f88f8936517d54ae6d589166810877fb2015d0a2", - "reference": "f88f8936517d54ae6d589166810877fb2015d0a2", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" - }, - "require-dev": { - "ext-mbstring": "*", - "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": "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-08-09T04:23:41+00:00" - }, - { - "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-12T03:26:01+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "994d4a811bafe801fb06dccbee797863ba2792ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba", - "reference": "994d4a811bafe801fb06dccbee797863ba2792ba", - "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-06-21T08:04:50+00:00" - }, - { - "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-21T13:59:46+00:00" - }, - { - "name": "seld/jsonlint", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1", - "reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1", - "shasum": "" - }, - "require": { - "php": "^5.3 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "bin": [ - "bin/jsonlint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "JSON Linter", - "keywords": [ - "json", - "linter", - "parser", - "validator" - ], - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", - "type": "tidelift" - } - ], - "time": "2020-04-30T19:05:18+00:00" - }, - { - "name": "seld/phar-utils", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8674b1d84ffb47cc59a101f5d5a3b61e87d23796", - "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Seld\\PharUtils\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "PHAR file format utilities, for when PHP phars you up", - "keywords": [ - "phar" - ], - "time": "2020-07-07T18:42:57+00:00" - }, - { - "name": "symfony/console", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "e4a70bd8c5a4382630197b7b87910b3fc0e6b526" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e4a70bd8c5a4382630197b7b87910b3fc0e6b526", - "reference": "e4a70bd8c5a4382630197b7b87910b3fc0e6b526", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.15", - "symfony/service-contracts": "^1.1|^2", - "symfony/string": "^5.1" - }, - "conflict": { - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/event-dispatcher": "^4.4|^5.0", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "symfony/var-dumper": "^4.4|^5.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.2-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", - "keywords": [ - "cli", - "command line", - "console", - "terminal" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-06T13:25:45+00:00" - }, - { - "name": "symfony/filesystem", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "e7550993849f986f01a9161b302d4aed8d4aab0a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/e7550993849f986f01a9161b302d4aed8d4aab0a", - "reference": "e7550993849f986f01a9161b302d4aed8d4aab0a", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "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 Filesystem Component", - "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-30T20:38:10+00:00" - }, - { - "name": "symfony/finder", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "2a63a45741144325f84d28ea1e67bc1b669b1748" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2a63a45741144325f84d28ea1e67bc1b669b1748", - "reference": "2a63a45741144325f84d28ea1e67bc1b669b1748", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.2-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", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-20T17:44:07+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.18-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-14T12:35:20+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b740103edbdcc39602239ee8860f0f45a8eb9aa5", - "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.18-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - }, - "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 intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-14T12:35:20+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", - "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.18-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "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 intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-14T12:35:20+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a", - "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.18-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "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" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-14T12:35:20+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fffa1a52a023e782cdcc221d781fe1ec8f87fcca", - "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.18-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "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 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-14T12:35:20+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981", - "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981", - "shasum": "" - }, - "require": { - "php": ">=7.0.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.18-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-14T12:35:20+00:00" - }, - { - "name": "symfony/process", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "d158a452d952049e0e55b7cfe5f360c973edc57c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d158a452d952049e0e55b7cfe5f360c973edc57c", - "reference": "d158a452d952049e0e55b7cfe5f360c973edc57c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.15" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.2-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", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-06T13:25:45+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/58c7475e5457c5492c26cc740cc0ad7464be9442", - "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.0" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "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": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-06T13:23:11+00:00" - }, - { - "name": "symfony/string", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "b7914561c03f8d78f83eec3ec4502adbdc343c48" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/b7914561c03f8d78f83eec3ec4502adbdc343c48", - "reference": "b7914561c03f8d78f83eec3ec4502adbdc343c48", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0", - "symfony/http-client": "^4.4|^5.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "files": [ - "Resources/functions.php" - ], - "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 String component", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-08T08:28:10+00:00" - }, - { - "name": "symfony/yaml", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "8d32eb597b531eb915b4fee3dc582ade5ae1fe6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/ab0314f7544d600ea7917f02cdad774358b81113", - "reference": "8d32eb597b531eb915b4fee3dc582ade5ae1fe6a", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - } - }, - "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": "2015-10-13T16:01:35+00:00" - } - ], - "aliases": [], - "minimum-stability": "dev", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.3.0", - "composer-plugin-api": "^1.0 || ^2.0" - }, - "platform-dev": [], - "plugin-api-version": "1.1.0" -} diff --git a/vendor/cweagans/composer-patches/phpunit.xml.dist b/vendor/cweagans/composer-patches/phpunit.xml.dist deleted file mode 100644 index 62409b3..0000000 --- a/vendor/cweagans/composer-patches/phpunit.xml.dist +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - ./tests/ - - - - - - src/ - - - vendor/ - - - diff --git a/vendor/cweagans/composer-patches/src/PatchEvent.php b/vendor/cweagans/composer-patches/src/PatchEvent.php deleted file mode 100644 index 31d36f8..0000000 --- a/vendor/cweagans/composer-patches/src/PatchEvent.php +++ /dev/null @@ -1,70 +0,0 @@ -package = $package; - $this->url = $url; - $this->description = $description; - } - - /** - * Returns the package that is patched. - * - * @return PackageInterface - */ - public function getPackage() { - return $this->package; - } - - /** - * Returns the url of the patch. - * - * @return string - */ - public function getUrl() { - return $this->url; - } - - /** - * Returns the description of the patch. - * - * @return string - */ - public function getDescription() { - return $this->description; - } - -} diff --git a/vendor/cweagans/composer-patches/src/PatchEvents.php b/vendor/cweagans/composer-patches/src/PatchEvents.php deleted file mode 100644 index ecee947..0000000 --- a/vendor/cweagans/composer-patches/src/PatchEvents.php +++ /dev/null @@ -1,30 +0,0 @@ -composer = $composer; - $this->io = $io; - $this->eventDispatcher = $composer->getEventDispatcher(); - $this->executor = new ProcessExecutor($this->io); - $this->patches = array(); - $this->installedPatches = array(); - } - - /** - * Returns an array of event names this subscriber wants to listen to. - */ - public static function getSubscribedEvents() { - return array( - ScriptEvents::PRE_INSTALL_CMD => array('checkPatches'), - ScriptEvents::PRE_UPDATE_CMD => array('checkPatches'), - PackageEvents::PRE_PACKAGE_INSTALL => array('gatherPatches'), - PackageEvents::PRE_PACKAGE_UPDATE => array('gatherPatches'), - // The following is a higher weight for compatibility with - // https://github.com/AydinHassan/magento-core-composer-installer and more generally for compatibility with - // every Composer plugin which deploys downloaded packages to other locations. - // In such cases you want that those plugins deploy patched files so they have to run after - // the "composer-patches" plugin. - // @see: https://github.com/cweagans/composer-patches/pull/153 - PackageEvents::POST_PACKAGE_INSTALL => array('postInstall', 10), - PackageEvents::POST_PACKAGE_UPDATE => array('postInstall', 10), - ); - } - - /** - * Before running composer install, - * @param Event $event - */ - public function checkPatches(Event $event) { - if (!$this->isPatchingEnabled()) { - return; - } - - try { - $repositoryManager = $this->composer->getRepositoryManager(); - $localRepository = $repositoryManager->getLocalRepository(); - $installationManager = $this->composer->getInstallationManager(); - $packages = $localRepository->getPackages(); - - $extra = $this->composer->getPackage()->getExtra(); - $patches_ignore = isset($extra['patches-ignore']) ? $extra['patches-ignore'] : array(); - - $tmp_patches = $this->grabPatches(); - foreach ($packages as $package) { - $extra = $package->getExtra(); - if (isset($extra['patches'])) { - if (isset($patches_ignore[$package->getName()])) { - foreach ($patches_ignore[$package->getName()] as $package_name => $patches) { - if (isset($extra['patches'][$package_name])) { - $extra['patches'][$package_name] = array_diff($extra['patches'][$package_name], $patches); - } - } - } - $this->installedPatches[$package->getName()] = $extra['patches']; - } - $patches = isset($extra['patches']) ? $extra['patches'] : array(); - $tmp_patches = $this->arrayMergeRecursiveDistinct($tmp_patches, $patches); - } - - if ($tmp_patches == FALSE) { - $this->io->write('No patches supplied.'); - return; - } - - // Remove packages for which the patch set has changed. - $promises = array(); - foreach ($packages as $package) { - if (!($package instanceof AliasPackage)) { - $package_name = $package->getName(); - $extra = $package->getExtra(); - $has_patches = isset($tmp_patches[$package_name]); - $has_applied_patches = isset($extra['patches_applied']) && count($extra['patches_applied']) > 0; - if (($has_patches && !$has_applied_patches) - || (!$has_patches && $has_applied_patches) - || ($has_patches && $has_applied_patches && $tmp_patches[$package_name] !== $extra['patches_applied'])) { - $uninstallOperation = new UninstallOperation($package, 'Removing package so it can be re-installed and re-patched.'); - $this->io->write('Removing package ' . $package_name . ' so that it can be re-installed and re-patched.'); - $promises[] = $installationManager->uninstall($localRepository, $uninstallOperation); - } - } - } - $promises = array_filter($promises); - if ($promises) { - $this->composer->getLoop()->wait($promises); - } - } - // If the Locker isn't available, then we don't need to do this. - // It's the first time packages have been installed. - catch (\LogicException $e) { - return; - } - } - - /** - * Gather patches from dependencies and store them for later use. - * - * @param PackageEvent $event - */ - public function gatherPatches(PackageEvent $event) { - // If we've already done this, then don't do it again. - if (isset($this->patches['_patchesGathered'])) { - $this->io->write('Patches already gathered. Skipping', TRUE, IOInterface::VERBOSE); - return; - } - // If patching has been disabled, bail out here. - elseif (!$this->isPatchingEnabled()) { - $this->io->write('Patching is disabled. Skipping.', TRUE, IOInterface::VERBOSE); - return; - } - - $this->patches = $this->grabPatches(); - if (empty($this->patches)) { - $this->io->write('No patches supplied.'); - } - - $extra = $this->composer->getPackage()->getExtra(); - $patches_ignore = isset($extra['patches-ignore']) ? $extra['patches-ignore'] : array(); - - // Now add all the patches from dependencies that will be installed. - $operations = $event->getOperations(); - $this->io->write('Gathering patches for dependencies. This might take a minute.'); - foreach ($operations as $operation) { - if ($operation instanceof InstallOperation || $operation instanceof UpdateOperation) { - $package = $this->getPackageFromOperation($operation); - $extra = $package->getExtra(); - if (isset($extra['patches'])) { - if (isset($patches_ignore[$package->getName()])) { - foreach ($patches_ignore[$package->getName()] as $package_name => $patches) { - if (isset($extra['patches'][$package_name])) { - $extra['patches'][$package_name] = array_diff($extra['patches'][$package_name], $patches); - } - } - } - $this->patches = $this->arrayMergeRecursiveDistinct($this->patches, $extra['patches']); - } - // Unset installed patches for this package - if(isset($this->installedPatches[$package->getName()])) { - unset($this->installedPatches[$package->getName()]); - } - } - } - - // Merge installed patches from dependencies that did not receive an update. - foreach ($this->installedPatches as $patches) { - $this->patches = $this->arrayMergeRecursiveDistinct($this->patches, $patches); - } - - // If we're in verbose mode, list the projects we're going to patch. - if ($this->io->isVerbose()) { - foreach ($this->patches as $package => $patches) { - $number = count($patches); - $this->io->write('Found ' . $number . ' patches for ' . $package . '.'); - } - } - - // Make sure we don't gather patches again. Extra keys in $this->patches - // won't hurt anything, so we'll just stash it there. - $this->patches['_patchesGathered'] = TRUE; - } - - /** - * Get the patches from root composer or external file - * @return Patches - * @throws \Exception - */ - public function grabPatches() { - // First, try to get the patches from the root composer.json. - $extra = $this->composer->getPackage()->getExtra(); - if (isset($extra['patches'])) { - $this->io->write('Gathering patches for root package.'); - $patches = $extra['patches']; - return $patches; - } - // If it's not specified there, look for a patches-file definition. - elseif (isset($extra['patches-file'])) { - $this->io->write('Gathering patches from patch file.'); - $patches = file_get_contents($extra['patches-file']); - $patches = json_decode($patches, TRUE); - $error = json_last_error(); - if ($error != 0) { - switch ($error) { - case JSON_ERROR_DEPTH: - $msg = ' - Maximum stack depth exceeded'; - break; - case JSON_ERROR_STATE_MISMATCH: - $msg = ' - Underflow or the modes mismatch'; - break; - case JSON_ERROR_CTRL_CHAR: - $msg = ' - Unexpected control character found'; - break; - case JSON_ERROR_SYNTAX: - $msg = ' - Syntax error, malformed JSON'; - break; - case JSON_ERROR_UTF8: - $msg = ' - Malformed UTF-8 characters, possibly incorrectly encoded'; - break; - default: - $msg = ' - Unknown error'; - break; - } - throw new \Exception('There was an error in the supplied patches file:' . $msg); - } - if (isset($patches['patches'])) { - $patches = $patches['patches']; - return $patches; - } - elseif(!$patches) { - throw new \Exception('There was an error in the supplied patch file'); - } - } - else { - return array(); - } - } - - /** - * @param PackageEvent $event - * @throws \Exception - */ - public function postInstall(PackageEvent $event) { - - // Check if we should exit in failure. - $extra = $this->composer->getPackage()->getExtra(); - $exitOnFailure = getenv('COMPOSER_EXIT_ON_PATCH_FAILURE') || !empty($extra['composer-exit-on-patch-failure']); - $skipReporting = getenv('COMPOSER_PATCHES_SKIP_REPORTING') || !empty($extra['composer-patches-skip-reporting']); - - // Get the package object for the current operation. - $operation = $event->getOperation(); - /** @var PackageInterface $package */ - $package = $this->getPackageFromOperation($operation); - $package_name = $package->getName(); - - if (!isset($this->patches[$package_name])) { - if ($this->io->isVerbose()) { - $this->io->write('No patches found for ' . $package_name . '.'); - } - return; - } - $this->io->write(' - Applying patches for ' . $package_name . ''); - - // Get the install path from the package object. - $manager = $event->getComposer()->getInstallationManager(); - $install_path = $manager->getInstaller($package->getType())->getInstallPath($package); - - // Set up a downloader. - $downloader = new RemoteFilesystem($this->io, $this->composer->getConfig()); - - // Track applied patches in the package info in installed.json - $localRepository = $this->composer->getRepositoryManager()->getLocalRepository(); - $localPackage = $localRepository->findPackage($package_name, $package->getVersion()); - $extra = $localPackage->getExtra(); - $extra['patches_applied'] = array(); - - foreach ($this->patches[$package_name] as $description => $url) { - $this->io->write(' ' . $url . ' (' . $description. ')'); - try { - $this->eventDispatcher->dispatch(NULL, new PatchEvent(PatchEvents::PRE_PATCH_APPLY, $package, $url, $description)); - $this->getAndApplyPatch($downloader, $install_path, $url, $package); - $this->eventDispatcher->dispatch(NULL, new PatchEvent(PatchEvents::POST_PATCH_APPLY, $package, $url, $description)); - $extra['patches_applied'][$description] = $url; - } - catch (\Exception $e) { - $this->io->write(' Could not apply patch! Skipping. The error was: ' . $e->getMessage() . ''); - if ($exitOnFailure) { - throw new \Exception("Cannot apply patch $description ($url)!"); - } - } - } - $localPackage->setExtra($extra); - - $this->io->write(''); - - if (true !== $skipReporting) { - $this->writePatchReport($this->patches[$package_name], $install_path); - } - } - - /** - * Get a Package object from an OperationInterface object. - * - * @param OperationInterface $operation - * @return PackageInterface - * @throws \Exception - */ - protected function getPackageFromOperation(OperationInterface $operation) { - if ($operation instanceof InstallOperation) { - $package = $operation->getPackage(); - } - elseif ($operation instanceof UpdateOperation) { - $package = $operation->getTargetPackage(); - } - else { - throw new \Exception('Unknown operation: ' . get_class($operation)); - } - - return $package; - } - - /** - * Apply a patch on code in the specified directory. - * - * @param RemoteFilesystem $downloader - * @param $install_path - * @param $patch_url - * @param PackageInterface $package - * @throws \Exception - */ - protected function getAndApplyPatch(RemoteFilesystem $downloader, $install_path, $patch_url, PackageInterface $package) { - - // Local patch file. - if (file_exists($patch_url)) { - $filename = realpath($patch_url); - } - else { - // Generate random (but not cryptographically so) filename. - $filename = uniqid(sys_get_temp_dir().'/') . ".patch"; - - // Download file from remote filesystem to this location. - $hostname = parse_url($patch_url, PHP_URL_HOST); - - try { - $downloader->copy($hostname, $patch_url, $filename, false); - } catch (\Exception $e) { - // In case of an exception, retry once as the download might - // have failed due to intermittent network issues. - $downloader->copy($hostname, $patch_url, $filename, false); - } - } - - // The order here is intentional. p1 is most likely to apply with git apply. - // p0 is next likely. p2 is extremely unlikely, but for some special cases, - // it might be useful. p4 is useful for Magento 2 patches - $patch_levels = array('-p1', '-p0', '-p2', '-p4'); - - // Check for specified patch level for this package. - $extra = $this->composer->getPackage()->getExtra(); - if (!empty($extra['patchLevel'][$package->getName()])){ - $patch_levels = array($extra['patchLevel'][$package->getName()]); - } - // Attempt to apply with git apply. - $patched = $this->applyPatchWithGit($install_path, $patch_levels, $filename); - - // In some rare cases, git will fail to apply a patch, fallback to using - // the 'patch' command. - if (!$patched) { - foreach ($patch_levels as $patch_level) { - // --no-backup-if-mismatch here is a hack that fixes some - // differences between how patch works on windows and unix. - if ($patched = $this->executeCommand("patch %s --no-backup-if-mismatch -d %s < %s", $patch_level, $install_path, $filename)) { - break; - } - } - } - - // Clean up the temporary patch file. - if (isset($hostname)) { - unlink($filename); - } - // If the patch *still* isn't applied, then give up and throw an Exception. - // Otherwise, let the user know it worked. - if (!$patched) { - throw new \Exception("Cannot apply patch $patch_url"); - } - } - - /** - * Checks if the root package enables patching. - * - * @return bool - * Whether patching is enabled. Defaults to TRUE. - */ - protected function isPatchingEnabled() { - $extra = $this->composer->getPackage()->getExtra(); - - if (empty($extra['patches']) && empty($extra['patches-ignore']) && !isset($extra['patches-file'])) { - // The root package has no patches of its own, so only allow patching if - // it has specifically opted in. - return isset($extra['enable-patching']) ? $extra['enable-patching'] : FALSE; - } - else { - return TRUE; - } - } - - /** - * Writes a patch report to the target directory. - * - * @param array $patches - * @param string $directory - */ - protected function writePatchReport($patches, $directory) { - $output = "This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches)\n"; - $output .= "Patches applied to this directory:\n\n"; - foreach ($patches as $description => $url) { - $output .= $description . "\n"; - $output .= 'Source: ' . $url . "\n\n\n"; - } - file_put_contents($directory . "/PATCHES.txt", $output); - } - - /** - * Executes a shell command with escaping. - * - * @param string $cmd - * @return bool - */ - protected function executeCommand($cmd) { - // Shell-escape all arguments except the command. - $args = func_get_args(); - foreach ($args as $index => $arg) { - if ($index !== 0) { - $args[$index] = escapeshellarg($arg); - } - } - - // And replace the arguments. - $command = call_user_func_array('sprintf', $args); - $output = ''; - if ($this->io->isVerbose()) { - $this->io->write('' . $command . ''); - $io = $this->io; - $output = function ($type, $data) use ($io) { - if ($type == Process::ERR) { - $io->write('' . $data . ''); - } - else { - $io->write('' . $data . ''); - } - }; - } - return ($this->executor->execute($command, $output) == 0); - } - - /** - * Recursively merge arrays without changing data types of values. - * - * Does not change the data types of the values in the arrays. Matching keys' - * values in the second array overwrite those in the first array, as is the - * case with array_merge. - * - * @param array $array1 - * The first array. - * @param array $array2 - * The second array. - * @return array - * The merged array. - * - * @see http://php.net/manual/en/function.array-merge-recursive.php#92195 - */ - protected function arrayMergeRecursiveDistinct(array $array1, array $array2) { - $merged = $array1; - - foreach ($array2 as $key => &$value) { - if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) { - $merged[$key] = $this->arrayMergeRecursiveDistinct($merged[$key], $value); - } - else { - $merged[$key] = $value; - } - } - - return $merged; - } - - /** - * Attempts to apply a patch with git apply. - * - * @param $install_path - * @param $patch_levels - * @param $filename - * - * @return bool - * TRUE if patch was applied, FALSE otherwise. - */ - protected function applyPatchWithGit($install_path, $patch_levels, $filename) { - // Do not use git apply unless the install path is itself a git repo - // @see https://stackoverflow.com/a/27283285 - if (!is_dir($install_path . '/.git')) { - return FALSE; - } - - $patched = FALSE; - foreach ($patch_levels as $patch_level) { - if ($this->io->isVerbose()) { - $comment = 'Testing ability to patch with git apply.'; - $comment .= ' This command may produce errors that can be safely ignored.'; - $this->io->write('' . $comment . ''); - } - $checked = $this->executeCommand('git -C %s apply --check -v %s %s', $install_path, $patch_level, $filename); - $output = $this->executor->getErrorOutput(); - if (substr($output, 0, 7) == 'Skipped') { - // Git will indicate success but silently skip patches in some scenarios. - // - // @see https://github.com/cweagans/composer-patches/pull/165 - $checked = FALSE; - } - if ($checked) { - // Apply the first successful style. - $patched = $this->executeCommand('git -C %s apply %s %s', $install_path, $patch_level, $filename); - break; - } - } - return $patched; - } - - /** - * Indicates if a package has been patched. - * - * @param \Composer\Package\PackageInterface $package - * The package to check. - * - * @return bool - * TRUE if the package has been patched. - */ - public static function isPackagePatched(PackageInterface $package) { - return array_key_exists('patches_applied', $package->getExtra()); - } - - /** - * {@inheritDoc} - */ - public function deactivate(Composer $composer, IOInterface $io) - { - } - - /** - * {@inheritDoc} - */ - public function uninstall(Composer $composer, IOInterface $io) - { - } - -} diff --git a/vendor/cweagans/composer-patches/tests/PatchEventTest.php b/vendor/cweagans/composer-patches/tests/PatchEventTest.php deleted file mode 100644 index 0f6adb7..0000000 --- a/vendor/cweagans/composer-patches/tests/PatchEventTest.php +++ /dev/null @@ -1,39 +0,0 @@ -assertEquals($event_name, $patch_event->getName()); - $this->assertEquals($package, $patch_event->getPackage()); - $this->assertEquals($url, $patch_event->getUrl()); - $this->assertEquals($description, $patch_event->getDescription()); - } - - public function patchEventDataProvider() { - $prophecy = $this->prophesize('Composer\Package\PackageInterface'); - $package = $prophecy->reveal(); - - return array( - array(PatchEvents::PRE_PATCH_APPLY, $package, 'https://www.drupal.org', 'A test patch'), - array(PatchEvents::POST_PATCH_APPLY, $package, 'https://www.drupal.org', 'A test patch'), - ); - } - -} diff --git a/vendor/iio/libmergepdf/.github/ISSUE_TEMPLATE/bug_report.md b/vendor/iio/libmergepdf/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index e6a8753..0000000 --- a/vendor/iio/libmergepdf/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Include files** -Please include the files that you are trying to merge so that the error can be reproduced. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Please complete the following information:** - - PHP-version - - Version of library [e.g. 22] - - Driver used - -**Additional context** -Add any other context about the problem here. - -> Please note that this library is only a wrapper around other pdf editing tools. -> As such we are only able to respond to problems regaring the actual -> merging process. Issues concering deeper pdf parsing problems should -> be directed elsewhere. diff --git a/vendor/iio/libmergepdf/PATCHES.txt b/vendor/iio/libmergepdf/PATCHES.txt deleted file mode 100644 index ae343e1..0000000 --- a/vendor/iio/libmergepdf/PATCHES.txt +++ /dev/null @@ -1,7 +0,0 @@ -This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches) -Patches applied to this directory: - -Add PHP 8.2 support -Source: patches/iio-libmergepdf-support-php82.patch - - diff --git a/vendor/iio/libmergepdf/composer.json b/vendor/iio/libmergepdf/composer.json deleted file mode 100644 index 195d0aa..0000000 --- a/vendor/iio/libmergepdf/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "iio/libmergepdf", - "description": "Library for merging multiple PDFs", - "keywords": ["pdf", "merge"], - "homepage": "https://github.com/hanneskod/libmergepdf", - "type": "library", - "license": "WTFPL", - "authors": [ - { - "name": "Hannes Forsgård", - "email": "hannes.forsgard@fripost.org" - } - ], - "autoload": { - "psr-4": { - "iio\\libmergepdf\\": "src/" - }, - "classmap": [ - "tcpdi/" - ] - }, - "require": { - "php": "^7.1||^8.0", - "tecnickcom/tcpdf": "^6.2.22", - "setasign/fpdi": "^2" - }, - "conflict": { - "setasign/fpdf": "*", - "rafikhaceb/tcpdi": "*" - }, - "require-dev": { - "phpunit/phpunit": "^7|^8", - "smalot/pdfparser": "~0.13" - } -} diff --git a/vendor/iio/libmergepdf/src/Driver/DefaultDriver.php b/vendor/iio/libmergepdf/src/Driver/DefaultDriver.php deleted file mode 100644 index 9ad6f73..0000000 --- a/vendor/iio/libmergepdf/src/Driver/DefaultDriver.php +++ /dev/null @@ -1,25 +0,0 @@ -wrapped = $wrapped ?: new Fpdi2Driver; - } - - public function merge(SourceInterface ...$sources): string - { - return $this->wrapped->merge(...$sources); - } -} diff --git a/vendor/iio/libmergepdf/src/Driver/DriverInterface.php b/vendor/iio/libmergepdf/src/Driver/DriverInterface.php deleted file mode 100644 index 39cca1e..0000000 --- a/vendor/iio/libmergepdf/src/Driver/DriverInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -fpdi = $fpdi ?: @new FpdiTcpdf; - - if (!($this->fpdi instanceof FpdiFpdf) && !($this->fpdi instanceof FpdiTcpdf)) { - throw new \InvalidArgumentException('Constructor argument must be an FPDI instance.'); - } - } - - public function merge(SourceInterface ...$sources): string - { - $sourceName = ''; - - try { - $fpdi = clone $this->fpdi; - - foreach ($sources as $source) { - $sourceName = $source->getName(); - $pageCount = $fpdi->setSourceFile(StreamReader::createByString($source->getContents())); - $pageNumbers = $source->getPages()->getPageNumbers() ?: range(1, $pageCount); - - foreach ($pageNumbers as $pageNr) { - $template = $fpdi->importPage($pageNr); - $size = $fpdi->getTemplateSize($template); - $fpdi->SetPrintHeader(false); - $fpdi->SetPrintFooter(false); - $fpdi->AddPage( - $size['width'] > $size['height'] ? 'L' : 'P', - [$size['width'], $size['height']] - ); - $fpdi->useTemplate($template); - } - } - - return $fpdi->Output('', 'S'); - } catch (\Exception $e) { - throw new Exception("'{$e->getMessage()}' in '$sourceName'", 0, $e); - } - } -} diff --git a/vendor/iio/libmergepdf/src/Driver/TcpdiDriver.php b/vendor/iio/libmergepdf/src/Driver/TcpdiDriver.php deleted file mode 100644 index fa0ddc8..0000000 --- a/vendor/iio/libmergepdf/src/Driver/TcpdiDriver.php +++ /dev/null @@ -1,52 +0,0 @@ -tcpdi = $tcpdi ?: new \TCPDI; - } - - public function merge(SourceInterface ...$sources): string - { - $sourceName = ''; - - try { - $tcpdi = clone $this->tcpdi; - - foreach ($sources as $source) { - $sourceName = $source->getName(); - $pageCount = $tcpdi->setSourceData($source->getContents()); - $pageNumbers = $source->getPages()->getPageNumbers() ?: range(1, $pageCount); - - foreach ($pageNumbers as $pageNr) { - $template = $tcpdi->importPage($pageNr); - $size = $tcpdi->getTemplateSize($template); - $tcpdi->SetPrintHeader(false); - $tcpdi->SetPrintFooter(false); - $tcpdi->AddPage( - $size['w'] > $size['h'] ? 'L' : 'P', - [$size['w'], $size['h']] - ); - $tcpdi->useTemplate($template); - } - } - - return $tcpdi->Output('', 'S'); - } catch (\Exception $e) { - throw new Exception("'{$e->getMessage()}' in '$sourceName'", 0, $e); - } - } -} diff --git a/vendor/iio/libmergepdf/src/Exception.php b/vendor/iio/libmergepdf/src/Exception.php deleted file mode 100644 index 69b3208..0000000 --- a/vendor/iio/libmergepdf/src/Exception.php +++ /dev/null @@ -1,7 +0,0 @@ -driver = $driver ?: new DefaultDriver; - } - - /** - * Add raw PDF from string - */ - public function addRaw(string $content, PagesInterface $pages = null): void - { - $this->sources[] = new RawSource($content, $pages); - } - - /** - * Add PDF from file - */ - public function addFile(string $filename, PagesInterface $pages = null): void - { - $this->sources[] = new FileSource($filename, $pages); - } - - /** - * Add files using iterator - * - * @param iterable $iterator Set of filenames to add - * @param PagesInterface $pages Optional pages constraint used for every added pdf - */ - public function addIterator(iterable $iterator, PagesInterface $pages = null): void - { - foreach ($iterator as $filename) { - $this->addFile($filename, $pages); - } - } - - /** - * Merges loaded PDFs - */ - public function merge(): string - { - return $this->driver->merge(...$this->sources); - } - - /** - * Reset internal state - */ - public function reset(): void - { - $this->sources = []; - } -} diff --git a/vendor/iio/libmergepdf/src/Pages.php b/vendor/iio/libmergepdf/src/Pages.php deleted file mode 100644 index 7675315..0000000 --- a/vendor/iio/libmergepdf/src/Pages.php +++ /dev/null @@ -1,67 +0,0 @@ -addPage((int)$expr); - continue; - } - if (preg_match("/^(\d+)-(\d+)/", $expr, $matches)) { - $this->addRange((int)$matches[1], (int)$matches[2]); - continue; - } - throw new Exception("Invalid page number(s) for expression '$expr'"); - } - } - - /** - * Add a single page - */ - public function addPage(int $page): void - { - $this->pages[] = $page; - } - - /** - * Add a range of pages - */ - public function addRange(int $start, int $end): void - { - $this->pages = array_merge($this->pages, range($start, $end)); - } - - public function getPageNumbers(): array - { - return $this->pages; - } -} diff --git a/vendor/iio/libmergepdf/src/PagesInterface.php b/vendor/iio/libmergepdf/src/PagesInterface.php deleted file mode 100644 index e85d3c8..0000000 --- a/vendor/iio/libmergepdf/src/PagesInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -filename = $filename; - $this->pages = $pages ?: new Pages; - } - - public function getName(): string - { - return $this->filename; - } - - public function getContents(): string - { - return (string)file_get_contents($this->filename); - } - - public function getPages(): PagesInterface - { - return $this->pages; - } -} diff --git a/vendor/iio/libmergepdf/src/Source/RawSource.php b/vendor/iio/libmergepdf/src/Source/RawSource.php deleted file mode 100644 index 7966918..0000000 --- a/vendor/iio/libmergepdf/src/Source/RawSource.php +++ /dev/null @@ -1,45 +0,0 @@ -contents = $contents; - $this->pages = $pages ?: new Pages; - } - - public function getName(): string - { - return "raw-content"; - } - - public function getContents(): string - { - return $this->contents; - } - - public function getPages(): PagesInterface - { - return $this->pages; - } -} diff --git a/vendor/iio/libmergepdf/src/Source/SourceInterface.php b/vendor/iio/libmergepdf/src/Source/SourceInterface.php deleted file mode 100644 index 4ce9c3c..0000000 --- a/vendor/iio/libmergepdf/src/Source/SourceInterface.php +++ /dev/null @@ -1,25 +0,0 @@ -AddPage(); -$pdf->setSourceFile('/path/to/file-to-import.pdf'); -$idx = $pdf->importPage(1); -$pdf->useTemplate($idx); - -$pdfdata = file_get_contents('/path/to/other-file.pdf'); // Simulate only having raw data available. -$pagecount = $pdf->setSourceData($pdfdata); -for ($i = 1; $i <= $pagecount; $i++) { - $tplidx = $pdf->importPage($i); - $pdf->AddPage(); - $pdf->useTemplate($tplidx); -} -``` - -As of version 1.1, TCPDI also includes additional functionality for handling PDF Annotations. As annotations are positioned relative to the bleed box rather than the crop box, you'll need to ensure that you're importing the full bleed box; a new function has also been introduced to set the page format (the various boxes, including the crop box) from the imported page, so that the imported page matches the original better. The following example demonstrates this: - -```php -// Create new PDF document. -$pdf = new TCPDI(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); - -// Add a page from a PDF by file path. -$pdf->setSourceFile('/path/to/file-to-import.pdf'); - -// Import the bleed box (default is crop box) for page 1. -$tplidx = $pdf->importPage(1, '/BleedBox'); -$size = $pdf->getTemplatesize($tplidx); -$orientation = ($size['w'] > $size['h']) ? 'L' : 'P'; - -$pdf->AddPage($orientation); - -// Set page boxes from imported page 1. -$pdf->setPageFormatFromTemplatePage(1, $orientation); - -// Import the content for page 1. -$pdf->useTemplate($tplidx); - -// Import the annotations for page 1. -$pdf->importAnnotations(1); -``` - -TCPDI_PARSER -============ - -Parser for use with TCPDI, based on TCPDF_PARSER. Supports PDFs up to v1.7. diff --git a/vendor/karriere/pdf-merge/.github/workflows/ci.yml b/vendor/karriere/pdf-merge/.github/workflows/ci.yml new file mode 100644 index 0000000..3e5deea --- /dev/null +++ b/vendor/karriere/pdf-merge/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + strategy: + fail-fast: true + matrix: + php: [ "8.1", "8.2", "8.3", "8.4" ] + + runs-on: ubuntu-latest + name: PHP@${{ matrix.php }} + + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + + - uses: actions/checkout@v4 + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Lint code + run: composer run-script lint + + - name: Analyse code + run: composer run-script analyse + + - name: Test code + run: composer run-script test diff --git a/vendor/karriere/pdf-merge/.gitignore b/vendor/karriere/pdf-merge/.gitignore new file mode 100644 index 0000000..dbb9aa7 --- /dev/null +++ b/vendor/karriere/pdf-merge/.gitignore @@ -0,0 +1,7 @@ +vendor/ +coverage/ +tests/output.pdf +.phpunit.result.cache +composer.lock +coverage.xml +junit.xml diff --git a/vendor/karriere/pdf-merge/CHANGELOG.md b/vendor/karriere/pdf-merge/CHANGELOG.md new file mode 100644 index 0000000..17f7d90 --- /dev/null +++ b/vendor/karriere/pdf-merge/CHANGELOG.md @@ -0,0 +1,81 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +## [3.3.1] - 2025-02-19 +### Added +- `@throws` to PHPDoc for better IDE support + +## [3.3.0] - 2025-02-19 +### Added +- Method `getFiles()` + +## [3.2.0] - 2024-12-05 +### Added +- Support for PHP 8.4 + +### Removed +- Support for PHP 8.0 + +## [3.1.0] - 2024-02-05 +### Added +- Support for PHP 8.3 + +## [3.0.0] - 2023-03-01 +### Added +- Support for PHP 8.2 + +### Changed +- [BREAKING] Header and footer config with dedicated classes instead of arrays +- Linting to `pint` +- Unit tests to `pest` + +### Removed +- Support for PHP 7.4 + +## [2.1.0] - 2022-12-07 +### Added +- Support for merging PDFs with mixed orientations (portrait and landscape) + +### Changed +- Default header and footer are removed if header/footer-data is empty + +## [2.0.0] - 2022-08-10 +### Added +- Support for PHP 8.1 +- Ability to get `TCPDF`-instance by calling `(new PdfMerge())->getPdf()` + +### Changed +- [BREAKING] `PdfMerge::merge()` now returns `string` instead of `bool` +- `PdfMerge::merge()` now accepts `string $destination` as second parameter + +### Removed +- Dropped support for PHP < 7.4 + +## [1.3.0] - 2021-10-20 +### Added +- Support for PHP 8 + +## [1.2.0] - 2021-09-06 +### Added +- Ability to set `tcpdf` footer and header data via PdfMerge constructor + +## [1.1.1] - 2020-04-03 +### Fixed +- removed unused imagick options + +## [1.1.0] - 2020-03-30 +### Added +- tcpdf dependency +- tcpdi implementation + +### Removed +- fpdf and fpdi dependencies + +## [1.0.0] - 2020-03-30 + +### Added +- Pdf merging implementation based on fpdf & fpdi diff --git a/vendor/karriere/pdf-merge/LICENSE b/vendor/karriere/pdf-merge/LICENSE new file mode 100644 index 0000000..760a4e8 --- /dev/null +++ b/vendor/karriere/pdf-merge/LICENSE @@ -0,0 +1,188 @@ + Copyright 2017 karriere.at GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/vendor/karriere/pdf-merge/README.md b/vendor/karriere/pdf-merge/README.md new file mode 100644 index 0000000..70a91e1 --- /dev/null +++ b/vendor/karriere/pdf-merge/README.md @@ -0,0 +1,67 @@ + +    +![](https://github.com/karriereat/pdf-merge/workflows/CI/badge.svg) +[![Packagist Downloads](https://img.shields.io/packagist/dt/karriere/pdf-merge.svg?style=flat-square)](https://packagist.org/packages/karriere/pdf-merge) + +# Pdf Merge Solution for PHP + +This package is a wrapper for the `TCPDF` class that provides an elegant API for merging PDF files. + +## Installation + +You can install the package via composer: + +```bash +composer require karriere/pdf-merge +``` + +## Usage + +```php +use Karriere\PdfMerge\PdfMerge; + +$pdfMerge = new PdfMerge(); + +$pdfMerge->add('/path/to/file1.pdf'); +$pdfMerge->add('/path/to/file2.pdf'); + +$pdfMerge->merge('/path/to/output.pdf'); +``` + +Please note, that the `merge()`-method will throw a `NoFilesDefinedException` if no files where added. + +### Check for file existence +You can check if a file was already added for merging by calling: + +```php +$pdfMerge->contains('/path/to/file.pdf'); +``` + +### Configuring header and footer +You can also configure the header of footer of all pages like this: + +```php +use Karriere\PdfMerge\PdfMerge; + +$pdfMerge = new PdfMerge( + new HeaderConfig( + imagePath: 'header_logo.png', + logoWidthMM: 20, + title: 'Header', + text: 'This is a header text', + textColor: new RGB(200, 200, 200), + lineColor: new RGB(0, 0, 255), + ), + new FooterConfig( + textColor: new RGB(100, 100, 100), + lineColor: new RGB(255, 0, 0), + margin: 20, + ), + ); +``` + +All config properties have default values, so you don't have to pass them all. + +## License + +Apache License 2.0 Please see [LICENSE](LICENSE) for more information. diff --git a/vendor/karriere/pdf-merge/UPGRADE.md b/vendor/karriere/pdf-merge/UPGRADE.md new file mode 100644 index 0000000..d98ecea --- /dev/null +++ b/vendor/karriere/pdf-merge/UPGRADE.md @@ -0,0 +1,69 @@ +# Upgrade Guide + +## v2 to v3 +The only breaking chance in this release is the way a header and/or footer configuration is done. +In version 2 this was done with simple arrays, in v3 dedicated classes were introduced to make the +configuration type safe and bullet proof. + +### Header/Footer Configuration +```php +// Before +use Karriere\PdfMerge\PdfMerge; + +new PdfMerge([ + 'ln' => 'header_logo.png', + 'lw' => 20, + 'ht' => 'title', + 'hs' => 'more text', + 'tc' => [255, 255, 255], + 'lc' => [0, 0, 0]. +], +[ + 'tc' => [255, 255, 255], + 'lc' => [0, 0, 0]. +]); + +// After +use Karriere\PdfMerge\Config\FooterConfig; +use Karriere\PdfMerge\Config\HeaderConfig; +use Karriere\PdfMerge\Config\RGB; +use Karriere\PdfMerge\PdfMerge; + +new PdfMerge( + new HeaderConfig( + imagePath: 'header_logo.png', + logoWidthMM: 20, + title: 'Header', + text: 'This is a header text', + textColor: new RGB(200, 200, 200), + lineColor: new RGB(0, 0, 255), + ), + new FooterConfig( + textColor: new RGB(100, 100, 100), + lineColor: new RGB(255, 0, 0), + margin: 20, + ), +); +``` + +The above example shows the full feature set of header/footer configuration, but when using named arguments you can +choose a subset of configuration values as well because all other values have defaults in place: + +```php +use Karriere\PdfMerge\Config\FooterConfig; +use Karriere\PdfMerge\Config\HeaderConfig; +use Karriere\PdfMerge\Config\RGB; +use Karriere\PdfMerge\PdfMerge; + +new PdfMerge( + new HeaderConfig( + imagePath: 'header_logo.png', + text: 'This is a header text', + textColor: new RGB(200, 200, 200), + ), + new FooterConfig( + lineColor: new RGB(255, 0, 0), + ), +); +``` + diff --git a/vendor/karriere/pdf-merge/composer.json b/vendor/karriere/pdf-merge/composer.json new file mode 100644 index 0000000..0603bd6 --- /dev/null +++ b/vendor/karriere/pdf-merge/composer.json @@ -0,0 +1,53 @@ +{ + "name": "karriere/pdf-merge", + "description": "A wrapper for the TCPDF class that provides an elegant API for merging PDFs", + "keywords": ["pdf", "merge"], + "license": "Apache-2.0", + "authors": [ + { + "name": "Alexander Lentner", + "email": "alexander.lentner@karriere.at", + "role": "Maintainer" + } + ], + "require": { + "php": "8.1.* | 8.2.* | 8.3.* | 8.4.*", + "tecnickcom/tcpdf": "^6.3" + }, + "require-dev": { + "laravel/pint": "^1.5 | ^1.6", + "pestphp/pest": "^1.22", + "phpstan/phpstan": "^1.10" + }, + "autoload": { + "psr-4": { + "Karriere\\PdfMerge\\": "src/" + }, + "classmap": [ + "tcpi/fpdf_tpl.php", + "tcpi/tcpdi.php", + "tcpi/tcpdi_parser.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Karriere\\PdfMerge\\Tests\\": "tests" + } + }, + "scripts": { + "analyse": "phpstan analyse --memory-limit 512M", + "lint": "pint --test", + "lint:verbose": "pint -v --test", + "fix": "pint", + "test": "vendor/bin/pest", + "coverage": "vendor/bin/pest --coverage --ci --coverage-html coverage --coverage-clover coverage.xml --log-junit junit.xml", + "report": "vendor/bin/pest --coverage", + "report:html": "vendor/bin/pest --coverage --coverage-html coverage" + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true + } + } +} diff --git a/vendor/karriere/pdf-merge/phpstan.neon b/vendor/karriere/pdf-merge/phpstan.neon new file mode 100644 index 0000000..43aa29f --- /dev/null +++ b/vendor/karriere/pdf-merge/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + paths: + - src + level: max + treatPhpDocTypesAsCertain: true diff --git a/vendor/karriere/pdf-merge/phpunit.xml b/vendor/karriere/pdf-merge/phpunit.xml new file mode 100644 index 0000000..72c39e5 --- /dev/null +++ b/vendor/karriere/pdf-merge/phpunit.xml @@ -0,0 +1,17 @@ + + + + + ./tests + + + + + ./src + + + diff --git a/vendor/karriere/pdf-merge/pint.json b/vendor/karriere/pdf-merge/pint.json new file mode 100644 index 0000000..e071280 --- /dev/null +++ b/vendor/karriere/pdf-merge/pint.json @@ -0,0 +1,8 @@ +{ + "preset": "psr12", + "rules": { + "concat_space": { + "spacing": "one" + } + } +} diff --git a/vendor/karriere/pdf-merge/src/Config/FooterConfig.php b/vendor/karriere/pdf-merge/src/Config/FooterConfig.php new file mode 100644 index 0000000..6947692 --- /dev/null +++ b/vendor/karriere/pdf-merge/src/Config/FooterConfig.php @@ -0,0 +1,33 @@ +textColor ?: new RGB(); + } + + public function lineColor(): RGB + { + return $this->lineColor ?: new RGB(); + } + + public function margin(): int + { + return $this->margin; + } +} diff --git a/vendor/karriere/pdf-merge/src/Config/HeaderConfig.php b/vendor/karriere/pdf-merge/src/Config/HeaderConfig.php new file mode 100644 index 0000000..d702273 --- /dev/null +++ b/vendor/karriere/pdf-merge/src/Config/HeaderConfig.php @@ -0,0 +1,54 @@ +imagePath; + } + + public function logoWidthMM(): int + { + return $this->logoWidthMM; + } + + public function title(): string + { + return $this->title; + } + + public function text(): string + { + return $this->text; + } + + public function textColor(): RGB + { + return $this->textColor ?: new RGB(); + } + + public function lineColor(): RGB + { + return $this->lineColor ?: new RGB(); + } +} diff --git a/vendor/karriere/pdf-merge/src/Config/RGB.php b/vendor/karriere/pdf-merge/src/Config/RGB.php new file mode 100644 index 0000000..b9bde8e --- /dev/null +++ b/vendor/karriere/pdf-merge/src/Config/RGB.php @@ -0,0 +1,18 @@ + + */ + public function toArray(): array + { + return [$this->red, $this->green, $this->blue]; + } +} diff --git a/vendor/karriere/pdf-merge/src/Exceptions/FileNotFoundException.php b/vendor/karriere/pdf-merge/src/Exceptions/FileNotFoundException.php new file mode 100644 index 0000000..9ee6138 --- /dev/null +++ b/vendor/karriere/pdf-merge/src/Exceptions/FileNotFoundException.php @@ -0,0 +1,13 @@ + + */ + private array $files = []; + private TCPDI $pdf; + + /** + * Passed parameters overrides settings for header and footer by calling tcpdf.php methods: + * setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) + * setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) + * For more info about tcpdf, please read https://tcpdf.org/docs/ + */ + public function __construct(?HeaderConfig $headerConfig = null, ?FooterConfig $footerConfig = null) + { + $this->pdf = new TCPDI(); + $this->configureHeaderAndFooter($headerConfig, $footerConfig); + } + + public function getPdf(): TCPDI + { + return $this->pdf; + } + + /** + * Adds a file to merge + * + * @throws FileNotFoundException + */ + public function add(string $file): void + { + if (!file_exists($file)) { + throw new FileNotFoundException($file); + } + + $this->files[] = $file; + } + + /** + * Checks if the given file is already registered for merging + */ + public function contains(string $file): bool + { + return in_array($file, $this->files); + } + + /** + * Resets the stored files + */ + public function reset(): void + { + $this->files = []; + } + + /** + * Generates a merged PDF file from the already stored PDF files + * + * @throws NoFilesDefinedException + */ + public function merge(string $outputFilename, string $destination = 'F'): string + { + if (count($this->files) === 0) { + throw new NoFilesDefinedException(); + } + + foreach ($this->files as $file) { + $pageCount = $this->pdf->setSourceFile($file); + + for ($i = 1; $i <= $pageCount; $i++) { + $pageId = $this->pdf->ImportPage($i); + $size = $this->pdf->getTemplateSize($pageId); + + $this->pdf->AddPage('', $size); + $this->pdf->useTemplate($pageId, null, null, 0, 0, true); + } + } + + return $this->pdf->Output($outputFilename, $destination); + } + + /** + * @return array + */ + public function getFiles(): array + { + return $this->files; + } + + private function configureHeaderAndFooter(?HeaderConfig $headerConfig, ?FooterConfig $footerConfig): void + { + if ($headerConfig) { + $this->pdf->setHeaderData( + $headerConfig->imagePath(), + $headerConfig->logoWidthMM(), + $headerConfig->title(), + $headerConfig->text(), + $headerConfig->textColor()->toArray(), + $headerConfig->lineColor()->toArray(), + ); + } else { + $this->pdf->setPrintHeader(false); + } + + if ($footerConfig) { + $this->pdf->setFooterData($footerConfig->textColor()->toArray(), $footerConfig->lineColor()->toArray()); + $this->pdf->setFooterMargin($footerConfig->margin()); + } else { + $this->pdf->setPrintFooter(false); + } + } +} diff --git a/vendor/iio/libmergepdf/tcpdi/fpdf_tpl.php b/vendor/karriere/pdf-merge/tcpi/fpdf_tpl.php similarity index 71% rename from vendor/iio/libmergepdf/tcpdi/fpdf_tpl.php rename to vendor/karriere/pdf-merge/tcpi/fpdf_tpl.php index 5322fa8..106d14a 100644 --- a/vendor/iio/libmergepdf/tcpdi/fpdf_tpl.php +++ b/vendor/karriere/pdf-merge/tcpi/fpdf_tpl.php @@ -1,4 +1,5 @@ Error('This method is only usable with FPDF. Use TCPDF methods startTemplate() instead.'); return; } - - if ($this->page <= 0) + + if ($this->page <= 0) { $this->error("You have to add a page to fpdf first!"); + } - if ($x == null) + if ($x == null) { $x = 0; - if ($y == null) + } + if ($y == null) { $y = 0; - if ($w == null) + } + if ($w == null) { $w = $this->w; - if ($h == null) + } + if ($h == null) { $h = $this->h; + } // Save settings $this->tpl++; - $tpl =& $this->tpls[$this->tpl]; + $tpl = & $this->tpls[$this->tpl]; $tpl = array( 'o_x' => $this->x, 'o_y' => $this->y, @@ -116,7 +124,7 @@ function beginTemplate($x = null, $y = null, $w = null, $h = null) { ); $this->SetAutoPageBreak(false); - + // Define own high and width to calculate possitions correct $this->h = $h; $this->w = $w; @@ -127,14 +135,14 @@ function beginTemplate($x = null, $y = null, $w = null, $h = null) { if ($this->CurrentFont) { $fontkey = $this->FontFamily . $this->FontStyle; - $this->_res['tpl'][$this->tpl]['fonts'][$fontkey] =& $this->fonts[$fontkey]; - + $this->_res['tpl'][$this->tpl]['fonts'][$fontkey] = & $this->fonts[$fontkey]; + $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); } - + return $this->tpl; } - + /** * End Template * @@ -142,15 +150,16 @@ function beginTemplate($x = null, $y = null, $w = null, $h = null) { * * @return mixed If a template is opened, the ID is returned. If not a false is returned. */ - function endTemplate() { + public function endTemplate() + { if (is_subclass_of($this, 'TCPDF')) { $args = func_get_args(); - return call_user_func_array(array('TCPDF', 'endTemplate'), $args); + return call_user_func_array([TCPDF::class, 'endTemplate'], $args); } - + if ($this->_intpl) { - $this->_intpl = false; - $tpl =& $this->tpls[$this->tpl]; + $this->_intpl = false; + $tpl = & $this->tpls[$this->tpl]; $this->SetXY($tpl['o_x'], $tpl['o_y']); $this->tMargin = $tpl['o_tMargin']; $this->lMargin = $tpl['o_lMargin']; @@ -158,22 +167,23 @@ function endTemplate() { $this->h = $tpl['o_h']; $this->w = $tpl['o_w']; $this->SetAutoPageBreak($tpl['o_AutoPageBreak'], $tpl['o_bMargin']); - + $this->FontFamily = $tpl['o_FontFamily']; $this->FontStyle = $tpl['o_FontStyle']; $this->FontSizePt = $tpl['o_FontSizePt']; $this->FontSize = $tpl['o_FontSize']; - + $fontkey = $this->FontFamily . $this->FontStyle; - if ($fontkey) - $this->CurrentFont =& $this->fonts[$fontkey]; - + if ($fontkey) { + $this->CurrentFont = & $this->fonts[$fontkey]; + } + return $this->tpl; } else { return false; } } - + /** * Use a Template in current Page or other Template * @@ -191,33 +201,38 @@ function endTemplate() { * @param int $_h The new height of the template * @retrun array The height and width of the template */ - function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0) { - if ($this->page <= 0) + public function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0) + { + if ($this->page <= 0) { $this->error('You have to add a page first!'); - - if (!isset($this->tpls[$tplidx])) + } + + if (!isset($this->tpls[$tplidx])) { $this->error('Template does not exist!'); - + } + if ($this->_intpl) { - $this->_res['tpl'][$this->tpl]['tpls'][$tplidx] =& $this->tpls[$tplidx]; + $this->_res['tpl'][$this->tpl]['tpls'][$tplidx] = & $this->tpls[$tplidx]; } - - $tpl =& $this->tpls[$tplidx]; + + $tpl = & $this->tpls[$tplidx]; $w = $tpl['w']; $h = $tpl['h']; - - if ($_x == null) + + if ($_x == null) { $_x = 0; - if ($_y == null) + } + if ($_y == null) { $_y = 0; - + } + $_x += $tpl['x']; $_y += $tpl['y']; - + $wh = $this->getTemplateSize($tplidx, $_w, $_h); $_w = $wh['w']; $_h = $wh['h']; - + $tData = array( 'x' => $this->x, 'y' => $this->y, @@ -229,15 +244,15 @@ function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0) { 'ty' => ($this->h - $_y - $_h), 'lty' => ($this->h - $_y - $_h) - ($this->h - $h) * ($_h / $h) ); - - $this->_out(sprintf('q %.4F 0 0 %.4F %.4F %.4F cm', $tData['scaleX'], $tData['scaleY'], $tData['tx'] * $this->k, $tData['ty'] * $this->k)); // Translate + + $this->_out(sprintf('q %.4F 0 0 %.4F %.4F %.4F cm', $tData['scaleX'], $tData['scaleY'], $tData['tx'] * $this->k, $tData['ty'] * $this->k)); // Translate $this->_out(sprintf('%s%d Do Q', $this->tplprefix, $tplidx)); $this->lastUsedTemplateData = $tData; - + return array('w' => $_w, 'h' => $_h); } - + /** * Get The calculated Size of a Template * @@ -248,139 +263,169 @@ function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0) { * @param int $_h The height of the template * @return array The height and width of the template */ - function getTemplateSize($tplidx, $_w = 0, $_h = 0) { - if (!isset($this->tpls[$tplidx])) + public function getTemplateSize($tplidx, $_w = 0, $_h = 0) + { + if (!isset($this->tpls[$tplidx])) { return false; + } - $tpl =& $this->tpls[$tplidx]; + $tpl = & $this->tpls[$tplidx]; $w = $tpl['w']; $h = $tpl['h']; - + if ($_w == 0 and $_h == 0) { $_w = $w; $_h = $h; } - if($_w == 0) + if ($_w == 0) { $_w = $_h * $w / $h; - if($_h == 0) + } + if ($_h == 0) { $_h = $_w * $h / $w; - + } + return array("w" => $_w, "h" => $_h); } - + /** * See FPDF/TCPDF-Documentation ;-) */ - public function SetFont($family, $style = '', $size = 0, $fontfile='', $subset='default', $out=true) { + public function SetFont($family, $style = '', $size = 0, $fontfile = '', $subset = 'default', $out = true) + { if (is_subclass_of($this, 'TCPDF')) { $args = func_get_args(); - return call_user_func_array(array('TCPDF', 'SetFont'), $args); + return call_user_func_array([TCPDF::class, 'SetFont'], $args); } - + parent::SetFont($family, $style, $size); - + $fontkey = $this->FontFamily . $this->FontStyle; - + if ($this->_intpl) { - $this->_res['tpl'][$this->tpl]['fonts'][$fontkey] =& $this->fonts[$fontkey]; + $this->_res['tpl'][$this->tpl]['fonts'][$fontkey] = & $this->fonts[$fontkey]; } else { - $this->_res['page'][$this->page]['fonts'][$fontkey] =& $this->fonts[$fontkey]; + $this->_res['page'][$this->page]['fonts'][$fontkey] = & $this->fonts[$fontkey]; } } - + /** * See FPDF/TCPDF-Documentation ;-) */ - function Image( - $file, $x = '', $y = '', $w = 0, $h = 0, $type = '', $link = '', $align = '', $resize = false, - $dpi = 300, $palign = '', $ismask = false, $imgmask = false, $border = 0, $fitbox = false, - $hidden = false, $fitonpage = false, $alt = false, $altimgs = array() + public function Image( + $file, + $x = '', + $y = '', + $w = 0, + $h = 0, + $type = '', + $link = '', + $align = '', + $resize = false, + $dpi = 300, + $palign = '', + $ismask = false, + $imgmask = false, + $border = 0, + $fitbox = false, + $hidden = false, + $fitonpage = false, + $alt = false, + $altimgs = [] ) { if (is_subclass_of($this, 'TCPDF')) { $args = func_get_args(); - return call_user_func_array(array('TCPDF', 'Image'), $args); + return call_user_func_array([TCPDF::class, 'Image'], $args); } - + $ret = parent::Image($file, $x, $y, $w, $h, $type, $link); if ($this->_intpl) { - $this->_res['tpl'][$this->tpl]['images'][$file] =& $this->images[$file]; + $this->_res['tpl'][$this->tpl]['images'][$file] = & $this->images[$file]; } else { - $this->_res['page'][$this->page]['images'][$file] =& $this->images[$file]; + $this->_res['page'][$this->page]['images'][$file] = & $this->images[$file]; } - + return $ret; } - + /** * See FPDF-Documentation ;-) * * AddPage is not available when you're "in" a template. */ - function AddPage($orientation = '', $format = '', $keepmargins = false, $tocpage = false) { + public function AddPage($orientation = '', $format = '', $keepmargins = false, $tocpage = false) + { if (is_subclass_of($this, 'TCPDF')) { $args = func_get_args(); - return call_user_func_array(array('TCPDF', 'AddPage'), $args); + return call_user_func_array([TCPDF::class, 'AddPage'], $args); } - - if ($this->_intpl) + + if ($this->_intpl) { $this->Error('Adding pages in templates isn\'t possible!'); - + } + parent::AddPage($orientation, $format); } /** * Preserve adding Links in Templates ...won't work */ - function Link($x, $y, $w, $h, $link, $spaces = 0) { + public function Link($x, $y, $w, $h, $link, $spaces = 0) + { if (is_subclass_of($this, 'TCPDF')) { $args = func_get_args(); - return call_user_func_array(array('TCPDF', 'Link'), $args); + return call_user_func_array([TCPDF::class, 'Link'], $args); } - - if ($this->_intpl) + + if ($this->_intpl) { $this->Error('Using links in templates aren\'t possible!'); - + } + parent::Link($x, $y, $w, $h, $link); } - - function AddLink() { + + public function AddLink() + { if (is_subclass_of($this, 'TCPDF')) { $args = func_get_args(); - return call_user_func_array(array('TCPDF', 'AddLink'), $args); + return call_user_func_array([TCPDF::class, 'AddLink'], $args); } - - if ($this->_intpl) + + if ($this->_intpl) { $this->Error('Adding links in templates aren\'t possible!'); + } return parent::AddLink(); } - - function SetLink($link, $y = 0, $page = -1) { + + public function SetLink($link, $y = 0, $page = -1) + { if (is_subclass_of($this, 'TCPDF')) { $args = func_get_args(); - return call_user_func_array(array('TCPDF', 'SetLink'), $args); + return call_user_func_array([TCPDF::class, 'SetLink'], $args); } - - if ($this->_intpl) + + if ($this->_intpl) { $this->Error('Setting links in templates aren\'t possible!'); + } parent::SetLink($link, $y, $page); } - + /** * Private Method that writes the form xobjects */ - function _putformxobjects() { - $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + public function _putformxobjects() + { + $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; reset($this->tpls); - foreach($this->tpls AS $tplidx => $tpl) { - - $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; + foreach ($this->tpls as $tplidx => $tpl) { + $p = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; $this->_newobj(); $this->tpls[$tplidx]['n'] = $this->n; - $this->_out('<<'.$filter.'/Type /XObject'); + $this->_out('<<' . $filter . '/Type /XObject'); $this->_out('/Subtype /Form'); $this->_out('/FormType 1'); - $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]', + $this->_out(sprintf( + '/BBox [%.2F %.2F %.2F %.2F]', // llx $tpl['x'] * $this->k, // lly @@ -390,58 +435,64 @@ function _putformxobjects() { // ury ($tpl['h'] - $tpl['y']) * $this->k )); - + if ($tpl['x'] != 0 || $tpl['y'] != 0) { - $this->_out(sprintf('/Matrix [1 0 0 1 %.5F %.5F]', - -$tpl['x'] * $this->k * 2, $tpl['y'] * $this->k * 2 + $this->_out(sprintf( + '/Matrix [1 0 0 1 %.5F %.5F]', + -$tpl['x'] * $this->k * 2, + $tpl['y'] * $this->k * 2 )); } - + $this->_out('/Resources '); $this->_out('<_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) { $this->_out('/Font <<'); - foreach($this->_res['tpl'][$tplidx]['fonts'] as $font) + foreach ($this->_res['tpl'][$tplidx]['fonts'] as $font) { $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R'); + } $this->_out('>>'); } - if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) || - isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) - { + if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) || + isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) { $this->_out('/XObject <<'); if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) { - foreach($this->_res['tpl'][$tplidx]['images'] as $image) - $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R'); + foreach ($this->_res['tpl'][$tplidx]['images'] as $image) { + $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R'); + } } if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) { - foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl) + foreach ($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl) { $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R'); + } } $this->_out('>>'); } $this->_out('>>'); - + $this->_out('/Length ' . strlen($p) . ' >>'); $this->_putstream($p); $this->_out('endobj'); } } - + /** * Overwritten to add _putformxobjects() after _putimages() * */ - function _putimages() { + public function _putimages() + { parent::_putimages(); $this->_putformxobjects(); } - - function _putxobjectdict() { + + public function _putxobjectdict() + { parent::_putxobjectdict(); - + if (count($this->tpls)) { - foreach($this->tpls as $tplidx => $tpl) { + foreach ($this->tpls as $tplidx => $tpl) { $this->_out(sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n'])); } } @@ -450,7 +501,8 @@ function _putxobjectdict() { /** * Private Method */ - function _out($s) { + public function _out($s) + { if ($this->state == 2 && $this->_intpl) { $this->tpls[$this->tpl]['buffer'] .= $s . "\n"; } else { diff --git a/vendor/iio/libmergepdf/tcpdi/tcpdi.php b/vendor/karriere/pdf-merge/tcpi/tcpdi.php similarity index 80% rename from vendor/iio/libmergepdf/tcpdi/tcpdi.php rename to vendor/karriere/pdf-merge/tcpi/tcpdi.php index f210d42..ce9725a 100644 --- a/vendor/iio/libmergepdf/tcpdi/tcpdi.php +++ b/vendor/karriere/pdf-merge/tcpi/tcpdi.php @@ -1,4 +1,5 @@ current_filename = $filename; - if (!isset($this->parsers[$filename])) + if (!isset($this->parsers[$filename])) { $this->parsers[$filename] = $this->_getPdfParser($filename); - $this->current_parser =& $this->parsers[$filename]; + } + $this->current_parser = & $this->parsers[$filename]; $this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion())); return $this->parsers[$filename]->getPageCount(); @@ -116,13 +122,15 @@ public function setSourceFile($filename) { * @param string $pdfdata The PDF file content * @return int number of available pages */ - public function setSourceData($pdfdata) { + public function setSourceData($pdfdata) + { $filename = uniqid('tcpdi-'); $this->current_filename = $filename; - if (!isset($this->parsers[$filename])) + if (!isset($this->parsers[$filename])) { $this->parsers[$filename] = new tcpdi_parser($pdfdata, $filename); - $this->current_parser =& $this->parsers[$filename]; + } + $this->current_parser = & $this->parsers[$filename]; $this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion())); return $this->parsers[$filename]->getPageCount(); @@ -134,7 +142,8 @@ public function setSourceData($pdfdata) { * @param string $filename * @return fpdi_pdf_parser */ - protected function _getPdfParser($filename) { + public function _getPdfParser($filename) + { $data = file_get_contents($filename); return new tcpdi_parser($data, $filename); } @@ -144,7 +153,8 @@ protected function _getPdfParser($filename) { * * @return string */ - public function getPDFVersion() { + public function getPDFVersion() + { return $this->PDFVersion; } @@ -153,7 +163,8 @@ public function getPDFVersion() { * * @return string */ - public function setPDFVersion($version = '1.3') { + public function setPDFVersion($version = '1.3') + { $this->PDFVersion = $version; } @@ -163,7 +174,8 @@ public function setPDFVersion($version = '1.3') { * @param int $pageno pagenumber * @return int Index of imported page - to use with fpdf_tpl::useTemplate() */ - public function importPage($pageno, $boxName = '/CropBox') { + public function importPage($pageno, $boxName = '/CropBox') + { if ($this->_intpl) { return $this->error('Please import the desired pages before creating a new template.'); } @@ -172,14 +184,16 @@ public function importPage($pageno, $boxName = '/CropBox') { // check if page already imported $pageKey = $fn . '-' . ((int)$pageno) . $boxName; - if (isset($this->_importedPages[$pageKey])) + if (isset($this->_importedPages[$pageKey])) { return $this->_importedPages[$pageKey]; + } - $parser =& $this->parsers[$fn]; + $parser = & $this->parsers[$fn]; $parser->setPageno($pageno); - if (!in_array($boxName, $parser->availableBoxes)) + if (!in_array($boxName, $parser->availableBoxes)) { return $this->Error(sprintf('Unknown box: %s', $boxName)); + } $pageboxes = $parser->getPageBoxes($pageno, $this->k); @@ -190,13 +204,16 @@ public function importPage($pageno, $boxName = '/CropBox') { * TrimBox: Default -> CropBox * ArtBox: Default -> CropBox */ - if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox')) + if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox')) { $boxName = '/CropBox'; - if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox') + } + if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox') { $boxName = '/MediaBox'; + } - if (!isset($pageboxes[$boxName])) + if (!isset($pageboxes[$boxName])) { return false; + } $this->lastUsedPageBox = $boxName; @@ -204,8 +221,8 @@ public function importPage($pageno, $boxName = '/CropBox') { $this->tpl++; $this->tpls[$this->tpl] = array(); - $tpl =& $this->tpls[$this->tpl]; - $tpl['parser'] =& $parser; + $tpl = & $this->tpls[$this->tpl]; + $tpl['parser'] = & $parser; $tpl['resources'] = $parser->getPageResources(); $tpl['buffer'] = $parser->getContent(); $tpl['box'] = $box; @@ -228,20 +245,21 @@ public function importPage($pageno, $boxName = '/CropBox') { $tpl['w'] = $steps % 2 == 0 ? $_w : $_h; $tpl['h'] = $steps % 2 == 0 ? $_h : $_w; - if ($angle < 0) + if ($angle < 0) { $angle += 360; + } $tpl['_rotationAngle'] = $angle * -1; } $this->_importedPages[$pageKey] = $this->tpl; - return $this->tpl; } - public function setPageFormatFromTemplatePage($pageno, $orientation) { + public function setPageFormatFromTemplatePage($pageno, $orientation) + { $fn = $this->current_filename; - $parser =& $this->parsers[$fn]; + $parser = & $this->parsers[$fn]; $parser->setPageno($pageno); $boxes = $parser->getPageBoxes($pageno, $this->k); foreach ($boxes as $name => $box) { @@ -254,7 +272,8 @@ public function setPageFormatFromTemplatePage($pageno, $orientation) { } /* Wrapper for AddPage() which tracks TOC pages to offset annotations later */ - public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) { + public function AddPage($orientation = '', $format = '', $keepmargins = false, $tocpage = false) + { if ($this->inxobj) { // we are inside an XObject template return; @@ -266,7 +285,8 @@ public function AddPage($orientation='', $format='', $keepmargins=false, $tocpag } /* Wrapper for AddTOC() which tracks TOC position to offset annotations later */ - public function AddTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) { + public function AddTOC($page = '', $numbersfont = '', $filler = '.', $toc_name = 'TOC', $style = '', $color = array(0,0,0)) + { if (!TCPDF_STATIC::empty_string($page)) { $this->_TOCpagenum = $page; } else { @@ -276,9 +296,10 @@ public function AddTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', parent::AddTOC($page, $numbersfont, $filler, $toc_name, $style, $color); } - public function importAnnotations($pageno) { + public function importAnnotations($pageno) + { $fn = $this->current_filename; - $parser =& $this->parsers[$fn]; + $parser = & $this->parsers[$fn]; $parser->setPageno($pageno); $annots = $parser->getPageAnnotations(); @@ -297,7 +318,8 @@ public function importAnnotations($pageno) { } } - public function importAnnotation($annotation) { + public function importAnnotation($annotation) + { $fn = $this->current_filename; $old_id = $annotation[1]; $value = array(PDF_TYPE_OBJREF, $old_id, 0); @@ -310,6 +332,7 @@ public function importAnnotation($annotation) { $this->_importedAnnots[$this->page][] = $objid; } + /** * Get references to page annotations. * @param $n (int) page number @@ -318,44 +341,45 @@ public function importAnnotation($annotation) { * @author Nicola Asuni * @since 5.0.010 (2010-05-17) */ - protected function _getannotsrefs($n) { + protected function _getannotsrefs($n) + { if (!empty($this->_numTOCpages) && $n >= $this->_TOCpagenum) { // Offset page number to account for TOC being inserted before page containing annotations. $n -= $this->_numTOCpages; } - if (!(isset($this->_importedAnnots[$n]) OR isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) { + if (!(isset($this->_importedAnnots[$n]) or isset($this->PageAnnots[$n]) or ($this->sign and isset($this->signature_data['cert_type'])))) { return ''; } $out = ' /Annots ['; if (isset($this->_importedAnnots[$n])) { foreach ($this->_importedAnnots[$n] as $key => $val) { - $out .= ' '.$val.' 0 R'; + $out .= ' ' . $val . ' 0 R'; } } if (isset($this->PageAnnots[$n])) { foreach ($this->PageAnnots[$n] as $key => $val) { if (!in_array($val['n'], $this->radio_groups)) { - $out .= ' '.$val['n'].' 0 R'; + $out .= ' ' . $val['n'] . ' 0 R'; } } // add radiobutton groups if (isset($this->radiobutton_groups[$n])) { foreach ($this->radiobutton_groups[$n] as $key => $data) { if (isset($data['n'])) { - $out .= ' '.$data['n'].' 0 R'; + $out .= ' ' . $data['n'] . ' 0 R'; } } } } - if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) { + if ($this->sign and ($n == $this->signature_appearance['page']) and isset($this->signature_data['cert_type'])) { // set reference for signature object - $out .= ' '.$this->sig_obj_id.' 0 R'; + $out .= ' ' . $this->sig_obj_id . ' 0 R'; } if (!empty($this->empty_signature_appearance)) { foreach ($this->empty_signature_appearance as $esa) { if ($esa['page'] == $n) { // set reference for empty signature objects - $out .= ' '.$esa['objid'].' 0 R'; + $out .= ' ' . $esa['objid'] . ' 0 R'; } } } @@ -363,17 +387,20 @@ protected function _getannotsrefs($n) { return $out; } + /** * Returns the last used page box * * @return string */ - public function getLastUsedPageBox() { + public function getLastUsedPageBox() + { return $this->lastUsedPageBox; } - public function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) { + public function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) + { if ($adjustPageSize == true && is_null($_x) && is_null($_y)) { $size = $this->getTemplateSize($tplidx, $_w, $_h); $orientation = $size['w'] > $size['h'] ? 'L' : 'P'; @@ -392,12 +419,13 @@ public function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $ /** * Private method, that rebuilds all needed objects of source files */ - public function _putimportedobjects() { + public function _putimportedobjects() + { if (is_array($this->parsers) && count($this->parsers) > 0) { - foreach($this->parsers AS $filename => $p) { - $this->current_parser =& $this->parsers[$filename]; + foreach ($this->parsers as $filename => $p) { + $this->current_parser = & $this->parsers[$filename]; if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) { - while(($n = key($this->_obj_stack[$filename])) !== null) { + while (($n = key($this->_obj_stack[$filename])) !== null) { $nObj = $this->current_parser->getObjectVal($this->_obj_stack[$filename][$n][1]); $this->_newobj($this->_obj_stack[$filename][$n][0]); @@ -426,11 +454,12 @@ public function _putimportedobjects() { /** * Private Method that writes the form xobjects */ - public function _putformxobjects() { - $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + public function _putformxobjects() + { + $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; reset($this->tpls); - foreach($this->tpls AS $tplidx => $tpl) { - $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; + foreach ($this->tpls as $tplidx => $tpl) { + $p = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; $this->_newobj(); $cN = $this->n; // TCPDF/Protection: rem current "n" @@ -439,7 +468,8 @@ public function _putformxobjects() { $this->_out('/Subtype /Form'); $this->_out('/FormType 1'); - $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]', + $this->_out(sprintf( + '/BBox [%.2F %.2F %.2F %.2F]', (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k, (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k, (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k, @@ -456,11 +486,11 @@ public function _putformxobjects() { $ty = -$tpl['box']['lly']; if ($tpl['_rotationAngle'] <> 0) { - $angle = $tpl['_rotationAngle'] * M_PI/180; - $c=cos($angle); - $s=sin($angle); + $angle = $tpl['_rotationAngle'] * M_PI / 180; + $c = cos($angle); + $s = sin($angle); - switch($tpl['_rotationAngle']) { + switch ($tpl['_rotationAngle']) { case -90: $tx = -$tpl['box']['lly']; $ty = $tpl['box']['urx']; @@ -484,35 +514,43 @@ public function _putformxobjects() { $ty *= $this->k; if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) { - $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]', - $c, $s, -$s, $c, $tx, $ty + $this->_out(sprintf( + '/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]', + $c, + $s, + -$s, + $c, + $tx, + $ty )); } $this->_out('/Resources '); if (isset($tpl['resources'])) { - $this->current_parser =& $tpl['parser']; + $this->current_parser = & $tpl['parser']; $this->pdf_write_value($tpl['resources']); // "n" will be changed } else { $this->_out('<_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) { $this->_out('/Font <<'); - foreach($this->_res['tpl'][$tplidx]['fonts'] as $font) + foreach ($this->_res['tpl'][$tplidx]['fonts'] as $font) { $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R'); + } $this->_out('>>'); } - if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) || - isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) - { + if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) || + isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) { $this->_out('/XObject <<'); if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) { - foreach($this->_res['tpl'][$tplidx]['images'] as $image) + foreach ($this->_res['tpl'][$tplidx]['images'] as $image) { $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R'); + } } if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) { - foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl) + foreach ($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl) { $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R'); + } } $this->_out('>>'); } @@ -538,7 +576,8 @@ public function _putformxobjects() { /** * Rewritten to handle existing own defined objects */ - protected function _newobj($obj_id = false, $onlynewobj = false) { + public function _newobj($obj_id = false, $onlynewobj = false) + { if (!$obj_id) { $obj_id = ++$this->n; } @@ -592,9 +631,8 @@ public function pdf_write_value(&$value) } switch ($value[0]) { - case PDF_TYPE_TOKEN: - $this->_straightOut('/'.$value[1] . ' '); + $this->_straightOut('/' . $value[1] . ' '); break; case PDF_TYPE_NUMERIC: case PDF_TYPE_REAL: @@ -623,6 +661,8 @@ public function pdf_write_value(&$value) // A dictionary. $this->_straightOut('<<'); + reset($value[1]); + foreach ($value[1] as $k => $v) { $this->_straightOut($k . ' '); $this->pdf_write_value($v); @@ -635,7 +675,7 @@ public function pdf_write_value(&$value) // An indirect object reference // Fill the object stack if needed - $cpfn =& $this->current_parser->uniqueid; + $cpfn = & $this->current_parser->uniqueid; if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) { $this->_newobj(false, true); @@ -684,17 +724,18 @@ public function pdf_write_value(&$value) /** * Modified so not each call will add a newline to the output. */ - protected function _straightOut($s) { + public function _straightOut($s) + { if ($this->state == 2) { if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['outdata'] .= $s; - } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { + } elseif ((!$this->InFooter) and isset($this->footerlen[$this->page]) and ($this->footerlen[$this->page] > 0)) { // puts data before page footer $pagebuff = $this->getPageBuffer($this->page); $page = substr($pagebuff, 0, -$this->footerlen[$this->page]); $footer = substr($pagebuff, -$this->footerlen[$this->page]); - $this->setPageBuffer($this->page, $page.$s.$footer); + $this->setPageBuffer($this->page, $page . $s . $footer); // update footer position $this->footerpos[$this->page] += strlen($s); } else { @@ -711,7 +752,8 @@ protected function _straightOut($s) { * rewritten to close opened parsers * */ - protected function _enddoc() { + public function _enddoc() + { parent::_enddoc(); $this->_closeParsers(); } @@ -719,8 +761,9 @@ protected function _enddoc() { /** * close all files opened by parsers */ - protected function _closeParsers() { - if ($this->state > 2 && count($this->parsers) > 0) { + public function _closeParsers() + { + if ($this->state > 2 && is_array($this->parsers) && count($this->parsers) > 0) { $this->cleanUp(); return true; } @@ -730,8 +773,9 @@ protected function _closeParsers() { /** * Removes cylced references and closes the file handles of the parser objects */ - public function cleanUp() { - foreach ($this->parsers as $k => $_){ + public function cleanUp() + { + foreach ($this->parsers as $k => $_) { $this->parsers[$k]->cleanUp(); $this->parsers[$k] = null; unset($this->parsers[$k]); @@ -739,14 +783,16 @@ public function cleanUp() { } // Functions from here on are taken from FPDI's fpdi2tcpdf_bridge.php to remove dependence on it - protected function _putstream($s, $n=0) { + public function _putstream($s, $n = 0) + { $this->_out($this->_getstream($s, $n)); } - protected function _getxobjectdict() { + public function _getxobjectdict() + { $out = parent::_getxobjectdict(); if (count($this->tpls)) { - foreach($this->tpls as $tplidx => $tpl) { + foreach ($this->tpls as $tplidx => $tpl) { $out .= sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']); } } @@ -760,10 +806,11 @@ protected function _getxobjectdict() { * @param string $s * @return string */ - protected function _unescape($s) { + public function _unescape($s) + { $out = ''; for ($count = 0, $n = strlen($s); $count < $n; $count++) { - if ($s[$count] != '\\' || $count == $n-1) { + if ($s[$count] != '\\' || $count == $n - 1) { $out .= $s[$count]; } else { switch ($s[++$count]) { @@ -788,8 +835,9 @@ protected function _unescape($s) { $out .= chr(0x0A); break; case "\r": - if ($count != $n-1 && $s[$count+1] == "\n") + if ($count != $n - 1 && $s[$count + 1] == "\n") { $count++; + } break; case "\n": break; @@ -797,14 +845,14 @@ protected function _unescape($s) { // Octal-Values if (ord($s[$count]) >= ord('0') && ord($s[$count]) <= ord('9')) { - $oct = ''. $s[$count]; + $oct = '' . $s[$count]; - if (ord($s[$count+1]) >= ord('0') && - ord($s[$count+1]) <= ord('9')) { + if (ord($s[$count + 1]) >= ord('0') && + ord($s[$count + 1]) <= ord('9')) { $oct .= $s[++$count]; - if (ord($s[$count+1]) >= ord('0') && - ord($s[$count+1]) <= ord('9')) { + if (ord($s[$count + 1]) >= ord('0') && + ord($s[$count + 1]) <= ord('9')) { $oct .= $s[++$count]; } } @@ -825,7 +873,8 @@ protected function _unescape($s) { * @param string $hex * @return string */ - public function hex2str($hex) { + public function hex2str($hex) + { return pack('H*', str_replace(array("\r", "\n", ' '), '', $hex)); } @@ -835,7 +884,8 @@ public function hex2str($hex) { * @param string $str * @return string */ - public function str2hex($str) { + public function str2hex($str) + { return current(unpack('H*', $str)); } } diff --git a/vendor/iio/libmergepdf/tcpdi/tcpdi_parser.php b/vendor/karriere/pdf-merge/tcpi/tcpdi_parser.php similarity index 87% rename from vendor/iio/libmergepdf/tcpdi/tcpdi_parser.php rename to vendor/karriere/pdf-merge/tcpi/tcpdi_parser.php index 2391934..5eb8c44 100644 --- a/vendor/iio/libmergepdf/tcpdi/tcpdi_parser.php +++ b/vendor/karriere/pdf-merge/tcpi/tcpdi_parser.php @@ -1,4 +1,5 @@ Error('Empty PDF data.'); } @@ -206,7 +222,8 @@ public function __construct($data, $uniqueid) { /** * Clean up when done, to free memory etc */ - public function cleanUp() { + public function cleanUp() + { unset($this->pdfdata); $this->pdfdata = ''; unset($this->objstreams); @@ -229,7 +246,8 @@ public function cleanUp() { * @public * @since 1.0.000 (2011-06-26) */ - public function getParsedData() { + public function getParsedData() + { return array($this->xref, $this->objects, $this->pages); } @@ -239,10 +257,12 @@ public function getParsedData() { * And reset the PDF Version used in FPDI if needed * @public */ - public function getPDFVersion() { + public function getPDFVersion() + { preg_match('/\d\.\d/', substr($this->pdfdata, 0, 16), $m); - if (isset($m[0])) + if (isset($m[0])) { $this->pdfVersion = $m[0]; + } return $this->pdfVersion; } @@ -250,15 +270,14 @@ public function getPDFVersion() { * Read all /Page(es) * */ - function readPages() { + public function readPages() + { $params = $this->getObjectVal($this->xref['trailer'][1]['/Root']); $objref = null; - if ($params && $params[1] && is_array($params[1][1])) { - foreach ($params[1][1] as $k=>$v) { - if ($k == '/Pages') { - $objref = $v; - break; - } + foreach ($params[1][1] as $k => $v) { + if ($k == '/Pages') { + $objref = $v; + break; } } if ($objref == null || $objref[0] !== PDF_TYPE_OBJREF) { @@ -294,7 +313,8 @@ function readPages() { * Read a single /Page element, recursing through /Kids if necessary * */ - private function readPage($page) { + private function readPage($page) + { if (isset($page[1][1]['/Kids'])) { // Nested pages! foreach ($page[1][1]['/Kids'][1] as $subref) { @@ -311,7 +331,8 @@ private function readPage($page) { * * @return int */ - function getPageCount() { + public function getPageCount() + { return $this->page_count; } @@ -323,7 +344,8 @@ function getPageCount() { * @protected * @since 1.0.000 (2011-05-24) */ - protected function getXrefData($offset=0, $xref=array()) { + protected function getXrefData($offset = 0, $xref = array()) + { if ($offset == 0) { // find last startxref if (preg_match('/.*[\r\n]startxref[\s\r\n]+([0-9]+)[\s\r\n]+%%EOF/is', $this->pdfdata, $matches) == 0) { @@ -369,7 +391,8 @@ protected function getXrefData($offset=0, $xref=array()) { * @protected * @since 1.0.000 (2011-06-20) */ - protected function decodeXref($startxref, $xref=array()) { + protected function decodeXref($startxref, $xref = array()) + { $this->xref_seen_offsets[] = $startxref; if (!isset($xref['xref_location'])) { $xref['xref_location'] = $startxref; @@ -385,7 +408,7 @@ protected function decodeXref($startxref, $xref=array()) { if ($matches[3][0] == 'n') { // create unique object index: [object number]_[generation number] $gen_num = intval($matches[2][0]); - $index = $obj_num.'_'.$gen_num; + $index = $obj_num . '_' . $gen_num; // check if object already exist if (!isset($xref['xref'][$obj_num][$gen_num])) { // store object offset position @@ -406,7 +429,7 @@ protected function decodeXref($startxref, $xref=array()) { // get trailer data if (preg_match('/trailer[\s]*<<(.*)>>[\s\r\n]+(?:[%].*[\r\n]+)*startxref[\s\r\n]+/isU', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $xoffset) > 0) { $trailer_data = $matches[1][0]; - if (!isset($xref['trailer']) OR empty($xref['trailer'])) { + if (!isset($xref['trailer']) or empty($xref['trailer'])) { // get only the last updated version $xref['trailer'] = array(); $xref['trailer'][0] = PDF_TYPE_DICTIONARY; @@ -453,7 +476,8 @@ protected function decodeXref($startxref, $xref=array()) { * @protected * @since 1.0.003 (2013-03-16) */ - protected function decodeXrefStream($startxref, $xref=array()) { + protected function decodeXrefStream($startxref, $xref = array()) + { // try to read Cross-Reference Stream list($xrefobj, $unused) = $this->getRawObject($startxref); $xrefcrs = $this->getIndirectObject($xrefobj[1], $startxref, true); @@ -464,7 +488,7 @@ protected function decodeXrefStream($startxref, $xref=array()) { if (!isset($xref['xref'])) { $xref['xref'] = array(); } - if (!isset($xref['trailer']) OR empty($xref['trailer'])) { + if (!isset($xref['trailer']) or empty($xref['trailer'])) { // get only the last updated version $xref['trailer'] = array(); $xref['trailer'][0] = PDF_TYPE_DICTIONARY; @@ -478,35 +502,35 @@ protected function decodeXrefStream($startxref, $xref=array()) { $keys = array_keys($sarr); $columns = 1; // Default as per PDF 32000-1:2008. $predictor = 1; // Default as per PDF 32000-1:2008. - foreach ($keys as $k=>$key) { + foreach ($keys as $k => $key) { $v = $sarr[$key]; - if (($key == '/Type') AND ($v[0] == PDF_TYPE_TOKEN AND ($v[1] == 'XRef'))) { + if (($key == '/Type') and ($v[0] == PDF_TYPE_TOKEN and ($v[1] == 'XRef'))) { $valid_crs = true; - } elseif (($key == '/Index') AND ($v[0] == PDF_TYPE_ARRAY AND count($v[1]) >= 2)) { + } elseif (($key == '/Index') and ($v[0] == PDF_TYPE_ARRAY and count($v[1]) >= 2)) { // first object number in the subsection $index_first = intval($v[1][0][1]); // number of entries in the subsection $index_entries = intval($v[1][1][1]); - } elseif (($key == '/Prev') AND ($v[0] == PDF_TYPE_NUMERIC)) { + } elseif (($key == '/Prev') and ($v[0] == PDF_TYPE_NUMERIC)) { // get previous xref offset $prevxref = intval($v[1]); - } elseif (($key == '/W') AND ($v[0] == PDF_TYPE_ARRAY)) { + } elseif (($key == '/W') and ($v[0] == PDF_TYPE_ARRAY)) { // number of bytes (in the decoded stream) of the corresponding field $wb = array(); $wb[0] = intval($v[1][0][1]); $wb[1] = intval($v[1][1][1]); $wb[2] = intval($v[1][2][1]); - } elseif (($key == '/DecodeParms') AND ($v[0] == PDF_TYPE_DICTIONARY)) { + } elseif (($key == '/DecodeParms') and ($v[0] == PDF_TYPE_DICTIONARY)) { $decpar = $v[1]; foreach ($decpar as $kdc => $vdc) { - if (($kdc == '/Columns') AND ($vdc[0] == PDF_TYPE_NUMERIC)) { + if (($kdc == '/Columns') and ($vdc[0] == PDF_TYPE_NUMERIC)) { $columns = intval($vdc[1]); - } elseif (($kdc == '/Predictor') AND ($vdc[0] == PDF_TYPE_NUMERIC)) { + } elseif (($kdc == '/Predictor') and ($vdc[0] == PDF_TYPE_NUMERIC)) { $predictor = intval($vdc[1]); } } } elseif ($filltrailer) { - switch($key) { + switch ($key) { case '/Size': case '/Root': case '/Info': @@ -520,7 +544,7 @@ protected function decodeXrefStream($startxref, $xref=array()) { } // decode data $obj_num = 0; - if ($valid_crs AND isset($xrefcrs[1][3][0])) { + if ($valid_crs and isset($xrefcrs[1][3][0])) { // number of bytes in a row $rowlen = ($columns + 1); // convert the stream into an array of integers @@ -530,7 +554,7 @@ protected function decodeXrefStream($startxref, $xref=array()) { // initialize decoded array $ddata = array(); // initialize first row with zeros - $prev_row = array_fill (0, $rowlen, 0); + $prev_row = array_fill(0, $rowlen, 0); // for each row apply PNG unpredictor foreach ($sdata as $k => $row) { // initialize new row @@ -540,7 +564,7 @@ protected function decodeXrefStream($startxref, $xref=array()) { $predictor = (10 + $row[0]); } // for each byte on the row - for ($i=1; $i<=$columns; ++$i) { + for ($i = 1; $i <= $columns; ++$i) { if (!isset($row[$i])) { // No more data in this row - we're done here. break; @@ -644,7 +668,7 @@ protected function decodeXrefStream($startxref, $xref=array()) { } case 1: { // (n) objects that are in use but are not compressed // create unique object index: [object number]_[generation number] - $index = $obj_num.'_'.$row[2]; + $index = $obj_num . '_' . $row[2]; // check if object already exist if (!isset($xref['xref'][$obj_num][$row[2]])) { // store object offset position @@ -681,7 +705,8 @@ protected function decodeXrefStream($startxref, $xref=array()) { * @return string Steam content * @protected */ - protected function getRawStream($offset, $length) { + protected function getRawStream($offset, $length) + { $offset += strspn($this->pdfdata, "\x00\x09\x0a\x0c\x0d\x20", $offset); $offset += 6; // "stream" $offset += strspn($this->pdfdata, "\x20", $offset); @@ -691,7 +716,7 @@ protected function getRawStream($offset, $length) { $obj[] = PDF_TYPE_STREAM; $obj[] = substr($this->pdfdata, $offset, $length); - return array($obj, $offset+$length); + return array($obj, $offset + $length); } /** @@ -701,9 +726,10 @@ protected function getRawStream($offset, $length) { * @protected * @since 1.0.000 (2011-06-20) */ - protected function getRawObject($offset=0, $data=null) { + protected function getRawObject($offset = 0, $data = null) + { if ($data == null) { - $data =& $this->pdfdata; + $data = & $this->pdfdata; } $objtype = ''; // object type to be returned $objval = ''; // object value to be returned @@ -791,11 +817,11 @@ protected function getRawObject($offset=0, $data=null) { } case '<': // \x3C LESS-THAN SIGN case '>': { // \x3E GREATER-THAN SIGN - if (isset($data[($offset + 1)]) AND ($data[($offset + 1)] == $char)) { + if (isset($data[($offset + 1)]) and ($data[($offset + 1)] == $char)) { // dictionary object $objtype = PDF_TYPE_DICTIONARY; if ($char == '<') { - list ($objval, $offset) = $this->getDictValue($offset, $data); + list($objval, $offset) = $this->getDictValue($offset, $data); } else { $objtype = '>>'; $offset += 2; @@ -805,7 +831,7 @@ protected function getRawObject($offset=0, $data=null) { $objtype = PDF_TYPE_HEX; ++$offset; // The "Panose" entry in the FontDescriptor Style dict seems to have hex bytes separated by spaces. - if (($char == '<') AND (preg_match('/^([0-9A-Fa-f ]+)[>]/iU', substr($data, $offset), $matches) == 1)) { + if (($char == '<') and (preg_match('/^([0-9A-Fa-f ]+)[>]/iU', substr($data, $offset), $matches) == 1)) { $objval = $matches[1]; $offset += strlen($matches[0]); unset($matches); @@ -814,7 +840,7 @@ protected function getRawObject($offset=0, $data=null) { break; } default: { - $frag = $data[$offset] . @$data[$offset+1] . @$data[$offset+2] . @$data[$offset+3]; + $frag = $data[$offset] . @$data[$offset + 1] . @$data[$offset + 2] . @$data[$offset + 3]; switch ($frag) { case 'endo': // indirect object @@ -857,8 +883,8 @@ protected function getRawObject($offset=0, $data=null) { } elseif ($matches[3] == 'obj') { // object start $objtype = PDF_TYPE_OBJECT; - $objval = intval($matches[1]).'_'.intval($matches[2]); - $offset += strlen ($matches[0]); + $objval = intval($matches[1]) . '_' . intval($matches[2]); + $offset += strlen($matches[0]); } } elseif (($numlen = strspn($data, '+-.0123456789', $offset)) > 0) { // numeric object @@ -883,19 +909,20 @@ protected function getRawObject($offset=0, $data=null) { } return array($obj, $offset); } - private function getDictValue($offset, &$data) { + private function getDictValue($offset, &$data) + { $objval = array(); // Extract dict from data. - $i=1; + $i = 1; $dict = ''; $offset += 2; do { - if ($data[$offset] == '>' && $data[$offset+1] == '>') { + if ($data[$offset] == '>' && $data[$offset + 1] == '>') { $i--; $dict .= '>>'; $offset += 2; - } else if ($data[$offset] == '<' && $data[$offset+1] == '<') { + } elseif ($data[$offset] == '<' && $data[$offset + 1] == '<') { $i++; $dict .= '<<'; $offset += 2; @@ -903,7 +930,7 @@ private function getDictValue($offset, &$data) { $dict .= $data[$offset]; $offset++; } - } while ($i>0); + } while ($i > 0); // Now that we have just the dict, parse it. $dictoffset = 0; @@ -914,7 +941,7 @@ private function getDictValue($offset, &$data) { break; } list($element, $dictoffset) = $this->getRawObject($eloffset, $dict); - $objval['/'.$key[1]] = $element; + $objval['/' . $key[1]] = $element; unset($key); unset($element); } while (true); @@ -931,13 +958,14 @@ private function getDictValue($offset, &$data) { * @protected * @since 1.0.000 (2011-05-24) */ - protected function getIndirectObject($obj_ref, $offset=0, $decoding=true) { + protected function getIndirectObject($obj_ref, $offset = 0, $decoding = true) + { $obj = explode('_', $obj_ref); - if (($obj === false) OR (count($obj) != 2)) { - $this->Error('Invalid object reference: '.$obj); + if (($obj === false) or (count($obj) != 2)) { + $this->Error('Invalid object reference: ' . $obj); return; } - $objref = $obj[0].' '.$obj[1].' obj'; + $objref = $obj[0] . ' ' . $obj[1] . ' obj'; if (strpos($this->pdfdata, $objref, $offset) != $offset) { // an indirect reference to an undefined object shall be considered a reference to the null object @@ -949,9 +977,9 @@ protected function getIndirectObject($obj_ref, $offset=0, $decoding=true) { $objdata = array(); $i = 0; // object main index do { - if (($i > 0) AND (isset($objdata[($i - 1)][0])) AND ($objdata[($i - 1)][0] == PDF_TYPE_DICTIONARY) AND array_key_exists('/Length', $objdata[($i - 1)][1])) { + if (($i > 0) and (isset($objdata[($i - 1)][0])) and ($objdata[($i - 1)][0] == PDF_TYPE_DICTIONARY) and array_key_exists('/Length', $objdata[($i - 1)][1])) { // Stream - get using /Length in stream's dict - $lengthobj = $objdata[($i-1)][1]['/Length']; + $lengthobj = $objdata[($i - 1)][1]['/Length']; if ($lengthobj[0] === PDF_TYPE_OBJREF) { $lengthobj = $this->getObjectVal($lengthobj); if ($lengthobj[0] === PDF_TYPE_OBJECT) { @@ -965,7 +993,7 @@ protected function getIndirectObject($obj_ref, $offset=0, $decoding=true) { list($element, $offset) = $this->getRawObject($offset); } // decode stream using stream's dictionary information - if ($decoding AND ($element[0] == PDF_TYPE_STREAM) AND (isset($objdata[($i - 1)][0])) AND ($objdata[($i - 1)][0] == PDF_TYPE_DICTIONARY)) { + if ($decoding and ($element[0] == PDF_TYPE_STREAM) and (isset($objdata[($i - 1)][0])) and ($objdata[($i - 1)][0] == PDF_TYPE_DICTIONARY)) { $element[3] = $this->decodeStream($objdata[($i - 1)][1], $element[1]); } $objdata[$i] = $element; @@ -984,7 +1012,8 @@ protected function getIndirectObject($obj_ref, $offset=0, $decoding=true) { * @public * @since 1.0.000 (2011-06-26) */ - public function getObjectVal($obj) { + public function getObjectVal($obj) + { if ($obj[0] == PDF_TYPE_OBJREF) { if (strpos($obj[1], '_') !== false) { $key = explode('_', $obj[1]); @@ -992,7 +1021,7 @@ public function getObjectVal($obj) { $key = array($obj[1], $obj[2]); } - $ret = array(0=>PDF_TYPE_OBJECT, 'obj'=>$key[0], 'gen'=>$key[1]); + $ret = array(0 => PDF_TYPE_OBJECT, 'obj' => $key[0], 'gen' => $key[1]); // reference to indirect object $object = null; @@ -1001,7 +1030,7 @@ public function getObjectVal($obj) { $object = $this->objects[$key[0]][$key[1]]; } elseif (($offset = $this->findObjectOffset($key)) !== false) { // parse new object - $this->objects[$key[0]][$key[1]] = $this->getIndirectObject($key[0].'_'.$key[1], $offset, false); + $this->objects[$key[0]][$key[1]] = $this->getIndirectObject($key[0] . '_' . $key[1], $offset, false); $object = $this->objects[$key[0]][$key[1]]; } elseif (($key[1] == 0) && isset($this->objstreamobjs[$key[0]])) { // Object is in an object stream @@ -1009,7 +1038,8 @@ public function getObjectVal($obj) { $objs = $streaminfo[0]; if (!isset($this->objstreams[$objs[0]][$objs[1]])) { // Fetch and decode object stream - $offset = $this->findObjectOffset($objs);; + $offset = $this->findObjectOffset($objs); + ; $objstream = $this->getObjectVal(array(PDF_TYPE_OBJREF, $objs[0], $objs[1])); $decoded = $this->decodeStream($objstream[1][1], $objstream[2][1]); $this->objstreams[$objs[0]][$objs[1]] = $decoded[0]; // Store just the data, in case we need more from this objstream @@ -1036,7 +1066,8 @@ public function getObjectVal($obj) { * Extract object stream to find out what it contains. * */ - function extractObjectStream($key) { + public function extractObjectStream($key) + { $objref = array(PDF_TYPE_OBJREF, $key[0], $key[1]); $obj = $this->getObjectVal($objref); if ($obj[0] !== PDF_TYPE_STREAM || !isset($obj[1][1]['/First'][1])) { @@ -1046,9 +1077,9 @@ function extractObjectStream($key) { $stream = $this->decodeStream($obj[1][1], $obj[2][1]);// Decode object stream, as we need the first bit $first = intval($obj[1][1]['/First'][1]); $ints = preg_split('/\s/', substr($stream[0], 0, $first)); // Get list of object / offset pairs - for ($j=1; $jobjstreamobjs[$ints[$j-1]] = array($key, $ints[$j]+$first); + $this->objstreamobjs[$ints[$j - 1]] = array($key, $ints[$j] + $first); } } @@ -1061,12 +1092,13 @@ function extractObjectStream($key) { * Find all object offsets. Saves having to scour the file multiple times. * @private */ - private function findObjectOffsets() { + private function findObjectOffsets() + { $this->objoffsets = array(); if (preg_match_all('/(*ANYCRLF)^[\s]*([0-9]+)[\s]+([0-9]+)[\s]+obj/im', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE) >= 1) { $i = 0; $laststreamend = 0; - foreach($matches[0] as $match) { + foreach ($matches[0] as $match) { $offset = $match[1] + strspn($match[0], "\x00\x09\x0a\x0c\x0d\x20"); if ($offset < $laststreamend) { // Contained within another stream, skip it. @@ -1095,8 +1127,9 @@ private function findObjectOffsets() { * @return int Offset of the object in $this->pdfdata. * @private */ - private function findObjectOffset($key) { - $objref = $key[0].' '.$key[1].' obj'; + private function findObjectOffset($key) + { + $objref = $key[0] . ' ' . $key[1] . ' obj'; if (isset($this->xref['xref'][$key[0]][$key[1]])) { $offset = $this->xref['xref'][$key[0]][$key[1]]; if (strpos($this->pdfdata, $objref, $offset) === $offset) { @@ -1120,7 +1153,8 @@ private function findObjectOffset($key) { * @protected * @since 1.0.000 (2011-06-22) */ - protected function decodeStream($sdic, $stream) { + protected function decodeStream($sdic, $stream) + { // get stream lenght and filters $slength = strlen($stream); if ($slength <= 0) { @@ -1129,7 +1163,7 @@ protected function decodeStream($sdic, $stream) { $filters = array(); foreach ($sdic as $k => $v) { if ($v[0] == PDF_TYPE_TOKEN) { - if (($k == '/Length') AND ($v[0] == PDF_TYPE_NUMERIC)) { + if (($k == '/Length') and ($v[0] == PDF_TYPE_NUMERIC)) { // get declared stream lenght $declength = intval($v[1]); if ($declength < $slength) { @@ -1170,11 +1204,12 @@ protected function decodeStream($sdic, $stream) { * * @param int $pageno Pagenumber to use */ - public function setPageno($pageno) { + public function setPageno($pageno) + { $pageno = ((int) $pageno) - 1; if ($pageno < 0 || $pageno >= $this->getPageCount()) { - $this->error("Pagenumber is wrong! (Requested $pageno, max ".$this->getPageCount().")"); + $this->error("Pagenumber is wrong! (Requested $pageno, max " . $this->getPageCount() . ")"); } $this->pageno = $pageno; @@ -1185,7 +1220,8 @@ public function setPageno($pageno) { * * @return array */ - public function getPageResources() { + public function getPageResources() + { return $this->_getPageResources($this->pages[$this->pageno]); } @@ -1194,25 +1230,27 @@ public function getPageResources() { * * @param array $obj Array of pdf-data */ - private function _getPageResources ($obj) { // $obj = /Page + private function _getPageResources($obj) // $obj = /Page + { $obj = $this->getObjectVal($obj); - // If the current object has a resources // dictionary associated with it, we use // it. Otherwise, we move back to its // parent object. - if (isset ($obj[1][1]['/Resources'])) { + if (isset($obj[1][1]['/Resources'])) { $res = $obj[1][1]['/Resources']; - if ($res[0] == PDF_TYPE_OBJECT) + if ($res[0] == PDF_TYPE_OBJECT) { return $res[1]; + } return $res; } else { - if (!isset ($obj[1][1]['/Parent'])) { + if (!isset($obj[1][1]['/Parent'])) { return false; } else { $res = $this->_getPageResources($obj[1][1]['/Parent']); - if ($res[0] == PDF_TYPE_OBJECT) + if ($res[0] == PDF_TYPE_OBJECT) { return $res[1]; + } return $res; } } @@ -1223,7 +1261,8 @@ private function _getPageResources ($obj) { // $obj = /Page * * @return array */ - public function getPageAnnotations() { + public function getPageAnnotations() + { return $this->_getPageAnnotations($this->pages[$this->pageno]); } @@ -1232,25 +1271,26 @@ public function getPageAnnotations() { * * @param array $obj Array of pdf-data */ - private function _getPageAnnotations ($obj) { // $obj = /Page + private function _getPageAnnotations($obj) // $obj = /Page + { $obj = $this->getObjectVal($obj); - // If the current object has an annotations // dictionary associated with it, we use // it. Otherwise, we move back to its // parent object. - if (isset ($obj[1][1]['/Annots'])) { + if (isset($obj[1][1]['/Annots'])) { $annots = $obj[1][1]['/Annots']; } else { - if (!isset ($obj[1][1]['/Parent'])) { + if (!isset($obj[1][1]['/Parent'])) { return false; } else { $annots = $this->_getPageAnnotations($obj[1][1]['/Parent']); } } - if ($annots[0] == PDF_TYPE_OBJREF) + if ($annots[0] == PDF_TYPE_OBJREF) { return $this->getObjectVal($annots); + } return $annots; } @@ -1262,12 +1302,13 @@ private function _getPageAnnotations ($obj) { // $obj = /Page * * @return string */ - public function getContent() { + public function getContent() + { $buffer = ''; if (isset($this->pages[$this->pageno][1][1]['/Contents'])) { $contents = $this->_getPageContent($this->pages[$this->pageno][1][1]['/Contents']); - foreach($contents AS $tmp_content) { + foreach ($contents as $tmp_content) { $buffer .= $this->_rebuildContentStream($tmp_content) . ' '; } } @@ -1282,7 +1323,8 @@ public function getContent() { * @param array $content_ref * @return array */ - private function _getPageContent($content_ref) { + private function _getPageContent($content_ref) + { $contents = array(); if ($content_ref[0] == PDF_TYPE_OBJREF) { @@ -1293,8 +1335,8 @@ private function _getPageContent($content_ref) { $contents[] = $content; } } elseif ($content_ref[0] == PDF_TYPE_ARRAY) { - foreach ($content_ref[1] AS $tmp_content_ref) { - $contents = array_merge($contents,$this->_getPageContent($tmp_content_ref)); + foreach ($content_ref[1] as $tmp_content_ref) { + $contents = array_merge($contents, $this->_getPageContent($tmp_content_ref)); } } @@ -1308,7 +1350,8 @@ private function _getPageContent($content_ref) { * @param array $obj * @return string */ - private function _rebuildContentStream($obj) { + private function _rebuildContentStream($obj) + { $filters = array(); if (isset($obj[1][1]['/Filter'])) { @@ -1328,7 +1371,7 @@ private function _rebuildContentStream($obj) { $stream = $obj[2][1]; - foreach ($filters AS $_filter) { + foreach ($filters as $_filter) { $stream = $this->FilterDecoders->decodeFilter($_filter[1], $stream); } @@ -1345,11 +1388,13 @@ private function _rebuildContentStream($obj) { * @param float Scale factor from user space units to points * @return array */ - public function getPageBox($page, $box_index, $k) { + public function getPageBox($page, $box_index, $k) + { $page = $this->getObjectVal($page); $box = null; - if (isset($page[1][1][$box_index])) - $box =& $page[1][1][$box_index]; + if (isset($page[1][1][$box_index])) { + $box = & $page[1][1][$box_index]; + } if (!is_null($box) && $box[0] == PDF_TYPE_OBJREF) { $tmp_box = $this->getObjectVal($box); @@ -1357,7 +1402,7 @@ public function getPageBox($page, $box_index, $k) { } if (!is_null($box) && $box[0] == PDF_TYPE_ARRAY) { - $b =& $box[1]; + $b = & $box[1]; return array('x' => $b[0][1] / $k, 'y' => $b[1][1] / $k, 'w' => abs($b[0][1] - $b[2][1]) / $k, @@ -1367,7 +1412,7 @@ public function getPageBox($page, $box_index, $k) { 'urx' => max($b[0][1], $b[2][1]) / $k, 'ury' => max($b[1][1], $b[3][1]) / $k, ); - } elseif (!isset ($page[1][1]['/Parent'])) { + } elseif (!isset($page[1][1]['/Parent'])) { return false; } else { return $this->getPageBox($this->getObjectVal($page[1][1]['/Parent']), $box_index, $k); @@ -1381,7 +1426,8 @@ public function getPageBox($page, $box_index, $k) { * @param float Scale factor from user space units to points * @return array */ - public function getPageBoxes($pageno, $k) { + public function getPageBoxes($pageno, $k) + { return $this->_getPageBoxes($this->pages[$pageno - 1], $k); } @@ -1391,10 +1437,11 @@ public function getPageBoxes($pageno, $k) { * @param array a /Page * @return array */ - private function _getPageBoxes($page, $k) { + private function _getPageBoxes($page, $k) + { $boxes = array(); - foreach($this->availableBoxes AS $box) { + foreach ($this->availableBoxes as $box) { if ($_box = $this->getPageBox($page, $box, $k)) { $boxes[$box] = $_box; } @@ -1409,24 +1456,29 @@ private function _getPageBoxes($page, $k) { * @param integer $pageno * @return array */ - public function getPageRotation($pageno) { + public function getPageRotation($pageno) + { return $this->_getPageRotation($this->pages[$pageno - 1]); } - private function _getPageRotation($obj) { // $obj = /Page + private function _getPageRotation($obj) // $obj = /Page + { $obj = $this->getObjectVal($obj); - if (isset ($obj[1][1]['/Rotate'])) { + if (isset($obj[1][1]['/Rotate'])) { $res = $this->getObjectVal($obj[1][1]['/Rotate']); - if ($res[0] == PDF_TYPE_OBJECT) + if ($res[0] == PDF_TYPE_OBJECT) { return $res[1]; + } return $res; } else { - if (!isset ($obj[1][1]['/Parent'])) { + if (!isset($obj[1][1]['/Parent'])) { return false; } else { - $res = (array)$this->_getPageRotation($obj[1][1]['/Parent']); - if ($res[0] == PDF_TYPE_OBJECT) + $res = $this->_getPageRotation($obj[1][1]['/Parent']); + + if (is_array($res) && $res[0] == PDF_TYPE_OBJECT) { return $res[1]; + } return $res; } } @@ -1438,13 +1490,9 @@ private function _getPageRotation($obj) { // $obj = /Page * @public * @since 1.0.000 (2011-05-23) */ - public function Error($msg) { + public function Error($msg) + { // exit program and print error - die("TCPDI_PARSER ERROR [{$this->uniqueid}]: ".$msg); + die("TCPDI_PARSER ERROR [{$this->uniqueid}]: " . $msg); } - } // END OF TCPDF_PARSER CLASS - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/karriere/pdf-merge/tests/PdfMergeTest.php b/vendor/karriere/pdf-merge/tests/PdfMergeTest.php new file mode 100644 index 0000000..8f11c39 --- /dev/null +++ b/vendor/karriere/pdf-merge/tests/PdfMergeTest.php @@ -0,0 +1,93 @@ +pdfMerge = new PdfMerge(); + $this->dummyFile = __DIR__ . '/files/dummy.pdf'; + $this->outputFile = __DIR__ . '/output.pdf'; +}); + +it('returns PDF instance', function () { + expect($this->pdfMerge)->getPdf()->toBeInstanceOf(TCPDI::class); +}); + +it('throws exception when trying to add not existing page', function () { + ($this->pdfMerge)->add('/foo.pdf'); +})->throws(FileNotFoundException::class); + +it('checks if file was already added', function () { + expect($this->pdfMerge)->contains($this->dummyFile)->toBeFalse(); + $this->pdfMerge->add($this->dummyFile); + expect($this->pdfMerge)->contains($this->dummyFile)->toBeTrue(); +}); + +it('resets files to merge', function () { + $this->pdfMerge->add($this->dummyFile); + $this->pdfMerge->reset(); + + expect($this->pdfMerge)->contains($this->dummyFile)->toBeFalse(); +}); + +it('generates merged file', function () { + $this->pdfMerge->add($this->dummyFile); + $this->pdfMerge->add($this->dummyFile); + + expect($this->pdfMerge)->merge($this->outputFile)->toBeEmptyString(); + expect($this->outputFile)->toEqualPDF(__DIR__ . '/files/expected/output.pdf'); +}); + +it('merges portrait and landscape files', function () { + $this->pdfMerge->add($this->dummyFile); + $this->pdfMerge->add(__DIR__ . '/files/dummy_landscape.pdf'); + + expect($this->pdfMerge)->merge($this->outputFile)->toBeEmptyString(); + expect($this->outputFile)->toEqualPDF(__DIR__ . '/files/expected/output_mixed_orientation.pdf'); +}); + +it('adds header to merged PDF', function () { + copy(__DIR__ . '/files/header_logo.jpg', K_PATH_IMAGES . 'header_logo.png'); + + $pdfMerge = new PdfMerge(new HeaderConfig(imagePath: 'header_logo.png', logoWidthMM: 20, title: 'Test')); + + $pdfMerge->add($this->dummyFile); + $pdfMerge->add($this->dummyFile); + + expect($pdfMerge)->merge($this->outputFile)->toBeEmptyString(); + expect($this->outputFile)->toEqualPDF(__DIR__ . '/files/expected/output_with_header.pdf'); +}); + +it('adds full header and full footer to merged PDF', function () { + copy(__DIR__ . '/files/header_logo.jpg', K_PATH_IMAGES . 'header_logo.png'); + + $pdfMerge = new PdfMerge( + new HeaderConfig( + imagePath: 'header_logo.png', + logoWidthMM: 20, + title: 'Header', + text: 'This is a header text', + textColor: new RGB(200, 200, 200), + lineColor: new RGB(0, 0, 255), + ), + new FooterConfig( + textColor: new RGB(100, 100, 100), + lineColor: new RGB(255, 0, 0), + margin: 20, + ), + ); + + $pdfMerge->add($this->dummyFile); + $pdfMerge->add($this->dummyFile); + + expect($pdfMerge)->merge($this->outputFile)->toBeEmptyString(); + expect($this->outputFile)->toEqualPDF(__DIR__ . '/files/expected/output_with_header_and_footer.pdf'); +}); + +it('throws exception when no files were added', function () { + $this->pdfMerge->merge('/foo.pdf'); +})->throws(NoFilesDefinedException::class); diff --git a/vendor/karriere/pdf-merge/tests/Pest.php b/vendor/karriere/pdf-merge/tests/Pest.php new file mode 100644 index 0000000..45a2057 --- /dev/null +++ b/vendor/karriere/pdf-merge/tests/Pest.php @@ -0,0 +1,22 @@ +extend('toEqualPDF', function (string $expected) { + if (filesize($expected) !== filesize($this->value)) { + throw new Exception('The file size of the PDF does not equal the file size from the expected output.'); + } + + $pdf = new TCPDI(); + + $expectedPageCount = $pdf->setSourceFile($expected); + $actualPageCount = $pdf->setSourceFile($this->value); + + if ($expectedPageCount !== $actualPageCount) { + throw new Exception('The page count of the PDF does not equal the page count from the expected output.'); + } + + return $this; +}); + +expect()->extend('toBeEmptyString', function () { + return expect($this->value)->toEqual(''); +}); diff --git a/vendor/karriere/pdf-merge/tests/files/dummy.pdf b/vendor/karriere/pdf-merge/tests/files/dummy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c361b52e5483e9ead33be5c3745dda6520f59ddf GIT binary patch literal 16307 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Š}O7QK7d6OWCwCm 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 literal 0 HcmV?d00001 diff --git a/vendor/karriere/pdf-merge/tests/files/dummy_landscape.pdf b/vendor/karriere/pdf-merge/tests/files/dummy_landscape.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e058185a944e360e80958637204a4f3b46dab1db GIT binary patch literal 16893 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

_(j^zQ8>+_+0_{0xu&z(o=yo0c@|-#i$sTe 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 Comparison + */ + +namespace Phrity\Comparison; + +/** + * Interface for comparable instances. + */ +interface Comparable extends Equalable +{ + /** + * Compare $this and $that and return result as comparison identifier as integer. + * @param mixed $that The instance to compare with + * @return integer Must return 0 if $this is equal to $that + * Must return -1 if $this is less than $that + * Must return 1 if $this is greater than $that + * @throws IncomparableException Must throw if $this can not be compared with $that + */ + public function compare(mixed $that): int; + + /** + * If $this is greater than $that. + * @param mixed $that The instance to compare with + * @return boolean True if $this is greater than $that + * @throws IncomparableException Must throw if $this can not be compared with $that + */ + public function greaterThan(mixed $that): bool; + + /** + * If $this is greater than or equal to $that. + * @param mixed $that The instance to compare with + * @return boolean True if $this is greater than or equal to $that + * @throws IncomparableException Must throw if $this can not be compared with $that + */ + public function greaterThanOrEqual(mixed $that): bool; + + /** + * If $this is less than $that. + * @param mixed $that The instance to compare with + * @return boolean True if $this is less than $that + * @throws IncomparableException Must throw if $this can not be compared with $that + */ + public function lessThan(mixed $that): bool; + + /** + * If $this is less than or equal to $this. + * @param mixed $that The instance to compare with + * @return boolean True if $this is less than or equal to $this + * @throws IncomparableException Must throw if $this can not be compared with $that + */ + public function lessThanOrEqual(mixed $that): bool; +} diff --git a/vendor/phrity/comparison/src/Comparator.php b/vendor/phrity/comparison/src/Comparator.php new file mode 100644 index 0000000..722253b --- /dev/null +++ b/vendor/phrity/comparison/src/Comparator.php @@ -0,0 +1,200 @@ + Comparison + */ + +namespace Phrity\Comparison; + +/** + * Utility class for comparing and filtering. + */ +class Comparator +{ + /** @var array */ + private $comparables; + + /** + * If comparables supplied in constructor, they will be used as defaults an operations + * @param array $comparables List of objects implementing Comparable + */ + public function __construct(array $comparables = []) + { + $this->comparables = $comparables; + } + + // Sort methods + + /** + * Sorts array of comparable items, low to high + * @param array|null $comparables List of objects implementing Comparable + * @return array The sorted list + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + public function sort(array|null $comparables = null): array + { + $comparables = $comparables ?: $this->comparables; + usort($comparables, function ($item_1, $item_2) { + $this->verifyComparable($item_1); + return $item_1->compare($item_2); + }); + return $comparables; + } + + /** + * Sorts array of comparable items, high to low + * @param array|null $comparables List of objects implementing Comparable + * @return array The sorted list + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + public function rsort(array|null $comparables = null): array + { + $comparables = $comparables ?: $this->comparables; + usort($comparables, function ($item_1, $item_2) { + $this->verifyComparable($item_2); + return $item_2->compare($item_1); + }); + return $comparables; + } + + + // Filter methods + + /** + * Filter array of comparable items that equals condition + * @param Comparable $condition To compare against + * @param array|null $comparables List of objects implementing Comparable + * @return array The filtered list + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + public function equals(Comparable $condition, array|null $comparables = null): array + { + return $this->applyFilter('equals', $condition, $comparables); + } + + /** + * Filter array of comparable items that are greater than condition + * @param Comparable $condition To compare against + * @param array|null $comparables List of objects implementing Comparable + * @return array The filtered list + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + public function greaterThan(Comparable $condition, array|null $comparables = null): array + { + return $this->applyFilter('greaterThan', $condition, $comparables); + } + + /** + * Filter array of comparable items that are greater than or equals condition + * @param Comparable $condition To compare against + * @param array|null $comparables List of objects implementing Comparable + * @return array The filtered list + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + public function greaterThanOrEqual(Comparable $condition, array|null $comparables = null): array + { + return $this->applyFilter('greaterThanOrEqual', $condition, $comparables); + } + + /** + * Filter array of comparable items that are less than condition + * @param Comparable $condition To compare against + * @param array|null $comparables List of objects implementing Comparable + * @return array The filtered list + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + public function lessThan(Comparable $condition, array|null $comparables = null): array + { + return $this->applyFilter('lessThan', $condition, $comparables); + } + + /** + * Filter array of comparable items that are less than or equals condition + * @param Comparable $condition To compare against + * @param array|null $comparables List of objects implementing Comparable + * @return array The filtered list + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + public function lessThanOrEqual(Comparable $condition, array|null $comparables = null): array + { + return $this->applyFilter('lessThanOrEqual', $condition, $comparables); + } + + + // Select methods + + /** + * Get minimum item from array of comparable items + * @param array|null $comparables List of objects implementing Comparable + * @return Comparable|null The resolved instance + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + public function min(array|null $comparables = null): Comparable|null + { + return $this->applyReduction('lessThan', $comparables); + } + + /** + * Get maximum item from array of comparable items + * @param array|null $comparables List of objects implementing Comparable + * @return Comparable|null The resolved instance + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + public function max(array|null $comparables = null): Comparable|null + { + return $this->applyReduction('greaterThan', $comparables); + } + + + // Private internal methods + + /** + * Verify input implements Comparable + * @param mixed $item Item to verify + * @throws IncomparableException Thrown if item do not implement Comparable + */ + private function verifyComparable(mixed $item): void + { + if (!$item instanceof Comparable) { + throw new IncomparableException('All items must implement Comparable'); + } + } + + /** + * Filter array of comparable items according to condition instance and method + * @param string $method Comparison method to use + * @param Comparable $condition To compare against + * @param array|null $comparables List of objects implementing Comparable + * @return array The filtered list + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + private function applyFilter(string $method, Comparable $condition, array|null $comparables = null): array + { + $comparables = $comparables ?: $this->comparables; + $filtered = array_filter($comparables, function ($item) use ($method, $condition) { + $this->verifyComparable($item); + return $item->$method($condition); + }); + return array_values($filtered); + } + + /** + * Reduce array of comparable items according comparison method + * @param string $method Comparison method to use + * @param array|null $comparables List of objects implementing Comparable + * @return Comparable|null The resolved instance + * @throws IncomparableException Thrown if any item in the list can not be compared + */ + private function applyReduction(string $method, array|null $comparables = null): Comparable|null + { + $comparables = $comparables ?: $this->comparables; + return array_reduce($comparables, function ($item_1, $item_2) use ($method) { + if (is_null($item_1)) { + return $item_2; + } + $this->verifyComparable($item_1); + return $item_1->$method($item_2) ? $item_1 : $item_2; + }); + } +} diff --git a/vendor/phrity/comparison/src/ComparisonTrait.php b/vendor/phrity/comparison/src/ComparisonTrait.php new file mode 100644 index 0000000..dbea328 --- /dev/null +++ b/vendor/phrity/comparison/src/ComparisonTrait.php @@ -0,0 +1,69 @@ + Comparison + */ + +namespace Phrity\Comparison; + +/** + * Trait that enables comparison methods. + */ +trait ComparisonTrait +{ + /** + * If $this is equal to $that. + * @param mixed $that The instance to compare with + * @return boolean True if $this is equal to $that + * @throws IncomparableException Thrown if $this can not be compared with $that + */ + public function equals(mixed $that): bool + { + return $this->compare($that) == 0; + } + + /** + * If $this is greater than $that. + * @param mixed $that The instance to compare with + * @return boolean True if $this is greater than $that + * @throws IncomparableException Thrown if $this can not be compared with $that + */ + public function greaterThan(mixed $that): bool + { + return $this->compare($that) > 0; + } + + /** + * If $this is greater than or equal to $that. + * @param mixed $that The instance to compare with + * @return boolean True if $this is greater than or equal to $that + * @throws IncomparableException Thrown if $this can not be compared with $that + */ + public function greaterThanOrEqual(mixed $that): bool + { + return $this->compare($that) >= 0; + } + + /** + * If $this is less than $that. + * @param mixed $that The instance to compare with + * @return boolean True if $this is less than $that + * @throws IncomparableException Thrown if $this can not be compared with $that + */ + public function lessThan(mixed $that): bool + { + return $this->compare($that) < 0; + } + + /** + * If $this is less than or equal to $this. + * @param mixed $that The instance to compare with + * @return boolean True if $this is less than or equal to $this + * @throws IncomparableException Thrown if $this can not be compared with $that + */ + public function lessThanOrEqual(mixed $that): bool + { + return $this->compare($that) <= 0; + } +} diff --git a/vendor/phrity/comparison/src/Equalable.php b/vendor/phrity/comparison/src/Equalable.php new file mode 100644 index 0000000..8e207e3 --- /dev/null +++ b/vendor/phrity/comparison/src/Equalable.php @@ -0,0 +1,22 @@ + Comparison + */ + +namespace Phrity\Comparison; + +/** + * Interface for equalable instances. + */ +interface Equalable +{ + /** + * If $this is equal to $that. + * @param mixed $that The instance to compare with + * @return boolean True if $this is equal to $that + * @throws IncomparableException Must throw if $this can not be compared with $that + */ + public function equals(mixed $that): bool; +} diff --git a/vendor/phrity/comparison/src/IncomparableException.php b/vendor/phrity/comparison/src/IncomparableException.php new file mode 100644 index 0000000..fbc086f --- /dev/null +++ b/vendor/phrity/comparison/src/IncomparableException.php @@ -0,0 +1,17 @@ + Comparison + */ + +namespace Phrity\Comparison; + +use InvalidArgumentException; + +/** + * Exception that should be thrown when instances can not be compared. + */ +class IncomparableException extends InvalidArgumentException +{ +} diff --git a/vendor/phrity/http/LICENSE b/vendor/phrity/http/LICENSE new file mode 100644 index 0000000..09eaada --- /dev/null +++ b/vendor/phrity/http/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Sören Jensen + +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/phrity/http/composer.json b/vendor/phrity/http/composer.json new file mode 100644 index 0000000..9302b94 --- /dev/null +++ b/vendor/phrity/http/composer.json @@ -0,0 +1,40 @@ +{ + "name": "phrity/http", + "type": "library", + "description": "Utilities and interfaces for handling HTTP.", + "homepage": "https://phrity.sirn.se/http", + "keywords": ["HTTP", "HTTP Factories", "PSR-17"], + "license": "MIT", + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "autoload": { + "psr-4": { + "Phrity\\Http\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Phrity\\Http\\Test\\": "tests/" + } + }, + "require": { + "php": "^8.1", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 | ^2.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "robiningelbrecht/phpunit-coverage-tools": "^1.9", + "squizlabs/php_codesniffer": "^3.5" + }, + "provides": { + "psr/http-factory-implementation": "*" + } +} diff --git a/vendor/phrity/http/docu.json b/vendor/phrity/http/docu.json new file mode 100644 index 0000000..e217c11 --- /dev/null +++ b/vendor/phrity/http/docu.json @@ -0,0 +1,10 @@ +[ + { + "name": "Overview", + "source": "README.md" + }, + { + "name": "License", + "source": "LICENSE" + } +] diff --git a/vendor/phrity/http/src/HttpFactory.php b/vendor/phrity/http/src/HttpFactory.php new file mode 100644 index 0000000..5e5ff6e --- /dev/null +++ b/vendor/phrity/http/src/HttpFactory.php @@ -0,0 +1,175 @@ +requestFactory)) { + throw new BadMethodCallException('HttpFactory.createRequest not implemented.'); + } + return $this->requestFactory->createRequest($method, $uri); + } + + /** + * @param int $code + * @param string $reasonPhrase + */ + public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface + { + if (is_null($this->responseFactory)) { + throw new BadMethodCallException('HttpFactory.createResponse not implemented.'); + } + return $this->responseFactory->createResponse($code, $reasonPhrase); + } + + /** + * @param string $method + * @param UriInterface|string $uri + * @param array $serverParams + */ + public function createServerRequest(string $method, mixed $uri, array $serverParams = []): ServerRequestInterface + { + if (is_null($this->serverRequestFactory)) { + throw new BadMethodCallException('HttpFactory.createServerRequest not implemented.'); + } + return $this->serverRequestFactory->createServerRequest($method, $uri); + } + + /** + * @param string $content + */ + public function createStream(string $content = ''): StreamInterface + { + if (is_null($this->streamFactory)) { + throw new BadMethodCallException('HttpFactory.createStream not implemented.'); + } + return $this->streamFactory->createStream($content); + } + + /** + * @param string $filename + * @param string $mode + */ + public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface + { + if (is_null($this->streamFactory)) { + throw new BadMethodCallException('HttpFactory.createStreamFromFile not implemented.'); + } + return $this->streamFactory->createStreamFromFile($filename, $mode); + } + + /** + * @param resource $resource + */ + public function createStreamFromResource($resource): StreamInterface + { + if (is_null($this->streamFactory)) { + throw new BadMethodCallException('HttpFactory.createStreamFromResource not implemented.'); + } + return $this->streamFactory->createStreamFromResource($resource); + } + + /** + * @param StreamInterface $stream + * @param int $size + * @param int $error + * @param string $clientFilename + * @param string $clientMediaType + */ + public function createUploadedFile( + StreamInterface $stream, + int|null $size = null, + int $error = UPLOAD_ERR_OK, + string|null $clientFilename = null, + string|null $clientMediaType = null + ): UploadedFileInterface { + if (is_null($this->uploadedFileFactory)) { + throw new BadMethodCallException('HttpFactory.createUploadedFile not implemented.'); + } + return $this->uploadedFileFactory->createUploadedFile( + $stream, + $size, + $error, + $clientFilename, + $clientMediaType + ); + } + + /** + * @param string $uri The URI to parse. + */ + public function createUri(string $uri = ''): UriInterface + { + if (is_null($this->uriFactory)) { + throw new BadMethodCallException('HttpFactory.createUri not implemented.'); + } + return $this->uriFactory->createUri($uri); + } + + public static function create(object ...$implementations): self + { + $created = new self(); + foreach ($implementations as $implementation) { + if ($implementation instanceof RequestFactoryInterface) { + $created->requestFactory = $implementation; + } + if ($implementation instanceof ResponseFactoryInterface) { + $created->responseFactory = $implementation; + } + if ($implementation instanceof ServerRequestFactoryInterface) { + $created->serverRequestFactory = $implementation; + } + if ($implementation instanceof StreamFactoryInterface) { + $created->streamFactory = $implementation; + } + if ($implementation instanceof UploadedFileFactoryInterface) { + $created->uploadedFileFactory = $implementation; + } + if ($implementation instanceof UriFactoryInterface) { + $created->uriFactory = $implementation; + } + } + return $created; + } +} diff --git a/vendor/phrity/net-stream/composer.json b/vendor/phrity/net-stream/composer.json new file mode 100644 index 0000000..d3778d4 --- /dev/null +++ b/vendor/phrity/net-stream/composer.json @@ -0,0 +1,38 @@ +{ + "name": "phrity/net-stream", + "type": "library", + "description": "Socket stream classes implementing PSR-7 Stream and PSR-17 StreamFactory", + "homepage": "https://phrity.sirn.se/net-stream", + "keywords": ["socket", "stream", "stream factory", "client", "server", "PSR-7", "PSR-17"], + "license": "MIT", + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "autoload": { + "psr-4": { + "Phrity\\Net\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Phrity\\Net\\Test\\": "tests/mock/" + } + }, + "require": { + "php": "^8.1", + "phrity/util-errorhandler": "^1.1", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 | ^2.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "phrity/net-uri": "^2.0", + "squizlabs/php_codesniffer": "^3.5" + } +} diff --git a/vendor/phrity/net-stream/src/Context.php b/vendor/phrity/net-stream/src/Context.php new file mode 100644 index 0000000..4376c69 --- /dev/null +++ b/vendor/phrity/net-stream/src/Context.php @@ -0,0 +1,214 @@ +, Closure> */ + protected array $notifiers = []; + + /** + * Create exception. + * @param open-resource|null $stream + * @throws InvalidArgumentException if not a resource + * @throws InvalidArgumentException if wrong resource type + */ + public function __construct(mixed $stream = null) + { + if (is_null($stream)) { + $stream = stream_context_create(); + } + $type = gettype($stream); + if ($type !== 'resource') { + throw new InvalidArgumentException("Invalid stream provided; got type '{$type}'."); + } + $rtype = get_resource_type($stream); + if (!in_array($rtype, ['stream', 'persistent stream', 'stream-context'])) { + throw new InvalidArgumentException("Invalid stream provided; got resource type '{$rtype}'."); + } + $this->stream = $stream; + stream_context_set_params($this->stream, ['notification' => function (...$input) { + $this->notifyCallback(...$input); + }]); + } + + public function getOption(string $wrapper, string $option): mixed + { + return stream_context_get_options($this->stream)[$wrapper][$option] ?? null; + } + + /** + * @return array> + */ + public function getOptions(): array + { + return stream_context_get_options($this->stream); + } + + /** + * @throws StreamException on failure + */ + public function setOption(string $wrapper, string $option, mixed $value): self + { + if (!is_resource($this->stream) || !stream_context_set_option($this->stream, $wrapper, $option, $value)) { + throw new StreamException(StreamException::CONTEXT_SET_ERR); + } + return $this; + } + + /** + * @param array> $options + */ + public function setOptions(array $options): self + { + foreach ($options as $wrapper => $wrapperOptions) { + foreach ($wrapperOptions as $option => $value) { + $this->setOption($wrapper, $option, $value); + } + } + return $this; + } + + /** + * @deprecated Use getOption. + */ + public function getParam(string $param): mixed + { + return stream_context_get_params($this->stream)[$param] ?? null; + } + + /** + * @return array + * @deprecated Use getOptions. + */ + public function getParams(): array + { + return stream_context_get_params($this->stream); + } + + /** + * @deprecated Use setOption and on- callbacks instead. + */ + public function setParam(string $param, mixed $value): self + { + $this->setParams([$param => $value]); + return $this; + } + + /** + * @param array $params + * @deprecated Use setOptions and on- callbacks instead. + * @throws StreamException on failure + */ + public function setParams(array $params): self + { + /** @phpstan-ignore booleanNot.alwaysFalse */ + if (!is_resource($this->stream) || !stream_context_set_params($this->stream, $params)) { + throw new StreamException(StreamException::CONTEXT_SET_ERR); + } + return $this; + } + + public function getResource(): mixed + { + return $this->stream; + } + + /** @param Closure(): void $closure */ + public function onResolve(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_RESOLVE] = $closure; + } + + /** @param Closure(): void $closure */ + public function onConnect(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_CONNECT] = $closure; + } + + /** @param Closure(): void $closure */ + public function onAuthRequired(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_AUTH_REQUIRED] = $closure; + } + + /** @param Closure(string $mimeType): void $closure */ + public function onMimeType(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_MIME_TYPE_IS] = $closure; + } + + /** @param Closure(int $fileSize): void $closure */ + public function onFileSize(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_FILE_SIZE_IS] = $closure; + } + + /** @param Closure(string $uri): void $closure */ + public function onRedirected(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_REDIRECTED] = $closure; + } + + /** @param Closure(int $transferred, int $max): void $closure */ + public function onProgress(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_PROGRESS] = $closure; + } + + /** @param Closure(): void $closure */ + public function onCompleted(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_COMPLETED] = $closure; + } + + /** @param Closure(string $message, int $code): void $closure */ + public function onFailure(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_FAILURE] = $closure; + } + + /** @param Closure(): void $closure */ + public function onAuthResult(Closure $closure): void + { + $this->notifiers[STREAM_NOTIFY_AUTH_RESULT] = $closure; + } + + protected function notifyCallback( + int $code, + int $severity, + string|null $message, + int $errorCode, + int $transferred, + int $max, + ): void { + if (!array_key_exists($code, $this->notifiers)) { + return; + } + $callback = $this->notifiers[$code]; + $params = match ($code) { + STREAM_NOTIFY_RESOLVE, + STREAM_NOTIFY_CONNECT, + STREAM_NOTIFY_AUTH_REQUIRED, + STREAM_NOTIFY_COMPLETED, + STREAM_NOTIFY_AUTH_RESULT => [], + STREAM_NOTIFY_MIME_TYPE_IS => ['mimeType' => $message], + STREAM_NOTIFY_FILE_SIZE_IS => ['fileSize' => $message], + STREAM_NOTIFY_REDIRECTED => ['uri' => $message], + STREAM_NOTIFY_PROGRESS => ['transferred' => $transferred, 'max' => $max], + STREAM_NOTIFY_FAILURE => ['message' => $message, 'code' => $errorCode], + }; + $callback(...$params); + } +} diff --git a/vendor/phrity/net-stream/src/SocketClient.php b/vendor/phrity/net-stream/src/SocketClient.php new file mode 100644 index 0000000..2a71c91 --- /dev/null +++ b/vendor/phrity/net-stream/src/SocketClient.php @@ -0,0 +1,108 @@ +|float|null */ + protected int|float|null $timeout = null; + protected Context $context; + + /** + * Create new socker server instance + * @param UriInterface $uri The URI to open socket on. + */ + public function __construct(UriInterface $uri, Context|null $context = null) + { + $this->uri = $uri; + $this->context = $context ?? new Context(); + $this->handler = new ErrorHandler(); + } + + + // ---------- Configuration --------------------------------------------------------------------------------------- + + /** + * Set stream context. + * @param Context|array>|null $options + * @param array|null $params + * @return SocketClient + */ + public function setContext(Context|array|null $options = null, array|null $params = null): self + { + if ($options instanceof Context) { + $this->context = $options; + return $this; + } + // @deprecated + // @todo Add deprecation warning + $this->context->setOptions($options ?? []); + $this->context->setParams($params ?? []); + return $this; + } + + public function getContext(): Context + { + return $this->context; + } + + /** + * Set connection persistency. + * @param bool $persistent + * @return SocketClient + */ + public function setPersistent(bool $persistent): self + { + $this->persistent = $persistent; + return $this; + } + + /** + * Set timeout in seconds. + * @param int<0, max>|float|null $timeout + * @return SocketClient + * @throws InvalidArgumentException if invalid timeout + */ + public function setTimeout(int|float|null $timeout): self + { + if (!is_null($timeout) && $timeout < 0) { + throw new InvalidArgumentException("Timeout must be 0 or more."); + } + $this->timeout = $timeout; + return $this; + } + + + // ---------- Operations ------------------------------------------------------------------------------------------ + + /** + * Create a connection on remote socket. + * @return SocketStream The stream for opened conenction. + */ + public function connect(): SocketStream + { + /** @throws StreamException if connection could not be created */ + $stream = $this->handler->with(function () { + $error_code = $error_message = ''; + return stream_socket_client( + $this->uri->__toString(), + $error_code, + $error_message, + $this->timeout, + $this->persistent ? STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT : STREAM_CLIENT_CONNECT, + $this->context->getResource() + ); + }, new StreamException(StreamException::CLIENT_CONNECT_ERR, ['uri' => $this->uri])); + return new SocketStream($stream); + } +} diff --git a/vendor/phrity/net-stream/src/SocketServer.php b/vendor/phrity/net-stream/src/SocketServer.php new file mode 100644 index 0000000..1b1dff5 --- /dev/null +++ b/vendor/phrity/net-stream/src/SocketServer.php @@ -0,0 +1,173 @@ + */ + private static array $internet_schemes = ['tcp', 'udp', 'tls', 'ssl']; + /** @var array */ + private static array $unix_schemes = ['unix', 'udg']; + + protected ErrorHandler $handler; + protected string $address; + /** @var resource */ + protected $stream; + protected Context $context; + + /** + * Create new socker server instance + * @param UriInterface $uri The URI to open socket on. + * @throws StreamException if invalid scheme. + * @throws StreamException if unsupported scheme. + * @throws StreamException if unable to create socket. + */ + public function __construct(UriInterface $uri, Context|null $context = null) + { + $this->handler = new ErrorHandler(); + if (!in_array($uri->getScheme(), $this->getTransports())) { + throw new StreamException(StreamException::SCHEME_TRANSPORT, ['scheme' => $uri->getScheme()]); + } + if (in_array(substr($uri->getScheme(), 0, 3), self::$internet_schemes)) { + $this->address = "{$uri->getScheme()}://{$uri->getAuthority()}"; + } elseif (in_array($uri->getScheme(), self::$unix_schemes)) { + $this->address = "{$uri->getScheme()}://{$uri->getPath()}"; + } else { + throw new StreamException(StreamException::SCHEME_HANDLER, ['scheme' => $uri->getScheme()]); + } + $this->context = $context ?? new Context(); + /** @throws StreamException on failure */ + $this->stream = $this->handler->with(function () { + $error_code = $error_message = ''; + return stream_socket_server( + $this->address, + $error_code, + $error_message, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, + $this->context->getResource() + ); + }, new StreamException(StreamException::SERVER_SOCKET_ERR, ['uri' => $uri->__toString()])); + $this->evalStream(); + } + + + // ---------- Configuration --------------------------------------------------------------------------------------- + + /** + * Set stream context. + * @param Context|array>|null $options + * @param array|null $params + * @return static + */ + public function setContext(Context|array|null $options = null, array|null $params = null): self + { + if ($options instanceof Context) { + $this->context = $options; + return $this; + } + // @deprecated + // @todo Add deprecation warning + $this->context->setOptions($options ?? []); + $this->context->setParams($params ?? []); + return $this; + } + + public function getContext(): Context + { + return $this->context; + } + + /** + * Retrieve list of registered socket transports. + * @return array List of registered transports. + */ + public function getTransports(): array + { + return stream_get_transports(); + } + + /** + * If server is in blocking mode. + * @return bool|null + */ + public function isBlocking(): bool|null + { + return $this->getMetadata('blocked'); + } + + /** + * Toggle blocking/non-blocking mode. + * @param bool $enable Blocking mode to set. + * @return bool If operation was succesful. + * @throws StreamException if socket is closed. + */ + public function setBlocking(bool $enable): bool + { + if (!is_resource($this->stream)) { + throw new StreamException(StreamException::SERVER_CLOSED); + } + return stream_set_blocking($this->stream, $enable); + } + + /** + * Get stream metadata as an associative array or retrieve a specific key. + * @param string $key Specific metadata to retrieve. + * @return array|mixed|null Returns an associative array if no key is + * provided. Returns a specific key value if a key is provided and the + * value is found, or null if the key is not found. + */ + public function getMetadata(string|null $key = null): mixed + { + if (!is_resource($this->stream)) { + return null; + } + // Add URI default for version compability + $meta = array_merge([ + 'uri' => $this->address, + ], stream_get_meta_data($this->stream)); + if (isset($key)) { + return array_key_exists($key, $meta) ? $meta[$key] : null; + } + return $meta; + } + + + // ---------- Operations ------------------------------------------------------------------------------------------ + + /** + * Accept a connection on a socket. + * @param int<0, max>|float|null $timeout Override the default socket accept timeout. + * @return SocketStream|null The stream for opened conenction. + * @throws InvalidArgumentException if invalid timeout + * @throws StreamException if socket is closed + */ + public function accept(int|float|null $timeout = null): SocketStream|null + { + if (!is_null($timeout) && $timeout < 0) { + throw new InvalidArgumentException("Timeout must be 0 or more."); + } + if (!is_resource($this->stream)) { + throw new StreamException(StreamException::SERVER_CLOSED); + } + /** @throws StreamException */ + $stream = $this->handler->with(function () use ($timeout) { + $peer_name = ''; + return stream_socket_accept($this->stream, $timeout, $peer_name); + }, function (ErrorException $e) { + // If non-blocking mode, don't throw error on time out + if ($this->getMetadata('blocked') === false && substr_count($e->getMessage(), 'timed out') > 0) { + return null; + } + throw new StreamException(StreamException::SERVER_ACCEPT_ERR); + }); + return $stream ? new SocketStream($stream) : null; + } +} diff --git a/vendor/phrity/net-stream/src/SocketStream.php b/vendor/phrity/net-stream/src/SocketStream.php new file mode 100644 index 0000000..d83dc98 --- /dev/null +++ b/vendor/phrity/net-stream/src/SocketStream.php @@ -0,0 +1,166 @@ +stream) && ($this->readable || $this->writable); + } + + /** + * Get name of remote socket, or null if not connected. + * @return string|null + */ + public function getRemoteName(): string|null + { + return is_resource($this->stream) ? (stream_socket_get_name($this->stream, true) ?: null) : null; + } + + /** + * Get name of local socket, or null if not connected. + * @return string|null + */ + public function getLocalName(): string|null + { + return is_resource($this->stream) ? (stream_socket_get_name($this->stream, false) ?: null) : null; + } + + /** + * Get type of stream resoucre. + * @return string + */ + public function getResourceType(): string + { + return $this->stream ? get_resource_type($this->stream) : ''; + } + + /** + * If stream is in blocking mode. + * @return bool|null + */ + public function isBlocking(): bool|null + { + return $this->getMetadata('blocked'); + } + + /** + * Toggle blocking/non-blocking mode. + * @param bool $enable Blocking mode to set. + * @return bool If operation was succesful. + * @throws StreamException if stream is closed. + */ + public function setBlocking(bool $enable): bool + { + if (!isset($this->stream)) { + throw new StreamException(StreamException::STREAM_DETACHED); + } + return stream_set_blocking($this->stream, $enable); + } + + /** + * If socket stream has unread content. + * @return bool If there is content to read. + * @throws StreamException if stream is unselectable. + */ + public function hasContents(): bool + { + if (!is_resource($this->stream)) { + return false; + } + /** @throws StreamException */ + return $this->handler->with(function () { + $read = [$this->getOpenResource()]; + $write = $oob = []; + return stream_select($read, $write, $oob, 0, 0) > 0; + }, new StreamException(StreamException::FAIL_SELECT)); + } + + /** + * Set timeout period on a stream. + * @param int<0, max>|float $timeout Seconds to be set. + * @param int|null $microseconds Microseconds to be set - deprecated + * @return bool If operation was succesful. + * @throws InvalidArgumentException if invalid timeout. + * @throws StreamException if stream is closed. + */ + public function setTimeout(int|float $timeout, int|null $microseconds = null): bool + { + // @deprecated Setting $microseconds is deprecated, use float value on $timeout instead + // @todo Add deprecation warning + if ($timeout < 0) { + throw new InvalidArgumentException("Timeout must be 0 or more."); + } + if (!isset($this->stream)) { + throw new StreamException(StreamException::STREAM_DETACHED); + } + $seconds = intval($timeout); + $microseconds = $microseconds ?? intval(round($timeout - $seconds, 6) * 1000000); + return stream_set_timeout($this->stream, $seconds, $microseconds); + } + + + // ---------- Operations ------------------------------------------------------------------------------------------ + + /** + * Read line from the stream. + * @param int<0, max> $length Read up to $length bytes from the object and return them. + * @return string|null Returns the data read from the stream, or null of eof. + * @throws StreamException if an error occurs. + */ + public function readLine(int $length): string|null + { + $stream = $this->getOpenResource(); + if (!$this->readable) { + throw new StreamException(StreamException::NOT_READABLE); + } + /** @throws StreamException */ + return $this->handler->with(function () use ($stream, $length) { + $result = fgets($stream, $length); + return $result === false ? null : $result; + }, new StreamException(StreamException::FAIL_GETS)); + } + + /** + * Closes the stream for further reading. + * @return void + */ + public function closeRead(): void + { + if (is_resource($this->stream)) { + if ($this->readable && $this->writable) { + stream_socket_shutdown($this->stream, STREAM_SHUT_RD); + $this->evalStream(); + } elseif (!$this->writable) { + $this->close(); + } + } + $this->readable = false; + } + /** + * Closes the stream for further writing. + * @return void + */ + public function closeWrite(): void + { + if ($this->readable && $this->writable) { + stream_socket_shutdown($this->getOpenResource(), STREAM_SHUT_WR); + $this->evalStream(); + } elseif (!$this->readable) { + $this->close(); + } + $this->writable = false; + } +} diff --git a/vendor/phrity/net-stream/src/Stream.php b/vendor/phrity/net-stream/src/Stream.php new file mode 100644 index 0000000..71fb8c1 --- /dev/null +++ b/vendor/phrity/net-stream/src/Stream.php @@ -0,0 +1,317 @@ + */ + private static array $readmodes = ['r', 'r+', 'w+', 'a+', 'x+', 'c+']; + /** @var array */ + private static array $writemodes = ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+']; + + /** @var resource|null */ + protected $stream; + protected Context $context; + protected ErrorHandler $handler; + protected bool $readable = false; + protected bool $writable = false; + protected bool $seekable = false; + + /** + * Create new stream wrapper instance + * @param resource $stream A stream resource to wrap + * @throws InvalidArgumentException If not a valid stream resource + */ + public function __construct($stream) + { + $type = gettype($stream); + if ($type !== 'resource') { + throw new InvalidArgumentException("Invalid stream provided; got type '{$type}'."); + } + $rtype = get_resource_type($stream); + if (!in_array($rtype, ['stream', 'persistent stream'])) { + throw new InvalidArgumentException("Invalid stream provided; got resource type '{$rtype}'."); + } + $this->stream = $stream; + $this->context = new Context($this->stream); + $this->handler = new ErrorHandler(); + $this->evalStream(); + } + + + // ---------- PSR-7 methods --------------------------------------------------------------------------------------- + + /** + * Closes the stream and any underlying resources. + * @return void + */ + public function close(): void + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + $this->evalStream(); + } + + /** + * Separates any underlying resources from the stream. + * After the stream has been detached, the stream is in an unusable state. + * @return resource|null Underlying stream, if any + */ + public function detach(): mixed + { + if (!isset($this->stream)) { + return null; + } + $stream = $this->stream; + $this->stream = null; + $this->evalStream(); + return $stream; + } + + /** + * Get stream metadata as an associative array or retrieve a specific key. + * @param string $key Specific metadata to retrieve. + * @return array|mixed|null Returns an associative array if no key is + * provided. Returns a specific key value if a key is provided and the + * value is found, or null if the key is not found. + */ + public function getMetadata(string|null $key = null): mixed + { + if (!isset($this->stream)) { + return null; + } + $meta = stream_get_meta_data($this->stream); + if (isset($key)) { + return array_key_exists($key, $meta) ? $meta[$key] : null; + } + return $meta; + } + + /** + * Returns the current position of the file read/write pointer + * @return int Position of the file pointer + * @throws StreamException on error. + */ + public function tell(): int + { + /** @throws StreamException */ + return $this->handler->with(function () { + return ftell($this->getOpenResource()); + }, new StreamException(StreamException::FAIL_TELL)); + } + + /** + * Returns true if the stream is at the end of the stream. + * @return bool + */ + public function eof(): bool + { + return empty($this->stream) || feof($this->stream); + } + + /** + * Read data from the stream. + * @param int<1, max> $length Read up to $length bytes from the object and return them. + * @return string Returns the data read from the stream, or an empty string. + * @throws StreamException if an error occurs. + */ + public function read(int $length): string + { + if ($length < 1) { + throw new InvalidArgumentException("Must read minimum 1 byte"); + } + $stream = $this->getOpenResource(); + if (!$this->readable) { + throw new StreamException(StreamException::NOT_READABLE); + } + /** @throws StreamException */ + return $this->handler->with(function () use ($stream, $length) { + return (string)fread($stream, $length); + }, new StreamException(StreamException::FAIL_READ)); + } + + /** + * Write data to the stream. + * @param string $string The string that is to be written. + * @return int Returns the number of bytes written to the stream. + * @throws StreamException on failure. + */ + public function write(string $string): int + { + $stream = $this->getOpenResource(); + if (!$this->writable) { + throw new StreamException(StreamException::NOT_WRITABLE); + } + /** @throws StreamException */ + return $this->handler->with(function () use ($stream, $string) { + return fwrite($stream, $string); + }, new StreamException(StreamException::FAIL_WRITE)); + } + + /** + * Get the size of the stream if known. + * @return int|null Returns the size in bytes if known, or null if unknown. + */ + public function getSize(): int|null + { + if (!is_resource($this->stream)) { + return null; + } + $stats = fstat($this->stream); + return $stats['size'] ?? null; + } + + /** + * Returns whether or not the stream is seekable. + * @return bool + */ + public function isSeekable(): bool + { + return $this->seekable; + } + + /** + * Seek to a position in the stream. + * @param int $offset Stream offset + * @param int $whence Specifies how the cursor position will be calculated based on the seek offset. + * @throws StreamException on failure. + */ + public function seek(int $offset, int $whence = SEEK_SET): void + { + $stream = $this->getOpenResource(); + if (!$this->seekable) { + throw new StreamException(StreamException::NOT_SEEKABLE); + } + $result = fseek($stream, $offset, $whence); + if ($result !== 0) { + throw new StreamException(StreamException::FAIL_SEEK); + } + } + + /** + * Seek to the beginning of the stream. + * If the stream is not seekable, this method will raise an exception; + * otherwise, it will perform a seek(0). + */ + public function rewind(): void + { + $this->seek(0); + } + + /** + * Returns whether or not the stream is writable. + * @return bool + */ + public function isWritable(): bool + { + return $this->writable; + } + + /** + * Returns whether or not the stream is readable. + * @return bool + */ + public function isReadable(): bool + { + return $this->readable; + } + + /** + * Returns the remaining contents in a string + * @return string + * @throws StreamException if unable to read. + * @throws StreamException if error occurs while reading. + */ + public function getContents(): string + { + $stream = $this->getOpenResource(); + if (!$this->readable) { + throw new StreamException(StreamException::NOT_READABLE); + } + /** @throws StreamException */ + return $this->handler->with(function () use ($stream) { + return stream_get_contents($stream); + }, new StreamException(StreamException::FAIL_CONTENTS)); + } + + /** + * Reads all data from the stream into a string, from the beginning to end. + * @return string + */ + public function __toString(): string + { + try { + if ($this->isSeekable()) { + $this->rewind(); + } + return $this->getContents(); + } catch (Throwable $e) { + trigger_error($e->getMessage(), E_USER_WARNING); + return ''; + } + } + + + // ---------- Extended methods ------------------------------------------------------------------------------------ + + /** + * Return context for stream. + * @return Context + */ + public function getContext(): Context + { + return $this->context; + } + + /** + * Return underlying resource. + * @return resource|null + */ + public function getResource(): mixed + { + return $this->stream; + } + + + // ---------- Protected helper methods ---------------------------------------------------------------------------- + + /** + * Evaluate stream state. + */ + protected function evalStream(): void + { + if ($this->stream && $meta = $this->getMetadata()) { + $mode = substr($meta['mode'], 0, 2); + $this->readable = in_array($mode, self::$readmodes); + $this->writable = in_array($mode, self::$writemodes); + $this->seekable = $meta['seekable']; + return; + } + $this->readable = $this->writable = $this->seekable = false; + } + + /** + * Return underlying resource. + * @return resource + * @throws StreamException if closed. + */ + protected function getOpenResource(): mixed + { + if (!is_resource($this->stream)) { + throw new StreamException(StreamException::STREAM_DETACHED); + } + return $this->stream; + } +} diff --git a/vendor/phrity/net-stream/src/StreamCollection.php b/vendor/phrity/net-stream/src/StreamCollection.php new file mode 100644 index 0000000..fd13154 --- /dev/null +++ b/vendor/phrity/net-stream/src/StreamCollection.php @@ -0,0 +1,215 @@ + + */ +class StreamCollection implements Countable, Iterator +{ + protected ErrorHandler $handler; + /** @var array */ + private array $streams = []; + + /** + * Create new stream collection instance. + */ + public function __construct() + { + $this->handler = new ErrorHandler(); + } + + + // ---------- Collectors and selectors ---------------------------------------------------------------------------- + + /** + * Attach stream to collection. + * @param Stream $attach Stream to attach. + * @param string|null $key Definable name of stream. + * @return string Name of stream. + * @throws StreamException If already attached. + */ + public function attach(Stream $attach, string|null $key = null): string + { + if ($key && array_key_exists($key, $this->streams)) { + throw new StreamException(StreamException::COLLECT_KEY_CONFLICT, ['key' => $key]); + } + $key = $key ?: $this->createKey(); + $this->streams[$key] = $attach; + return $key; + } + + /** + * Detach stream from collection. + * @param Stream|string $detach Stream or name of stream to detach. + * @return bool If a stream was detached. + */ + public function detach(Stream|string $detach): bool + { + if (is_string($detach)) { + if (array_key_exists($detach, $this->streams)) { + unset($this->streams[$detach]); + return true; + } + } + if ($detach instanceof Stream) { + foreach ($this->streams as $key => $stream) { + if ($stream === $detach) { + unset($this->streams[$key]); + return true; + } + } + } + return false; + } + + /** + * Collect all readable streams into new collection. + * @return self New collection instance. + */ + public function getReadable(): self + { + $readables = new self(); + foreach ($this->streams as $key => $stream) { + if ($stream->isReadable()) { + $readables->attach($stream, $key); + } + } + return $readables; + } + + /** + * Collect all writable streams into new collection. + * @return self New collection instance. + */ + public function getWritable(): self + { + $writables = new self(); + foreach ($this->streams as $key => $stream) { + if ($stream->isWritable()) { + $writables->attach($stream, $key); + } + } + return $writables; + } + + /** + * Wait for redable content in stream collection. + * @param int<0, max>|float $timeout Timeout in seconds. + * @return self New collection instance. + * @throws InvalidArgumentException If invalid timeout. + */ + public function waitRead(int|float $timeout = 60): self + { + if ($timeout < 0) { + throw new InvalidArgumentException("Timeout must be 0 or more."); + } + $seconds = intval($timeout); + $microseconds = intval(round($timeout - $seconds, 6) * 1000000); + + $read = []; + foreach ($this->streams as $key => $stream) { + if ($stream->isReadable()) { + $read[$key] = $stream->getResource(); + } + } + if (empty($read)) { + return new self(); // Nothing to select + } + + $changed = $this->handler->with(function () use ($read, $seconds, $microseconds) { + $write = $oob = []; + /** @phpstan-ignore argument.type */ + stream_select($read, $write, $oob, $seconds, $microseconds); + return $read; + }, function (ErrorException $error) { + return []; // Ignore, but don't use result + }); + + $ready = new self(); + foreach ($changed as $key => $resource) { + $ready->attach($this->streams[$key], $key); + } + return $ready; + } + + + // ---------- Countable interface implementation ------------------------------------------------------------------ + + /** + * Count contained streams. + * @return int Number of streams in collection. + */ + public function count(): int + { + return count($this->streams); + } + + + // ---------- Iterator interface implementation ------------------------------------------------------------------- + + /** + * Return the current stream. + * @return Stream|null Current stream. + */ + public function current(): Stream|null + { + return current($this->streams) ?: null; + } + + /** + * Return the key of the current stream. + * @return string Current key. + */ + public function key(): string|null + { + return key($this->streams); + } + + /** + * Move forward to next stream. + */ + public function next(): void + { + next($this->streams); + } + + /** + * Rewind the Iterator to the first stream. + */ + public function rewind(): void + { + reset($this->streams); + } + + /** + * Checks if current position is valid. + * @return bool True if valid. + */ + public function valid(): bool + { + return array_key_exists(key($this->streams) ?? -1, $this->streams); + } + + + // ---------- Protected helper methods ---------------------------------------------------------------------------- + + /** + * Create unique key. + * @return string Unique key. + */ + protected function createKey(): string + { + do { + $key = bin2hex(random_bytes(16)); + } while (array_key_exists($key, $this->streams)); + return $key; + } +} diff --git a/vendor/phrity/net-stream/src/StreamException.php b/vendor/phrity/net-stream/src/StreamException.php new file mode 100644 index 0000000..f5f0367 --- /dev/null +++ b/vendor/phrity/net-stream/src/StreamException.php @@ -0,0 +1,84 @@ + */ + private static array $messages = [ + self::STREAM_DETACHED => 'Stream is detached.', + self::NOT_READABLE => 'Stream is not readable.', + self::NOT_WRITABLE => 'Stream is not writable.', + self::NOT_SEEKABLE => 'Stream is not seekable.', + self::FAIL_READ => 'Failed read() on stream.', + self::FAIL_WRITE => 'Failed write() on stream.', + self::FAIL_SEEK => 'Failed seek() on stream.', + self::FAIL_TELL => 'Failed tell() on stream.', + self::FAIL_CONTENTS => 'Failed getContents() on stream.', + self::FAIL_GETS => 'Failed gets() on stream.', + self::FAIL_SELECT => 'Failed select() on stream.', + self::CLIENT_CONNECT_ERR => 'Client could not connect to "{uri}".', + self::SCHEME_TRANSPORT => 'Scheme "{scheme}" is not supported.', + self::SCHEME_HANDLER => 'Could not handle scheme "{scheme}".', + self::SERVER_SOCKET_ERR => 'Could not create socket for "{uri}".', + self::SERVER_CLOSED => 'Server is closed.', + self::SERVER_ACCEPT_ERR => 'Could not accept on socket.', + self::COLLECT_KEY_CONFLICT => 'Stream with name "{key}" already attached.', + self::COLLECT_SELECT_ERR => 'Failed to select streams for reading.', + self::CONTEXT_SET_ERR => 'Failed to set option/param on context.', + ]; + + /** + * Create exception. + * @param int $code Error code + * @param array $data Additional data + * @param Throwable|null $previous Previous exception + */ + public function __construct(int $code, array $data = [], Throwable|null $previous = null) + { + $message = self::$messages[$code]; + foreach ($data as $key => $content) { + $message = str_replace('{' . $key . '}', $content, $message); + } + if ($previous) { + $message .= " ({$previous->getMessage()})"; + } + parent::__construct($message, $code, $previous); + } +} diff --git a/vendor/phrity/net-stream/src/StreamFactory.php b/vendor/phrity/net-stream/src/StreamFactory.php new file mode 100644 index 0000000..d37ed32 --- /dev/null +++ b/vendor/phrity/net-stream/src/StreamFactory.php @@ -0,0 +1,133 @@ + */ + private static array $modes = ['r', 'r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+', 'e']; + + private ErrorHandler $handler; + + /** + * Create new stream wrapper instance. + */ + public function __construct() + { + $this->handler = new ErrorHandler(); + } + + + // ---------- PSR-17 methods -------------------------------------------------------------------------------------- + + /** + * Create a new stream from a string. + * @param string $content String content with which to populate the stream. + * @return Stream A stream instance. + */ + public function createStream(string $content = ''): Stream + { + $resource = $this->createResource('php://temp', 'r+'); + fwrite($resource, $content); + return $this->createStreamFromResource($resource); + } + + /** + * Create a stream from an existing file. + * @param string $filename The filename or stream URI to use as basis of stream. + * @param string $mode The mode with which to open the underlying filename/stream. + * @throws InvalidArgumentException If the mode is invalid. + * @return Stream A stream instance. + */ + public function createStreamFromFile(string $filename, string $mode = 'r'): Stream + { + if (!in_array($mode, self::$modes)) { + throw new InvalidArgumentException("Invalid mode '{$mode}'."); + } + $resource = $this->createResource($filename, $mode); + return $this->createStreamFromResource($resource); + } + + /** + * Create a new stream from an existing resource. + * The stream MUST be readable and may be writable. + * @param resource $resource The PHP resource to use as the basis for the stream. + * @return Stream A stream instance. + */ + public function createStreamFromResource($resource): Stream + { + return new Stream($resource); + } + + + // ---------- Extensions ------------------------------------------------------------------------------------------ + + /** + * Create a new socket client. + * @param UriInterface $uri The URI to connect to. + * @return SocketClient A socket client instance. + */ + public function createSocketClient(UriInterface $uri, Context|null $context = null): SocketClient + { + return new SocketClient($uri, $context); + } + + /** + * Create a new socket server. + * @param UriInterface $uri The URI to create server on. + * @return SocketServer A socket server instance. + */ + public function createSocketServer(UriInterface $uri, Context|null $context = null): SocketServer + { + return new SocketServer($uri, $context); + } + + /** + * Create a new ocket stream from an existing resource. + * The stream MUST be readable and may be writable. + * @param resource $resource The PHP resource to use as the basis for the stream. + * @return SocketStream A socket stream instance. + */ + public function createSocketStreamFromResource($resource): SocketStream + { + return new SocketStream($resource); + } + + /** + * Create a new stream collection. + * @return StreamCollection A stream collection. + */ + public function createStreamCollection(): StreamCollection + { + return new StreamCollection(); + } + + + // ---------- Helpers --------------------------------------------------------------------------------------------- + + /** + * @return resource + * @throws RuntimeException If fails to open resource + */ + private function createResource(string $filename, string $mode) + { + /** @throws RuntimeException */ + return $this->handler->with(function () use ($filename, $mode) { + /** @var resource $resource */ + $resource = fopen($filename, $mode); + return $resource; + }, new RuntimeException("Could not open '{$filename}'.")); + } +} diff --git a/vendor/phrity/net-uri/composer.json b/vendor/phrity/net-uri/composer.json new file mode 100644 index 0000000..c0f5294 --- /dev/null +++ b/vendor/phrity/net-uri/composer.json @@ -0,0 +1,37 @@ +{ + "name": "phrity/net-uri", + "type": "library", + "description": "PSR-7 Uri and PSR-17 UriFactory implementation", + "homepage": "https://phrity.sirn.se/net-uri", + "keywords": ["uri", "uri factory", "PSR-7", "PSR-17"], + "license": "MIT", + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "autoload": { + "psr-4": { + "Phrity\\Net\\": "src/" + } + }, + "require": { + "php": "^8.1", + "ext-mbstring": "*", + "phrity/comparison": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 | ^2.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "phrity/util-errorhandler": "^1.1", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "ext-intl": "Enables IDN conversion for non-ASCII domains" + } +} diff --git a/vendor/phrity/net-uri/src/Uri.php b/vendor/phrity/net-uri/src/Uri.php new file mode 100644 index 0000000..5d28b03 --- /dev/null +++ b/vendor/phrity/net-uri/src/Uri.php @@ -0,0 +1,710 @@ + Net > Uri + * @see https://www.rfc-editor.org/rfc/rfc3986 + * @see https://www.php-fig.org/psr/psr-7/#35-psrhttpmessageuriinterface + */ + +namespace Phrity\Net; + +use InvalidArgumentException; +use JsonSerializable; +use Phrity\Comparison\{ + Equalable, + IncomparableException, +}; +use Psr\Http\Message\UriInterface; +use Stringable; + +/** + * Net\Uri class. + */ +class Uri implements Equalable, JsonSerializable, Stringable, UriInterface +{ + public const REQUIRE_PORT = 1; // Always include port, explicit or default + public const ABSOLUTE_PATH = 2; // Enforce absolute path + public const NORMALIZE_PATH = 4; // Normalize path + public const IDNA = 8; // @deprecated, replaced by IDN_ENCODE + public const IDN_ENCODE = 16; // IDN-encode host + public const IDN_DECODE = 32; // IDN-decode host + public const URI_DECODE = 64; // Decoded URI + public const URI_ENCODE = 128; // Minimal URI encoded + public const URI_ENCODE_3986 = 256; // URI encoded RFC 3986 + + private const RE_MAIN = '!^(?P(?P[^:/?#]+):)?(?P//(?P[^/?#]*))?' + . '(?P[^?#]*)(?P\?(?P[^#]*))?(?P#(?P.*))?$!'; + private const RE_AUTH = '!^(?P(?P[^:/?#]+)(?P:(?P[^:/?#]+))?@)?' + . '(?P[^:/?#]*|\[[^/?#]*\])(?P:(?P[0-9]*))?$!'; + + /** @var array $portDefaults */ + private static array $portDefaults = [ + 'acap' => 674, + 'afp' => 548, + 'dict' => 2628, + 'dns' => 53, + 'ftp' => 21, + 'git' => 9418, + 'gopher' => 70, + 'http' => 80, + 'https' => 443, + 'imap' => 143, + 'ipp' => 631, + 'ipps' => 631, + 'irc' => 194, + 'ircs' => 6697, + 'ldap' => 389, + 'ldaps' => 636, + 'mms' => 1755, + 'msrp' => 2855, + 'mtqp' => 1038, + 'nfs' => 111, + 'nntp' => 119, + 'nntps' => 563, + 'pop' => 110, + 'prospero' => 1525, + 'redis' => 6379, + 'rsync' => 873, + 'rtsp' => 554, + 'rtsps' => 322, + 'rtspu' => 5005, + 'sftp' => 22, + 'smb' => 445, + 'snmp' => 161, + 'ssh' => 22, + 'svn' => 3690, + 'telnet' => 23, + 'ventrilo' => 3784, + 'vnc' => 5900, + 'wais' => 210, + 'ws' => 80, + 'wss' => 443, + ]; + + private string $scheme = ''; + private bool $authority = false; + private string $host = ''; + private int|null $port = null; + private string $user = ''; + private string|null $pass = null; + private string $path = ''; + private string $query = ''; + private string $fragment = ''; + + /** + * Create new URI instance using a string + * @param string $uriString URI as string + * @throws InvalidArgumentException If the given URI cannot be parsed + */ + public function __construct(string $uriString = '') + { + $this->parse($uriString); + } + + + // ---------- PSR-7 getters --------------------------------------------------------------------------------------- + + /** + * Retrieve the scheme component of the URI. + * @param int $flags Optional modifier flags + * @return string The URI scheme + */ + public function getScheme(int $flags = 0): string + { + return $this->scheme; + } + + /** + * Retrieve the authority component of the URI. + * @param int $flags Optional modifier flags + * @return string The URI authority, in "[user-info@]host[:port]" format + */ + public function getAuthority(int $flags = 0): string + { + $host = $this->formatComponent($this->getHost($flags)); + if ($host === '') { + return ''; + } + $userinfo = $this->formatComponent($this->getUserInfo($flags), '', '@'); + $port = $this->formatComponent($this->getPort($flags), ':'); + return "{$userinfo}{$host}{$port}"; + } + + /** + * Retrieve the user information component of the URI. + * @param int $flags Optional modifier flags + * @return string The URI user information, in "username[:password]" format + */ + public function getUserInfo(int $flags = 0): string + { + $user = $this->formatComponent($this->uriEncode($this->user, $flags)); + $pass = $this->formatComponent($this->uriEncode($this->pass ?? '', $flags), ':'); + return $user === '' ? '' : "{$user}{$pass}"; + } + + /** + * Retrieve the host component of the URI. + * @param int $flags Optional modifier flags + * @return string The URI host + */ + public function getHost(int $flags = 0): string + { + if ($flags & self::IDNA) { + trigger_error("Flag IDNA is deprecated; use IDN_ENCODE instead", E_USER_DEPRECATED); + return $this->idnEncode($this->host); + } + if ($flags & self::IDN_ENCODE) { + return $this->idnEncode($this->host); + } + if ($flags & self::IDN_DECODE) { + return $this->idnDecode($this->host); + } + return $this->host; + } + + /** + * Retrieve the port component of the URI. + * @param int $flags Optional modifier flags + * @return null|int The URI port + */ + public function getPort(int $flags = 0): int|null + { + $default = self::$portDefaults[$this->scheme] ?? null; + if ($flags & self::REQUIRE_PORT) { + return $this->port !== null ? $this->port : $default; + } + return $this->port === $default ? null : $this->port; + } + + /** + * Retrieve the path component of the URI. + * @param int $flags Optional modifier flags + * @return string The URI path + */ + public function getPath(int $flags = 0): string + { + $path = $this->path; + if ($flags & self::NORMALIZE_PATH) { + $path = $this->normalizePath($path); + } + if ($flags & self::ABSOLUTE_PATH && substr($path, 0, 1) !== '/') { + $path = "/{$path}"; + } + return $this->uriEncode($path, $flags, '\/:@'); + } + + /** + * Retrieve the query string of the URI. + * @param int $flags Optional modifier flags + * @return string The URI query string + */ + public function getQuery(int $flags = 0): string + { + return $this->uriEncode($this->query, $flags, '\/:@?'); + } + + /** + * Retrieve the fragment component of the URI. + * @param int $flags Optional modifier flags + * @return string The URI fragment + */ + public function getFragment(int $flags = 0): string + { + return $this->uriEncode($this->fragment, $flags, '\/:@?'); + } + + + // ---------- PSR-7 setters --------------------------------------------------------------------------------------- + + /** + * Return an instance with the specified scheme. + * @param string $scheme The scheme to use with the new instance + * @param int $flags Optional modifier flags + * @return self A new instance with the specified scheme + * @throws InvalidArgumentException for invalid schemes + * @throws InvalidArgumentException for unsupported schemes + */ + public function withScheme(string $scheme, int $flags = 0): self + { + $clone = $this->clone($flags); + $clone->setScheme($scheme, $flags); + return $clone; + } + + /** + * Return an instance with the specified user information. + * @param string $user The user name to use for authority + * @param null|string $password The password associated with $user + * @param int $flags Optional modifier flags + * @return self A new instance with the specified user information + */ + public function withUserInfo(string $user, string|null $password = null, int $flags = 0): self + { + $clone = $this->clone($flags); + $clone->setUserInfo($user, $password); + return $clone; + } + + /** + * Return an instance with the specified host. + * @param string $host The hostname to use with the new instance + * @param int $flags Optional modifier flags + * @return self A new instance with the specified host + * @throws InvalidArgumentException for invalid hostnames + */ + public function withHost(string $host, int $flags = 0): self + { + $clone = $this->clone($flags); + $clone->setHost($host, $flags); + return $clone; + } + + /** + * Return an instance with the specified port. + * @param null|int $port The port to use with the new instance + * @param int $flags Optional modifier flags + * @return self A new instance with the specified port + * @throws InvalidArgumentException for invalid ports + */ + public function withPort(int|null $port, int $flags = 0): self + { + $clone = $this->clone($flags); + $clone->setPort($port, $flags); + return $clone; + } + + /** + * Return an instance with the specified path. + * @param string $path The path to use with the new instance + * @param int $flags Optional modifier flags + * @return self A new instance with the specified path + * @throws InvalidArgumentException for invalid paths + */ + public function withPath(string $path, int $flags = 0): self + { + $clone = $this->clone($flags); + $clone->setPath($path, $flags); + return $clone; + } + + /** + * Return an instance with the specified query string. + * @param string $query The query string to use with the new instance + * @param int $flags Optional modifier flags + * @return self A new instance with the specified query string + * @throws InvalidArgumentException for invalid query strings + */ + public function withQuery(string $query, int $flags = 0): self + { + $clone = $this->clone($flags); + $clone->setQuery($query, $flags); + return $clone; + } + + /** + * Return an instance with the specified URI fragment. + * @param string $fragment The fragment to use with the new instance + * @param int $flags Optional modifier flags + * @return self A new instance with the specified fragment + */ + public function withFragment(string $fragment, int $flags = 0): self + { + $clone = $this->clone($flags); + $clone->setFragment($fragment, $flags); + return $clone; + } + + + // ---------- PSR-7 string & Stringable --------------------------------------------------------------------------- + + /** + * Return the string representation as a URI reference. + * @return string + */ + public function __toString(): string + { + return $this->toString(); + } + + + // ---------- JsonSerializable ------------------------------------------------------------------------------------ + + /** + * Return JSON encode value as URI reference. + * @return string + */ + public function jsonSerialize(): string + { + return $this->toString(); + } + + + // ---------- Equalable ------------------------------------------------------------------------------------------ + + /** + * Return JSON encode value as URI reference. + * @param UriInterface|string $compareWith + * @return bool + */ + public function equals(mixed $compareWith): bool + { + if (!$compareWith instanceof UriInterface && !is_string($compareWith)) { + throw new IncomparableException(sprintf("Can not compare with type '%s'", get_debug_type($compareWith))); + } + $flags = self::REQUIRE_PORT | self::NORMALIZE_PATH | self::IDN_ENCODE; + $them = $compareWith instanceof self ? $compareWith : new self((string)$compareWith); + return $this->toString($flags) == $them->toString($flags); + } + + + // ---------- Extensions ------------------------------------------------------------------------------------------ + + /** + * Return the string representation as a URI reference. + * @param int $flags Optional modifier flags + * @param string $format Optional format specification + * @return string + */ + public function toString(int $flags = 0, string $format = '{scheme}{authority}{path}{query}{fragment}'): string + { + $pathFlags = ($this->authority && $this->path ? self::ABSOLUTE_PATH : 0) | $flags; + return str_replace([ + '{scheme}', + '{authority}', + '{path}', + '{query}', + '{fragment}', + ], [ + $this->formatComponent($this->getScheme($flags), '', ':'), + $this->authority ? "//{$this->formatComponent($this->getAuthority($flags))}" : '', + $this->formatComponent($this->getPath($pathFlags)), + $this->formatComponent($this->getQuery(), '?'), + $this->formatComponent($this->getFragment(), '#'), + ], $format); + } + + /** + * Get compontsns as array; as parse_url() method + * @param int $flags Optional modifier flags + * @return array + */ + public function getComponents(int $flags = 0): array + { + return array_filter([ + 'scheme' => $this->getScheme($flags), + 'host' => $this->getHost($flags), + 'port' => $this->getPort($flags | self::REQUIRE_PORT), + 'user' => $this->user, + 'pass' => $this->pass, + 'path' => $this->getPath($flags), + 'query' => $this->getQuery($flags), + 'fragment' => $this->getFragment($flags), + ]); + } + + /** + * Return an instance with the specified compontents set. + * @param array $components + * @param int<0, 256> $flags + * @return self A new instance with the specified components + */ + public function withComponents(array $components, int $flags = 0): self + { + $clone = $this->clone($flags); + foreach ($components as $component => $value) { + switch ($component) { + case 'port': + $clone->setPort($value, $flags); + break; + case 'scheme': + $clone->setScheme($value, $flags); + break; + case 'host': + $clone->setHost($value, $flags); + break; + case 'path': + $clone->setPath($value, $flags); + break; + case 'query': + $clone->setQuery($value, $flags); + break; + case 'fragment': + $clone->setFragment($value, $flags); + break; + case 'userInfo': + $clone->setUserInfo(...$value); + break; + default: + throw new InvalidArgumentException("Invalid URI component: '{$component}'"); + } + } + return $clone; + } + + /** + * Return all query items (if any) as associative array. + * @param int $flags Optional modifier flags + * @return array Query items + */ + public function getQueryItems(int $flags = 0): array + { + parse_str($this->getQuery(), $result); + return $result; + } + + /** + * Return query item value for named query item, or null if not present. + * @param string $name Name of query item to retrieve + * @param int $flags Optional modifier flags + * @return string|null|array Query item value + */ + public function getQueryItem(string $name, int $flags = 0): array|string|null + { + parse_str($this->getQuery(), $result); + return $result[$name] ?? null; + } + + /** + * Add query items as associative array that will be merged qith current items. + * @param array $items Array of query items to add + * @param int $flags Optional modifier flags + * @return self A new instance with the added query items + */ + public function withQueryItems(array $items, int $flags = 0): self + { + $clone = $this->clone($flags); + $clone->setQuery(http_build_query( + $this->queryMerge($this->getQueryItems($flags), $items), + '', + null, + PHP_QUERY_RFC3986 + ), $flags); + return $clone; + } + + /** + * Add query item value for named query item + * @param string $name Name of query item to add + * @param string|null|array $value Value of query item to add + * @param int $flags Optional modifier flags + * @return self A new instance with the added query items + */ + public function withQueryItem(string $name, array|string|null $value, int $flags = 0): self + { + return $this->withQueryItems([$name => $value], $flags); + } + + + // ---------- Protected helper methods ---------------------------------------------------------------------------- + + protected function setPort(int|null $port, int $flags = 0): void + { + if ($port !== null && ($port < 0 || $port > 65535)) { + throw new InvalidArgumentException("Invalid port '{$port}'"); + } + $this->port = $port; + } + + protected function setScheme(string $scheme, int $flags = 0): void + { + $pattern = '/^[a-z][a-z0-9-+.]*$/i'; + if ($scheme !== '' && preg_match($pattern, $scheme) == 0) { + throw new InvalidArgumentException("Invalid scheme '{$scheme}': Should match {$pattern}"); + } + $this->scheme = mb_strtolower($scheme); + } + + protected function setHost(string $host, int $flags = 0): void + { + $this->authority = $this->authority || $host !== ''; + if ($flags & self::IDNA) { + trigger_error("Flag IDNA is deprecated; use IDN_ENCODE instead", E_USER_DEPRECATED); + $host = $this->idnEncode($host); + } + if ($flags & self::IDN_ENCODE) { + $host = $this->idnEncode($host); + } + if ($flags & self::IDN_DECODE) { + $host = $this->idnDecode($host); + } + $this->host = mb_strtolower($host); + } + + protected function setPath(string $path, int $flags = 0): void + { + if ($flags & self::NORMALIZE_PATH) { + $path = $this->normalizePath($path); + } + if ($flags & self::ABSOLUTE_PATH && substr($path, 0, 1) !== '/') { + $path = "/{$path}"; + } + $this->path = $this->uriDecode($path); + } + + protected function setQuery(string $query, int $flags = 0): void + { + $this->query = $this->uriDecode($query); + } + + protected function setFragment(string $fragment, int $flags = 0): void + { + $this->fragment = $this->uriDecode($fragment); + } + + protected function setUser(string $user, int $flags = 0): void + { + $this->user = $this->uriDecode($user); + } + + protected function setPassword(string|null $pass, int $flags = 0): void + { + $this->pass = $pass === null ? null : $this->uriDecode($pass); + } + + protected function setUserInfo(string $user = '', string|null $pass = null, int $flags = 0): void + { + $this->setUser($user); + $this->setPassword($pass); + } + + + // ---------- Private helper methods ------------------------------------------------------------------------------ + + private function parse(string $uriString = ''): void + { + if ($uriString === '') { + return; + } + preg_match(self::RE_MAIN, $uriString, $main); + $this->authority = !empty($main['authorityc']); + $this->setScheme($main['scheme'] ?? ''); + $this->setPath($main['path'] ?? ''); + $this->setQuery($main['query'] ?? ''); + $this->setFragment($main['fragment'] ?? ''); + if ($this->authority && !empty($main['authority'])) { + preg_match(self::RE_AUTH, $main['authority'], $auth); + if (empty($auth)) { + throw new InvalidArgumentException("Invalid 'authority'."); + } + if ($auth['host'] === '' && $auth['user'] !== '') { + throw new InvalidArgumentException("Invalid 'authority'."); + } + $this->setUser($auth['user'] ?? ''); + $this->setPassword($auth['pass'] ?? null); + $this->setHost($auth['host'] ?? ''); + $this->setPort(isset($auth['port']) ? (int)$auth['port'] : null); + } + } + + private function clone(int $flags = 0): self + { + $clone = clone $this; + if ($flags & self::REQUIRE_PORT) { + $clone->setPort($this->getPort(self::REQUIRE_PORT), $flags); + } + return $clone; + } + + private function uriEncode(string $source, int $flags = 0, string $keep = ''): string + { + if ($flags & self::URI_DECODE) { + return $source; + } + + $unreserved = 'a-zA-Z0-9_\-\.~'; + $subdelim = '!\$&\'\(\)\*\+,;='; + $char = '\pL'; + $pct = '%(?![A-Fa-f0-9]{2}))'; + + $re = "/(?:[^%{$unreserved}{$subdelim}{$keep}]+|{$pct}/u"; + + if ($flags & self::URI_ENCODE) { + $re = "/(?:[^%{$unreserved}{$subdelim}{$keep}{$char}]+|{$pct}/u"; + } + return preg_replace_callback($re, function ($matches) { + return rawurlencode($matches[0]); + }, $source) ?? $source; + } + + private function uriDecode(string $source): string + { + $re = "/(%[A-Fa-f0-9]{2})/u"; + return preg_replace_callback($re, function ($matches) { + return rawurldecode($matches[0]); + }, $source) ?? $source; + } + + private function formatComponent(string|int|null $value, string $before = '', string $after = ''): string + { + $string = strval($value); + return $string === '' ? '' : "{$before}{$string}{$after}"; + } + + private function normalizePath(string $path): string + { + $result = []; + preg_match_all('!([^/]*/|[^/]*$)!', $path, $items); + foreach ($items[0] as $item) { + switch ($item) { + case '': + case './': + case '.': + break; // just skip + case '/': + if (empty($result)) { + array_push($result, $item); // add + } + break; + case '..': + case '../': + if (empty($result) || end($result) == '../') { + array_push($result, $item); // add + } else { + array_pop($result); // remove previous + } + break; + default: + array_push($result, $item); // add + } + } + return implode('', $result); + } + + private function idnEncode(string $value): string + { + if ($value === '' || !function_exists('idn_to_ascii')) { + return $value; // Can't convert, but don't cause exception + } + return idn_to_ascii($value, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46) ?: $value; + } + + private function idnDecode(string $value): string + { + if ($value === '' || !function_exists('idn_to_utf8')) { + return $value; // Can't convert, but don't cause exception + } + return idn_to_utf8($value, IDNA_NONTRANSITIONAL_TO_UNICODE, INTL_IDNA_VARIANT_UTS46) ?: $value; + } + + /** + * @param array $a + * @param array $b + * @return array + */ + private function queryMerge(array $a, array $b): array + { + foreach ($b as $key => $value) { + if (is_int($key)) { + $a[] = $value; + } elseif (is_array($value)) { + $a[$key] = $this->queryMerge($a[$key] ?? [], $b[$key] ?? []); + } elseif (is_scalar($value)) { + $a[$key] = $this->uriDecode($b[$key]); + } else { + unset($a[$key]); + } + } + return $a; + } +} diff --git a/vendor/phrity/net-uri/src/UriFactory.php b/vendor/phrity/net-uri/src/UriFactory.php new file mode 100644 index 0000000..4b9b91d --- /dev/null +++ b/vendor/phrity/net-uri/src/UriFactory.php @@ -0,0 +1,47 @@ + Net > Uri + * @see https://www.rfc-editor.org/rfc/rfc3986 + * @see https://www.php-fig.org/psr/psr-17/#26-urifactoryinterface + */ + +namespace Phrity\Net; + +use InvalidArgumentException; +use Psr\Http\Message\{ + UriFactoryInterface, + UriInterface +}; + +/** + * Net\UriFactory class. + */ +class UriFactory implements UriFactoryInterface +{ + // ---------- PSR-7 methods --------------------------------------------------------------------------------------- + + /** + * Create a new URI. + * @param string $uri The URI to parse. + * @throws InvalidArgumentException If the given URI cannot be parsed + */ + public function createUri(string $uri = ''): UriInterface + { + return new Uri($uri); + } + + + // ---------- Extensions ------------------------------------------------------------------------------------------ + + /** + * Create a new URI from existing. + * @param UriInterface $uri A URI instance to create from. + * @throws InvalidArgumentException If the given URI cannot be parsed + */ + public function createUriFromInterface(UriInterface $uri): UriInterface + { + return new Uri($uri->__toString()); + } +} diff --git a/vendor/phrity/util-errorhandler/composer.json b/vendor/phrity/util-errorhandler/composer.json new file mode 100644 index 0000000..29a7ae2 --- /dev/null +++ b/vendor/phrity/util-errorhandler/composer.json @@ -0,0 +1,34 @@ +{ + "name": "phrity/util-errorhandler", + "type": "library", + "description": "Inline error handler; catch and resolve errors for code block.", + "homepage": "https://phrity.sirn.se/util-errorhandler", + "keywords": ["error", "warning"], + "license": "MIT", + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "autoload": { + "psr-4": { + "Phrity\\Util\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Phrity\\Util\\Tests\\": "tests/" + } + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "squizlabs/php_codesniffer": "^3.5" + } +} diff --git a/vendor/phrity/util-errorhandler/src/ErrorHandler.php b/vendor/phrity/util-errorhandler/src/ErrorHandler.php new file mode 100644 index 0000000..70534db --- /dev/null +++ b/vendor/phrity/util-errorhandler/src/ErrorHandler.php @@ -0,0 +1,131 @@ + Util > ErrorHandler + */ + +namespace Phrity\Util; + +use Closure; +use ErrorException; +use Throwable; + +/** + * ErrorHandler utility class. + * Allows catching and resolving errors inline. + */ +class ErrorHandler +{ + /* ----------------- Public methods ---------------------------------------------- */ + + /** + * Set error handler to run until removed. + * @param Closure|Throwable|null $handling + * - If null, handler will throw ErrorException + * - If Throwable $t, throw $t with ErrorException attached as previous + * - If callable, will invoke callback with ErrorException as argument + * @param int $levels Error levels to catch, all errors by default + * @return (callable(): mixed)|null Previously registered error handler, if any + */ + public function set(Closure|Throwable|null $handling = null, int $levels = E_ALL): callable|null + { + return set_error_handler($this->getHandler($handling), $levels); + } + + /** + * Remove error handler. + * @return bool True if removed + */ + public function restore(): bool + { + return restore_error_handler(); + } + + /** + * Run code with error handling, breaks on first encountered error. + * @param callable $callback The code to run + * @param Closure|Throwable|null $handling + * - If null, handler will throw ErrorException + * - If Throwable $t, throw $t with ErrorException attached as previous + * - If callable, will invoke callback with ErrorException as argument + * @param int $levels Error levels to catch, all errors by default + * @return mixed Return what $callback returns, or what $handling retuns on error + */ + public function with(callable $callback, Closure|Throwable|null $handling = null, int $levels = E_ALL): mixed + { + $error = null; + $result = null; + try { + $this->set(null, $levels); + $result = $callback(); + } catch (ErrorException $e) { + $error = $this->handle($handling, $e); + } finally { + $this->restore(); + } + return $error ?? $result; + } + + /** + * Run code with error handling, comletes code before handling errors + * @param callable $callback The code to run + * @param Closure|Throwable|null $handling + * - If null, handler will throw ErrorException + * - If Throwable $t, throw $t with ErrorException attached as previous + * - If callable, will invoke callback with ErrorException as argument + * @param int $levels Error levels to catch, all errors by default + * @return mixed Return what $callback returns, or what $handling retuns on error + */ + public function withAll(callable $callback, Closure|Throwable|null $handling = null, int $levels = E_ALL): mixed + { + $errors = []; + $this->set(function (ErrorException $e) use (&$errors) { + $errors[] = $e; + }, $levels); + $result = $callback(); + $this->restore(); + $error = empty($errors) ? null : $this->handle($handling, $errors, $result); + return $error ?? $result; + } + + + /* ----------------- Private helpers --------------------------------------------- */ + + // Get handler function + private function getHandler(Closure|Throwable|null $handling): Closure + { + return function ($severity, $message, $file, $line) use ($handling) { + $error = new ErrorException($message, 0, $severity, $file, $line); + $this->handle($handling, $error); + }; + } + + /** + * Handle error according to $handlig type + * @param Closure|Throwable|null $handling + * @param ErrorException|non-empty-array $error + * @mixed $result + * @return mixed + * @throws Throwable + */ + private function handle(Closure|Throwable|null $handling, ErrorException|array $error, mixed $result = null): mixed + { + if (is_callable($handling)) { + return $handling($error, $result); + } + if (is_array($error)) { + $error = array_shift($error); + } + if ($handling instanceof Throwable) { + try { + /* @phpstan-ignore finally.exitPoint */ + throw $error; + } finally { + /* @phpstan-ignore finally.exitPoint */ + throw $handling; + } + } + throw $error; + } +} diff --git a/vendor/textalk/websocket/COPYING.md b/vendor/phrity/websocket/LICENSE.md similarity index 100% rename from vendor/textalk/websocket/COPYING.md rename to vendor/phrity/websocket/LICENSE.md diff --git a/vendor/phrity/websocket/composer.json b/vendor/phrity/websocket/composer.json new file mode 100644 index 0000000..62b3164 --- /dev/null +++ b/vendor/phrity/websocket/composer.json @@ -0,0 +1,50 @@ +{ + "name": "phrity/websocket", + "type": "library", + "description": "WebSocket client and server", + "homepage": "https://phrity.sirn.se/websocket", + "keywords": ["websocket", "client", "server"], + "license": "ISC", + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "autoload": { + "psr-4": { + "WebSocket\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "WebSocket\\Test\\": "tests/mock/" + } + }, + "require": { + "php": "^8.1", + "phrity/http": "^1.0", + "phrity/net-uri": "^2.1", + "phrity/net-stream": "^2.3", + "psr/http-message": "^1.1 | ^2.0", + "psr/log": "^1.0 | ^2.0 | ^3.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.0", + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0 | ^11.0 | ^12.0", + "phrity/logger-console": "^1.0", + "phrity/net-mock": "^2.3", + "phrity/util-errorhandler": "^1.1", + "robiningelbrecht/phpunit-coverage-tools": "^1.9", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggests": { + "ext-zlib": "Required for per-message deflate compression" + } +} diff --git a/vendor/phrity/websocket/docu.json b/vendor/phrity/websocket/docu.json new file mode 100644 index 0000000..4331769 --- /dev/null +++ b/vendor/phrity/websocket/docu.json @@ -0,0 +1,102 @@ +[ + { + "name": "Overview", + "source": "README.md" + }, + { + "name": "Entry points", + "content": [ + { + "name": "Client", + "source": "docs/Client.md" + }, + { + "name": "Server", + "source": "docs/Server.md" + }, + ], + "name": "Resources", + "content": [ + { + "name": "Connection", + "source": "docs/Connection.md" + }, + { + "name": "Listener", + "source": "docs/Listener.md" + }, + { + "name": "Message", + "source": "docs/Message.md" + }, + { + "name": "Middleware", + "source": "docs/Middleware.md", + "content": [ + { + "name": "Callback", + "source": "docs/Middleware/Callback.md" + }, + { + "name": "Close Handler", + "source": "docs/Middleware/CloseHandler.md" + }, + { + "name": "Compression Extension", + "source": "docs/Middleware/CompressionExtension.md" + }, + { + "name": "Follow Redirect", + "source": "docs/Middleware/FollowRedirect.md" + }, + { + "name": "Ping Interval", + "source": "docs/Middleware/PingInterval.md" + }, + { + "name": "Ping Responder", + "source": "docs/Middleware/PingResponder.md" + }, + { + "name": "Subprotocol Negotiation", + "source": "docs/Middleware/SubprotocolNegotiation.md" + }, + { + "name": "Creating your own middleware", + "source": "docs/Middleware/Creating.md" + }, + ] + }, + ], + "name": "Developer resources", + "content": [ + { + "name": "Changelog", + "source": "docs/Changelog.md" + }, + { + "name": "Contributing", + "source": "docs/Contributing.md" + }, + { + "name": "Examples", + "source": "docs/Examples.md" + }, + { + "name": "Class synopsis", + "source": "docs/Class_Synopsis.md" + }, + ], + "name": "Migration", + "content": [ + { + "name": "Migration v2 -> v3", + "source": "docs/Migrate_2_3.md" + }, + ] + }, + { + "name": "License", + "source": "LICENSE.md" + } +] \ No newline at end of file diff --git a/vendor/phrity/websocket/src/Client.php b/vendor/phrity/websocket/src/Client.php new file mode 100644 index 0000000..95be629 --- /dev/null +++ b/vendor/phrity/websocket/src/Client.php @@ -0,0 +1,667 @@ + */ + use ListenerTrait; + use LoggerAwareTrait; + use SendMethodsTrait; + use StringableTrait; + + // Settings + /** @var int<0, max>|float $timeout */ + private int|float $timeout = 60; + /** @var int<1, max> $frameSize */ + private int $frameSize = 4096; + private bool $persistent = false; + private Context $context; + /** @var array $headers */ + private array $headers = []; + + // Internal resources + private StreamFactory $streamFactory; + private Uri $socketUri; + private Connection|null $connection = null; + /** @var array $middlewares */ + private array $middlewares = []; + private StreamCollection|null $streams = null; + private bool $running = false; + private HttpFactory $httpFactory; + + + /* ---------- Magic methods ------------------------------------------------------------------------------------ */ + + /** + * @param UriInterface|string $uri A ws/wss-URI + */ + public function __construct(UriInterface|string $uri) + { + $this->socketUri = $this->parseUri($uri); + $this->initLogger(); + $this->context = new Context(); + $this->setStreamFactory(new StreamFactory()); + $this->httpFactory = new DefaultHttpFactory(); + } + + /** + * Get string representation of instance. + * @return string String representation + */ + public function __toString(): string + { + return $this->stringable('%s', $this->connection ? $this->socketUri->__toString() : 'closed'); + } + + + /* ---------- Configuration ------------------------------------------------------------------------------------ */ + + /** + * Set stream factory to use. + * @param StreamFactory $streamFactory + * @return self + */ + public function setStreamFactory(StreamFactory $streamFactory): self + { + $this->streamFactory = $streamFactory; + return $this; + } + + /** + * Set HTTP factory to use. + * @param HttpFactory $httpFactory + * @return self + */ + public function setHttpFactory(HttpFactory $httpFactory): self + { + $this->httpFactory = $httpFactory; + return $this; + } + + /** + * Set logger. + * @param LoggerInterface $logger Logger implementation + */ + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + if ($this->connection) { + $this->connection->setLogger($this->logger); + } + } + + /** + * Set timeout. + * @param int<0, max>|float $timeout Timeout in seconds + * @return self + * @throws InvalidArgumentException If invalid timeout provided + */ + public function setTimeout(int|float $timeout): self + { + if ($timeout < 0) { + throw new InvalidArgumentException("Invalid timeout '{$timeout}' provided"); + } + $this->timeout = $timeout; + if ($this->connection) { + $this->connection->setTimeout($timeout); + } + return $this; + } + + /** + * Get timeout. + * @return int<0, max>|float Timeout in seconds + */ + public function getTimeout(): int|float + { + return $this->timeout; + } + + /** + * Set frame size. + * @param int<1, max> $frameSize Max frame payload size in bytes + * @return self + * @throws InvalidArgumentException If invalid frameSize provided + */ + public function setFrameSize(int $frameSize): self + { + if ($frameSize < 1) { + throw new InvalidArgumentException("Invalid frameSize '{$frameSize}' provided"); + } + $this->frameSize = $frameSize; + if ($this->connection) { + $this->connection->setFrameSize($frameSize); + } + return $this; + } + + /** + * Get frame size. + * @return int Frame size in bytes + */ + public function getFrameSize(): int + { + return $this->frameSize; + } + + /** + * Set connection persistence. + * @param bool $persistent True for persistent connection. + * @return self + */ + public function setPersistent(bool $persistent): self + { + $this->persistent = $persistent; + return $this; + } + + /** + * Set stream context. + * @param Context|array $context Context or options as array + * @see https://www.php.net/manual/en/context.php + * @return self + */ + public function setContext(Context|array $context): self + { + if ($context instanceof Context) { + $this->context = $context; + } else { + $this->context->setOptions($context); + trigger_error('Calling Client.setContext with array is deprecated, use Context class.', E_USER_DEPRECATED); + } + return $this; + } + + /** + * Get current stream context. + * @return Context + */ + public function getContext(): Context + { + return $this->context; + } + + /** + * Add header for handshake. + * @param string $name Header name + * @param string $content Header content + * @return self + */ + public function addHeader(string $name, string $content): self + { + $this->headers[$name] = $content; + return $this; + } + + /** + * Add a middleware. + * @param MiddlewareInterface $middleware + * @return self + */ + public function addMiddleware(MiddlewareInterface $middleware): self + { + $this->middlewares[] = $middleware; + if ($this->connection) { + $this->connection->addMiddleware($middleware); + } + return $this; + } + + + /* ---------- Messaging operations ----------------------------------------------------------------------------- */ + + /** + * Send message. + * @template T of Message + * @param T $message + * @return T + */ + public function send(Message $message): Message + { + return $this->connection()->pushMessage($message); + } + + /** + * Receive message. + * Note that this operation will block reading. + * @return Message + */ + public function receive(): Message + { + return $this->connection()->pullMessage(); + } + + + /* ---------- Listener operations ------------------------------------------------------------------------------ */ + + /** + * Start client listener. + * @throws ExceptionInterface On high level error + * @throws Throwable On low level error + */ + public function start(int|float|null $timeout = null): void + { + // Check if running + if ($this->running) { + $this->logger->warning("[client] Client is already running"); + return; + } + $this->running = true; + $reconnect = false; + $this->logger->info("[client] Client is running"); + + $connection = $this->connection(); + + // Run handler + while ($this->running) { + /** @var StreamCollection */ + $streams = $this->streams; + try { + // Get streams with readable content + $readables = $streams->waitRead($timeout ?? $this->timeout); + foreach ($readables as $key => $readable) { + try { + // Read from connection + $message = $connection->pullMessage(); + $this->dispatch($message->getOpcode(), [$this, $connection, $message]); + } catch (MessageLevelInterface $e) { + // Error, but keep connection open + $this->logger->error("[client] {$e->getMessage()}", ['exception' => $e]); + $this->dispatch('error', [$this, $connection, $e]); + } catch (ConnectionLevelInterface $e) { + // Error, disconnect connection + $this->disconnect(); + $this->logger->error("[client] {$e->getMessage()}", ['exception' => $e]); + $this->dispatch('error', [$this, $connection, $e]); + } + } + if (!$connection->isConnected()) { + $this->running = false; + } + $connection->tick(); + $this->dispatch('tick', [$this]); + } catch (CloseException $e) { + // Close connection + $connection->close($e->getCloseStatus(), $e->getMessage()); + $this->logger->error("[server] {$e->getMessage()}", ['exception' => $e]); + $this->dispatch('error', [$this, $connection, $e]); + } catch (ReconnectException $e) { + // Reconnect connection + $reconnect = true; + if ($uri = $e->getUri()) { + $this->socketUri = $uri; + } + $connection->close(); + $this->logger->error("[server] {$e->getMessage()}", ['exception' => $e]); + $this->dispatch('error', [$this, $connection, $e]); + } catch (ExceptionInterface $e) { + $this->disconnect(); + $this->running = false; + + // Low-level error + $this->logger->error("[client] {$e->getMessage()}", ['exception' => $e]); + $this->dispatch('error', [$this, null, $e]); + } catch (Throwable $e) { + $this->disconnect(); + $this->running = false; + + // Crash it + $this->logger->error("[client] {$e->getMessage()}", ['exception' => $e]); + throw $e; + } + gc_collect_cycles(); // Collect garbage + + if ($reconnect && !$connection->isConnected()) { + $reconnect = false; + $this->running = true; + } + } + } + + /** + * Stop client listener (resumable). + */ + public function stop(): void + { + $this->running = false; + $this->logger->info("[client] Client is stopped"); + } + + /** + * If client is running (accepting messages). + * @return bool + */ + public function isRunning(): bool + { + return $this->running; + } + + + /* ---------- Connection management ---------------------------------------------------------------------------- */ + + /** + * If Client has active connection. + * @return bool True if active connection. + */ + public function isConnected(): bool + { + return $this->connection && $this->connection->isConnected(); + } + + /** + * If Client is readable. + * @return bool + */ + public function isReadable(): bool + { + return $this->connection && $this->connection->isReadable(); + } + + /** + * If Client is writable. + * @return bool + */ + public function isWritable(): bool + { + return $this->connection && $this->connection->isWritable(); + } + + + /** + * Connect to server and perform upgrade. + * @throws ClientException On failed connection + */ + public function connect(): void + { + $this->disconnect(); + $this->streams = $this->streamFactory->createStreamCollection(); + + $hostUri = (new Uri()) + ->withScheme(match ($this->socketUri->getScheme()) { + 'ws', 'http' => 'tcp', + 'wss', 'https' => 'ssl', + default => throw new ClientException("Invalid socket scheme: {$this->socketUri->getScheme()}") + }) + ->withHost($this->socketUri->getHost(Uri::IDN_ENCODE)) + ->withPort($this->socketUri->getPort(Uri::REQUIRE_PORT)); + + $stream = null; + + try { + $client = $this->streamFactory->createSocketClient($hostUri, $this->context); + $client->setPersistent($this->persistent); + $client->setTimeout($this->timeout); + $stream = $client->connect(); + } catch (Throwable $e) { + $error = "Could not open socket to \"{$hostUri}\": {$e->getMessage()}"; + $this->logger->error("[client] {$error}", ['exception' => $e]); + throw new ClientException($error); + } + $name = $stream->getRemoteName(); + $this->streams->attach($stream, $name); + $this->connection = new Connection( + $stream, + true, + false, + $hostUri->getScheme() === 'ssl', + $this->httpFactory + ); + $this->connection->setFrameSize($this->frameSize); + $this->connection->setTimeout($this->timeout); + $this->connection->setLogger($this->logger); + foreach ($this->middlewares as $middleware) { + $this->connection->addMiddleware($middleware); + } + + if (!$this->isConnected()) { + $error = "Invalid stream on \"{$hostUri}\"."; + $this->logger->error("[client] {$error}"); + throw new ClientException($error); + } + try { + if (!$this->persistent || $stream->tell() == 0) { + /** @throws ReconnectException */ + $response = $this->performHandshake($this->socketUri, $this->connection); + } + } catch (ReconnectException $e) { + $this->logger->info("[client] {$e->getMessage()}", ['exception' => $e]); + if ($uri = $e->getUri()) { + $this->socketUri = $uri; + } + $this->connect(); + return; + } + $this->logger->info("[client] Client connected to {$this->socketUri}"); + $this->dispatch('handshake', [ + $this, + $this->connection, + $this->connection->getHandshakeRequest(), + $this->connection->getHandshakeResponse(), + ]); + $this->dispatch('connect', [$this, $this->connection, $this->connection?->getHandshakeResponse()]); + } + + /** + * Disconnect from server. + */ + public function disconnect(): void + { + if ($this->connection && $this->isConnected()) { + $this->connection->disconnect(); + $this->logger->info('[client] Client disconnected'); + $this->dispatch('disconnect', [$this, $this->connection]); + } + } + + + /* ---------- Connection wrapper methods ----------------------------------------------------------------------- */ + + /** + * Get name of local socket, or null if not connected. + * @return string|null + */ + public function getName(): string|null + { + return $this->isConnected() ? $this->connection?->getName() : null; + } + + /** + * Get name of remote socket, or null if not connected. + * @return string|null + */ + public function getRemoteName(): string|null + { + return $this->isConnected() ? $this->connection?->getRemoteName() : null; + } + + /** + * Get meta value on connection. + * @param string $key Meta key + * @return mixed Meta value + * @deprecated Will be removed in v4 + */ + public function getMeta(string $key): mixed + { + trigger_error('Client.getMeta is deprecated and will be removed in v4.', E_USER_DEPRECATED); + return $this->isConnected() ? $this->connection?->getMeta($key) : null; + } + + /** + * Get Response for handshake procedure. + * @return ResponseInterface|null Handshake. + */ + public function getHandshakeResponse(): ResponseInterface|null + { + return $this->connection ? $this->connection->getHandshakeResponse() : null; + } + + + /* ---------- Internal helper methods -------------------------------------------------------------------------- */ + + /** + * Perform upgrade handshake on new connections. + * @throws HandshakeException On failed handshake + */ + protected function performHandshake(Uri $uri, Connection $connection): ResponseInterface + { + // Generate the WebSocket key. + $key = $this->generateKey(); + + $request = $this->httpFactory->createRequest('GET', $uri); + + $request = $request + ->withHeader('User-Agent', 'websocket-client-php') + ->withHeader('Connection', 'Upgrade') + ->withHeader('Upgrade', 'websocket') + ->withHeader('Sec-WebSocket-Key', $key) + ->withHeader('Sec-WebSocket-Version', '13'); + + // Handle basic authentication. + if ($userinfo = $uri->getUserInfo(Uri::URI_DECODE)) { + $request = $request->withHeader('Authorization', 'Basic ' . base64_encode($userinfo)); + } + + // Add and override with headers. + foreach ($this->headers as $name => $content) { + $request = $request->withHeader($name, $content); + } + + try { + /** @var RequestInterface */ + $request = $connection->pushHttp($request); + /** @var ResponseInterface */ + $response = $connection->pullHttp(); + + if ($response->getStatusCode() != 101) { + throw new HandshakeException("Invalid status code {$response->getStatusCode()}.", $response); + } + + if (empty($response->getHeaderLine('Sec-WebSocket-Accept'))) { + throw new HandshakeException( + "Connection to '{$uri}' failed: Server sent invalid upgrade response.", + $response + ); + } + + $responseKey = trim($response->getHeaderLine('Sec-WebSocket-Accept')); + $expectedKey = base64_encode( + pack('H*', sha1($key . Constant::GUID)) + ); + + if ($responseKey !== $expectedKey) { + throw new HandshakeException("Server sent bad upgrade response.", $response); + } + } catch (HandshakeException $e) { + $this->logger->error("[client] {$e->getMessage()}", ['exception' => $e]); + throw $e; + } + + $this->logger->debug("[client] Handshake on {$uri->getPath()}"); + $connection->setHandshakeRequest($request); + $connection->setHandshakeResponse($response); + + return $response; + } + + /** + * Generate a random string for WebSocket key. + * @return string Random string + */ + protected function generateKey(): string + { + $key = ''; + for ($i = 0; $i < 16; $i++) { + $key .= chr(rand(33, 126)); + } + return base64_encode($key); + } + + /** + * Ensure URI instance to use in client. + * @param UriInterface|string $uri A ws/wss-URI + * @return Uri + * @throws BadUriException On invalid URI + */ + protected function parseUri(UriInterface|string $uri): Uri + { + try { + if ($uri instanceof Uri) { + $uriInstance = $uri; + } elseif ($uri instanceof UriInterface) { + $uriInstance = new Uri("{$uri}"); + } else { + $uriInstance = new Uri($uri); + } + } catch (InvalidArgumentException $e) { + throw new BadUriException("Invalid URI '{$uri}' provided."); + } + + + if (!in_array($uriInstance->getScheme(), ['ws', 'wss'])) { + throw new BadUriException("Invalid URI scheme, must be 'ws' or 'wss'."); + } + if (!$uriInstance->getHost()) { + throw new BadUriException("Invalid URI host."); + } + return $uriInstance; + } + + protected function connection(): Connection + { + if (!$this->isConnected()) { + $this->connect(); + } + /** @var Connection */ + $connection = $this->connection; + return $connection; + } +} diff --git a/vendor/phrity/websocket/src/Connection.php b/vendor/phrity/websocket/src/Connection.php new file mode 100644 index 0000000..227270d --- /dev/null +++ b/vendor/phrity/websocket/src/Connection.php @@ -0,0 +1,435 @@ + $frameSize */ + private int $frameSize = 4096; + /** @var int<0, max>|float $timeout */ + private int|float $timeout = 60; + private string $localName; + private string $remoteName; + private RequestInterface|null $handshakeRequest = null; + private ResponseInterface|null $handshakeResponse = null; + /** @var array $meta */ + private array $meta = []; + private bool $closed = false; + + + /* ---------- Magic methods ------------------------------------------------------------------------------------ */ + + public function __construct( + SocketStream $stream, + bool $pushMasked, + bool $pullMaskedRequired, + bool $ssl = false, + HttpFactory|null $httpFactory = null + ) { + $this->stream = $stream; + $this->httpHandler = new HttpHandler($this->stream, $ssl, $httpFactory); + $this->messageHandler = new MessageHandler(new FrameHandler($this->stream, $pushMasked, $pullMaskedRequired)); + $this->middlewareHandler = new MiddlewareHandler($this->messageHandler, $this->httpHandler); + $this->localName = $this->stream->getLocalName() ?? ''; + $this->remoteName = $this->stream->getRemoteName() ?? ''; + $this->initLogger(); + } + + public function __destruct() + { + if (!$this->closed && $this->isConnected()) { + $this->stream->close(); + } + } + + public function __toString(): string + { + return $this->stringable('%s:%s', $this->localName, $this->remoteName); + } + + + /* ---------- Configuration ------------------------------------------------------------------------------------ */ + + /** + * Set logger. + * @param LoggerInterface $logger Logger implementation + */ + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + $this->messageHandler->setLogger($logger); + $this->middlewareHandler->setLogger($logger); + $this->logger->debug("[connection] Setting logger: " . get_class($logger)); + } + + /** + * Set time out on connection. + * @param int<0, max>|float $timeout Timeout part in seconds + * @return self + * @throws InvalidArgumentException + */ + public function setTimeout(int|float $timeout): self + { + if ($timeout < 0) { + throw new InvalidArgumentException("Invalid timeout '{$timeout}' provided"); + } + $this->timeout = $timeout; + $this->stream->setTimeout($timeout); + $this->logger->debug("[connection] Setting timeout: {$timeout} seconds"); + return $this; + } + + /** + * Get timeout. + * @return int<0, max>|float Timeout in seconds. + */ + public function getTimeout(): int|float + { + return $this->timeout; + } + + /** + * Set frame size. + * @param int<1, max> $frameSize Frame size in bytes. + * @return self + * @throws InvalidArgumentException + */ + public function setFrameSize(int $frameSize): self + { + if ($frameSize < 1) { + throw new InvalidArgumentException("Invalid frameSize '{$frameSize}' provided"); + } + $this->frameSize = $frameSize; + return $this; + } + + /** + * Get frame size. + * @return int<1, max> Frame size in bytes + */ + public function getFrameSize(): int + { + return max(1, $this->frameSize); + } + + /** + * Get current stream context. + * @return Context + */ + public function getContext(): Context + { + return $this->stream->getContext(); + } + + /** + * Add a middleware. + * @param MiddlewareInterface $middleware + * @return self + */ + public function addMiddleware(MiddlewareInterface $middleware): self + { + $this->middlewareHandler->add($middleware); + $this->logger->debug("[connection] Added middleware: {$middleware}"); + return $this; + } + + + /* ---------- Connection management ---------------------------------------------------------------------------- */ + + /** + * If connected to stream. + * @return bool + */ + public function isConnected(): bool + { + return $this->stream->isConnected(); + } + + /** + * If connection is readable. + * @return bool + */ + public function isReadable(): bool + { + return $this->stream->isReadable(); + } + + /** + * If connection is writable. + * @return bool + */ + public function isWritable(): bool + { + return $this->stream->isWritable(); + } + + /** + * Close connection stream. + * @return self + */ + public function disconnect(): self + { + $this->logger->info('[connection] Closing connection'); + $this->stream->close(); + $this->closed = true; + return $this; + } + + /** + * Close connection stream reading. + * @return self + */ + public function closeRead(): self + { + $this->logger->info('[connection] Closing further reading'); + $this->stream->closeRead(); + return $this; + } + + /** + * Close connection stream writing. + * @return self + */ + public function closeWrite(): self + { + $this->logger->info('[connection] Closing further writing'); + $this->stream->closeWrite(); + return $this; + } + + + /* ---------- Connection state --------------------------------------------------------------------------------- */ + + /** + * Get name of local socket, or null if not connected. + * @return string|null + */ + public function getName(): string|null + { + return $this->localName; + } + + /** + * Get name of remote socket, or null if not connected. + * @return string|null + */ + public function getRemoteName(): string|null + { + return $this->remoteName; + } + + /** + * Set meta value on connection. + * @param string $key Meta key + * @param mixed $value Meta value + */ + public function setMeta(string $key, mixed $value): void + { + $this->meta[$key] = $value; + } + + /** + * Get meta value on connection. + * @param string $key Meta key + * @return mixed Meta value + */ + public function getMeta(string $key): mixed + { + return $this->meta[$key] ?? null; + } + + /** + * Tick operation on connection. + */ + public function tick(): void + { + $this->middlewareHandler->processTick($this); + } + + + /* ---------- WebSocket Message methods ------------------------------------------------------------------------ */ + + /** + * Send message. + * @template T of Message + * @param T $message + * @return T + */ + public function send(Message $message): Message + { + return $this->pushMessage($message); + } + + /** + * Push a message to stream. + * @template T of Message + * @param T $message + * @return T + */ + public function pushMessage(Message $message): Message + { + try { + /** @throws Throwable */ + return $this->middlewareHandler->processOutgoing($this, $message); + } catch (Throwable $e) { + $this->throwException($e); + } + } + + /** + * Pull a message from stream + * @throws ExceptionInterface + */ + public function pullMessage(): Message + { + try { + /** @throws Throwable */ + return $this->middlewareHandler->processIncoming($this); + } catch (Throwable $e) { + $this->throwException($e); + } + } + + + /* ---------- HTTP Message methods ----------------------------------------------------------------------------- */ + + public function pushHttp(MessageInterface $message): MessageInterface + { + try { + /** @throws Throwable */ + return $this->middlewareHandler->processHttpOutgoing($this, $message); + } catch (Throwable $e) { + $this->throwException($e); + } + } + + public function pullHttp(): MessageInterface + { + try { + /** @throws Throwable */ + return $this->middlewareHandler->processHttpIncoming($this); + } catch (Throwable $e) { + $this->throwException($e); + } + } + + public function setHandshakeRequest(RequestInterface $request): self + { + $this->handshakeRequest = $request; + return $this; + } + + public function getHandshakeRequest(): RequestInterface|null + { + return $this->handshakeRequest; + } + + public function setHandshakeResponse(ResponseInterface $response): self + { + $this->handshakeResponse = $response; + return $this; + } + + public function getHandshakeResponse(): ResponseInterface|null + { + return $this->handshakeResponse; + } + + + /* ---------- Internal helper methods -------------------------------------------------------------------------- */ + + /** + * @throws ReconnectException + * @throws ExceptionInterface + * @throws ConnectionTimeoutException + * @throws ConnectionClosedException + * @throws ConnectionFailureException + */ + protected function throwException(Throwable $e): never + { + // Internal exceptions are handled and re-thrown + if ($e instanceof ReconnectException) { + $this->logger->info("[connection] {$e->getMessage()}", ['exception' => $e]); + throw $e; + } + if ($e instanceof ExceptionInterface) { + $this->logger->error("[connection] {$e->getMessage()}", ['exception' => $e]); + throw $e; + } + // External exceptions are converted to internal + if ($this->isConnected()) { + $meta = $this->stream->getMetadata(); + $json = json_encode($meta); + if (!empty($meta['timed_out'])) { + $this->logger->error("[connection] {$e->getMessage()}", ['exception' => $e, 'meta' => $meta]); + throw new ConnectionTimeoutException(); + } + if (!empty($meta['eof'])) { + $this->logger->error("[connection] {$e->getMessage()}", ['exception' => $e, 'meta' => $meta]); + throw new ConnectionClosedException(); + } + } + $this->logger->error("[connection] {$e->getMessage()}", ['exception' => $e]); + throw new ConnectionFailureException(); + } +} diff --git a/vendor/phrity/websocket/src/Constant.php b/vendor/phrity/websocket/src/Constant.php new file mode 100644 index 0000000..f9b997a --- /dev/null +++ b/vendor/phrity/websocket/src/Constant.php @@ -0,0 +1,17 @@ +status = $status; + parent::__construct($content); + } + + public function getCloseStatus(): int + { + return $this->status ?? 1000; + } +} diff --git a/vendor/phrity/websocket/src/Exception/ConnectionClosedException.php b/vendor/phrity/websocket/src/Exception/ConnectionClosedException.php new file mode 100644 index 0000000..bac2240 --- /dev/null +++ b/vendor/phrity/websocket/src/Exception/ConnectionClosedException.php @@ -0,0 +1,20 @@ +response = $response; + } + + public function getResponse(): ResponseInterface + { + return $this->response; + } +} diff --git a/vendor/phrity/websocket/src/Exception/MessageLevelInterface.php b/vendor/phrity/websocket/src/Exception/MessageLevelInterface.php new file mode 100644 index 0000000..5451fd1 --- /dev/null +++ b/vendor/phrity/websocket/src/Exception/MessageLevelInterface.php @@ -0,0 +1,16 @@ +uri = $uri; + parent::__construct("Reconnect requested" . ($uri ? ": {$uri}" : '')); + } + + public function getUri(): Uri|null + { + return $this->uri; + } +} diff --git a/vendor/phrity/websocket/src/Exception/ServerException.php b/vendor/phrity/websocket/src/Exception/ServerException.php new file mode 100644 index 0000000..43d69f5 --- /dev/null +++ b/vendor/phrity/websocket/src/Exception/ServerException.php @@ -0,0 +1,16 @@ +opcode = $opcode; + $this->payload = $payload; + $this->final = $final; + $this->rsv1 = $rsv1; + $this->rsv2 = $rsv2; + $this->rsv3 = $rsv3; + } + + public function isFinal(): bool + { + return $this->final; + } + + public function getRsv1(): bool + { + return $this->rsv1; + } + + public function setRsv1(bool $rsv1): void + { + $this->rsv1 = $rsv1; + } + + public function getRsv2(): bool + { + return $this->rsv2; + } + + public function getRsv3(): bool + { + return $this->rsv3; + } + + public function isContinuation(): bool + { + return $this->opcode === 'continuation'; + } + + public function getOpcode(): string + { + return $this->opcode; + } + + public function getPayload(): string + { + return $this->payload; + } + + public function getPayloadLength(): int + { + return strlen($this->payload); + } + + public function __toString(): string + { + return $this->stringable('%s', $this->opcode); + } +} diff --git a/vendor/phrity/websocket/src/Frame/FrameHandler.php b/vendor/phrity/websocket/src/Frame/FrameHandler.php new file mode 100644 index 0000000..755dc27 --- /dev/null +++ b/vendor/phrity/websocket/src/Frame/FrameHandler.php @@ -0,0 +1,212 @@ +stream = $stream; + $this->pushMasked = $pushMasked; + $this->pullMaskedRequired = $pullMaskedRequired; + $this->initLogger(); + } + + /** + * Pull frame from stream + * @throws CloseException + */ + public function pull(): Frame + { + // Read the frame "header" first, two bytes. + $data = $this->read(2); + list ($byte1, $byte2) = array_values($this->unpack('C*', $data)); + $final = (bool)($byte1 & 0b10000000); // Final fragment marker. + $rsv1 = (bool)($byte1 & 0b01000000); + $rsv2 = (bool)($byte1 & 0b00100000); + $rsv3 = (bool)($byte1 & 0b00010000); + + // Parse opcode + $opcodeInt = $byte1 & 0b00001111; + $opcodeInts = array_flip(self::$opcodes); + $opcode = array_key_exists($opcodeInt, $opcodeInts) ? $opcodeInts[$opcodeInt] : strval($opcodeInt); + + // Masking bit + $masked = (bool)($byte2 & 0b10000000); + + $payload = ''; + + // Payload length + $payloadLength = $byte2 & 0b01111111; + + if ($payloadLength > 125) { + if ($payloadLength === 126) { + $data = $this->read(2); // 126: Payload length is a 16-bit unsigned int + $payloadLength = current($this->unpack('n', $data)); + } else { + $data = $this->read(8); // 127: Payload length is a 64-bit unsigned int + $payloadLength = current($this->unpack('J', $data)); + } + } + + // Get masking key. + if ($masked) { + $maskingKey = $this->stream->read(4); + } + + // Get the actual payload, if any (might not be for e.g. close frames). + if ($payloadLength > 0) { + $data = $this->read($payloadLength); + if ($masked) { + // Unmask payload. + for ($i = 0; $i < $payloadLength; $i++) { + $payload .= ($data[$i] ^ $maskingKey[$i % 4]); + } + } else { + $payload = $data; + } + } + + $frame = new Frame($opcode, $payload, $final, $rsv1, $rsv2, $rsv3); + $this->logger->debug("[frame-handler] Pulled '{$opcode}' frame", [ + 'opcode' => $frame->getOpcode(), + 'final' => $frame->isFinal(), + 'content-length' => $frame->getPayloadLength(), + ]); + + if ($this->pullMaskedRequired && !$masked) { + $this->logger->error("[frame-handler] Masking required, but frame was unmasked"); + throw new CloseException(1002, 'Masking required'); + } + + return $frame; + } + + // Push frame to stream + public function push(Frame $frame): int + { + $payload = $frame->getPayload(); + $payloadLength = $frame->getPayloadLength(); + + $data = ''; + $byte1 = $frame->isFinal() ? 0b10000000 : 0b00000000; // Final fragment marker. + $byte1 |= $frame->getRsv1() ? 0b01000000 : 0b00000000; // RSV1 bit. + $byte1 |= $frame->getRsv2() ? 0b00100000 : 0b00000000; // RSV2 bit. + $byte1 |= $frame->getRsv3() ? 0b00010000 : 0b00000000; // RSV3 bit. + $byte1 |= self::$opcodes[$frame->getOpcode()]; // Set opcode. + $data .= pack('C', $byte1); + + $byte2 = $this->pushMasked ? 0b10000000 : 0b00000000; // Masking bit marker. + + // 7 bits of payload length + if ($payloadLength > 65535) { + $data .= pack('C', $byte2 | 0b01111111); + $data .= pack('J', $payloadLength); + } elseif ($payloadLength > 125) { + $data .= pack('C', $byte2 | 0b01111110); + $data .= pack('n', $payloadLength); + } else { + $data .= pack('C', $byte2 | $payloadLength); + } + + // Handle masking. + if ($this->pushMasked) { + // Generate a random mask. + $mask = ''; + for ($i = 0; $i < 4; $i++) { + $mask .= chr(rand(0, 255)); + } + $data .= $mask; + + // Append masked payload to frame. + for ($i = 0; $i < $payloadLength; $i++) { + $data .= $payload[$i] ^ $mask[$i % 4]; + } + } else { + // Append payload as-is to frame. + $data .= $payload; + } + + // Write to stream. + $written = $this->write($data); + + $this->logger->debug("[frame-handler] Pushed '{opcode}' frame", [ + 'opcode' => $frame->getOpcode(), + 'final' => $frame->isFinal(), + 'content-length' => $frame->getPayloadLength(), + ]); + return $written; + } + + /** + * Secured read op + * @param int<1, max> $length + * @throws RuntimeException + */ + private function read(int $length): string + { + $data = ''; + $read = 0; + while ($read < $length) { + /** @var int<1, max> $readLength */ + $readLength = $length - $read; + $got = $this->stream->read($readLength); + if (empty($got)) { + throw new RuntimeException('Empty read; connection dead?'); + } + $data .= $got; + $read = strlen($data); + } + return $data; + } + + /** + * Secured write op + * @throws RuntimeException + */ + private function write(string $data): int + { + $length = strlen($data); + $written = $this->stream->write($data); + if ($written < $length) { + throw new RuntimeException("Could only write {$written} out of {$length} bytes."); + } + return $written; + } + + /** @return array */ + private function unpack(string $format, string $string): array + { + /** @var array $result */ + $result = unpack($format, $string); + return $result; + } +} diff --git a/vendor/phrity/websocket/src/Http/DefaultHttpFactory.php b/vendor/phrity/websocket/src/Http/DefaultHttpFactory.php new file mode 100644 index 0000000..fd0e5c3 --- /dev/null +++ b/vendor/phrity/websocket/src/Http/DefaultHttpFactory.php @@ -0,0 +1,72 @@ +uriFactory = new UriFactory(); + } + + /** + * Create a new request. + * @param string $method + * @param UriInterface|string $uri + */ + public function createRequest(string $method, mixed $uri): RequestInterface + { + return new Request($method, $uri); + } + + /** + * @param int $code + * @param string $reasonPhrase + */ + public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface + { + return new Response($code, $reasonPhrase); + } + + /** + * @param string $method + * @param UriInterface|string $uri + * @param array $serverParams + */ + public function createServerRequest(string $method, mixed $uri, array $serverParams = []): ServerRequestInterface + { + return new ServerRequest($method, $uri); + } + + /** + * @param string $uri The URI to parse. + */ + public function createUri(string $uri = ''): UriInterface + { + return $this->uriFactory->createUri($uri); + } +} diff --git a/vendor/phrity/websocket/src/Http/HttpHandler.php b/vendor/phrity/websocket/src/Http/HttpHandler.php new file mode 100644 index 0000000..41f4307 --- /dev/null +++ b/vendor/phrity/websocket/src/Http/HttpHandler.php @@ -0,0 +1,140 @@ +stream = $stream; + $this->ssl = $ssl; + $this->httpFactory = $httpFactory ?? new DefaultHttpFactory(); + } + + /** + * @deprecated Remove in v4 + */ + public function setLogger(LoggerInterface $logger): void + { + trigger_error('HttpHandler.setLogger is deprecated and will be removed in v4.', E_USER_DEPRECATED); + } + + /** + * @throws RuntimeException + */ + public function pull(): MessageInterface + { + $status = $this->readLine(); + $path = $version = null; + + // Pulling server request + preg_match('!^(?P[A-Z]+) (?P[^ ]*) HTTP/(?P[0-9/.]+)!', $status, $matches); + if (!empty($matches)) { + $message = $this->httpFactory->createServerRequest($matches['method'], ''); + $path = $matches['path']; + $version = $matches['version']; + } + + // Pulling response + preg_match('!^HTTP/(?P[0-9/.]+) (?P[0-9]*)($|\s(?P.*))!', $status, $matches); + if (!empty($matches)) { + $message = $this->httpFactory->createResponse((int)$matches['code'], $matches['reason'] ?? ''); + $version = $matches['version']; + } + + if (empty($message)) { + throw new RuntimeException('Invalid Http request.'); + } + + if ($version) { + $message = $message->withProtocolVersion($version); + } + + while ($header = $this->readLine()) { + $parts = explode(':', $header, 2); + if (count($parts) == 2) { + if ($message->getheaderLine($parts[0]) === '') { + $message = $message->withHeader($parts[0], trim($parts[1])); + } else { + $message = $message->withAddedHeader($parts[0], trim($parts[1])); + } + } + } + if ($message instanceof Request) { + $scheme = $this->ssl ? 'wss' : 'ws'; + $uri = $this->httpFactory->createUri("{$scheme}://{$message->getHeaderLine('Host')}{$path}"); + $message = $message->withUri($uri, true); + } + + return $message; + } + + /** + * @param MessageInterface $message + * @return MessageInterface + * @throws RuntimeException + */ + public function push(MessageInterface $message): MessageInterface + { + if (!$message instanceof Message) { + throw new RuntimeException('Generic MessageInterface currently not supported.'); + } + $data = implode("\r\n", $message->getAsArray()) . "\r\n\r\n"; + $this->stream->write($data); + return $message; + } + + /** + * @throws RuntimeException + */ + private function readLine(): string + { + $data = ''; + do { + $buffer = $this->stream->readLine(1024); + if (is_null($buffer)) { + throw new RuntimeException('Could not read Http request.'); + } + $data .= $buffer; + } while (!str_ends_with($data, "\n")); + return trim($data); + } +} diff --git a/vendor/phrity/websocket/src/Http/Message.php b/vendor/phrity/websocket/src/Http/Message.php new file mode 100644 index 0000000..837e665 --- /dev/null +++ b/vendor/phrity/websocket/src/Http/Message.php @@ -0,0 +1,185 @@ +> $headers */ + private array $headers = []; + + /** + * Retrieves the HTTP protocol version as a string. + * @return string HTTP protocol version. + */ + public function getProtocolVersion(): string + { + return $this->version; + } + + /** + * Return an instance with the specified HTTP protocol version. + * @param string $version HTTP protocol version + * @return static + */ + public function withProtocolVersion(string $version): self + { + $new = clone $this; + $new->version = $version; + return $new; + } + + /** + * Retrieves all message header values. + * @return string[][] Returns an associative array of the message's headers. + */ + public function getHeaders(): array + { + return array_merge(...array_values($this->headers)); + } + + /** + * Checks if a header exists by the given case-insensitive name. + * @param string $name Case-insensitive header field name. + * @return bool Returns true if any header names match the given header. + */ + public function hasHeader(string $name): bool + { + return array_key_exists(strtolower($name), $this->headers); + } + + /** + * Retrieves a message header value by the given case-insensitive name. + * @param string $name Case-insensitive header field name. + * @return string[] An array of string values as provided for the given header. + */ + public function getHeader(string $name): array + { + return $this->hasHeader($name) + ? array_merge(...array_values($this->headers[strtolower($name)] ?: [])) + : []; + } + + /** + * Retrieves a comma-separated string of the values for a single header. + * @param string $name Case-insensitive header field name. + * @return string A string of values as provided for the given header. + */ + public function getHeaderLine(string $name): string + { + return implode(',', $this->getHeader($name)); + } + + /** + * Return an instance with the provided value replacing the specified header. + * @param string $name Case-insensitive header field name. + * @param string|string[] $value Header value(s). + * @return static + */ + public function withHeader(string $name, mixed $value): self + { + $new = clone $this; + $new->removeHeader($name); + $new->handleHeader($name, $value); + return $new; + } + + /** + * Return an instance with the specified header appended with the given value. + * @param string $name Case-insensitive header field name to add. + * @param string|string[] $value Header value(s). + * @return static + */ + public function withAddedHeader(string $name, mixed $value): self + { + $new = clone $this; + $new->handleHeader($name, $value); + return $new; + } + + /** + * Return an instance without the specified header. + * @param string $name Case-insensitive header field name to remove. + * @return static + */ + public function withoutHeader(string $name): self + { + $new = clone $this; + $new->removeHeader($name); + return $new; + } + + /** + * Not implemented, WebSocket only use headers. + * @throws BadMethodCallException + */ + public function getBody(): StreamInterface + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Not implemented, WebSocket only use headers. + * @throws BadMethodCallException + */ + public function withBody(StreamInterface $body): self + { + throw new BadMethodCallException("Not implemented."); + } + + /** @return array */ + public function getAsArray(): array + { + $lines = []; + foreach ($this->getHeaders() as $name => $values) { + foreach ($values as $value) { + $lines[] = "{$name}: {$value}"; + } + } + return $lines; + } + + /** + * @throws InvalidArgumentException + */ + protected function handleHeader(string $name, mixed $value): void + { + if (!preg_match('|^[0-9a-zA-Z#_-]+$|', $name)) { + throw new InvalidArgumentException("'{$name}' is not a valid header field name."); + } + $value = is_array($value) ? $value : [$value]; + foreach ($value as $content) { + if (!is_string($content) && !is_numeric($content)) { + throw new InvalidArgumentException("Invalid header value(s) provided."); + } + $this->headers[strtolower($name)][$name][] = trim((string)$content); + } + } + + protected function removeHeader(string $name): void + { + if ($this->hasHeader($name)) { + unset($this->headers[strtolower($name)]); + } + } +} diff --git a/vendor/phrity/websocket/src/Http/Request.php b/vendor/phrity/websocket/src/Http/Request.php new file mode 100644 index 0000000..37d7c6a --- /dev/null +++ b/vendor/phrity/websocket/src/Http/Request.php @@ -0,0 +1,137 @@ + $methods */ + private static array $methods = ['GET', 'HEAD', 'OPTIONS', 'TRACE', 'PUT', 'DELETE', 'POST', 'PATCH', 'CONNECT']; + + private string $target = ''; + private string $method; + private UriInterface $uri; + + public function __construct(string $method = 'GET', UriInterface|string $uri = '') + { + if (!in_array($method, self::$methods)) { + throw new InvalidArgumentException("Invalid method '{$method}' provided."); + } + $this->uri = $uri instanceof UriInterface ? $uri : new Uri($uri); + $this->method = $method; + $this->handleHeader('Host', $this->formatHostHeader($this->uri)); + } + + /** + * Retrieves the message's request target. + * @return string + */ + public function getRequestTarget(): string + { + if ($this->target) { + return $this->target; + } + $uri = (new Uri())->withPath($this->uri->getPath())->withQuery($this->uri->getQuery()); + return $uri->toString(Uri::ABSOLUTE_PATH); + } + + /** + * Return an instance with the specific request-target. + * @param mixed $requestTarget + * @return static + */ + public function withRequestTarget(mixed $requestTarget): self + { + $new = clone $this; + $new->target = $requestTarget; + return $new; + } + + /** + * Retrieves the HTTP method of the request. + * @return string Returns the request method. + */ + public function getMethod(): string + { + return $this->method; + } + + /** + * Return an instance with the provided HTTP method. + * @param string $method Case-sensitive method. + * @return static + * @throws InvalidArgumentException for invalid HTTP methods. + */ + public function withMethod(string $method): self + { + if (!in_array($method, self::$methods)) { + throw new InvalidArgumentException("Invalid method '{$method}' provided."); + } + $new = clone $this; + $new->method = $method; + return $new; + } + + /** + * Retrieves the URI instance. + * This method MUST return a UriInterface instance. + * @return UriInterface Returns a UriInterface instance representing the URI of the request. + */ + public function getUri(): UriInterface + { + return $this->uri; + } + + /** + * Returns an instance with the provided URI. + * @param UriInterface $uri New request URI to use. + * @param bool $preserveHost Preserve the original state of the Host header. + * @return static + */ + public function withUri(UriInterface $uri, bool $preserveHost = false): self + { + $new = clone $this; + $new->uri = $uri instanceof Uri ? $uri : new Uri((string)$uri); + if (!$preserveHost || !$new->hasHeader('host')) { + $new->removeHeader('host'); + $new->handleHeader('Host', $this->formatHostHeader($uri)); + } + return $new; + } + + public function __toString(): string + { + return $this->stringable('%s %s', $this->getMethod(), $this->getUri()); + } + + /** @return array */ + public function getAsArray(): array + { + return array_merge([ + "{$this->getMethod()} {$this->getRequestTarget()} HTTP/{$this->getProtocolVersion()}", + ], parent::getAsArray()); + } + + private function formatHostHeader(UriInterface $uri): string + { + $host = $uri->getHost(); + $port = $uri->getPort(); + return $host && $port ? "{$host}:{$port}" : $host; + } +} diff --git a/vendor/phrity/websocket/src/Http/Response.php b/vendor/phrity/websocket/src/Http/Response.php new file mode 100644 index 0000000..15d7ec2 --- /dev/null +++ b/vendor/phrity/websocket/src/Http/Response.php @@ -0,0 +1,150 @@ + $codes */ + private static array $codes = [ + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 103 => 'Early Hints', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Content Too Large', + 414 => 'URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Range Not Satisfiable', + 417 => 'Expectation Failed', + 421 => 'Misdirected Request', + 422 => 'Unprocessable Content', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Too Early', + 426 => 'Upgrade Required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 451 => 'Unavailable For Legal Reasons', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 510 => 'Not Extended', + 511 => 'Network Authentication Required', + ]; + + private int $code; + private string $reason; + + public function __construct(int $code = 200, string $reasonPhrase = '') + { + if ($code < 100 || $code > 599) { + throw new InvalidArgumentException("Invalid status code '{$code}' provided."); + } + $this->code = $code; + $this->reason = $reasonPhrase; + } + + /** + * Gets the response status code. + * @return int Status code. + */ + public function getStatusCode(): int + { + return $this->code; + } + + /** + * Return an instance with the specified status code and, optionally, reason phrase. + * @param int $code The 3-digit integer result code to set. + * @param string $reasonPhrase The reason phrase to use. + * @return static + * @throws InvalidArgumentException For invalid status code arguments. + */ + public function withStatus(int $code, string $reasonPhrase = ''): self + { + if ($code < 100 || $code > 599) { + throw new InvalidArgumentException("Invalid status code '{$code}' provided."); + } + $new = clone $this; + $new->code = $code; + $new->reason = $reasonPhrase; + return $new; + } + + /** + * Gets the response reason phrase associated with the status code. + * @return string Reason phrase; must return an empty string if none present. + */ + public function getReasonPhrase(): string + { + $d = self::$codes[$this->code]; + return $this->reason ?: $d; + } + + public function __toString(): string + { + return $this->stringable('%s', $this->getStatusCode()); + } + + /** @return array */ + public function getAsArray(): array + { + return array_merge([ + "HTTP/{$this->getProtocolVersion()} {$this->getStatusCode()} {$this->getReasonPhrase()}", + ], parent::getAsArray()); + } +} diff --git a/vendor/phrity/websocket/src/Http/ServerRequest.php b/vendor/phrity/websocket/src/Http/ServerRequest.php new file mode 100644 index 0000000..7478c61 --- /dev/null +++ b/vendor/phrity/websocket/src/Http/ServerRequest.php @@ -0,0 +1,153 @@ + + */ + public function getServerParams(): array + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Retrieves cookies sent by the client to the server. + * @return array + */ + public function getCookieParams(): array + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Return an instance with the specified cookies. + * @param array $cookies Array of key/value pairs representing cookies. + * @return static + */ + public function withCookieParams(array $cookies): self + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Retrieves the deserialized query string arguments, if any. + * @return array + */ + public function getQueryParams(): array + { + parse_str($this->getUri()->getQuery(), $result); + return $result; + } + + /** + * Return an instance with the specified query string arguments. + * @param array $query Array of query string arguments + * @return static + */ + public function withQueryParams(array $query): self + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Retrieve normalized file upload data. + * @return array An array tree of UploadedFileInterface instances. + */ + public function getUploadedFiles(): array + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Create a new instance with the specified uploaded files. + * @param array $uploadedFiles An array tree of UploadedFileInterface instances. + * @return static + */ + public function withUploadedFiles(array $uploadedFiles): self + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Retrieve any parameters provided in the request body. + * @return null|array|object The deserialized body parameters, if any. + */ + public function getParsedBody() + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Return an instance with the specified body parameters. + * @param null|array|object $data The deserialized body data. + * @return static + */ + public function withParsedBody($data): self + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Retrieve attributes derived from the request. + * @return mixed[] Attributes derived from the request. + */ + public function getAttributes(): array + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Retrieve a single derived request attribute. + * @param string $name The attribute name. + * @param mixed $default Default value to return if the attribute does not exist. + * @return mixed + */ + public function getAttribute(string $name, $default = null) + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Return an instance with the specified derived request attribute. + * @param string $name The attribute name. + * @param mixed $value The value of the attribute. + * @return static + */ + public function withAttribute(string $name, $value): self + { + throw new BadMethodCallException("Not implemented."); + } + + /** + * Return an instance that removes the specified derived request attribute. + * @param string $name The attribute name. + * @return static + */ + public function withoutAttribute(string $name): self + { + throw new BadMethodCallException("Not implemented."); + } + + public function __toString(): string + { + return $this->stringable('%s %s', $this->getMethod(), $this->getRequestTarget()); + } +} diff --git a/vendor/phrity/websocket/src/Message/Binary.php b/vendor/phrity/websocket/src/Message/Binary.php new file mode 100644 index 0000000..29c049f --- /dev/null +++ b/vendor/phrity/websocket/src/Message/Binary.php @@ -0,0 +1,27 @@ +compress; + } + + public function setCompress(bool $compress): void + { + $this->compress = $compress; + } +} diff --git a/vendor/phrity/websocket/src/Message/Close.php b/vendor/phrity/websocket/src/Message/Close.php new file mode 100644 index 0000000..7727d6b --- /dev/null +++ b/vendor/phrity/websocket/src/Message/Close.php @@ -0,0 +1,56 @@ +status = $status; + parent::__construct($content); + } + + public function getCloseStatus(): int|null + { + return $this->status; + } + + public function setCloseStatus(int|null $status): void + { + $this->status = $status; + } + + public function getPayload(): string + { + $statusBinstr = sprintf('%016b', $this->status); + $statusStr = ''; + foreach (str_split($statusBinstr, 8) as $binstr) { + $statusStr .= chr((int)bindec($binstr)); + } + return $statusStr . $this->content; + } + + public function setPayload(string $payload = ''): void + { + $this->status = 0; + $this->content = ''; + if (strlen($payload) > 0) { + $this->status = current(unpack('n', $payload) ?: []); + } + if (strlen($payload) > 2) { + $this->content = substr($payload, 2); + } + } +} diff --git a/vendor/phrity/websocket/src/Message/Message.php b/vendor/phrity/websocket/src/Message/Message.php new file mode 100644 index 0000000..c4db070 --- /dev/null +++ b/vendor/phrity/websocket/src/Message/Message.php @@ -0,0 +1,113 @@ +content = $content; + $this->timestamp = new DateTimeImmutable(); + } + + public function getOpcode(): string + { + return $this->opcode; + } + + public function getLength(): int + { + return strlen($this->content); + } + + public function getTimestamp(): DateTimeInterface + { + return $this->timestamp; + } + + public function getContent(): string + { + return $this->content; + } + + public function setContent(string $content = ''): void + { + $this->content = $content; + } + + public function hasContent(): bool + { + return $this->content != ''; + } + + public function getPayload(): string + { + return $this->content; + } + + public function setPayload(string $payload = ''): void + { + $this->content = $payload; + } + + public function isCompressed(): bool + { + return false; + } + + /** @throws ConnectionFailureException */ + public function setCompress(bool $compress): void + { + if ($compress) { + throw new ConnectionFailureException('Must not compress control message.'); + } + } + + /** + * Split messages into frames + * @param int<1, max> $frameSize + * @return array + */ + public function getFrames(int $frameSize = 4096): array + { + $frames = []; + $split = str_split($this->getPayload(), $frameSize); + if (empty($split)) { + $split = ['']; + } + foreach ($split as $i => $payload) { + $frames[] = new Frame( + $i === 0 ? $this->opcode : 'continuation', + $payload, + $i === array_key_last($split) + ); + } + if ($this->isCompressed()) { + $frames[0]->setRsv1(true); + } + return $frames; + } +} diff --git a/vendor/phrity/websocket/src/Message/MessageHandler.php b/vendor/phrity/websocket/src/Message/MessageHandler.php new file mode 100644 index 0000000..87be81c --- /dev/null +++ b/vendor/phrity/websocket/src/Message/MessageHandler.php @@ -0,0 +1,118 @@ + $frameBuffer */ + private array $frameBuffer = []; + + public function __construct(FrameHandler $frameHandler) + { + $this->frameHandler = $frameHandler; + $this->initLogger(); + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + $this->frameHandler->setLogger($logger); + } + + /** + * Push message + * @template T of Message + * @param T $message + * @param int<1, max> $size + * @return T + */ + public function push(Message $message, int $size = self::DEFAULT_SIZE): Message + { + $frames = $message->getFrames($size); + foreach ($frames as $frame) { + $this->frameHandler->push($frame); + } + $this->logger->info("[message-handler] Pushed {$message}", [ + 'opcode' => $message->getOpcode(), + 'content-length' => $message->getLength(), + 'frames' => count($frames), + ]); + return $message; + } + + // Pull message + public function pull(): Message + { + do { + $frame = $this->frameHandler->pull(); + if ($frame->isFinal()) { + if ($frame->isContinuation()) { + $frames = array_merge($this->frameBuffer, [$frame]); + $this->frameBuffer = []; // Clear buffer + } else { + $frames = [$frame]; + } + return $this->createMessage($frames); + } + // Non-final frame - add to buffer for continuous reading + $this->frameBuffer[] = $frame; + } while (true); + } + + /** + * @param non-empty-array $frames + * @throws BadOpcodeException + */ + private function createMessage(array $frames): Message + { + $opcode = $frames[0]->getOpcode() ?? null; + $message = match ($opcode) { + 'text' => new Text(), + 'binary' => new Binary(), + 'ping' => new Ping(), + 'pong' => new Pong(), + 'close' => new Close(), + default => throw new BadOpcodeException("Invalid opcode '{$opcode}' provided"), + }; + $message->setPayload(array_reduce($frames, function (string $carry, Frame $item) { + return $carry . $item->getPayload(); + }, '')); + $message->setCompress($frames[0]->getRsv1() ?? false); + $this->logger->info("[message-handler] Pulled {$message}", [ + 'opcode' => $message->getOpcode(), + 'content-length' => $message->getLength(), + 'frames' => count($frames), + ]); + return $message; + } +} diff --git a/vendor/phrity/websocket/src/Message/Ping.php b/vendor/phrity/websocket/src/Message/Ping.php new file mode 100644 index 0000000..8192003 --- /dev/null +++ b/vendor/phrity/websocket/src/Message/Ping.php @@ -0,0 +1,17 @@ +compress; + } + + public function setCompress(bool $compress): void + { + $this->compress = $compress; + } +} diff --git a/vendor/phrity/websocket/src/Middleware/Callback.php b/vendor/phrity/websocket/src/Middleware/Callback.php new file mode 100644 index 0000000..2162c26 --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/Callback.php @@ -0,0 +1,100 @@ +incoming = $incoming; + $this->outgoing = $outgoing; + $this->httpIncoming = $httpIncoming; + $this->httpOutgoing = $httpOutgoing; + $this->tick = $tick; + $this->initLogger(); + } + + public function processIncoming(ProcessStack $stack, Connection $connection): Message + { + if (is_callable($this->incoming)) { + return call_user_func($this->incoming, $stack, $connection); + } + return $stack->handleIncoming(); + } + + public function processOutgoing(ProcessStack $stack, Connection $connection, Message $message): Message + { + if (is_callable($this->outgoing)) { + return call_user_func($this->outgoing, $stack, $connection, $message); + } + return $stack->handleOutgoing($message); + } + + public function processHttpIncoming(ProcessHttpStack $stack, Connection $connection): MessageInterface + { + if (is_callable($this->httpIncoming)) { + return call_user_func($this->httpIncoming, $stack, $connection); + } + return $stack->handleHttpIncoming(); + } + + public function processHttpOutgoing( + ProcessHttpStack $stack, + Connection $connection, + MessageInterface $message + ): MessageInterface { + if (is_callable($this->httpOutgoing)) { + return call_user_func($this->httpOutgoing, $stack, $connection, $message); + } + return $stack->handleHttpOutgoing($message); + } + + public function processTick(ProcessTickStack $stack, Connection $connection): void + { + if (is_callable($this->tick)) { + call_user_func($this->tick, $stack, $connection); + } + $stack->handleTick(); + } +} diff --git a/vendor/phrity/websocket/src/Middleware/CloseHandler.php b/vendor/phrity/websocket/src/Middleware/CloseHandler.php new file mode 100644 index 0000000..c5eafb5 --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/CloseHandler.php @@ -0,0 +1,73 @@ +initLogger(); + } + + public function processIncoming(ProcessStack $stack, Connection $connection): Message + { + $message = $stack->handleIncoming(); // Proceed before logic + if (!$message instanceof Close) { + return $message; + } + if ($connection->isWritable()) { + // Remote sent Close; acknowledge and close for further reading + $this->logger->debug("[close-handler] Received 'close', status: {$message->getCloseStatus()}"); + $ack = "Close acknowledged: {$message->getCloseStatus()}"; + $connection->closeRead(); + $connection->send(new Close($message->getCloseStatus(), $ack)); + } else { + // Remote sent Close/Ack: disconnect + $this->logger->debug("[close-handler] Received 'close' acknowledge, disconnecting"); + $connection->disconnect(); + } + return $message; + } + + public function processOutgoing(ProcessStack $stack, Connection $connection, Message $message): Message + { + $message = $stack->handleOutgoing($message); // Proceed before logic + if (!$message instanceof Close) { + return $message; + } + if ($connection->isReadable()) { + // Local sent Close: close for further writing, expect remote acknowledge + $this->logger->debug("[close-handler] Sent 'close', status: {$message->getCloseStatus()}"); + $connection->closeWrite(); + } else { + // Local sent Close/Ack: disconnect + $this->logger->debug("[close-handler] Sent 'close' acknowledge, disconnecting"); + $connection->disconnect(); + } + return $message; + } +} diff --git a/vendor/phrity/websocket/src/Middleware/CompressionExtension.php b/vendor/phrity/websocket/src/Middleware/CompressionExtension.php new file mode 100644 index 0000000..a8f8027 --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/CompressionExtension.php @@ -0,0 +1,163 @@ + $compressors */ + private array $compressors = []; + + public function __construct(CompressorInterface ...$compressors) + { + $this->compressors = $compressors; + $this->initLogger(); + } + + public function processHttpOutgoing( + ProcessHttpStack $stack, + Connection $connection, + MessageInterface $message + ): MessageInterface { + if ($message instanceof RequestInterface) { + // Outgoing requests on Client + $connection->setMeta('compressionExtension.compressor', null); + $connection->setMeta('compressionExtension.configuration', null); + $headerValues = []; + foreach ($this->compressors as $compressor) { + $headerValues[] = $compressor->getRequestHeaderValue(); + } + $message = $message->withAddedHeader('Sec-WebSocket-Extensions', implode(', ', $headerValues)); + } elseif ($message instanceof ResponseInterface) { + // Outgoing Response on Server + if ($compressor = $connection->getMeta('compressionExtension.compressor')) { + $configuration = $connection->getMeta('compressionExtension.configuration'); + $message = $message->withHeader( + 'Sec-WebSocket-Extensions', + $compressor->getResponseHeaderValue($configuration) + ); + } + } + return $stack->handleHttpOutgoing($message); + } + + public function processHttpIncoming(ProcessHttpStack $stack, Connection $connection): MessageInterface + { + $message = $stack->handleHttpIncoming(); + if ($message instanceof ServerRequestInterface) { + // Incoming requests on Server + $connection->setMeta('compressionExtension.compressor', null); + $connection->setMeta('compressionExtension.configuration', null); + if ($preferred = $this->getPreferred($message)) { + $connection->setMeta('compressionExtension.compressor', $preferred->compressor); + $connection->setMeta('compressionExtension.configuration', $preferred->configuration); + $this->logger->debug( + "[permessage-compression] Using {$preferred->compressor}", + (array)$preferred->configuration + ); + } + } elseif ($message instanceof ResponseInterface) { + // Incoming Response on Client + if ($preferred = $this->getPreferred($message)) { + $connection->setMeta('compressionExtension.compressor', $preferred->compressor); + $connection->setMeta('compressionExtension.configuration', $preferred->configuration); + $this->logger->debug( + "[permessage-compression] Using {$preferred->compressor}", + (array)$preferred->configuration + ); + } + // @todo: If not found? + } + return $message; + } + + public function processIncoming(ProcessStack $stack, Connection $connection): Message + { + $message = $stack->handleIncoming(); + if ( + ($message instanceof Text || $message instanceof Binary) + && $message->isCompressed() + && $compressor = $connection->getMeta('compressionExtension.compressor') + ) { + $message = $compressor->decompress($message, $connection->getMeta('compressionExtension.configuration')); + } + return $message; + } + + /** + * @template T of Message + * @param T $message + * @return T|Text|Binary + */ + public function processOutgoing(ProcessStack $stack, Connection $connection, Message $message): Message + { + if ( + ($message instanceof Text || $message instanceof Binary) + && !$message->isCompressed() + && $compressor = $connection->getMeta('compressionExtension.compressor') + ) { + /** @var Text|Binary $message */ + $message = $compressor->compress($message, $connection->getMeta('compressionExtension.configuration')); + } + return $stack->handleOutgoing($message); + } + + /** + * @return object{compressor: CompressorInterface, configuration: object}|null + */ + protected function getPreferred(MessageInterface $request): object|null + { + $isServer = $request instanceof ServerRequestInterface; + foreach ($request->getHeader('Sec-WebSocket-Extensions') as $header) { + foreach (explode(',', $header) as $element) { + foreach ($this->compressors as $compressor) { + $configuration = $compressor->getConfiguration(trim($element), $isServer); + if ($compressor->isEligable($configuration)) { + return (object)['compressor' => $compressor, 'configuration' => $configuration]; + } + } + } + } + return null; + } +} diff --git a/vendor/phrity/websocket/src/Middleware/CompressionExtension/CompressorInterface.php b/vendor/phrity/websocket/src/Middleware/CompressionExtension/CompressorInterface.php new file mode 100644 index 0000000..f15fe71 --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/CompressionExtension/CompressorInterface.php @@ -0,0 +1,39 @@ +, + * clientMaxWindowBits: int, + * } + */ +class DeflateCompressor implements CompressorInterface, Stringable +{ + use StringableTrait; + + private const MIN_WINDOW_SIZE = 9; + private const MAX_WINDOW_SIZE = 15; + + private bool $serverNoContextTakeover; + private bool $clientNoContextTakeover; + /** @var int $serverMaxWindowBits */ + private int $serverMaxWindowBits; + /** @var int $clientMaxWindowBits */ + private int $clientMaxWindowBits; + + /** + * @throws RuntimeException + * @throws RangeException + */ + public function __construct( + bool $serverNoContextTakeover = false, + bool $clientNoContextTakeover = false, + int $serverMaxWindowBits = self::MAX_WINDOW_SIZE, + int $clientMaxWindowBits = self::MAX_WINDOW_SIZE, + string $extension = 'zlib', + ) { + if (!extension_loaded($extension)) { + throw new RuntimeException("DeflateCompressor require {$extension} extension."); + } + if ($serverMaxWindowBits < self::MIN_WINDOW_SIZE || $serverMaxWindowBits > self::MAX_WINDOW_SIZE) { + throw new RangeException("DeflateCompressor serverMaxWindowBits must be in range 9-15."); + } + if ($clientMaxWindowBits < self::MIN_WINDOW_SIZE || $clientMaxWindowBits > self::MAX_WINDOW_SIZE) { + throw new RangeException("DeflateCompressor clientMaxWindowBits must be in range 9-15."); + } + $this->serverNoContextTakeover = $serverNoContextTakeover; + $this->clientNoContextTakeover = $clientNoContextTakeover; + $this->serverMaxWindowBits = $serverMaxWindowBits; + $this->clientMaxWindowBits = $clientMaxWindowBits; + } + + public function getRequestHeaderValue(): string + { + $header = "permessage-deflate"; + if ($this->serverNoContextTakeover) { + $header .= "; server_no_context_takeover"; + } + if ($this->clientNoContextTakeover) { + $header .= "; client_no_context_takeover"; + } + if ($this->serverMaxWindowBits != self::MAX_WINDOW_SIZE) { + $header .= "; server_max_window_bits={$this->serverMaxWindowBits}"; + } + if ($this->clientMaxWindowBits != self::MAX_WINDOW_SIZE) { + $header .= "; client_max_window_bits={$this->clientMaxWindowBits}"; + } + return $header; + } + + /** + * @param Config $configuration + */ + public function getResponseHeaderValue(object $configuration): string + { + // @todo: throw HandshakeException or bad config + $header = "permessage-deflate"; + if ($configuration->serverNoContextTakeover) { + $header .= "; server_no_context_takeover"; + } + if ($configuration->clientNoContextTakeover) { + $header .= "; client_no_context_takeover"; + } + $serverMaxWindowBits = min($configuration->serverMaxWindowBits, $this->serverMaxWindowBits); + if ($serverMaxWindowBits != self::MAX_WINDOW_SIZE) { + $header .= "; server_max_window_bits={$serverMaxWindowBits}"; + } + $clientMaxWindowBits = min($configuration->clientMaxWindowBits, $this->clientMaxWindowBits); + if ($clientMaxWindowBits != self::MAX_WINDOW_SIZE) { + $header .= "; client_max_window_bits={$clientMaxWindowBits}"; + } + return $header; + } + + /** + * @param Config $configuration + */ + public function isEligable(object $configuration): bool + { + return + $configuration->compressor == 'permessage-deflate' + && $configuration->serverMaxWindowBits <= $this->serverMaxWindowBits + && $configuration->clientMaxWindowBits <= $this->clientMaxWindowBits + ; + } + + /** + * @return Config + */ + public function getConfiguration(string $element, bool $isServer): object + { + $configuration = (object)[ + 'compressor' => null, + 'isServer' => $isServer, + 'serverNoContextTakeover' => $this->serverNoContextTakeover, + 'clientNoContextTakeover' => $this->clientNoContextTakeover, + 'serverMaxWindowBits' => $this->serverMaxWindowBits, + 'clientMaxWindowBits' => $this->clientMaxWindowBits, + 'deflator' => null, + 'inflator' => null, + ]; + foreach (explode(';', $element) as $parameter) { + $parts = explode('=', $parameter); + $key = trim($parts[0]); + // @todo: Error handling when parsing + switch ($key) { + case 'permessage-deflate': + $configuration->compressor = $key; + break; + case 'server_no_context_takeover': + $configuration->serverNoContextTakeover = true; + break; + case 'client_no_context_takeover': + $configuration->clientNoContextTakeover = true; + break; + case 'server_max_window_bits': + $bits = intval($parts[1] ?? self::MAX_WINDOW_SIZE); + $configuration->serverMaxWindowBits = min($bits, $this->serverMaxWindowBits); + break; + case 'client_max_window_bits': + $bits = intval($parts[1] ?? self::MAX_WINDOW_SIZE); + $configuration->clientMaxWindowBits = min($bits, $this->clientMaxWindowBits); + break; + } + } + return $configuration; + } + + /** + * @template T of Binary|Text + * @param T $message + * @param Config $configuration + * @return T + */ + public function compress(Binary|Text $message, object $configuration): Binary|Text + { + $windowBits = $configuration->isServer + ? $configuration->serverMaxWindowBits + : $configuration->clientMaxWindowBits; + $noContextTakeover = $configuration->isServer + ? $configuration->serverNoContextTakeover + : $configuration->clientNoContextTakeover; + + if (is_null($configuration->deflator) || $noContextTakeover) { + $configuration->deflator = deflate_init(ZLIB_ENCODING_RAW, [ + 'level' => -1, + 'window' => $windowBits, + 'strategy' => ZLIB_DEFAULT_STRATEGY + ]) ?: null; + } + /** @var DeflateContext $deflator */ + $deflator = $configuration->deflator; + /** @var string $deflated */ + $deflated = deflate_add($deflator, $message->getPayload(), ZLIB_SYNC_FLUSH); + $deflated = substr($deflated, 0, -4); // Remove 4 last chars + $message->setCompress(true); + $message->setPayload($deflated); + return $message; + } + + /** + * @param Config $configuration + */ + public function decompress(Binary|Text $message, object $configuration): Binary|Text + { + $windowBits = $configuration->isServer + ? $configuration->clientMaxWindowBits + : $configuration->serverMaxWindowBits; + $noContextTakeover = $configuration->isServer + ? $configuration->clientNoContextTakeover + : $configuration->serverNoContextTakeover; + + if (is_null($configuration->inflator) || $noContextTakeover) { + $configuration->inflator = inflate_init(ZLIB_ENCODING_RAW, [ + 'level' => -1, + 'window' => $windowBits, + 'strategy' => ZLIB_DEFAULT_STRATEGY + ]) ?: null; + } + /** @var InflateContext $inflator */ + $inflator = $configuration->inflator; + /** @var string $inflated */ + $inflated = inflate_add($inflator, $message->getPayload() . "\x00\x00\xff\xff"); + $message->setCompress(false); + $message->setPayload($inflated); + return $message; + } +} diff --git a/vendor/phrity/websocket/src/Middleware/FollowRedirect.php b/vendor/phrity/websocket/src/Middleware/FollowRedirect.php new file mode 100644 index 0000000..addc826 --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/FollowRedirect.php @@ -0,0 +1,71 @@ +limit = $limit; + $this->initLogger(); + } + + /** + * @throws HandshakeException + * @throws ReconnectException + */ + public function processHttpIncoming(ProcessHttpStack $stack, Connection $connection): MessageInterface + { + $message = $stack->handleHttpIncoming(); + if ( + $message instanceof ResponseInterface + && $message->getStatusCode() >= 300 + && $message->getStatusCode() < 400 + && $locationHeader = $message->getHeaderLine('Location') + ) { + $note = "{$this->attempts} of {$this->limit} redirect attempts"; + if ($this->attempts > $this->limit) { + $this->logger->debug("[follow-redirect] Too many redirect attempts, giving up"); + throw new HandshakeException("Too many redirect attempts, giving up", $message); + } + $this->attempts++; + $this->logger->debug("[follow-redirect] {$message->getStatusCode()} {$locationHeader} ($note)"); + throw new ReconnectException(new Uri($locationHeader)); + } + return $message; + } +} diff --git a/vendor/phrity/websocket/src/Middleware/MiddlewareHandler.php b/vendor/phrity/websocket/src/Middleware/MiddlewareHandler.php new file mode 100644 index 0000000..59ba495 --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/MiddlewareHandler.php @@ -0,0 +1,172 @@ + */ + private array $middlewares = []; + /** @var array */ + private array $incoming = []; + /** @var array */ + private array $outgoing = []; + /** @var array */ + private array $httpIncoming = []; + /** @var array */ + private array $httpOutgoing = []; + /** @var array */ + private array $tick = []; + + // Handlers + private HttpHandler $httpHandler; + private MessageHandler $messageHandler; + + /** + * Create MiddlewareHandler. + * @param MessageHandler $messageHandler + * @param HttpHandler $httpHandler + */ + public function __construct(MessageHandler $messageHandler, HttpHandler $httpHandler) + { + $this->messageHandler = $messageHandler; + $this->httpHandler = $httpHandler; + $this->initLogger(); + } + + /** + * Set logger on MiddlewareHandler and all LoggerAware middlewares. + * @param LoggerInterface $logger + */ + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + foreach ($this->middlewares as $middleware) { + $this->attachLogger($middleware); + } + } + + /** + * Add a middleware. + * @param MiddlewareInterface $middleware + * @return $this + */ + public function add(MiddlewareInterface $middleware): self + { + if ($middleware instanceof ProcessIncomingInterface) { + $this->logger->info("[middleware-handler] Added incoming: {$middleware}"); + $this->incoming[] = $middleware; + } + if ($middleware instanceof ProcessOutgoingInterface) { + $this->logger->info("[middleware-handler] Added outgoing: {$middleware}"); + $this->outgoing[] = $middleware; + } + if ($middleware instanceof ProcessHttpIncomingInterface) { + $this->logger->info("[middleware-handler] Added http incoming: {$middleware}"); + $this->httpIncoming[] = $middleware; + } + if ($middleware instanceof ProcessHttpOutgoingInterface) { + $this->logger->info("[middleware-handler] Added http outgoing: {$middleware}"); + $this->httpOutgoing[] = $middleware; + } + if ($middleware instanceof ProcessTickInterface) { + $this->logger->info("[middleware-handler] Added tick: {$middleware}"); + $this->tick[] = $middleware; + } + $this->attachLogger($middleware); + $this->middlewares[] = $middleware; + return $this; + } + + /** + * Process middlewares for incoming messages. + * @param Connection $connection + * @return Message + */ + public function processIncoming(Connection $connection): Message + { + $this->logger->info("[middleware-handler] Processing incoming"); + $stack = new ProcessStack($connection, $this->messageHandler, $this->incoming); + return $stack->handleIncoming(); + } + + /** + * Process middlewares for outgoing messages. + * @template T of Message + * @param Connection $connection + * @param T $message + * @return T + */ + public function processOutgoing(Connection $connection, Message $message): Message + { + $this->logger->info("[middleware-handler] Processing outgoing"); + $stack = new ProcessStack($connection, $this->messageHandler, $this->outgoing); + return $stack->handleOutgoing($message); + } + + /** + * Process middlewares for http requests. + * @param Connection $connection + * @return MessageInterface + */ + public function processHttpIncoming(Connection $connection): MessageInterface + { + $this->logger->info("[middleware-handler] Processing http incoming"); + $stack = new ProcessHttpStack($connection, $this->httpHandler, $this->httpIncoming); + return $stack->handleHttpIncoming(); + } + + /** + * Process middlewares for http requests. + * @param Connection $connection + * @param MessageInterface $message + * @return MessageInterface + */ + public function processHttpOutgoing(Connection $connection, MessageInterface $message): MessageInterface + { + $this->logger->info("[middleware-handler] Processing http outgoing"); + $stack = new ProcessHttpStack($connection, $this->httpHandler, $this->httpOutgoing); + return $stack->handleHttpOutgoing($message); + } + + /** + * Process middlewares for tick. + * @param Connection $connection + */ + public function processTick(Connection $connection): void + { + $this->logger->info("[middleware-handler] Processing tick"); + $stack = new ProcessTickStack($connection, $this->tick); + $stack->handleTick(); + } +} diff --git a/vendor/phrity/websocket/src/Middleware/MiddlewareInterface.php b/vendor/phrity/websocket/src/Middleware/MiddlewareInterface.php new file mode 100644 index 0000000..4f5ce43 --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/MiddlewareInterface.php @@ -0,0 +1,18 @@ +interval = $interval; + $this->initLogger(); + } + + public function processOutgoing(ProcessStack $stack, Connection $connection, Message $message): Message + { + $this->setNext($connection); // Update timestamp for next ping + return $stack->handleOutgoing($message); + } + + public function processTick(ProcessTickStack $stack, Connection $connection): void + { + // Push if time exceeds timestamp for next ping + if ($connection->isWritable() && microtime(true) >= $this->getNext($connection)) { + $this->logger->debug("[ping-interval] Auto-pushing ping"); + $connection->send(new Ping()); + $this->setNext($connection); // Update timestamp for next ping + } + $stack->handleTick(); + } + + private function getNext(Connection $connection): float + { + return $connection->getMeta('pingInterval.next') ?? $this->setNext($connection); + } + + private function setNext(Connection $connection): float + { + $next = microtime(true) + ($this->interval ?? $connection->getTimeout()); + $connection->setMeta('pingInterval.next', $next); + return $next; + } +} diff --git a/vendor/phrity/websocket/src/Middleware/PingResponder.php b/vendor/phrity/websocket/src/Middleware/PingResponder.php new file mode 100644 index 0000000..482465e --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/PingResponder.php @@ -0,0 +1,45 @@ +initLogger(); + } + + public function processIncoming(ProcessStack $stack, Connection $connection): Message + { + $message = $stack->handleIncoming(); + if ($message instanceof Ping && $connection->isWritable()) { + $connection->send(new Pong($message->getContent())); + } + return $message; + } +} diff --git a/vendor/phrity/websocket/src/Middleware/ProcessHttpIncomingInterface.php b/vendor/phrity/websocket/src/Middleware/ProcessHttpIncomingInterface.php new file mode 100644 index 0000000..a986b3b --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/ProcessHttpIncomingInterface.php @@ -0,0 +1,20 @@ + $processors */ + private array $processors; + + /** + * Create ProcessStack. + * @param Connection $connection + * @param HttpHandler $httpHandler + * @param array $processors + */ + public function __construct(Connection $connection, HttpHandler $httpHandler, array $processors) + { + $this->connection = $connection; + $this->httpHandler = $httpHandler; + $this->processors = $processors; + } + + /** + * Process middleware for incoming http message. + * @return MessageInterface + */ + public function handleHttpIncoming(): MessageInterface + { + /** @var ProcessHttpIncomingInterface|null $processor */ + $processor = array_shift($this->processors); + if ($processor) { + return $processor->processHttpIncoming($this, $this->connection); + } + return $this->httpHandler->pull(); + } + + /** + * Process middleware for outgoing http message. + * @param MessageInterface $message + * @return MessageInterface + */ + public function handleHttpOutgoing(MessageInterface $message): MessageInterface + { + /** @var ProcessHttpOutgoingInterface|null $processor */ + $processor = array_shift($this->processors); + if ($processor) { + return $processor->processHttpOutgoing($this, $this->connection, $message); + } + return $this->httpHandler->push($message); + } +} diff --git a/vendor/phrity/websocket/src/Middleware/ProcessIncomingInterface.php b/vendor/phrity/websocket/src/Middleware/ProcessIncomingInterface.php new file mode 100644 index 0000000..495ed4e --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/ProcessIncomingInterface.php @@ -0,0 +1,20 @@ + $processors */ + private array $processors; + + /** + * Create ProcessStack. + * @param Connection $connection + * @param MessageHandler $messageHandler + * @param array $processors + */ + public function __construct(Connection $connection, MessageHandler $messageHandler, array $processors) + { + $this->connection = $connection; + $this->messageHandler = $messageHandler; + $this->processors = $processors; + } + + /** + * Process middleware for incoming message. + * @return Message + */ + public function handleIncoming(): Message + { + /** @var ProcessIncomingInterface|null $processor */ + $processor = array_shift($this->processors); + if ($processor) { + return $processor->processIncoming($this, $this->connection); + } + return $this->messageHandler->pull(); + } + + /** + * Process middleware for outgoing message. + * @template T of Message + * @param T $message + * @return T + */ + public function handleOutgoing(Message $message): Message + { + /** @var ProcessOutgoingInterface|null $processor */ + $processor = array_shift($this->processors); + if ($processor) { + return $processor->processOutgoing($this, $this->connection, $message); + } + return $this->messageHandler->push($message, $this->connection->getFrameSize()); + } +} diff --git a/vendor/phrity/websocket/src/Middleware/ProcessTickInterface.php b/vendor/phrity/websocket/src/Middleware/ProcessTickInterface.php new file mode 100644 index 0000000..0a37f77 --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/ProcessTickInterface.php @@ -0,0 +1,19 @@ + $processors */ + private array $processors; + + /** + * Create ProcessStack. + * @param Connection $connection + * @param array $processors + */ + public function __construct(Connection $connection, array $processors) + { + $this->connection = $connection; + $this->processors = $processors; + } + + /** + * Process middleware for tick. + */ + public function handleTick(): void + { + $processor = array_shift($this->processors); + if ($processor) { + $processor->processTick($this, $this->connection); + } + } +} diff --git a/vendor/phrity/websocket/src/Middleware/SubprotocolNegotiation.php b/vendor/phrity/websocket/src/Middleware/SubprotocolNegotiation.php new file mode 100644 index 0000000..dcaacfe --- /dev/null +++ b/vendor/phrity/websocket/src/Middleware/SubprotocolNegotiation.php @@ -0,0 +1,113 @@ + $subprotocols */ + private array $subprotocols; + private bool $require; + + /** @param array $subprotocols */ + public function __construct(array $subprotocols, bool $require = false) + { + $this->subprotocols = $subprotocols; + $this->require = $require; + $this->initLogger(); + } + + public function processHttpOutgoing( + ProcessHttpStack $stack, + Connection $connection, + MessageInterface $message + ): MessageInterface { + if ($message instanceof RequestInterface) { + // Outgoing requests on Client + foreach ($this->subprotocols as $subprotocol) { + $message = $message->withAddedHeader('Sec-WebSocket-Protocol', $subprotocol); + } + if ($supported = implode(', ', $this->subprotocols)) { + $this->logger->debug("[subprotocol-negotiation] Requested subprotocols: {$supported}"); + } + } elseif ($message instanceof ResponseInterface) { + // Outgoing Response on Server + if ($selected = $connection->getMeta('subprotocolNegotiation.selected')) { + $message = $message->withHeader('Sec-WebSocket-Protocol', $selected); + $this->logger->info("[subprotocol-negotiation] Selected subprotocol: {$selected}"); + } elseif ($this->require) { + // No matching subprotocol, fail handshake + $message = $message->withStatus(426); + } + } + return $stack->handleHttpOutgoing($message); + } + + /** + * @throws HandshakeException + */ + public function processHttpIncoming(ProcessHttpStack $stack, Connection $connection): MessageInterface + { + $connection->setMeta('subprotocolNegotiation.selected', null); + $message = $stack->handleHttpIncoming(); + + if ($message instanceof ServerRequestInterface) { + // Incoming requests on Server + if ($requested = $message->getHeaderLine('Sec-WebSocket-Protocol')) { + $this->logger->debug("[subprotocol-negotiation] Requested subprotocols: {$requested}"); + } + if ($supported = implode(', ', $this->subprotocols)) { + $this->logger->debug("[subprotocol-negotiation] Supported subprotocols: {$supported}"); + } + foreach ($message->getHeader('Sec-WebSocket-Protocol') as $subprotocol) { + if (in_array($subprotocol, $this->subprotocols)) { + $connection->setMeta('subprotocolNegotiation.selected', $subprotocol); + return $message; + } + } + } elseif ($message instanceof ResponseInterface) { + // Incoming Response on Client + if ($selected = $message->getHeaderLine('Sec-WebSocket-Protocol')) { + $connection->setMeta('subprotocolNegotiation.selected', $selected); + $this->logger->info("[subprotocol-negotiation] Selected subprotocol: {$selected}"); + } elseif ($this->require) { + // No matching subprotocol, close and fail + $connection->close(); + throw new HandshakeException("Could not resolve subprotocol.", $message); + } + } + return $message; + } +} diff --git a/vendor/phrity/websocket/src/Server.php b/vendor/phrity/websocket/src/Server.php new file mode 100644 index 0000000..a1f7c2f --- /dev/null +++ b/vendor/phrity/websocket/src/Server.php @@ -0,0 +1,671 @@ + */ + use ListenerTrait; + use LoggerAwareTrait; + use SendMethodsTrait; + use StringableTrait; + + // Settings + private int $port; + private string $scheme; + /** @var int<0, max>|float $timeout */ + private int|float $timeout = 60; + /** @var int<1, max> $frameSize */ + private int $frameSize = 4096; + private Context $context; + + // Internal resources + private StreamFactory $streamFactory; + private SocketServer|null $server = null; + private StreamCollection|null $streams = null; + private bool $running = false; + /** @var array $connections */ + private array $connections = []; + /** @var array $middlewares */ + private array $middlewares = []; + private int|null $maxConnections = null; + private HttpFactory $httpFactory; + + + /* ---------- Magic methods ------------------------------------------------------------------------------------ */ + + /** + * @param int $port Socket port to listen to + * @param bool $ssl If SSL should be used + * @throws InvalidArgumentException If invalid port provided + */ + public function __construct(int $port = 80, bool $ssl = false) + { + if ($port < 0 || $port > 65535) { + throw new InvalidArgumentException("Invalid port '{$port}' provided"); + } + $this->port = $port; + $this->scheme = $ssl ? 'ssl' : 'tcp'; + $this->initLogger(); + $this->context = new Context(); + $this->httpFactory = new DefaultHttpFactory(); + $this->setStreamFactory(new StreamFactory()); + } + + /** + * Get string representation of instance. + * @return string String representation + */ + public function __toString(): string + { + return $this->stringable('%s', $this->server ? "{$this->scheme}://0.0.0.0:{$this->port}" : 'closed'); + } + + + /* ---------- Configuration ------------------------------------------------------------------------------------ */ + + /** + * Set stream factory to use. + * @param StreamFactory $streamFactory + * @return self + */ + public function setStreamFactory(StreamFactory $streamFactory): self + { + $this->streamFactory = $streamFactory; + return $this; + } + + /** + * Set HTTP factory to use. + * @param HttpFactory $httpFactory + * @return self + */ + public function setHttpFactory(HttpFactory $httpFactory): self + { + $this->httpFactory = $httpFactory; + return $this; + } + + /** + * Set logger. + * @param LoggerInterface $logger Logger implementation + */ + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + foreach ($this->connections as $connection) { + $connection->setLogger($this->logger); + } + } + + /** + * Set timeout. + * @param int<0, max>|float $timeout Timeout in seconds + * @return self + * @throws InvalidArgumentException If invalid timeout provided + */ + public function setTimeout(int|float $timeout): self + { + if ($timeout < 0) { + throw new InvalidArgumentException("Invalid timeout '{$timeout}' provided"); + } + $this->timeout = $timeout; + foreach ($this->connections as $connection) { + $connection->setTimeout($timeout); + } + return $this; + } + + /** + * Get timeout. + * @return int<0, max>|float Timeout in seconds + */ + public function getTimeout(): int|float + { + return $this->timeout; + } + + /** + * Set frame size. + * @param int<1, max> $frameSize Frame size in bytes + * @return self + * @throws InvalidArgumentException If invalid frameSize provided + */ + public function setFrameSize(int $frameSize): self + { + if ($frameSize < 3) { + throw new InvalidArgumentException("Invalid frameSize '{$frameSize}' provided"); + } + $this->frameSize = $frameSize; + foreach ($this->connections as $connection) { + $connection->setFrameSize($frameSize); + } + return $this; + } + + /** + * Get frame size. + * @return int Frame size in bytes + */ + public function getFrameSize(): int + { + return $this->frameSize; + } + + /** + * Get socket port number. + * @return int port + */ + public function getPort(): int + { + return $this->port; + } + + /** + * Get connection scheme. + * @return string scheme + */ + public function getScheme(): string + { + return $this->scheme; + } + + /** + * Get connection scheme. + * @return bool SSL mode + */ + public function isSsl(): bool + { + return $this->scheme === 'ssl'; + } + + /** + * Number of currently connected clients. + * @return int Connection count + */ + public function getConnectionCount(): int + { + return count($this->connections); + } + + /** + * Get currently connected clients. + * @return array Connections + */ + public function getConnections(): array + { + return $this->connections; + } + + /** + * Get currently readable clients. + * @return array Connections + */ + public function getReadableConnections(): array + { + return array_filter($this->connections, function (Connection $connection) { + return $connection->isReadable(); + }); + } + + /** + * Get currently writable clients. + * @return array Connections + */ + public function getWritableConnections(): array + { + return array_filter($this->connections, function (Connection $connection) { + return $connection->isWritable(); + }); + } + + /** + * Set stream context. + * @param Context|array $context Context or options as array + * @see https://www.php.net/manual/en/context.php + * @return self + */ + public function setContext(Context|array $context): self + { + if ($context instanceof Context) { + $this->context = $context; + } else { + $this->context->setOptions($context); + trigger_error('Calling Server.setContext with array is deprecated, use Context class.', E_USER_DEPRECATED); + } + return $this; + } + + /** + * Get current stream context. + * @return Context + */ + public function getContext(): Context + { + return $this->context; + } + + /** + * Add a middleware. + * @param MiddlewareInterface $middleware + * @return self + */ + public function addMiddleware(MiddlewareInterface $middleware): self + { + $this->middlewares[] = $middleware; + foreach ($this->connections as $connection) { + $connection->addMiddleware($middleware); + } + return $this; + } + + /** + * Set maximum number of connections allowed, null means unlimited. + * @param int|null $maxConnections + * @return self + * @throws InvalidArgumentException If number provided + */ + public function setMaxConnections(int|null $maxConnections): self + { + if ($maxConnections !== null && $maxConnections < 1) { + throw new InvalidArgumentException("Invalid maxConnections '{$maxConnections}' provided"); + } + $this->maxConnections = $maxConnections; + return $this; + } + + + /* ---------- Messaging operations ----------------------------------------------------------------------------- */ + + /** + * Send message (broadcast to all connected clients). + * @template T of Message + * @param T $message + * @return T + */ + public function send(Message $message): Message + { + foreach ($this->connections as $connection) { + if ($connection->isWritable()) { + $connection->send($message); + } + } + return $message; + } + + + /* ---------- Listener operations ------------------------------------------------------------------------------ */ + + /** + * Start server listener. + * @throws Throwable On low level error + */ + public function start(int|float|null $timeout = null): void + { + // Create socket server + if (empty($this->server)) { + $this->createSocketServer(); + } + + // Check if running + if ($this->running) { + $this->logger->warning("[server] Server is already running"); + return; + } + $this->running = true; + $this->logger->info("[server] Server is running"); + + /** @var StreamCollection */ + $streams = $this->streams; + + // Run handler + while ($this->running) { + try { + // Clear closed connections + $this->detachUnconnected(); + if (is_null($this->streams)) { + $this->stop(); + return; + } + + // Get streams with readable content + $readables = $this->streams->waitRead($timeout ?? $this->timeout); + foreach ($readables as $key => $readable) { + try { + $connection = null; + // Accept new client connection + if ($readable instanceof SocketServer) { + $this->acceptSocket($readable); + continue; + } + // Read from connection + $connection = $this->connections[$key]; + $message = $connection->pullMessage(); + $this->dispatch($message->getOpcode(), [$this, $connection, $message]); + } catch (MessageLevelInterface $e) { + // Error, but keep connection open + $this->logger->error("[server] {$e->getMessage()}", ['exception' => $e]); + $this->dispatch('error', [$this, $connection, $e]); + } catch (ConnectionLevelInterface $e) { + // Error, disconnect connection + if ($connection) { + $this->streams()->detach($key); + unset($this->connections[$key]); + $connection->disconnect(); + } + $this->logger->error("[server] {$e->getMessage()}", ['exception' => $e]); + $this->dispatch('error', [$this, $connection, $e]); + } catch (CloseException $e) { + // Should close + if ($connection) { + $connection->close($e->getCloseStatus(), $e->getMessage()); + } + $this->logger->error("[server] {$e->getMessage()}", ['exception' => $e]); + $this->dispatch('error', [$this, $connection, $e]); + } + } + foreach ($this->connections as $connection) { + $connection->tick(); + } + $this->dispatch('tick', [$this]); + } catch (ExceptionInterface $e) { + // Low-level error + $this->logger->error("[server] {$e->getMessage()}", ['exception' => $e]); + $this->dispatch('error', [$this, null, $e]); + } catch (Throwable $e) { + // Crash it + $this->logger->error("[server] {$e->getMessage()}", ['exception' => $e]); + $this->disconnect(); + throw $e; + } + gc_collect_cycles(); // Collect garbage + } + } + + /** + * Stop server listener (resumable). + */ + public function stop(): void + { + $this->running = false; + $this->logger->info("[server] Server is stopped"); + } + + /** + * If server is running (accepting connections and messages). + * @return bool + */ + public function isRunning(): bool + { + return $this->running; + } + + + /* ---------- Connection management ---------------------------------------------------------------------------- */ + + /** + * Orderly shutdown of server. + * @param int $closeStatus Default is 1001 "Going away" + */ + public function shutdown(int $closeStatus = 1001): void + { + $this->logger->info('[server] Shutting down'); + if ($this->getConnectionCount() == 0) { + $this->disconnect(); + return; + } + // Store and reset settings, lock new connections, reset listeners + $max = $this->maxConnections; + $this->maxConnections = 0; + $listeners = $this->listeners; + $this->listeners = []; + // Track disconnects + $this->onDisconnect(function () use ($max, $listeners) { + if ($this->getConnectionCount() > 0) { + return; + } + $this->disconnect(); + // Restore settings + $this->maxConnections = $max; + $this->listeners = $listeners; + }); + // Close all current connections, listen to acks + $this->close($closeStatus); + $this->start(); + } + + /** + * Disconnect all connections and stop server. + */ + public function disconnect(): void + { + $this->running = false; + foreach ($this->connections as $connection) { + $connection->disconnect(); + $this->dispatch('disconnect', [$this, $connection]); + } + $this->connections = []; + if ($this->server) { + $this->server->close(); + } + $this->server = $this->streams = null; + $this->logger->info('[server] Server disconnected'); + } + + + /* ---------- Internal helper methods -------------------------------------------------------------------------- */ + + // Create socket server + protected function createSocketServer(): void + { + try { + $uri = new Uri("{$this->scheme}://0.0.0.0:{$this->port}"); + $this->server = $this->streamFactory->createSocketServer($uri, $this->context); + $this->streams = $this->streamFactory->createStreamCollection(); + $this->streams->attach($this->server, '@server'); + $this->logger->info("[server] Starting server on {$uri}."); + } catch (Throwable $e) { + $error = "Server failed to start: {$e->getMessage()}"; + throw new ServerException($error); + } + } + + // Accept connection on socket server + protected function acceptSocket(SocketServer $socket): void + { + if (!is_null($this->maxConnections) && $this->getConnectionCount() >= $this->maxConnections) { + $this->logger->warning("[server] Denied connection, reached max {$this->maxConnections}"); + return; + } + try { + /** @var SocketStream $stream */ + $stream = $socket->accept(); + $name = $stream->getRemoteName(); + $this->streams()->attach($stream, $name); + $connection = new Connection( + $stream, + false, + true, + $this->isSsl(), + $this->httpFactory + ); + } catch (StreamException $e) { + throw new ConnectionFailureException("Server failed to accept: {$e->getMessage()}"); + } + try { + $connection->setLogger($this->logger); + $connection + ->setFrameSize($this->frameSize) + ->setTimeout($this->timeout) + ; + foreach ($this->middlewares as $middleware) { + $connection->addMiddleware($middleware); + } + /** @throws StreamException */ + $request = $this->performHandshake($connection); + $this->connections[$name] = $connection; + $this->logger->info("[server] Accepted connection from {$name}."); + $this->dispatch('handshake', [ + $this, + $connection, + $connection->getHandshakeRequest(), + $connection->getHandshakeResponse(), + ]); + $this->dispatch('connect', [$this, $connection, $request]); + } catch (ExceptionInterface | StreamException $e) { + $connection->disconnect(); + throw new ConnectionFailureException("Server failed to accept: {$e->getMessage()}"); + } + } + + // Detach connections no longer available + protected function detachUnconnected(): void + { + foreach ($this->connections as $key => $connection) { + if (!$connection->isConnected()) { + $this->streams()->detach($key); + unset($this->connections[$key]); + $this->logger->info("[server] Disconnected {$key}."); + $this->dispatch('disconnect', [$this, $connection]); + } + } + } + + // Perform upgrade handshake on new connections. + protected function performHandshake(Connection $connection): ServerRequest + { + $response = $this->httpFactory->createResponse(101); + $exception = null; + + // Read handshake request + /** @var ServerRequest */ + $request = $connection->pullHttp(); + + // Verify handshake request + try { + if ($request->getMethod() != 'GET') { + throw new HandshakeException( + "Handshake request with invalid method: '{$request->getMethod()}'", + $response->withStatus(405) + ); + } + $connectionHeader = trim($request->getHeaderLine('Connection')); + if (!str_contains(strtolower($connectionHeader), 'upgrade')) { + throw new HandshakeException( + "Handshake request with invalid Connection header: '{$connectionHeader}'", + $response->withStatus(426) + ); + } + $upgradeHeader = trim($request->getHeaderLine('Upgrade')); + if (strtolower($upgradeHeader) != 'websocket') { + throw new HandshakeException( + "Handshake request with invalid Upgrade header: '{$upgradeHeader}'", + $response->withStatus(426) + ); + } + $versionHeader = trim($request->getHeaderLine('Sec-WebSocket-Version')); + if ($versionHeader != '13') { + throw new HandshakeException( + "Handshake request with invalid Sec-WebSocket-Version header: '{$versionHeader}'", + $response->withStatus(426)->withHeader('Sec-WebSocket-Version', '13') + ); + } + $keyHeader = trim($request->getHeaderLine('Sec-WebSocket-Key')); + if (empty($keyHeader)) { + throw new HandshakeException( + "Handshake request with invalid Sec-WebSocket-Key header: '{$keyHeader}'", + $response->withStatus(426) + ); + } + if (strlen(base64_decode($keyHeader)) != 16) { + throw new HandshakeException( + "Handshake request with invalid Sec-WebSocket-Key header: '{$keyHeader}'", + $response->withStatus(426) + ); + } + + $responseKey = base64_encode(pack('H*', sha1($keyHeader . Constant::GUID))); + $response = $response + ->withHeader('Upgrade', 'websocket') + ->withHeader('Connection', 'Upgrade') + ->withHeader('Sec-WebSocket-Accept', $responseKey); + } catch (HandshakeException $e) { + $this->logger->warning("[server] {$e->getMessage()}", ['exception' => $e]); + $response = $e->getResponse(); + $exception = $e; + } + + // Respond to handshake + /** @var Response */ + $response = $connection->pushHttp($response); + if ($response->getStatusCode() != 101) { + $exception = new HandshakeException("Invalid status code {$response->getStatusCode()}", $response); + } + + if ($exception) { + throw $exception; + } + + $this->logger->debug("[server] Handshake on {$request->getUri()->getPath()}"); + $connection->setHandshakeRequest($request); + $connection->setHandshakeResponse($response); + + return $request; + } + + protected function streams(): StreamCollection + { + /** @var StreamCollection $streams */ + $streams = $this->streams; + return $streams; + } +} diff --git a/vendor/phrity/websocket/src/Trait/ListenerTrait.php b/vendor/phrity/websocket/src/Trait/ListenerTrait.php new file mode 100644 index 0000000..222c35e --- /dev/null +++ b/vendor/phrity/websocket/src/Trait/ListenerTrait.php @@ -0,0 +1,126 @@ + $listeners */ + private array $listeners = []; + + /** + * @param Closure(T, Connection, RequestInterface|ResponseInterface): void $closure + * @deprecated Will be removed in v4 + */ + public function onConnect(Closure $closure): self + { + $msg = 'onConnect() is deprecated and will be removed in v4. Use onHandshake() instead.'; + trigger_error($msg, E_USER_DEPRECATED); + $this->listeners['connect'] = $closure; + return $this; + } + + /** @param Closure(T, Connection): void $closure */ + public function onDisconnect(Closure $closure): self + { + $this->listeners['disconnect'] = $closure; + return $this; + } + + /** @param Closure(T, Connection, RequestInterface, ResponseInterface): void $closure */ + public function onHandshake(Closure $closure): self + { + $this->listeners['handshake'] = $closure; + return $this; + } + + /** @param Closure(T, Connection, Text): void $closure */ + public function onText(Closure $closure): self + { + $this->listeners['text'] = $closure; + return $this; + } + + /** @param Closure(T, Connection, Binary): void $closure */ + public function onBinary(Closure $closure): self + { + $this->listeners['binary'] = $closure; + return $this; + } + + /** @param Closure(T, Connection, Ping): void $closure */ + public function onPing(Closure $closure): self + { + $this->listeners['ping'] = $closure; + return $this; + } + + /** @param Closure(T, Connection, Pong): void $closure */ + public function onPong(Closure $closure): self + { + $this->listeners['pong'] = $closure; + return $this; + } + + /** @param Closure(T, Connection, Close): void $closure */ + public function onClose(Closure $closure): self + { + $this->listeners['close'] = $closure; + return $this; + } + + /** @param Closure(T, Connection|null, ExceptionInterface): void $closure */ + public function onError(Closure $closure): self + { + $this->listeners['error'] = $closure; + return $this; + } + + /** @param Closure(T): void $closure */ + public function onTick(Closure $closure): self + { + $this->listeners['tick'] = $closure; + return $this; + } + + /** + * @param array{ + * 0: T, + * 1?: Connection|null, + * 2?: Message|Text|Binary|Close|Ping|Pong|RequestInterface|ResponseInterface|ExceptionInterface|null, + * 3?: ResponseInterface|null + * } $args + */ + private function dispatch(string $type, array $args): void + { + if (array_key_exists($type, $this->listeners)) { + $closure = $this->listeners[$type]; + call_user_func_array($closure, $args); + } + } +} diff --git a/vendor/phrity/websocket/src/Trait/LoggerAwareTrait.php b/vendor/phrity/websocket/src/Trait/LoggerAwareTrait.php new file mode 100644 index 0000000..5890b33 --- /dev/null +++ b/vendor/phrity/websocket/src/Trait/LoggerAwareTrait.php @@ -0,0 +1,44 @@ +logger = $logger ?? new NullLogger(); + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + + public function attachLogger(mixed $instance): void + { + if ($instance instanceof LoggerAwareInterface) { + $instance->setLogger($this->logger); + } + } +} diff --git a/vendor/phrity/websocket/src/Trait/OpcodeTrait.php b/vendor/phrity/websocket/src/Trait/OpcodeTrait.php new file mode 100644 index 0000000..aa0926d --- /dev/null +++ b/vendor/phrity/websocket/src/Trait/OpcodeTrait.php @@ -0,0 +1,25 @@ + $opcodes */ + private static array $opcodes = [ + 'continuation' => 0, + 'text' => 1, + 'binary' => 2, + 'close' => 8, + 'ping' => 9, + 'pong' => 10, + ]; +} diff --git a/vendor/phrity/websocket/src/Trait/SendMethodsTrait.php b/vendor/phrity/websocket/src/Trait/SendMethodsTrait.php new file mode 100644 index 0000000..6d216a0 --- /dev/null +++ b/vendor/phrity/websocket/src/Trait/SendMethodsTrait.php @@ -0,0 +1,74 @@ +send(new Text($message)); + } + + /** + * Send binary message. + * @param string $message Content as binary string. + * @return Binary instance + */ + public function binary(string $message): Binary + { + return $this->send(new Binary($message)); + } + + /** + * Send ping. + * @param string $message Optional text as string. + * @return Ping instance + */ + public function ping(string $message = ''): Ping + { + return $this->send(new Ping($message)); + } + + /** + * Send unsolicited pong. + * @param string $message Optional text as string. + * @return Pong instance + */ + public function pong(string $message = ''): Pong + { + return $this->send(new Pong($message)); + } + + /** + * Tell the socket to close. + * @param integer $status http://tools.ietf.org/html/rfc6455#section-7.4 + * @param string $message A closing message, max 125 bytes. + * @return Close instance + */ + public function close(int $status = 1000, string $message = 'ttfn'): Close + { + return $this->send(new Close($status, $message)); + } +} diff --git a/vendor/phrity/websocket/src/Trait/StringableTrait.php b/vendor/phrity/websocket/src/Trait/StringableTrait.php new file mode 100644 index 0000000..ba67f1d --- /dev/null +++ b/vendor/phrity/websocket/src/Trait/StringableTrait.php @@ -0,0 +1,25 @@ +=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/psr/http-factory/src/RequestFactoryInterface.php b/vendor/psr/http-factory/src/RequestFactoryInterface.php new file mode 100644 index 0000000..cb39a08 --- /dev/null +++ b/vendor/psr/http-factory/src/RequestFactoryInterface.php @@ -0,0 +1,18 @@ + `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`. +> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered. + diff --git a/vendor/psr/http-message/docs/PSR7-Usage.md b/vendor/psr/http-message/docs/PSR7-Usage.md new file mode 100644 index 0000000..b6d048a --- /dev/null +++ b/vendor/psr/http-message/docs/PSR7-Usage.md @@ -0,0 +1,159 @@ +### PSR-7 Usage + +All PSR-7 applications comply with these interfaces +They were created to establish a standard between middleware implementations. + +> `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`. +> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered. + + +The following examples will illustrate how basic operations are done in PSR-7. + +##### Examples + + +For this examples to work (at least) a PSR-7 implementation package is required. (eg: zendframework/zend-diactoros, guzzlehttp/psr7, slim/slim, etc) +All PSR-7 implementations should have the same behaviour. + +The following will be assumed: +`$request` is an object of `Psr\Http\Message\RequestInterface` and + +`$response` is an object implementing `Psr\Http\Message\RequestInterface` + + +### Working with HTTP Headers + +#### Adding headers to response: + +```php +$response->withHeader('My-Custom-Header', 'My Custom Message'); +``` + +#### Appending values to headers + +```php +$response->withAddedHeader('My-Custom-Header', 'The second message'); +``` + +#### Checking if header exists: + +```php +$request->hasHeader('My-Custom-Header'); // will return false +$response->hasHeader('My-Custom-Header'); // will return true +``` + +> Note: My-Custom-Header was only added in the Response + +#### Getting comma-separated values from a header (also applies to request) + +```php +// getting value from request headers +$request->getHeaderLine('Content-Type'); // will return: "text/html; charset=UTF-8" +// getting value from response headers +$response->getHeaderLine('My-Custom-Header'); // will return: "My Custom Message; The second message" +``` + +#### Getting array of value from a header (also applies to request) +```php +// getting value from request headers +$request->getHeader('Content-Type'); // will return: ["text/html", "charset=UTF-8"] +// getting value from response headers +$response->getHeader('My-Custom-Header'); // will return: ["My Custom Message", "The second message"] +``` + +#### Removing headers from HTTP Messages +```php +// removing a header from Request, removing deprecated "Content-MD5" header +$request->withoutHeader('Content-MD5'); + +// removing a header from Response +// effect: the browser won't know the size of the stream +// the browser will download the stream till it ends +$response->withoutHeader('Content-Length'); +``` + +### Working with HTTP Message Body + +When working with the PSR-7 there are two methods of implementation: +#### 1. Getting the body separately + +> This method makes the body handling easier to understand and is useful when repeatedly calling body methods. (You only call `getBody()` once). Using this method mistakes like `$response->write()` are also prevented. + +```php +$body = $response->getBody(); +// operations on body, eg. read, write, seek +// ... +// replacing the old body +$response->withBody($body); +// this last statement is optional as we working with objects +// in this case the "new" body is same with the "old" one +// the $body variable has the same value as the one in $request, only the reference is passed +``` + +#### 2. Working directly on response + +> This method is useful when only performing few operations as the `$request->getBody()` statement fragment is required + +```php +$response->getBody()->write('hello'); +``` + +### Getting the body contents + +The following snippet gets the contents of a stream contents. +> Note: Streams must be rewinded, if content was written into streams, it will be ignored when calling `getContents()` because the stream pointer is set to the last character, which is `\0` - meaning end of stream. +```php +$body = $response->getBody(); +$body->rewind(); // or $body->seek(0); +$bodyText = $body->getContents(); +``` +> Note: If `$body->seek(1)` is called before `$body->getContents()`, the first character will be ommited as the starting pointer is set to `1`, not `0`. This is why using `$body->rewind()` is recommended. + +### Append to body + +```php +$response->getBody()->write('Hello'); // writing directly +$body = $request->getBody(); // which is a `StreamInterface` +$body->write('xxxxx'); +``` + +### Prepend to body +Prepending is different when it comes to streams. The content must be copied before writing the content to be prepended. +The following example will explain the behaviour of streams. + +```php +// assuming our response is initially empty +$body = $repsonse->getBody(); +// writing the string "abcd" +$body->write('abcd'); + +// seeking to start of stream +$body->seek(0); +// writing 'ef' +$body->write('ef'); // at this point the stream contains "efcd" +``` + +#### Prepending by rewriting separately + +```php +// assuming our response body stream only contains: "abcd" +$body = $response->getBody(); +$body->rewind(); +$contents = $body->getContents(); // abcd +// seeking the stream to beginning +$body->rewind(); +$body->write('ef'); // stream contains "efcd" +$body->write($contents); // stream contains "efabcd" +``` + +> Note: `getContents()` seeks the stream while reading it, therefore if the second `rewind()` method call was not present the stream would have resulted in `abcdefabcd` because the `write()` method appends to stream if not preceeded by `rewind()` or `seek(0)`. + +#### Prepending by using contents as a string +```php +$body = $response->getBody(); +$body->rewind(); +$contents = $body->getContents(); // efabcd +$contents = 'ef'.$contents; +$body->rewind(); +$body->write($contents); +``` diff --git a/vendor/psr/http-message/src/MessageInterface.php b/vendor/psr/http-message/src/MessageInterface.php new file mode 100644 index 0000000..a83c985 --- /dev/null +++ b/vendor/psr/http-message/src/MessageInterface.php @@ -0,0 +1,187 @@ +getHeaders() as $name => $values) { + * echo $name . ": " . implode(", ", $values); + * } + * + * // Emit headers iteratively: + * foreach ($message->getHeaders() as $name => $values) { + * foreach ($values as $value) { + * header(sprintf('%s: %s', $name, $value), false); + * } + * } + * + * While header names are not case-sensitive, getHeaders() will preserve the + * exact case in which headers were originally specified. + * + * @return string[][] Returns an associative array of the message's headers. Each + * key MUST be a header name, and each value MUST be an array of strings + * for that header. + */ + public function getHeaders(): array; + + /** + * Checks if a header exists by the given case-insensitive name. + * + * @param string $name Case-insensitive header field name. + * @return bool Returns true if any header names match the given header + * name using a case-insensitive string comparison. Returns false if + * no matching header name is found in the message. + */ + public function hasHeader(string $name): bool; + + /** + * Retrieves a message header value by the given case-insensitive name. + * + * This method returns an array of all the header values of the given + * case-insensitive header name. + * + * If the header does not appear in the message, this method MUST return an + * empty array. + * + * @param string $name Case-insensitive header field name. + * @return string[] An array of string values as provided for the given + * header. If the header does not appear in the message, this method MUST + * return an empty array. + */ + public function getHeader(string $name): array; + + /** + * Retrieves a comma-separated string of the values for a single header. + * + * This method returns all of the header values of the given + * case-insensitive header name as a string concatenated together using + * a comma. + * + * NOTE: Not all header values may be appropriately represented using + * comma concatenation. For such headers, use getHeader() instead + * and supply your own delimiter when concatenating. + * + * If the header does not appear in the message, this method MUST return + * an empty string. + * + * @param string $name Case-insensitive header field name. + * @return string A string of values as provided for the given header + * concatenated together using a comma. If the header does not appear in + * the message, this method MUST return an empty string. + */ + public function getHeaderLine(string $name): string; + + /** + * Return an instance with the provided value replacing the specified header. + * + * While header names are case-insensitive, the casing of the header will + * be preserved by this function, and returned from getHeaders(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new and/or updated header and value. + * + * @param string $name Case-insensitive header field name. + * @param string|string[] $value Header value(s). + * @return static + * @throws \InvalidArgumentException for invalid header names or values. + */ + public function withHeader(string $name, $value): MessageInterface; + + /** + * Return an instance with the specified header appended with the given value. + * + * Existing values for the specified header will be maintained. The new + * value(s) will be appended to the existing list. If the header did not + * exist previously, it will be added. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new header and/or value. + * + * @param string $name Case-insensitive header field name to add. + * @param string|string[] $value Header value(s). + * @return static + * @throws \InvalidArgumentException for invalid header names or values. + */ + public function withAddedHeader(string $name, $value): MessageInterface; + + /** + * Return an instance without the specified header. + * + * Header resolution MUST be done without case-sensitivity. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the named header. + * + * @param string $name Case-insensitive header field name to remove. + * @return static + */ + public function withoutHeader(string $name): MessageInterface; + + /** + * Gets the body of the message. + * + * @return StreamInterface Returns the body as a stream. + */ + public function getBody(): StreamInterface; + + /** + * Return an instance with the specified message body. + * + * The body MUST be a StreamInterface object. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new body stream. + * + * @param StreamInterface $body Body. + * @return static + * @throws \InvalidArgumentException When the body is not valid. + */ + public function withBody(StreamInterface $body): MessageInterface; +} diff --git a/vendor/psr/http-message/src/RequestInterface.php b/vendor/psr/http-message/src/RequestInterface.php new file mode 100644 index 0000000..33f85e5 --- /dev/null +++ b/vendor/psr/http-message/src/RequestInterface.php @@ -0,0 +1,130 @@ +getQuery()` + * or from the `QUERY_STRING` server param. + * + * @return array + */ + public function getQueryParams(): array; + + /** + * Return an instance with the specified query string arguments. + * + * These values SHOULD remain immutable over the course of the incoming + * request. They MAY be injected during instantiation, such as from PHP's + * $_GET superglobal, or MAY be derived from some other value such as the + * URI. In cases where the arguments are parsed from the URI, the data + * MUST be compatible with what PHP's parse_str() would return for + * purposes of how duplicate query parameters are handled, and how nested + * sets are handled. + * + * Setting query string arguments MUST NOT change the URI stored by the + * request, nor the values in the server params. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated query string arguments. + * + * @param array $query Array of query string arguments, typically from + * $_GET. + * @return static + */ + public function withQueryParams(array $query): ServerRequestInterface; + + /** + * Retrieve normalized file upload data. + * + * This method returns upload metadata in a normalized tree, with each leaf + * an instance of Psr\Http\Message\UploadedFileInterface. + * + * These values MAY be prepared from $_FILES or the message body during + * instantiation, or MAY be injected via withUploadedFiles(). + * + * @return array An array tree of UploadedFileInterface instances; an empty + * array MUST be returned if no data is present. + */ + public function getUploadedFiles(): array; + + /** + * Create a new instance with the specified uploaded files. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated body parameters. + * + * @param array $uploadedFiles An array tree of UploadedFileInterface instances. + * @return static + * @throws \InvalidArgumentException if an invalid structure is provided. + */ + public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface; + + /** + * Retrieve any parameters provided in the request body. + * + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, this method MUST + * return the contents of $_POST. + * + * Otherwise, this method may return any results of deserializing + * the request body content; as parsing returns structured content, the + * potential types MUST be arrays or objects only. A null value indicates + * the absence of body content. + * + * @return null|array|object The deserialized body parameters, if any. + * These will typically be an array or object. + */ + public function getParsedBody(); + + /** + * Return an instance with the specified body parameters. + * + * These MAY be injected during instantiation. + * + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, use this method + * ONLY to inject the contents of $_POST. + * + * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of + * deserializing the request body content. Deserialization/parsing returns + * structured data, and, as such, this method ONLY accepts arrays or objects, + * or a null value if nothing was available to parse. + * + * As an example, if content negotiation determines that the request data + * is a JSON payload, this method could be used to create a request + * instance with the deserialized parameters. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated body parameters. + * + * @param null|array|object $data The deserialized body data. This will + * typically be in an array or object. + * @return static + * @throws \InvalidArgumentException if an unsupported argument type is + * provided. + */ + public function withParsedBody($data): ServerRequestInterface; + + /** + * Retrieve attributes derived from the request. + * + * The request "attributes" may be used to allow injection of any + * parameters derived from the request: e.g., the results of path + * match operations; the results of decrypting cookies; the results of + * deserializing non-form-encoded message bodies; etc. Attributes + * will be application and request specific, and CAN be mutable. + * + * @return array Attributes derived from the request. + */ + public function getAttributes(): array; + + /** + * Retrieve a single derived request attribute. + * + * Retrieves a single derived request attribute as described in + * getAttributes(). If the attribute has not been previously set, returns + * the default value as provided. + * + * This method obviates the need for a hasAttribute() method, as it allows + * specifying a default value to return if the attribute is not found. + * + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $default Default value to return if the attribute does not exist. + * @return mixed + */ + public function getAttribute(string $name, $default = null); + + /** + * Return an instance with the specified derived request attribute. + * + * This method allows setting a single derived request attribute as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated attribute. + * + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $value The value of the attribute. + * @return static + */ + public function withAttribute(string $name, $value): ServerRequestInterface; + + /** + * Return an instance that removes the specified derived request attribute. + * + * This method allows removing a single derived request attribute as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the attribute. + * + * @see getAttributes() + * @param string $name The attribute name. + * @return static + */ + public function withoutAttribute(string $name): ServerRequestInterface; +} diff --git a/vendor/psr/http-message/src/StreamInterface.php b/vendor/psr/http-message/src/StreamInterface.php new file mode 100644 index 0000000..a62aabb --- /dev/null +++ b/vendor/psr/http-message/src/StreamInterface.php @@ -0,0 +1,158 @@ + + * [user-info@]host[:port] + * + * + * If the port component is not set or is the standard port for the current + * scheme, it SHOULD NOT be included. + * + * @see https://tools.ietf.org/html/rfc3986#section-3.2 + * @return string The URI authority, in "[user-info@]host[:port]" format. + */ + public function getAuthority(): string; + + /** + * Retrieve the user information component of the URI. + * + * If no user information is present, this method MUST return an empty + * string. + * + * If a user is present in the URI, this will return that value; + * additionally, if the password is also present, it will be appended to the + * user value, with a colon (":") separating the values. + * + * The trailing "@" character is not part of the user information and MUST + * NOT be added. + * + * @return string The URI user information, in "username[:password]" format. + */ + public function getUserInfo(): string; + + /** + * Retrieve the host component of the URI. + * + * If no host is present, this method MUST return an empty string. + * + * The value returned MUST be normalized to lowercase, per RFC 3986 + * Section 3.2.2. + * + * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 + * @return string The URI host. + */ + public function getHost(): string; + + /** + * Retrieve the port component of the URI. + * + * If a port is present, and it is non-standard for the current scheme, + * this method MUST return it as an integer. If the port is the standard port + * used with the current scheme, this method SHOULD return null. + * + * If no port is present, and no scheme is present, this method MUST return + * a null value. + * + * If no port is present, but a scheme is present, this method MAY return + * the standard port for that scheme, but SHOULD return null. + * + * @return null|int The URI port. + */ + public function getPort(): ?int; + + /** + * Retrieve the path component of the URI. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * Normally, the empty path "" and absolute path "/" are considered equal as + * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically + * do this normalization because in contexts with a trimmed base path, e.g. + * the front controller, this difference becomes significant. It's the task + * of the user to handle both "" and "/". + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.3. + * + * As an example, if the value should include a slash ("/") not intended as + * delimiter between path segments, that value MUST be passed in encoded + * form (e.g., "%2F") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.3 + * @return string The URI path. + */ + public function getPath(): string; + + /** + * Retrieve the query string of the URI. + * + * If no query string is present, this method MUST return an empty string. + * + * The leading "?" character is not part of the query and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.4. + * + * As an example, if a value in a key/value pair of the query string should + * include an ampersand ("&") not intended as a delimiter between values, + * that value MUST be passed in encoded form (e.g., "%26") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.4 + * @return string The URI query string. + */ + public function getQuery(): string; + + /** + * Retrieve the fragment component of the URI. + * + * If no fragment is present, this method MUST return an empty string. + * + * The leading "#" character is not part of the fragment and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.5. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.5 + * @return string The URI fragment. + */ + public function getFragment(): string; + + /** + * Return an instance with the specified scheme. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified scheme. + * + * Implementations MUST support the schemes "http" and "https" case + * insensitively, and MAY accommodate other schemes if required. + * + * An empty scheme is equivalent to removing the scheme. + * + * @param string $scheme The scheme to use with the new instance. + * @return static A new instance with the specified scheme. + * @throws \InvalidArgumentException for invalid or unsupported schemes. + */ + public function withScheme(string $scheme): UriInterface; + + /** + * Return an instance with the specified user information. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified user information. + * + * Password is optional, but the user information MUST include the + * user; an empty string for the user is equivalent to removing user + * information. + * + * @param string $user The user name to use for authority. + * @param null|string $password The password associated with $user. + * @return static A new instance with the specified user information. + */ + public function withUserInfo(string $user, ?string $password = null): UriInterface; + + /** + * Return an instance with the specified host. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified host. + * + * An empty host value is equivalent to removing the host. + * + * @param string $host The hostname to use with the new instance. + * @return static A new instance with the specified host. + * @throws \InvalidArgumentException for invalid hostnames. + */ + public function withHost(string $host): UriInterface; + + /** + * Return an instance with the specified port. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified port. + * + * Implementations MUST raise an exception for ports outside the + * established TCP and UDP port ranges. + * + * A null value provided for the port is equivalent to removing the port + * information. + * + * @param null|int $port The port to use with the new instance; a null value + * removes the port information. + * @return static A new instance with the specified port. + * @throws \InvalidArgumentException for invalid ports. + */ + public function withPort(?int $port): UriInterface; + + /** + * Return an instance with the specified path. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified path. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * If the path is intended to be domain-relative rather than path relative then + * it must begin with a slash ("/"). Paths not starting with a slash ("/") + * are assumed to be relative to some base path known to the application or + * consumer. + * + * Users can provide both encoded and decoded path characters. + * Implementations ensure the correct encoding as outlined in getPath(). + * + * @param string $path The path to use with the new instance. + * @return static A new instance with the specified path. + * @throws \InvalidArgumentException for invalid paths. + */ + public function withPath(string $path): UriInterface; + + /** + * Return an instance with the specified query string. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified query string. + * + * Users can provide both encoded and decoded query characters. + * Implementations ensure the correct encoding as outlined in getQuery(). + * + * An empty query string value is equivalent to removing the query string. + * + * @param string $query The query string to use with the new instance. + * @return static A new instance with the specified query string. + * @throws \InvalidArgumentException for invalid query strings. + */ + public function withQuery(string $query): UriInterface; + + /** + * Return an instance with the specified URI fragment. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified URI fragment. + * + * Users can provide both encoded and decoded fragment characters. + * Implementations ensure the correct encoding as outlined in getFragment(). + * + * An empty fragment value is equivalent to removing the fragment. + * + * @param string $fragment The fragment to use with the new instance. + * @return static A new instance with the specified fragment. + */ + public function withFragment(string $fragment): UriInterface; + + /** + * Return the string representation as a URI reference. + * + * Depending on which components of the URI are present, the resulting + * string is either a full URI or relative reference according to RFC 3986, + * Section 4.1. The method concatenates the various components of the URI, + * using the appropriate delimiters: + * + * - If a scheme is present, it MUST be suffixed by ":". + * - If an authority is present, it MUST be prefixed by "//". + * - The path can be concatenated without delimiters. But there are two + * cases where the path has to be adjusted to make the URI reference + * valid as PHP does not allow to throw an exception in __toString(): + * - If the path is rootless and an authority is present, the path MUST + * be prefixed by "/". + * - If the path is starting with more than one "/" and no authority is + * present, the starting slashes MUST be reduced to one. + * - If a query is present, it MUST be prefixed by "?". + * - If a fragment is present, it MUST be prefixed by "#". + * + * @see http://tools.ietf.org/html/rfc3986#section-4.1 + * @return string + */ + public function __toString(): string; +} diff --git a/vendor/psr/log/Psr/Log/AbstractLogger.php b/vendor/psr/log/Psr/Log/AbstractLogger.php deleted file mode 100644 index e02f9da..0000000 --- a/vendor/psr/log/Psr/Log/AbstractLogger.php +++ /dev/null @@ -1,128 +0,0 @@ -log(LogLevel::EMERGENCY, $message, $context); - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function alert($message, array $context = array()) - { - $this->log(LogLevel::ALERT, $message, $context); - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function critical($message, array $context = array()) - { - $this->log(LogLevel::CRITICAL, $message, $context); - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function error($message, array $context = array()) - { - $this->log(LogLevel::ERROR, $message, $context); - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function warning($message, array $context = array()) - { - $this->log(LogLevel::WARNING, $message, $context); - } - - /** - * Normal but significant events. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function notice($message, array $context = array()) - { - $this->log(LogLevel::NOTICE, $message, $context); - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function info($message, array $context = array()) - { - $this->log(LogLevel::INFO, $message, $context); - } - - /** - * Detailed debug information. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function debug($message, array $context = array()) - { - $this->log(LogLevel::DEBUG, $message, $context); - } -} diff --git a/vendor/psr/log/Psr/Log/Test/DummyTest.php b/vendor/psr/log/Psr/Log/Test/DummyTest.php deleted file mode 100644 index 9638c11..0000000 --- a/vendor/psr/log/Psr/Log/Test/DummyTest.php +++ /dev/null @@ -1,18 +0,0 @@ - ". - * - * Example ->error('Foo') would yield "error Foo". - * - * @return string[] - */ - abstract public function getLogs(); - - public function testImplements() - { - $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); - } - - /** - * @dataProvider provideLevelsAndMessages - */ - public function testLogsAtAllLevels($level, $message) - { - $logger = $this->getLogger(); - $logger->{$level}($message, array('user' => 'Bob')); - $logger->log($level, $message, array('user' => 'Bob')); - - $expected = array( - $level.' message of level '.$level.' with context: Bob', - $level.' message of level '.$level.' with context: Bob', - ); - $this->assertEquals($expected, $this->getLogs()); - } - - public function provideLevelsAndMessages() - { - return array( - LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), - LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), - LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), - LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), - LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), - LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), - LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), - LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), - ); - } - - /** - * @expectedException \Psr\Log\InvalidArgumentException - */ - public function testThrowsOnInvalidLevel() - { - $logger = $this->getLogger(); - $logger->log('invalid level', 'Foo'); - } - - public function testContextReplacement() - { - $logger = $this->getLogger(); - $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); - - $expected = array('info {Message {nothing} Bob Bar a}'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testObjectCastToString() - { - if (method_exists($this, 'createPartialMock')) { - $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); - } else { - $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); - } - $dummy->expects($this->once()) - ->method('__toString') - ->will($this->returnValue('DUMMY')); - - $this->getLogger()->warning($dummy); - - $expected = array('warning DUMMY'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testContextCanContainAnything() - { - $closed = fopen('php://memory', 'r'); - fclose($closed); - - $context = array( - 'bool' => true, - 'null' => null, - 'string' => 'Foo', - 'int' => 0, - 'float' => 0.5, - 'nested' => array('with object' => new DummyTest), - 'object' => new \DateTime, - 'resource' => fopen('php://memory', 'r'), - 'closed' => $closed, - ); - - $this->getLogger()->warning('Crazy context data', $context); - - $expected = array('warning Crazy context data'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testContextExceptionKeyCanBeExceptionOrOtherValues() - { - $logger = $this->getLogger(); - $logger->warning('Random message', array('exception' => 'oops')); - $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); - - $expected = array( - 'warning Random message', - 'critical Uncaught Exception!' - ); - $this->assertEquals($expected, $this->getLogs()); - } -} diff --git a/vendor/psr/log/Psr/Log/Test/TestLogger.php b/vendor/psr/log/Psr/Log/Test/TestLogger.php deleted file mode 100644 index 1be3230..0000000 --- a/vendor/psr/log/Psr/Log/Test/TestLogger.php +++ /dev/null @@ -1,147 +0,0 @@ - $level, - 'message' => $message, - 'context' => $context, - ]; - - $this->recordsByLevel[$record['level']][] = $record; - $this->records[] = $record; - } - - public function hasRecords($level) - { - return isset($this->recordsByLevel[$level]); - } - - public function hasRecord($record, $level) - { - if (is_string($record)) { - $record = ['message' => $record]; - } - return $this->hasRecordThatPasses(function ($rec) use ($record) { - if ($rec['message'] !== $record['message']) { - return false; - } - if (isset($record['context']) && $rec['context'] !== $record['context']) { - return false; - } - return true; - }, $level); - } - - public function hasRecordThatContains($message, $level) - { - return $this->hasRecordThatPasses(function ($rec) use ($message) { - return strpos($rec['message'], $message) !== false; - }, $level); - } - - public function hasRecordThatMatches($regex, $level) - { - return $this->hasRecordThatPasses(function ($rec) use ($regex) { - return preg_match($regex, $rec['message']) > 0; - }, $level); - } - - public function hasRecordThatPasses(callable $predicate, $level) - { - if (!isset($this->recordsByLevel[$level])) { - return false; - } - foreach ($this->recordsByLevel[$level] as $i => $rec) { - if (call_user_func($predicate, $rec, $i)) { - return true; - } - } - return false; - } - - public function __call($method, $args) - { - if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { - $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; - $level = strtolower($matches[2]); - if (method_exists($this, $genericMethod)) { - $args[] = $level; - return call_user_func_array([$this, $genericMethod], $args); - } - } - throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); - } - - public function reset() - { - $this->records = []; - $this->recordsByLevel = []; - } -} diff --git a/vendor/psr/log/composer.json b/vendor/psr/log/composer.json index ca05695..879fc6f 100644 --- a/vendor/psr/log/composer.json +++ b/vendor/psr/log/composer.json @@ -11,16 +11,16 @@ } ], "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } } } diff --git a/vendor/psr/log/src/AbstractLogger.php b/vendor/psr/log/src/AbstractLogger.php new file mode 100644 index 0000000..d60a091 --- /dev/null +++ b/vendor/psr/log/src/AbstractLogger.php @@ -0,0 +1,15 @@ +logger = $logger; } diff --git a/vendor/psr/log/Psr/Log/LoggerInterface.php b/vendor/psr/log/src/LoggerInterface.php similarity index 63% rename from vendor/psr/log/Psr/Log/LoggerInterface.php rename to vendor/psr/log/src/LoggerInterface.php index 2206cfd..cb4cf64 100644 --- a/vendor/psr/log/Psr/Log/LoggerInterface.php +++ b/vendor/psr/log/src/LoggerInterface.php @@ -22,12 +22,9 @@ interface LoggerInterface /** * System is unusable. * - * @param string $message * @param mixed[] $context - * - * @return void */ - public function emergency($message, array $context = array()); + public function emergency(string|\Stringable $message, array $context = []): void; /** * Action must be taken immediately. @@ -35,35 +32,26 @@ public function emergency($message, array $context = array()); * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * - * @param string $message * @param mixed[] $context - * - * @return void */ - public function alert($message, array $context = array()); + public function alert(string|\Stringable $message, array $context = []): void; /** * Critical conditions. * * Example: Application component unavailable, unexpected exception. * - * @param string $message * @param mixed[] $context - * - * @return void */ - public function critical($message, array $context = array()); + public function critical(string|\Stringable $message, array $context = []): void; /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * - * @param string $message * @param mixed[] $context - * - * @return void */ - public function error($message, array $context = array()); + public function error(string|\Stringable $message, array $context = []): void; /** * Exceptional occurrences that are not errors. @@ -71,55 +59,40 @@ public function error($message, array $context = array()); * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * - * @param string $message * @param mixed[] $context - * - * @return void */ - public function warning($message, array $context = array()); + public function warning(string|\Stringable $message, array $context = []): void; /** * Normal but significant events. * - * @param string $message * @param mixed[] $context - * - * @return void */ - public function notice($message, array $context = array()); + public function notice(string|\Stringable $message, array $context = []): void; /** * Interesting events. * * Example: User logs in, SQL logs. * - * @param string $message * @param mixed[] $context - * - * @return void */ - public function info($message, array $context = array()); + public function info(string|\Stringable $message, array $context = []): void; /** * Detailed debug information. * - * @param string $message * @param mixed[] $context - * - * @return void */ - public function debug($message, array $context = array()); + public function debug(string|\Stringable $message, array $context = []): void; /** * Logs with an arbitrary level. * - * @param mixed $level - * @param string $message + * @param mixed $level * @param mixed[] $context * - * @return void - * * @throws \Psr\Log\InvalidArgumentException */ - public function log($level, $message, array $context = array()); + public function log($level, string|\Stringable $message, array $context = []): void; } diff --git a/vendor/psr/log/Psr/Log/LoggerTrait.php b/vendor/psr/log/src/LoggerTrait.php similarity index 57% rename from vendor/psr/log/Psr/Log/LoggerTrait.php rename to vendor/psr/log/src/LoggerTrait.php index e392fef..a5d9980 100644 --- a/vendor/psr/log/Psr/Log/LoggerTrait.php +++ b/vendor/psr/log/src/LoggerTrait.php @@ -14,13 +14,8 @@ trait LoggerTrait { /** * System is unusable. - * - * @param string $message - * @param array $context - * - * @return void */ - public function emergency($message, array $context = array()) + public function emergency(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::EMERGENCY, $message, $context); } @@ -30,13 +25,8 @@ public function emergency($message, array $context = array()) * * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param array $context - * - * @return void */ - public function alert($message, array $context = array()) + public function alert(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::ALERT, $message, $context); } @@ -45,13 +35,8 @@ public function alert($message, array $context = array()) * Critical conditions. * * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param array $context - * - * @return void */ - public function critical($message, array $context = array()) + public function critical(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::CRITICAL, $message, $context); } @@ -59,13 +44,8 @@ public function critical($message, array $context = array()) /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. - * - * @param string $message - * @param array $context - * - * @return void */ - public function error($message, array $context = array()) + public function error(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::ERROR, $message, $context); } @@ -75,26 +55,16 @@ public function error($message, array $context = array()) * * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. - * - * @param string $message - * @param array $context - * - * @return void */ - public function warning($message, array $context = array()) + public function warning(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::WARNING, $message, $context); } /** * Normal but significant events. - * - * @param string $message - * @param array $context - * - * @return void */ - public function notice($message, array $context = array()) + public function notice(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::NOTICE, $message, $context); } @@ -103,26 +73,16 @@ public function notice($message, array $context = array()) * Interesting events. * * Example: User logs in, SQL logs. - * - * @param string $message - * @param array $context - * - * @return void */ - public function info($message, array $context = array()) + public function info(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::INFO, $message, $context); } /** * Detailed debug information. - * - * @param string $message - * @param array $context - * - * @return void */ - public function debug($message, array $context = array()) + public function debug(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::DEBUG, $message, $context); } @@ -130,13 +90,9 @@ public function debug($message, array $context = array()) /** * Logs with an arbitrary level. * - * @param mixed $level - * @param string $message - * @param array $context - * - * @return void + * @param mixed $level * * @throws \Psr\Log\InvalidArgumentException */ - abstract public function log($level, $message, array $context = array()); + abstract public function log($level, string|\Stringable $message, array $context = []): void; } diff --git a/vendor/psr/log/Psr/Log/NullLogger.php b/vendor/psr/log/src/NullLogger.php similarity index 74% rename from vendor/psr/log/Psr/Log/NullLogger.php rename to vendor/psr/log/src/NullLogger.php index c8f7293..de0561e 100644 --- a/vendor/psr/log/Psr/Log/NullLogger.php +++ b/vendor/psr/log/src/NullLogger.php @@ -15,15 +15,11 @@ class NullLogger extends AbstractLogger /** * Logs with an arbitrary level. * - * @param mixed $level - * @param string $message - * @param array $context - * - * @return void + * @param mixed[] $context * * @throws \Psr\Log\InvalidArgumentException */ - public function log($level, $message, array $context = array()) + public function log($level, string|\Stringable $message, array $context = []): void { // noop } diff --git a/vendor/setasign/fpdi/README.md b/vendor/setasign/fpdi/README.md deleted file mode 100644 index e27d205..0000000 --- a/vendor/setasign/fpdi/README.md +++ /dev/null @@ -1,131 +0,0 @@ -FPDI - Free PDF Document Importer -================================= - -[![Latest Stable Version](https://poser.pugx.org/setasign/fpdi/v/stable.svg)](https://packagist.org/packages/setasign/fpdi) -[![Total Downloads](https://poser.pugx.org/setasign/fpdi/downloads.svg)](https://packagist.org/packages/setasign/fpdi) -[![Latest Unstable Version](https://poser.pugx.org/setasign/fpdi/v/unstable.svg)](https://packagist.org/packages/setasign/fpdi) -[![License](https://poser.pugx.org/setasign/fpdi/license.svg)](https://packagist.org/packages/setasign/fpdi) - -:heavy_exclamation_mark: This document refers to FPDI 2. Version 1 is deprecated and development is discontinued. :heavy_exclamation_mark: - -FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF -documents and use them as templates in [FPDF](http://www.fpdf.org), which was developed by Olivier Plathey. Apart -from a copy of [FPDF](http://www.fpdf.org), FPDI does not require any special PHP extensions. - -FPDI can also be used as an extension for [TCPDF](https://github.com/tecnickcom/TCPDF) or -[tFPDF](http://fpdf.org/en/script/script92.php), too. - -## Installation with [Composer](https://packagist.org/packages/setasign/fpdi) - -Because FPDI can be used with FPDF, TCPDF or tFPDF we haven't added a fixed dependency in the main -composer.json file. You need to add the dependency to the PDF generation library of your choice -yourself. - -To use FPDI with FPDF include following in your composer.json file: - -```json -{ - "require": { - "setasign/fpdf": "1.8.*", - "setasign/fpdi": "^2.5" - } -} -``` - -If you want to use TCPDF, you have to update your composer.json to: - -```json -{ - "require": { - "tecnickcom/tcpdf": "6.6.*", - "setasign/fpdi": "^2.5" - } -} -``` - -If you want to use tFPDF, you have to update your composer.json to: - -```json -{ - "require": { - "setasign/tfpdf": "1.33.*", - "setasign/fpdi": "^2.3" - } -} -``` - -## Manual Installation - -If you do not use composer, just require the autoload.php in the /src folder: - -```php -require_once('src/autoload.php'); -``` - -If you have a PSR-4 autoloader implemented, just register the src path as follows: -```php -$loader = new \Example\Psr4AutoloaderClass; -$loader->register(); -$loader->addNamespace('setasign\Fpdi', 'path/to/src/'); -``` - -## Changes to Version 1 - -Version 2 is a complete rewrite from scratch of FPDI which comes with: -- Namespaced code -- Clean and up-to-date code base and style -- PSR-4 compatible autoloading -- Performance improvements by up to 100% -- Less memory consumption -- Native support for reading PDFs from strings or stream-resources -- Support for documents with "invalid" data before their file-header -- Optimized page tree resolving -- Usage of individual exceptions -- Several test types (unit, functional and visual tests) - -We tried to keep the main methods and logical workflow the same as in version 1 but please -notice that there were incompatible changes which you should consider when updating to -version 2: -- You need to load the code using the `src/autoload.php` file instead of `classes/FPDI.php`. -- The classes and traits are namespaced now: `setasign\Fpdi` -- Page boundaries beginning with a slash, such as `/MediaBox`, are not supported anymore. Remove - the slash or use a constant of `PdfReader\PageBoundaries`. -- The parameters $x, $y, $width and $height of the `useTemplate()` or `getTemplateSize()` - method have more logical correct default values now. Passing `0` as width or height will - result in an `InvalidArgumentException` now. -- The return value of `getTemplateSize()` had changed to an array with more speaking keys - and reusability: Use `width` instead of `w` and `height` instead of `h`. -- If you want to use **FPDI with TCPDF** you need to refactor your code to use the class `Tcpdf\Fpdi` -(since 2.1; before it was `TcpdfFpdi`) instead of `FPDI`. - -## Example and Documentation - -A simple example, that imports a single page and places this onto a new created page: - -```php -AddPage(); -// set the source file -$pdf->setSourceFile("Fantastic-Speaker.pdf"); -// import page 1 -$tplId = $pdf->importPage(1); -// use the imported page and place it at point 10,10 with a width of 100 mm -$pdf->useTemplate($tplId, 10, 10, 100); - -$pdf->Output(); -``` - -A full end-user documentation and API reference is available [here](https://manuals.setasign.com/fpdi-manual/). diff --git a/vendor/setasign/fpdi/SECURITY.md b/vendor/setasign/fpdi/SECURITY.md deleted file mode 100644 index da9c516..0000000 --- a/vendor/setasign/fpdi/SECURITY.md +++ /dev/null @@ -1,5 +0,0 @@ -## Security contact information - -To report a security vulnerability, please use the -[Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure. diff --git a/vendor/setasign/fpdi/composer.json b/vendor/setasign/fpdi/composer.json deleted file mode 100644 index c0eb4df..0000000 --- a/vendor/setasign/fpdi/composer.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "setasign/fpdi", - "homepage": "https://www.setasign.com/fpdi", - "description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", - "type": "library", - "keywords": [ - "pdf", - "fpdi", - "fpdf" - ], - "license": "MIT", - "autoload": { - "psr-4": { - "setasign\\Fpdi\\": "src/" - } - }, - "require": { - "php": "^5.6 || ^7.0 || ^8.0", - "ext-zlib": "*" - }, - "conflict": { - "setasign/tfpdf": "<1.31" - }, - "authors": [ - { - "name": "Jan Slabon", - "email": "jan.slabon@setasign.com", - "homepage": "https://www.setasign.com" - }, - { - "name": "Maximilian Kresse", - "email": "maximilian.kresse@setasign.com", - "homepage": "https://www.setasign.com" - } - ], - "suggest": { - "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured." - }, - "require-dev": { - "phpunit/phpunit": "~5.7", - "setasign/fpdf": "~1.8.6", - "tecnickcom/tcpdf": "~6.2", - "setasign/tfpdf": "~1.33", - "squizlabs/php_codesniffer": "^3.5" - }, - "autoload-dev": { - "psr-4": { - "setasign\\Fpdi\\": "tests/" - } - } -} diff --git a/vendor/setasign/fpdi/src/FpdfTpl.php b/vendor/setasign/fpdi/src/FpdfTpl.php deleted file mode 100644 index 0f3a6fc..0000000 --- a/vendor/setasign/fpdi/src/FpdfTpl.php +++ /dev/null @@ -1,21 +0,0 @@ -currentTemplateId !== null) { - throw new \BadMethodCallException('The page format cannot be changed when writing to a template.'); - } - - if (!\in_array($orientation, ['P', 'L'], true)) { - throw new \InvalidArgumentException(\sprintf( - 'Invalid page orientation "%s"! Only "P" and "L" are allowed!', - $orientation - )); - } - - $size = $this->_getpagesize($size); - - if ( - $orientation != $this->CurOrientation - || $size[0] != $this->CurPageSize[0] - || $size[1] != $this->CurPageSize[1] - ) { - // New size or orientation - if ($orientation === 'P') { - $this->w = $size[0]; - $this->h = $size[1]; - } else { - $this->w = $size[1]; - $this->h = $size[0]; - } - $this->wPt = $this->w * $this->k; - $this->hPt = $this->h * $this->k; - $this->PageBreakTrigger = $this->h - $this->bMargin; - $this->CurOrientation = $orientation; - $this->CurPageSize = $size; - - $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt); - } - } - - /** - * Draws a template onto the page or another template. - * - * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the - * aspect ratio. - * - * @param mixed $tpl The template id - * @param array|float|int $x The abscissa of upper-left corner. Alternatively you could use an assoc array - * with the keys "x", "y", "width", "height", "adjustPageSize". - * @param float|int $y The ordinate of upper-left corner. - * @param float|int|null $width The width. - * @param float|int|null $height The height. - * @param bool $adjustPageSize - * @return array The size - * @see FpdfTplTrait::getTemplateSize() - */ - public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false) - { - if (!isset($this->templates[$tpl])) { - throw new \InvalidArgumentException('Template does not exist!'); - } - - if (\is_array($x)) { - unset($x['tpl']); - \extract($x, EXTR_IF_EXISTS); - /** @noinspection NotOptimalIfConditionsInspection */ - /** @noinspection PhpConditionAlreadyCheckedInspection */ - if (\is_array($x)) { - $x = 0; - } - } - - $template = $this->templates[$tpl]; - - $originalSize = $this->getTemplateSize($tpl); - $newSize = $this->getTemplateSize($tpl, $width, $height); - if ($adjustPageSize) { - $this->setPageFormat($newSize, $newSize['orientation']); - } - - $this->_out( - // reset standard values, translate and scale - \sprintf( - 'q 0 J 1 w 0 j 0 G 0 g %.4F 0 0 %.4F %.4F %.4F cm /%s Do Q', - ($newSize['width'] / $originalSize['width']), - ($newSize['height'] / $originalSize['height']), - $x * $this->k, - ($this->h - $y - $newSize['height']) * $this->k, - $template['id'] - ) - ); - - return $newSize; - } - - /** - * Get the size of a template. - * - * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the - * aspect ratio. - * - * @param mixed $tpl The template id - * @param float|int|null $width The width. - * @param float|int|null $height The height. - * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P) - */ - public function getTemplateSize($tpl, $width = null, $height = null) - { - if (!isset($this->templates[$tpl])) { - return false; - } - - if ($width === null && $height === null) { - $width = $this->templates[$tpl]['width']; - $height = $this->templates[$tpl]['height']; - } elseif ($width === null) { - $width = $height * $this->templates[$tpl]['width'] / $this->templates[$tpl]['height']; - } - - if ($height === null) { - $height = $width * $this->templates[$tpl]['height'] / $this->templates[$tpl]['width']; - } - - if ($height <= 0. || $width <= 0.) { - throw new \InvalidArgumentException('Width or height parameter needs to be larger than zero.'); - } - - return [ - 'width' => $width, - 'height' => $height, - 0 => $width, - 1 => $height, - 'orientation' => $width > $height ? 'L' : 'P' - ]; - } - - /** - * Begins a new template. - * - * @param float|int|null $width The width of the template. If null, the current page width is used. - * @param float|int|null $height The height of the template. If null, the current page height is used. - * @param bool $groupXObject Define the form XObject as a group XObject to support transparency (if used). - * @return int A template identifier. - */ - public function beginTemplate($width = null, $height = null, $groupXObject = false) - { - if ($width === null) { - $width = $this->w; - } - - if ($height === null) { - $height = $this->h; - } - - $templateId = $this->getNextTemplateId(); - - // initiate buffer with current state of FPDF - $buffer = "2 J\n" - . \sprintf('%.2F w', $this->LineWidth * $this->k) . "\n"; - - if ($this->FontFamily) { - $buffer .= \sprintf("BT /F%d %.2F Tf ET\n", $this->CurrentFont['i'], $this->FontSizePt); - } - - if ($this->DrawColor !== '0 G') { - $buffer .= $this->DrawColor . "\n"; - } - if ($this->FillColor !== '0 g') { - $buffer .= $this->FillColor . "\n"; - } - - if ($groupXObject && \version_compare('1.4', $this->PDFVersion, '>')) { - $this->PDFVersion = '1.4'; - } - - $this->templates[$templateId] = [ - 'objectNumber' => null, - 'id' => 'TPL' . $templateId, - 'buffer' => $buffer, - 'width' => $width, - 'height' => $height, - 'groupXObject' => $groupXObject, - 'state' => [ - 'x' => $this->x, - 'y' => $this->y, - 'AutoPageBreak' => $this->AutoPageBreak, - 'bMargin' => $this->bMargin, - 'tMargin' => $this->tMargin, - 'lMargin' => $this->lMargin, - 'rMargin' => $this->rMargin, - 'h' => $this->h, - 'hPt' => $this->hPt, - 'w' => $this->w, - 'wPt' => $this->wPt, - 'FontFamily' => $this->FontFamily, - 'FontStyle' => $this->FontStyle, - 'FontSizePt' => $this->FontSizePt, - 'FontSize' => $this->FontSize, - 'underline' => $this->underline, - 'TextColor' => $this->TextColor, - 'DrawColor' => $this->DrawColor, - 'FillColor' => $this->FillColor, - 'ColorFlag' => $this->ColorFlag - ] - ]; - - $this->SetAutoPageBreak(false); - $this->currentTemplateId = $templateId; - - $this->h = $height; - $this->hPt = $height / $this->k; - $this->w = $width; - $this->wPt = $width / $this->k; - - $this->SetXY($this->lMargin, $this->tMargin); - $this->SetRightMargin($this->w - $width + $this->rMargin); - - return $templateId; - } - - /** - * Ends a template. - * - * @return bool|int|null A template identifier. - */ - public function endTemplate() - { - if ($this->currentTemplateId === null) { - return false; - } - - $templateId = $this->currentTemplateId; - $template = $this->templates[$templateId]; - - $state = $template['state']; - $this->SetXY($state['x'], $state['y']); - $this->tMargin = $state['tMargin']; - $this->lMargin = $state['lMargin']; - $this->rMargin = $state['rMargin']; - $this->h = $state['h']; - $this->hPt = $state['hPt']; - $this->w = $state['w']; - $this->wPt = $state['wPt']; - $this->SetAutoPageBreak($state['AutoPageBreak'], $state['bMargin']); - - $this->FontFamily = $state['FontFamily']; - $this->FontStyle = $state['FontStyle']; - $this->FontSizePt = $state['FontSizePt']; - $this->FontSize = $state['FontSize']; - - $this->TextColor = $state['TextColor']; - $this->DrawColor = $state['DrawColor']; - $this->FillColor = $state['FillColor']; - $this->ColorFlag = $state['ColorFlag']; - - $this->underline = $state['underline']; - - $fontKey = $this->FontFamily . $this->FontStyle; - if ($fontKey) { - $this->CurrentFont =& $this->fonts[$fontKey]; - } else { - unset($this->CurrentFont); - } - - $this->currentTemplateId = null; - - return $templateId; - } - - /** - * Get the next template id. - * - * @return int - */ - protected function getNextTemplateId() - { - return $this->templateId++; - } - - /* overwritten FPDF methods: */ - - /** - * @inheritdoc - */ - public function AddPage($orientation = '', $size = '', $rotation = 0) - { - if ($this->currentTemplateId !== null) { - throw new \BadMethodCallException('Pages cannot be added when writing to a template.'); - } - parent::AddPage($orientation, $size, $rotation); - } - - /** - * @inheritdoc - */ - public function Link($x, $y, $w, $h, $link) - { - if ($this->currentTemplateId !== null) { - throw new \BadMethodCallException('Links cannot be set when writing to a template.'); - } - parent::Link($x, $y, $w, $h, $link); - } - - /** - * @inheritdoc - */ - public function SetLink($link, $y = 0, $page = -1) - { - if ($this->currentTemplateId !== null) { - throw new \BadMethodCallException('Links cannot be set when writing to a template.'); - } - return parent::SetLink($link, $y, $page); - } - - /** - * @inheritdoc - */ - public function SetDrawColor($r, $g = null, $b = null) - { - parent::SetDrawColor($r, $g, $b); - if ($this->page === 0 && $this->currentTemplateId !== null) { - $this->_out($this->DrawColor); - } - } - - /** - * @inheritdoc - */ - public function SetFillColor($r, $g = null, $b = null) - { - parent::SetFillColor($r, $g, $b); - if ($this->page === 0 && $this->currentTemplateId !== null) { - $this->_out($this->FillColor); - } - } - - /** - * @inheritdoc - */ - public function SetLineWidth($width) - { - parent::SetLineWidth($width); - if ($this->page === 0 && $this->currentTemplateId !== null) { - $this->_out(\sprintf('%.2F w', $width * $this->k)); - } - } - - /** - * @inheritdoc - */ - public function SetFont($family, $style = '', $size = 0) - { - parent::SetFont($family, $style, $size); - if ($this->page === 0 && $this->currentTemplateId !== null) { - $this->_out(\sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); - } - } - - /** - * @inheritdoc - */ - public function SetFontSize($size) - { - parent::SetFontSize($size); - if ($this->page === 0 && $this->currentTemplateId !== null) { - $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); - } - } - - protected function _putimages() - { - parent::_putimages(); - - foreach ($this->templates as $key => $template) { - $this->_newobj(); - $this->templates[$key]['objectNumber'] = $this->n; - - $this->_put('<_put(\sprintf( - '/BBox[0 0 %.2F %.2F]', - $template['width'] * $this->k, - $template['height'] * $this->k - )); - $this->_put('/Resources 2 0 R'); // default resources dictionary of FPDF - - if ($this->compress) { - $buffer = \gzcompress($template['buffer']); - $this->_put('/Filter/FlateDecode'); - } else { - $buffer = $template['buffer']; - } - - $this->_put('/Length ' . \strlen($buffer)); - - if ($template['groupXObject']) { - $this->_put('/Group <>'); - } - - $this->_put('>>'); - $this->_putstream($buffer); - $this->_put('endobj'); - } - } - - /** - * @inheritdoc - */ - protected function _putxobjectdict() - { - foreach ($this->templates as $key => $template) { - $this->_put('/' . $template['id'] . ' ' . $template['objectNumber'] . ' 0 R'); - } - - parent::_putxobjectdict(); - } - - /** - * @inheritdoc - */ - public function _out($s) - { - if ($this->currentTemplateId !== null) { - $this->templates[$this->currentTemplateId]['buffer'] .= $s . "\n"; - } else { - parent::_out($s); - } - } -} diff --git a/vendor/setasign/fpdi/src/FpdfTrait.php b/vendor/setasign/fpdi/src/FpdfTrait.php deleted file mode 100644 index 67fa561..0000000 --- a/vendor/setasign/fpdi/src/FpdfTrait.php +++ /dev/null @@ -1,192 +0,0 @@ -cleanUp(); - } - - /** - * Draws an imported page or a template onto the page or another template. - * - * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the - * aspect ratio. - * - * @param mixed $tpl The template id - * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array - * with the keys "x", "y", "width", "height", "adjustPageSize". - * @param float|int $y The ordinate of upper-left corner. - * @param float|int|null $width The width. - * @param float|int|null $height The height. - * @param bool $adjustPageSize - * @return array The size - * @see Fpdi::getTemplateSize() - */ - public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false) - { - if (isset($this->importedPages[$tpl])) { - $size = $this->useImportedPage($tpl, $x, $y, $width, $height, $adjustPageSize); - if ($this->currentTemplateId !== null) { - $this->templates[$this->currentTemplateId]['resources']['templates']['importedPages'][$tpl] = $tpl; - } - return $size; - } - - return parent::useTemplate($tpl, $x, $y, $width, $height, $adjustPageSize); - } - - /** - * Get the size of an imported page or template. - * - * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the - * aspect ratio. - * - * @param mixed $tpl The template id - * @param float|int|null $width The width. - * @param float|int|null $height The height. - * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P) - */ - public function getTemplateSize($tpl, $width = null, $height = null) - { - $size = parent::getTemplateSize($tpl, $width, $height); - if ($size === false) { - return $this->getImportedPageSize($tpl, $width, $height); - } - - return $size; - } - - /** - * @throws CrossReferenceException - * @throws PdfParserException - */ - protected function _putimages() - { - $this->currentReaderId = null; - parent::_putimages(); - - foreach ($this->importedPages as $key => $pageData) { - $this->_newobj(); - $this->importedPages[$key]['objectNumber'] = $this->n; - $this->currentReaderId = $pageData['readerId']; - $this->writePdfType($pageData['stream']); - $this->_put('endobj'); - } - - foreach (\array_keys($this->readers) as $readerId) { - $parser = $this->getPdfReader($readerId)->getParser(); - $this->currentReaderId = $readerId; - - while (($objectNumber = \array_pop($this->objectsToCopy[$readerId])) !== null) { - try { - $object = $parser->getIndirectObject($objectNumber); - } catch (CrossReferenceException $e) { - if ($e->getCode() === CrossReferenceException::OBJECT_NOT_FOUND) { - $object = PdfIndirectObject::create($objectNumber, 0, new PdfNull()); - } else { - throw $e; - } - } - - $this->writePdfType($object); - } - } - - $this->currentReaderId = null; - } - - /** - * @inheritdoc - */ - protected function _putxobjectdict() - { - foreach ($this->importedPages as $pageData) { - $this->_put('/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R'); - } - - parent::_putxobjectdict(); - } - - /** - * @param int $n - * @return void - * @throws PdfParser\Type\PdfTypeException - */ - protected function _putlinks($n) - { - foreach ($this->PageLinks[$n] as $pl) { - $this->_newobj(); - $rect = sprintf('%.2F %.2F %.2F %.2F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]); - $this->_put('<_put('/A <_textstring($pl[4]) . '>>'); - if (isset($pl['importedLink'])) { - $values = $pl['importedLink']['pdfObject']->value; - - foreach ($values as $name => $entry) { - $this->_put('/' . $name . ' ', false); - $this->writePdfType($entry); - } - - if (isset($pl['quadPoints'])) { - $s = '/QuadPoints['; - foreach ($pl['quadPoints'] as $value) { - $s .= sprintf('%.2F ', $value); - } - $s .= ']'; - $this->_put($s); - } - } else { - $this->_put('/Border [0 0 0]', false); - } - $this->_put('>>'); - } else { - $this->_put('/Border [0 0 0] ', false); - $l = $this->links[$pl[4]]; - if (isset($this->PageInfo[$l[0]]['size'])) { - $h = $this->PageInfo[$l[0]]['size'][1]; - } else { - $h = ($this->DefOrientation === 'P') - ? $this->DefPageSize[1] * $this->k - : $this->DefPageSize[0] * $this->k; - } - $this->_put(sprintf( - '/Dest [%d 0 R /XYZ 0 %.2F null]>>', - $this->PageInfo[$l[0]]['n'], - $h - $l[1] * $this->k - )); - } - $this->_put('endobj'); - } - } - - protected function _put($s, $newLine = true) - { - if ($newLine) { - $this->buffer .= $s . "\n"; - } else { - $this->buffer .= $s; - } - } -} diff --git a/vendor/setasign/fpdi/src/Fpdi.php b/vendor/setasign/fpdi/src/Fpdi.php deleted file mode 100644 index fd158a6..0000000 --- a/vendor/setasign/fpdi/src/Fpdi.php +++ /dev/null @@ -1,34 +0,0 @@ -readers) : $this->createdReaders; - foreach ($readers as $id) { - $this->readers[$id]->getParser()->getStreamReader()->cleanUp(); - unset($this->readers[$id]); - } - - $this->createdReaders = []; - } - - /** - * Set the minimal PDF version. - * - * @param string $pdfVersion - */ - protected function setMinPdfVersion($pdfVersion) - { - if (\version_compare($pdfVersion, $this->PDFVersion, '>')) { - $this->PDFVersion = $pdfVersion; - } - } - - /** @noinspection PhpUndefinedClassInspection */ - /** - * Get a new pdf parser instance. - * - * @param StreamReader $streamReader - * @param array $parserParams Individual parameters passed to the parser instance. - * @return PdfParser|FpdiPdfParser - */ - protected function getPdfParserInstance(StreamReader $streamReader, array $parserParams = []) - { - // note: if you get an exception here - turn off errors/warnings on not found classes for your autoloader. - // psr-4 (https://www.php-fig.org/psr/psr-4/) says: Autoloader implementations MUST NOT throw - // exceptions, MUST NOT raise errors of any level, and SHOULD NOT return a value. - /** @noinspection PhpUndefinedClassInspection */ - if (\class_exists(FpdiPdfParser::class)) { - /** @noinspection PhpUndefinedClassInspection */ - return new FpdiPdfParser($streamReader, $parserParams); - } - - return new PdfParser($streamReader); - } - - /** - * Get an unique reader id by the $file parameter. - * - * @param string|resource|PdfReader|StreamReader $file An open file descriptor, a path to a file, a PdfReader - * instance or a StreamReader instance. - * @param array $parserParams Individual parameters passed to the parser instance. - * @return string - */ - protected function getPdfReaderId($file, array $parserParams = []) - { - if (\is_resource($file)) { - $id = (string) $file; - } elseif (\is_string($file)) { - $id = \realpath($file); - if ($id === false) { - $id = $file; - } - } elseif (\is_object($file)) { - $id = \spl_object_hash($file); - } else { - throw new \InvalidArgumentException( - \sprintf('Invalid type in $file parameter (%s)', \gettype($file)) - ); - } - - /** @noinspection OffsetOperationsInspection */ - if (isset($this->readers[$id])) { - return $id; - } - - if (\is_resource($file)) { - $streamReader = new StreamReader($file); - } elseif (\is_string($file)) { - $streamReader = StreamReader::createByFile($file); - $this->createdReaders[] = $id; - } else { - $streamReader = $file; - } - - $reader = new PdfReader($this->getPdfParserInstance($streamReader, $parserParams)); - /** @noinspection OffsetOperationsInspection */ - $this->readers[$id] = $reader; - - return $id; - } - - /** - * Get a pdf reader instance by its id. - * - * @param string $id - * @return PdfReader - */ - protected function getPdfReader($id) - { - if (isset($this->readers[$id])) { - return $this->readers[$id]; - } - - throw new \InvalidArgumentException( - \sprintf('No pdf reader with the given id (%s) exists.', $id) - ); - } - - /** - * Set the source PDF file. - * - * @param string|resource|StreamReader $file Path to the file or a stream resource or a StreamReader instance. - * @return int The page count of the PDF document. - * @throws PdfParserException - */ - public function setSourceFile($file) - { - return $this->setSourceFileWithParserParams($file); - } - - /** - * Set the source PDF file with parameters which are passed to the parser instance. - * - * This method allows us to pass e.g. authentication information to the parser instance. - * - * @param string|resource|StreamReader $file Path to the file or a stream resource or a StreamReader instance. - * @param array $parserParams Individual parameters passed to the parser instance. - * @return int The page count of the PDF document. - * @throws CrossReferenceException - * @throws PdfParserException - * @throws PdfTypeException - */ - public function setSourceFileWithParserParams($file, array $parserParams = []) - { - $this->currentReaderId = $this->getPdfReaderId($file, $parserParams); - $this->objectsToCopy[$this->currentReaderId] = []; - - $reader = $this->getPdfReader($this->currentReaderId); - $this->setMinPdfVersion($reader->getPdfVersion()); - - return $reader->getPageCount(); - } - - /** - * Imports a page. - * - * @param int $pageNumber The page number. - * @param string $box The page boundary to import. Default set to PageBoundaries::CROP_BOX. - * @param bool $groupXObject Define the form XObject as a group XObject to support transparency (if used). - * @param bool $importExternalLinks Define whether external links are imported or not. - * @return string A unique string identifying the imported page. - * @throws CrossReferenceException - * @throws FilterException - * @throws PdfParserException - * @throws PdfTypeException - * @throws PdfReaderException - * @see PageBoundaries - */ - public function importPage( - $pageNumber, - $box = PageBoundaries::CROP_BOX, - $groupXObject = true, - $importExternalLinks = false - ) { - if ($this->currentReaderId === null) { - throw new \BadMethodCallException('No reader initiated. Call setSourceFile() first.'); - } - - $pageId = $this->currentReaderId; - - $pageNumber = (int)$pageNumber; - $pageId .= '|' . $pageNumber . '|' . ($groupXObject ? '1' : '0') . '|' . ($importExternalLinks ? '1' : '0'); - - // for backwards compatibility with FPDI 1 - $box = \ltrim($box, '/'); - if (!PageBoundaries::isValidName($box)) { - throw new \InvalidArgumentException( - \sprintf('Box name is invalid: "%s"', $box) - ); - } - - $pageId .= '|' . $box; - - if (isset($this->importedPages[$pageId])) { - return $pageId; - } - - $reader = $this->getPdfReader($this->currentReaderId); - $page = $reader->getPage($pageNumber); - - $bbox = $page->getBoundary($box); - if ($bbox === false) { - throw new PdfReaderException( - \sprintf("Page doesn't have a boundary box (%s).", $box), - PdfReaderException::MISSING_DATA - ); - } - - $dict = new PdfDictionary(); - $dict->value['Type'] = PdfName::create('XObject'); - $dict->value['Subtype'] = PdfName::create('Form'); - $dict->value['FormType'] = PdfNumeric::create(1); - $dict->value['BBox'] = $bbox->toPdfArray(); - - if ($groupXObject) { - $this->setMinPdfVersion('1.4'); - $dict->value['Group'] = PdfDictionary::create([ - 'Type' => PdfName::create('Group'), - 'S' => PdfName::create('Transparency') - ]); - } - - $resources = $page->getAttribute('Resources'); - if ($resources !== null) { - $dict->value['Resources'] = $resources; - } - - list($width, $height) = $page->getWidthAndHeight($box); - - $a = 1; - $b = 0; - $c = 0; - $d = 1; - $e = -$bbox->getLlx(); - $f = -$bbox->getLly(); - - $rotation = $page->getRotation(); - - if ($rotation !== 0) { - $rotation *= -1; - $angle = $rotation * M_PI / 180; - $a = \cos($angle); - $b = \sin($angle); - $c = -$b; - $d = $a; - - switch ($rotation) { - case -90: - $e = -$bbox->getLly(); - $f = $bbox->getUrx(); - break; - case -180: - $e = $bbox->getUrx(); - $f = $bbox->getUry(); - break; - case -270: - $e = $bbox->getUry(); - $f = -$bbox->getLlx(); - break; - } - } - - // we need to rotate/translate - if ($a != 1 || $b != 0 || $c != 0 || $d != 1 || $e != 0 || $f != 0) { - $dict->value['Matrix'] = PdfArray::create([ - PdfNumeric::create($a), PdfNumeric::create($b), PdfNumeric::create($c), - PdfNumeric::create($d), PdfNumeric::create($e), PdfNumeric::create($f) - ]); - } - - // try to use the existing content stream - $pageDict = $page->getPageDictionary(); - - try { - $contentsObject = PdfType::resolve(PdfDictionary::get($pageDict, 'Contents'), $reader->getParser(), true); - $contents = PdfType::resolve($contentsObject, $reader->getParser()); - - // just copy the stream reference if it is only a single stream - if ( - ($contentsIsStream = ($contents instanceof PdfStream)) - || ($contents instanceof PdfArray && \count($contents->value) === 1) - ) { - if ($contentsIsStream) { - /** - * @var PdfIndirectObject $contentsObject - */ - $stream = $contents; - } else { - $stream = PdfType::resolve($contents->value[0], $reader->getParser()); - } - - $filter = PdfDictionary::get($stream->value, 'Filter'); - if (!$filter instanceof PdfNull) { - $dict->value['Filter'] = $filter; - } - $length = PdfType::resolve(PdfDictionary::get($stream->value, 'Length'), $reader->getParser()); - $dict->value['Length'] = $length; - $stream->value = $dict; - // otherwise extract it from the array and re-compress the whole stream - } else { - $streamContent = $this->compress - ? \gzcompress($page->getContentStream()) - : $page->getContentStream(); - - $dict->value['Length'] = PdfNumeric::create(\strlen($streamContent)); - if ($this->compress) { - $dict->value['Filter'] = PdfName::create('FlateDecode'); - } - - $stream = PdfStream::create($dict, $streamContent); - } - // Catch faulty pages and use an empty content stream - } catch (FpdiException $e) { - $dict->value['Length'] = PdfNumeric::create(0); - $stream = PdfStream::create($dict, ''); - } - - $externalLinks = []; - if ($importExternalLinks) { - $externalLinks = $page->getExternalLinks($box); - } - - $this->importedPages[$pageId] = [ - 'objectNumber' => null, - 'readerId' => $this->currentReaderId, - 'id' => 'TPL' . $this->getNextTemplateId(), - 'width' => $width / $this->k, - 'height' => $height / $this->k, - 'stream' => $stream, - 'externalLinks' => $externalLinks - ]; - - return $pageId; - } - - /** - * Draws an imported page onto the page. - * - * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the - * aspect ratio. - * - * @param mixed $pageId The page id - * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array - * with the keys "x", "y", "width", "height", "adjustPageSize". - * @param float|int $y The ordinate of upper-left corner. - * @param float|int|null $width The width. - * @param float|int|null $height The height. - * @param bool $adjustPageSize - * @return array The size. - * @see Fpdi::getTemplateSize() - */ - public function useImportedPage($pageId, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false) - { - if (\is_array($x)) { - /** @noinspection OffsetOperationsInspection */ - unset($x['pageId']); - \extract($x, EXTR_IF_EXISTS); - /** @noinspection NotOptimalIfConditionsInspection */ - if (\is_array($x)) { - $x = 0; - } - } - - if (!isset($this->importedPages[$pageId])) { - throw new \InvalidArgumentException('Imported page does not exist!'); - } - - $importedPage = $this->importedPages[$pageId]; - - $originalSize = $this->getTemplateSize($pageId); - $newSize = $this->getTemplateSize($pageId, $width, $height); - if ($adjustPageSize) { - $this->setPageFormat($newSize, $newSize['orientation']); - } - - $scaleX = ($newSize['width'] / $originalSize['width']); - $scaleY = ($newSize['height'] / $originalSize['height']); - $xPt = $x * $this->k; - $yPt = $y * $this->k; - $newHeightPt = $newSize['height'] * $this->k; - - $this->_out( - // reset standard values, translate and scale - \sprintf( - 'q 0 J 1 w 0 j 0 G 0 g %.4F 0 0 %.4F %.4F %.4F cm /%s Do Q', - $scaleX, - $scaleY, - $xPt, - $this->hPt - $yPt - $newHeightPt, - $importedPage['id'] - ) - ); - - if (count($importedPage['externalLinks']) > 0) { - foreach ($importedPage['externalLinks'] as $externalLink) { - // mPDF uses also 'externalLinks' but doesn't come with a rect-value - if (!isset($externalLink['rect'])) { - continue; - } - - /** @var Rectangle $rect */ - $rect = $externalLink['rect']; - $this->Link( - $x + $rect->getLlx() / $this->k * $scaleX, - $y + $newSize['height'] - ($rect->getLly() + $rect->getHeight()) / $this->k * $scaleY, - $rect->getWidth() / $this->k * $scaleX, - $rect->getHeight() / $this->k * $scaleY, - $externalLink['uri'] - ); - - $this->adjustLastLink($externalLink, $xPt, $scaleX, $yPt, $newHeightPt, $scaleY, $importedPage); - } - } - - return $newSize; - } - - /** - * This method will add additional data to the last created link/annotation. - * - * It is separated because TCPDF uses its own logic to handle link annotations. - * This method is overwritten in the TCPDF implementation. - * - * @param array $externalLink - * @param float|int $xPt - * @param float|int $scaleX - * @param float|int $yPt - * @param float|int $newHeightPt - * @param float|int $scaleY - * @param array $importedPage - * @return void - */ - protected function adjustLastLink($externalLink, $xPt, $scaleX, $yPt, $newHeightPt, $scaleY, $importedPage) - { - // let's create a relation of the newly created link to the data of the external link - $lastLink = count($this->PageLinks[$this->page]); - $this->PageLinks[$this->page][$lastLink - 1]['importedLink'] = $externalLink; - if (count($externalLink['quadPoints']) > 0) { - $quadPoints = []; - for ($i = 0, $n = count($externalLink['quadPoints']); $i < $n; $i += 2) { - $quadPoints[] = $xPt + $externalLink['quadPoints'][$i] * $scaleX; - $quadPoints[] = $this->hPt - $yPt - $newHeightPt + $externalLink['quadPoints'][$i + 1] * $scaleY; - } - - $this->PageLinks[$this->page][$lastLink - 1]['quadPoints'] = $quadPoints; - } - } - - /** - * Get the size of an imported page. - * - * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the - * aspect ratio. - * - * @param mixed $tpl The template id - * @param float|int|null $width The width. - * @param float|int|null $height The height. - * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P) - */ - public function getImportedPageSize($tpl, $width = null, $height = null) - { - if (isset($this->importedPages[$tpl])) { - $importedPage = $this->importedPages[$tpl]; - - if ($width === null && $height === null) { - $width = $importedPage['width']; - $height = $importedPage['height']; - } elseif ($width === null) { - $width = $height * $importedPage['width'] / $importedPage['height']; - } - - if ($height === null) { - $height = $width * $importedPage['height'] / $importedPage['width']; - } - - if ($height <= 0. || $width <= 0.) { - throw new \InvalidArgumentException('Width or height parameter needs to be larger than zero.'); - } - - return [ - 'width' => $width, - 'height' => $height, - 0 => $width, - 1 => $height, - 'orientation' => $width > $height ? 'L' : 'P' - ]; - } - - return false; - } - - /** - * Writes a PdfType object to the resulting buffer. - * - * @param PdfType $value - * @throws PdfTypeException - */ - protected function writePdfType(PdfType $value) - { - if ($value instanceof PdfNumeric) { - if (\is_int($value->value)) { - $this->_put($value->value . ' ', false); - } else { - $this->_put(\rtrim(\rtrim(\sprintf('%.5F', $value->value), '0'), '.') . ' ', false); - } - } elseif ($value instanceof PdfName) { - $this->_put('/' . $value->value . ' ', false); - } elseif ($value instanceof PdfString) { - $this->_put('(' . $value->value . ')', false); - } elseif ($value instanceof PdfHexString) { - $this->_put('<' . $value->value . '>', false); - } elseif ($value instanceof PdfBoolean) { - $this->_put($value->value ? 'true ' : 'false ', false); - } elseif ($value instanceof PdfArray) { - $this->_put('[', false); - foreach ($value->value as $entry) { - $this->writePdfType($entry); - } - $this->_put(']'); - } elseif ($value instanceof PdfDictionary) { - $this->_put('<<', false); - foreach ($value->value as $name => $entry) { - $this->_put('/' . $name . ' ', false); - $this->writePdfType($entry); - } - $this->_put('>>'); - } elseif ($value instanceof PdfToken) { - $this->_put($value->value); - } elseif ($value instanceof PdfNull) { - $this->_put('null ', false); - } elseif ($value instanceof PdfStream) { - $this->writePdfType($value->value); - $this->_put('stream'); - $this->_put($value->getStream()); - $this->_put('endstream'); - } elseif ($value instanceof PdfIndirectObjectReference) { - if (!isset($this->objectMap[$this->currentReaderId])) { - $this->objectMap[$this->currentReaderId] = []; - } - - if (!isset($this->objectMap[$this->currentReaderId][$value->value])) { - $this->objectMap[$this->currentReaderId][$value->value] = ++$this->n; - $this->objectsToCopy[$this->currentReaderId][] = $value->value; - } - - $this->_put($this->objectMap[$this->currentReaderId][$value->value] . ' 0 R ', false); - } elseif ($value instanceof PdfIndirectObject) { - $n = $this->objectMap[$this->currentReaderId][$value->objectNumber]; - $this->_newobj($n); - $this->writePdfType($value->value); - - // add newline before "endobj" for all objects in view to PDF/A conformance - if ( - !( - ($value->value instanceof PdfArray) || - ($value->value instanceof PdfDictionary) || - ($value->value instanceof PdfToken) || - ($value->value instanceof PdfStream) - ) - ) { - $this->_put("\n", false); - } - - $this->_put('endobj'); - } - } -} diff --git a/vendor/setasign/fpdi/src/GraphicsState.php b/vendor/setasign/fpdi/src/GraphicsState.php deleted file mode 100644 index 27feafb..0000000 --- a/vendor/setasign/fpdi/src/GraphicsState.php +++ /dev/null @@ -1,97 +0,0 @@ -ctm = $ctm; - } - - /** - * @param Matrix $matrix - * @return $this - */ - public function add(Matrix $matrix) - { - $this->ctm = $matrix->multiply($this->ctm); - return $this; - } - - /** - * @param int|float $x - * @param int|float $y - * @param int|float $angle - * @return $this - */ - public function rotate($x, $y, $angle) - { - if (abs($angle) < 1e-5) { - return $this; - } - - $angle = deg2rad($angle); - $c = cos($angle); - $s = sin($angle); - - $this->add(new Matrix($c, $s, -$s, $c, $x, $y)); - - return $this->translate(-$x, -$y); - } - - /** - * @param int|float $shiftX - * @param int|float $shiftY - * @return $this - */ - public function translate($shiftX, $shiftY) - { - return $this->add(new Matrix(1, 0, 0, 1, $shiftX, $shiftY)); - } - - /** - * @param int|float $scaleX - * @param int|float $scaleY - * @return $this - */ - public function scale($scaleX, $scaleY) - { - return $this->add(new Matrix($scaleX, 0, 0, $scaleY, 0, 0)); - } - - /** - * @param Vector $vector - * @return Vector - */ - public function toUserSpace(Vector $vector) - { - return $vector->multiplyWithMatrix($this->ctm); - } -} diff --git a/vendor/setasign/fpdi/src/Math/Matrix.php b/vendor/setasign/fpdi/src/Math/Matrix.php deleted file mode 100644 index 662a5a2..0000000 --- a/vendor/setasign/fpdi/src/Math/Matrix.php +++ /dev/null @@ -1,116 +0,0 @@ -a = (float)$a; - $this->b = (float)$b; - $this->c = (float)$c; - $this->d = (float)$d; - $this->e = (float)$e; - $this->f = (float)$f; - } - - /** - * @return float[] - */ - public function getValues() - { - return [$this->a, $this->b, $this->c, $this->d, $this->e, $this->f]; - } - - /** - * @param Matrix $by - * @return Matrix - */ - public function multiply(self $by) - { - $a = - $this->a * $by->a - + $this->b * $by->c - //+ 0 * $by->e - ; - - $b = - $this->a * $by->b - + $this->b * $by->d - //+ 0 * $by->f - ; - - $c = - $this->c * $by->a - + $this->d * $by->c - //+ 0 * $by->e - ; - - $d = - $this->c * $by->b - + $this->d * $by->d - //+ 0 * $by->f - ; - - $e = - $this->e * $by->a - + $this->f * $by->c - + /*1 * */$by->e; - - $f = - $this->e * $by->b - + $this->f * $by->d - + /*1 * */$by->f; - - return new self($a, $b, $c, $d, $e, $f); - } -} diff --git a/vendor/setasign/fpdi/src/Math/Vector.php b/vendor/setasign/fpdi/src/Math/Vector.php deleted file mode 100644 index df782d4..0000000 --- a/vendor/setasign/fpdi/src/Math/Vector.php +++ /dev/null @@ -1,66 +0,0 @@ -x = (float)$x; - $this->y = (float)$y; - } - - /** - * @return float - */ - public function getX() - { - return $this->x; - } - - /** - * @return float - */ - public function getY() - { - return $this->y; - } - - /** - * @param Matrix $matrix - * @return Vector - */ - public function multiplyWithMatrix(Matrix $matrix) - { - list($a, $b, $c, $d, $e, $f) = $matrix->getValues(); - $x = $a * $this->x + $c * $this->y + $e; - $y = $b * $this->x + $d * $this->y + $f; - - return new self($x, $y); - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/CrossReference/AbstractReader.php b/vendor/setasign/fpdi/src/PdfParser/CrossReference/AbstractReader.php deleted file mode 100644 index bcf21d6..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/CrossReference/AbstractReader.php +++ /dev/null @@ -1,95 +0,0 @@ -parser = $parser; - $this->readTrailer(); - } - - /** - * Get the trailer dictionary. - * - * @return PdfDictionary - */ - public function getTrailer() - { - return $this->trailer; - } - - /** - * Read the trailer dictionary. - * - * @throws CrossReferenceException - * @throws PdfTypeException - */ - protected function readTrailer() - { - try { - $trailerKeyword = $this->parser->readValue(null, PdfToken::class); - if ($trailerKeyword->value !== 'trailer') { - throw new CrossReferenceException( - \sprintf( - 'Unexpected end of cross reference. "trailer"-keyword expected, got: %s.', - $trailerKeyword->value - ), - CrossReferenceException::UNEXPECTED_END - ); - } - } catch (PdfTypeException $e) { - throw new CrossReferenceException( - 'Unexpected end of cross reference. "trailer"-keyword expected, got an invalid object type.', - CrossReferenceException::UNEXPECTED_END, - $e - ); - } - - try { - $trailer = $this->parser->readValue(null, PdfDictionary::class); - } catch (PdfTypeException $e) { - throw new CrossReferenceException( - 'Unexpected end of cross reference. Trailer not found.', - CrossReferenceException::UNEXPECTED_END, - $e - ); - } - - $this->trailer = $trailer; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/CrossReference/CrossReference.php b/vendor/setasign/fpdi/src/PdfParser/CrossReference/CrossReference.php deleted file mode 100644 index 7fa146d..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/CrossReference/CrossReference.php +++ /dev/null @@ -1,326 +0,0 @@ -parser = $parser; - $this->fileHeaderOffset = $fileHeaderOffset; - - $offset = $this->findStartXref(); - $reader = null; - /** @noinspection TypeUnsafeComparisonInspection */ - while ($offset != false) { // By doing an unsafe comparsion we ignore faulty references to byte offset 0 - try { - $reader = $this->readXref($offset + $this->fileHeaderOffset); - } catch (CrossReferenceException $e) { - // sometimes the file header offset is part of the byte offsets, so let's retry by resetting it to zero. - if ($e->getCode() === CrossReferenceException::INVALID_DATA && $this->fileHeaderOffset !== 0) { - $this->fileHeaderOffset = 0; - $reader = $this->readXref($offset); - } else { - throw $e; - } - } - - $trailer = $reader->getTrailer(); - $this->checkForEncryption($trailer); - $this->readers[] = $reader; - - if (isset($trailer->value['Prev'])) { - $offset = $trailer->value['Prev']->value; - } else { - $offset = false; - } - } - - // fix faulty sub-section header - if ($reader instanceof FixedReader) { - /** - * @var FixedReader $reader - */ - $reader->fixFaultySubSectionShift(); - } - - if ($reader === null) { - throw new CrossReferenceException('No cross-reference found.', CrossReferenceException::NO_XREF_FOUND); - } - } - - /** - * Get the size of the cross reference. - * - * @return integer - */ - public function getSize() - { - return $this->getTrailer()->value['Size']->value; - } - - /** - * Get the trailer dictionary. - * - * @return PdfDictionary - */ - public function getTrailer() - { - return $this->readers[0]->getTrailer(); - } - - /** - * Get the cross reference readser instances. - * - * @return ReaderInterface[] - */ - public function getReaders() - { - return $this->readers; - } - - /** - * Get the offset by an object number. - * - * @param int $objectNumber - * @return integer|bool - */ - public function getOffsetFor($objectNumber) - { - foreach ($this->getReaders() as $reader) { - $offset = $reader->getOffsetFor($objectNumber); - if ($offset !== false) { - return $offset; - } - } - - return false; - } - - /** - * Get an indirect object by its object number. - * - * @param int $objectNumber - * @return PdfIndirectObject - * @throws CrossReferenceException - */ - public function getIndirectObject($objectNumber) - { - $offset = $this->getOffsetFor($objectNumber); - if ($offset === false) { - throw new CrossReferenceException( - \sprintf('Object (id:%s) not found.', $objectNumber), - CrossReferenceException::OBJECT_NOT_FOUND - ); - } - - $parser = $this->parser; - - $parser->getTokenizer()->clearStack(); - $parser->getStreamReader()->reset($offset + $this->fileHeaderOffset); - - try { - /** @var PdfIndirectObject $object */ - $object = $parser->readValue(null, PdfIndirectObject::class); - } catch (PdfTypeException $e) { - throw new CrossReferenceException( - \sprintf('Object (id:%s) not found at location (%s).', $objectNumber, $offset), - CrossReferenceException::OBJECT_NOT_FOUND, - $e - ); - } - - if ($object->objectNumber !== $objectNumber) { - throw new CrossReferenceException( - \sprintf('Wrong object found, got %s while %s was expected.', $object->objectNumber, $objectNumber), - CrossReferenceException::OBJECT_NOT_FOUND - ); - } - - return $object; - } - - /** - * Read the cross-reference table at a given offset. - * - * Internally the method will try to evaluate the best reader for this cross-reference. - * - * @param int $offset - * @return ReaderInterface - * @throws CrossReferenceException - * @throws PdfTypeException - */ - protected function readXref($offset) - { - $this->parser->getStreamReader()->reset($offset); - $this->parser->getTokenizer()->clearStack(); - $initValue = $this->parser->readValue(); - - return $this->initReaderInstance($initValue); - } - - /** - * Get a cross-reference reader instance. - * - * @param PdfToken|PdfIndirectObject $initValue - * @return ReaderInterface|bool - * @throws CrossReferenceException - * @throws PdfTypeException - */ - protected function initReaderInstance($initValue) - { - $position = $this->parser->getStreamReader()->getPosition() - + $this->parser->getStreamReader()->getOffset() + $this->fileHeaderOffset; - - if ($initValue instanceof PdfToken && $initValue->value === 'xref') { - try { - return new FixedReader($this->parser); - } catch (CrossReferenceException $e) { - $this->parser->getStreamReader()->reset($position); - $this->parser->getTokenizer()->clearStack(); - - return new LineReader($this->parser); - } - } - - if ($initValue instanceof PdfIndirectObject) { - try { - $stream = PdfStream::ensure($initValue->value); - } catch (PdfTypeException $e) { - throw new CrossReferenceException( - 'Invalid object type at xref reference offset.', - CrossReferenceException::INVALID_DATA, - $e - ); - } - - $type = PdfDictionary::get($stream->value, 'Type'); - if ($type->value !== 'XRef') { - throw new CrossReferenceException( - 'The xref position points to an incorrect object type.', - CrossReferenceException::INVALID_DATA - ); - } - - $this->checkForEncryption($stream->value); - - throw new CrossReferenceException( - 'This PDF document probably uses a compression technique which is not supported by the ' . - 'free parser shipped with FPDI. (See https://www.setasign.com/fpdi-pdf-parser for more details)', - CrossReferenceException::COMPRESSED_XREF - ); - } - - throw new CrossReferenceException( - 'The xref position points to an incorrect object type.', - CrossReferenceException::INVALID_DATA - ); - } - - /** - * Check for encryption. - * - * @param PdfDictionary $dictionary - * @throws CrossReferenceException - */ - protected function checkForEncryption(PdfDictionary $dictionary) - { - if (isset($dictionary->value['Encrypt'])) { - throw new CrossReferenceException( - 'This PDF document is encrypted and cannot be processed with FPDI.', - CrossReferenceException::ENCRYPTED - ); - } - } - - /** - * Find the start position for the first cross-reference. - * - * @return int The byte-offset position of the first cross-reference. - * @throws CrossReferenceException - */ - protected function findStartXref() - { - $reader = $this->parser->getStreamReader(); - $reader->reset(-self::$trailerSearchLength, self::$trailerSearchLength); - - $buffer = $reader->getBuffer(false); - $pos = \strrpos($buffer, 'startxref'); - $addOffset = 9; - if ($pos === false) { - // Some corrupted documents uses startref, instead of startxref - $pos = \strrpos($buffer, 'startref'); - if ($pos === false) { - throw new CrossReferenceException( - 'Unable to find pointer to xref table', - CrossReferenceException::NO_STARTXREF_FOUND - ); - } - $addOffset = 8; - } - - $reader->setOffset($pos + $addOffset); - - try { - $value = $this->parser->readValue(null, PdfNumeric::class); - } catch (PdfTypeException $e) { - throw new CrossReferenceException( - 'Invalid data after startxref keyword.', - CrossReferenceException::INVALID_DATA, - $e - ); - } - - return $value->value; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/CrossReference/CrossReferenceException.php b/vendor/setasign/fpdi/src/PdfParser/CrossReference/CrossReferenceException.php deleted file mode 100644 index 8a1a589..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/CrossReference/CrossReferenceException.php +++ /dev/null @@ -1,79 +0,0 @@ -reader = $parser->getStreamReader(); - $this->read(); - parent::__construct($parser); - } - - /** - * Get all subsection data. - * - * @return array - */ - public function getSubSections() - { - return $this->subSections; - } - - /** - * @inheritdoc - * @return int|false - */ - public function getOffsetFor($objectNumber) - { - foreach ($this->subSections as $offset => list($startObject, $objectCount)) { - /** - * @var int $startObject - * @var int $objectCount - */ - if ($objectNumber >= $startObject && $objectNumber < ($startObject + $objectCount)) { - $position = $offset + 20 * ($objectNumber - $startObject); - $this->reader->ensure($position, 20); - $line = $this->reader->readBytes(20); - if ($line[17] === 'f') { - return false; - } - - return (int) \substr($line, 0, 10); - } - } - - return false; - } - - /** - * Read the cross-reference. - * - * This reader will only read the subsections in this method. The offsets were resolved individually by this - * information. - * - * @throws CrossReferenceException - */ - protected function read() - { - $subSections = []; - - $startObject = $entryCount = $lastLineStart = null; - $validityChecked = false; - while (($line = $this->reader->readLine(20)) !== false) { - if (\strpos($line, 'trailer') !== false) { - $this->reader->reset($lastLineStart); - break; - } - - // jump over if line content doesn't match the expected string - if (\sscanf($line, '%d %d', $startObject, $entryCount) !== 2) { - continue; - } - - $oldPosition = $this->reader->getPosition(); - $position = $oldPosition + $this->reader->getOffset(); - - if (!$validityChecked && $entryCount > 0) { - $nextLine = $this->reader->readBytes(21); - /* Check the next line for maximum of 20 bytes and not longer - * By catching 21 bytes and trimming the length should be still 21. - */ - if (\strlen(\trim($nextLine)) !== 21) { - throw new CrossReferenceException( - 'Cross-reference entries are larger than 20 bytes.', - CrossReferenceException::ENTRIES_TOO_LARGE - ); - } - - /* Check for less than 20 bytes: cut the line to 20 bytes and trim; have to result in exactly 18 bytes. - * If it would have less bytes the substring would get the first bytes of the next line which would - * evaluate to a 20 bytes long string after trimming. - */ - if (\strlen(\trim(\substr($nextLine, 0, 20))) !== 18) { - throw new CrossReferenceException( - 'Cross-reference entries are less than 20 bytes.', - CrossReferenceException::ENTRIES_TOO_SHORT - ); - } - - $validityChecked = true; - } - - $subSections[$position] = [$startObject, $entryCount]; - - $lastLineStart = $position + $entryCount * 20; - $this->reader->reset($lastLineStart); - } - - // reset after the last correct parsed line - $this->reader->reset($lastLineStart); - - if (\count($subSections) === 0) { - throw new CrossReferenceException( - 'No entries found in cross-reference.', - CrossReferenceException::NO_ENTRIES - ); - } - - $this->subSections = $subSections; - } - - /** - * Fixes an invalid object number shift. - * - * This method can be used to repair documents with an invalid subsection header: - * - * - * xref - * 1 7 - * 0000000000 65535 f - * 0000000009 00000 n - * 0000412075 00000 n - * 0000412172 00000 n - * 0000412359 00000 n - * 0000412417 00000 n - * 0000412468 00000 n - * - * - * It shall only be called on the first table. - * - * @return bool - */ - public function fixFaultySubSectionShift() - { - $subSections = $this->getSubSections(); - if (\count($subSections) > 1) { - return false; - } - - $subSection = \current($subSections); - if ($subSection[0] != 1) { - return false; - } - - if ($this->getOffsetFor(1) === false) { - foreach ($subSections as $offset => list($startObject, $objectCount)) { - $this->subSections[$offset] = [$startObject - 1, $objectCount]; - } - return true; - } - - return false; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/CrossReference/LineReader.php b/vendor/setasign/fpdi/src/PdfParser/CrossReference/LineReader.php deleted file mode 100644 index bcbd8e4..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/CrossReference/LineReader.php +++ /dev/null @@ -1,168 +0,0 @@ - 20 bytes). - */ -class LineReader extends AbstractReader implements ReaderInterface -{ - /** - * The object offsets. - * - * @var array - */ - protected $offsets; - - /** - * LineReader constructor. - * - * @param PdfParser $parser - * @throws CrossReferenceException - */ - public function __construct(PdfParser $parser) - { - $this->read($this->extract($parser->getStreamReader())); - parent::__construct($parser); - } - - /** - * @inheritdoc - * @return int|false - */ - public function getOffsetFor($objectNumber) - { - if (isset($this->offsets[$objectNumber])) { - return $this->offsets[$objectNumber][0]; - } - - return false; - } - - /** - * Get all found offsets. - * - * @return array - */ - public function getOffsets() - { - return $this->offsets; - } - - /** - * Extracts the cross reference data from the stream reader. - * - * @param StreamReader $reader - * @return string - * @throws CrossReferenceException - */ - protected function extract(StreamReader $reader) - { - $bytesPerCycle = 100; - $reader->reset(null, $bytesPerCycle); - - $cycles = 0; - do { - // 6 = length of "trailer" - 1 - $pos = \max(($bytesPerCycle * $cycles) - 6, 0); - $trailerPos = \strpos($reader->getBuffer(false), 'trailer', $pos); - $cycles++; - } while ($trailerPos === false && $reader->increaseLength($bytesPerCycle) !== false); - - if ($trailerPos === false) { - throw new CrossReferenceException( - 'Unexpected end of cross reference. "trailer"-keyword not found.', - CrossReferenceException::NO_TRAILER_FOUND - ); - } - - $xrefContent = \substr($reader->getBuffer(false), 0, $trailerPos); - $reader->reset($reader->getPosition() + $trailerPos); - - return $xrefContent; - } - - /** - * Read the cross-reference entries. - * - * @param string $xrefContent - * @throws CrossReferenceException - */ - protected function read($xrefContent) - { - // get eol markers in the first 100 bytes - \preg_match_all("/(\r\n|\n|\r)/", \substr($xrefContent, 0, 100), $m); - - if (\count($m[0]) === 0) { - throw new CrossReferenceException( - 'No data found in cross-reference.', - CrossReferenceException::INVALID_DATA - ); - } - - // count(array_count_values()) is faster then count(array_unique()) - // @see https://github.com/symfony/symfony/pull/23731 - // can be reverted for php7.2 - $differentLineEndings = \count(\array_count_values($m[0])); - if ($differentLineEndings > 1) { - $lines = \preg_split("/(\r\n|\n|\r)/", $xrefContent, -1, PREG_SPLIT_NO_EMPTY); - } else { - $lines = \explode($m[0][0], $xrefContent); - } - - unset($differentLineEndings, $m); - if (!\is_array($lines)) { - $this->offsets = []; - return; - } - - $start = 0; - $offsets = []; - - // trim all lines and remove empty lines - $lines = \array_filter(\array_map('\trim', $lines)); - foreach ($lines as $line) { - $pieces = \explode(' ', $line); - - switch (\count($pieces)) { - case 2: - $start = (int) $pieces[0]; - break; - - case 3: - switch ($pieces[2]) { - case 'n': - $offsets[$start] = [(int) $pieces[0], (int) $pieces[1]]; - $start++; - break 2; - case 'f': - $start++; - break 2; - } - // fall through if pieces doesn't match - - default: - throw new CrossReferenceException( - \sprintf('Unexpected data in xref table (%s)', \implode(' ', $pieces)), - CrossReferenceException::INVALID_DATA - ); - } - } - - $this->offsets = $offsets; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/CrossReference/ReaderInterface.php b/vendor/setasign/fpdi/src/PdfParser/CrossReference/ReaderInterface.php deleted file mode 100644 index 0bdc0ab..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/CrossReference/ReaderInterface.php +++ /dev/null @@ -1,34 +0,0 @@ - - if ($ch === 126 && isset($data[$k + 1]) && (\ord($data[$k + 1]) & 0xFF) === 62) { - break; - } - - if ($ch === 122 /* z */ && $state === 0) { - $out .= \chr(0) . \chr(0) . \chr(0) . \chr(0); - continue; - } - - if ($ch < 33 /* ! */ || $ch > 117 /* u */) { - throw new Ascii85Exception( - 'Illegal character found while ASCII85 decode.', - Ascii85Exception::ILLEGAL_CHAR_FOUND - ); - } - - $chn[$state] = $ch - 33;/* ! */ - $state++; - - if ($state === 5) { - $state = 0; - $r = 0; - for ($j = 0; $j < 5; ++$j) { - /** @noinspection UnnecessaryCastingInspection */ - $r = (int)($r * 85 + $chn[$j]); - } - - $out .= \chr($r >> 24) - . \chr($r >> 16) - . \chr($r >> 8) - . \chr($r); - } - } - - if ($state === 1) { - throw new Ascii85Exception( - 'Illegal length while ASCII85 decode.', - Ascii85Exception::ILLEGAL_LENGTH - ); - } - - if ($state === 2) { - $r = $chn[0] * 85 * 85 * 85 * 85 + ($chn[1] + 1) * 85 * 85 * 85; - $out .= \chr($r >> 24); - } elseif ($state === 3) { - $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + ($chn[2] + 1) * 85 * 85; - $out .= \chr($r >> 24); - $out .= \chr($r >> 16); - } elseif ($state === 4) { - $r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + $chn[2] * 85 * 85 + ($chn[3] + 1) * 85; - $out .= \chr($r >> 24); - $out .= \chr($r >> 16); - $out .= \chr($r >> 8); - } - - return $out; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Filter/Ascii85Exception.php b/vendor/setasign/fpdi/src/PdfParser/Filter/Ascii85Exception.php deleted file mode 100644 index 83a780c..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Filter/Ascii85Exception.php +++ /dev/null @@ -1,27 +0,0 @@ -')); - if ((\strlen($data) % 2) === 1) { - $data .= '0'; - } - - return \pack('H*', $data); - } - - /** - * Converts a string into ASCII hexadecimal representation. - * - * @param string $data The input string - * @param boolean $leaveEOD - * @return string - */ - public function encode($data, $leaveEOD = false) - { - $t = \unpack('H*', $data); - return \current($t) - . ($leaveEOD ? '' : '>'); - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Filter/FilterException.php b/vendor/setasign/fpdi/src/PdfParser/Filter/FilterException.php deleted file mode 100644 index c71ff38..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Filter/FilterException.php +++ /dev/null @@ -1,23 +0,0 @@ -extensionLoaded()) { - $oData = $data; - $data = (($data !== '') ? @\gzuncompress($data) : ''); - if ($data === false) { - // let's try if the checksum is CRC32 - $fh = fopen('php://temp', 'w+b'); - fwrite($fh, "\x1f\x8b\x08\x00\x00\x00\x00\x00" . $oData); - // "window" == 31 -> 16 + (8 to 15): Uses the low 4 bits of the value as the window size logarithm. - // The input must include a gzip header and trailer (via 16). - stream_filter_append($fh, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 31]); - fseek($fh, 0); - $data = @stream_get_contents($fh); - fclose($fh); - - if ($data) { - return $data; - } - - // Try this fallback (remove the zlib stream header) - $data = @(gzinflate(substr($oData, 2))); - - if ($data === false) { - throw new FlateException( - 'Error while decompressing stream.', - FlateException::DECOMPRESS_ERROR - ); - } - } - } else { - throw new FlateException( - 'To handle FlateDecode filter, enable zlib support in PHP.', - FlateException::NO_ZLIB - ); - } - - return $data; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Filter/FlateException.php b/vendor/setasign/fpdi/src/PdfParser/Filter/FlateException.php deleted file mode 100644 index 7791ca7..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Filter/FlateException.php +++ /dev/null @@ -1,27 +0,0 @@ -initsTable(); - - $this->data = $data; - $this->dataLength = \strlen($data); - - // Initialize pointers - $this->bytePointer = 0; - - $this->nextData = 0; - $this->nextBits = 0; - - $prevCode = 0; - - $uncompData = ''; - - while (($code = $this->getNextCode()) !== 257) { - if ($code === 256) { - $this->initsTable(); - } elseif ($prevCode === 256) { - $uncompData .= $this->sTable[$code]; - } elseif ($code < $this->tIdx) { - $string = $this->sTable[$code]; - $uncompData .= $string; - - $this->addStringToTable($this->sTable[$prevCode], $string[0]); - } else { - $string = $this->sTable[$prevCode]; - $string .= $string[0]; - $uncompData .= $string; - - $this->addStringToTable($string); - } - $prevCode = $code; - } - - return $uncompData; - } - - /** - * Initialize the string table. - */ - protected function initsTable() - { - $this->sTable = []; - - for ($i = 0; $i < 256; $i++) { - $this->sTable[$i] = \chr($i); - } - - $this->tIdx = 258; - $this->bitsToGet = 9; - } - - /** - * Add a new string to the string table. - * - * @param string $oldString - * @param string $newString - */ - protected function addStringToTable($oldString, $newString = '') - { - $string = $oldString . $newString; - - // Add this new String to the table - $this->sTable[$this->tIdx++] = $string; - - if ($this->tIdx === 511) { - $this->bitsToGet = 10; - } elseif ($this->tIdx === 1023) { - $this->bitsToGet = 11; - } elseif ($this->tIdx === 2047) { - $this->bitsToGet = 12; - } - } - - /** - * Returns the next 9, 10, 11 or 12 bits. - * - * @return int - */ - protected function getNextCode() - { - if ($this->bytePointer === $this->dataLength) { - return 257; - } - - $this->nextData = ($this->nextData << 8) | (\ord($this->data[$this->bytePointer++]) & 0xff); - $this->nextBits += 8; - - if ($this->nextBits < $this->bitsToGet) { - $this->nextData = ($this->nextData << 8) | (\ord($this->data[$this->bytePointer++]) & 0xff); - $this->nextBits += 8; - } - - $code = ($this->nextData >> ($this->nextBits - $this->bitsToGet)) & $this->andTable[$this->bitsToGet - 9]; - $this->nextBits -= $this->bitsToGet; - - return $code; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Filter/LzwException.php b/vendor/setasign/fpdi/src/PdfParser/Filter/LzwException.php deleted file mode 100644 index 9f42038..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Filter/LzwException.php +++ /dev/null @@ -1,22 +0,0 @@ -streamReader = $streamReader; - $this->tokenizer = new Tokenizer($streamReader); - } - - /** - * Removes cycled references. - * - * @internal - */ - public function cleanUp() - { - $this->xref = null; - } - - /** - * Get the stream reader instance. - * - * @return StreamReader - */ - public function getStreamReader() - { - return $this->streamReader; - } - - /** - * Get the tokenizer instance. - * - * @return Tokenizer - */ - public function getTokenizer() - { - return $this->tokenizer; - } - - /** - * Resolves the file header. - * - * @throws PdfParserException - * @return int - */ - protected function resolveFileHeader() - { - if ($this->fileHeader) { - return $this->fileHeaderOffset; - } - - $this->streamReader->reset(0); - $maxIterations = 1000; - while (true) { - $buffer = $this->streamReader->getBuffer(false); - $offset = \strpos($buffer, '%PDF-'); - if ($offset === false) { - if (!$this->streamReader->increaseLength(100) || (--$maxIterations === 0)) { - throw new PdfParserException( - 'Unable to find PDF file header.', - PdfParserException::FILE_HEADER_NOT_FOUND - ); - } - continue; - } - break; - } - - $this->fileHeaderOffset = $offset; - $this->streamReader->setOffset($offset); - - $this->fileHeader = \trim($this->streamReader->readLine()); - return $this->fileHeaderOffset; - } - - /** - * Get the cross-reference instance. - * - * @return CrossReference - * @throws CrossReferenceException - * @throws PdfParserException - */ - public function getCrossReference() - { - if ($this->xref === null) { - $this->xref = new CrossReference($this, $this->resolveFileHeader()); - } - - return $this->xref; - } - - /** - * Get the PDF version. - * - * @return int[] An array of major and minor version. - * @throws PdfParserException - */ - public function getPdfVersion() - { - $this->resolveFileHeader(); - - if (\preg_match('/%PDF-(\d)\.(\d)/', $this->fileHeader, $result) === 0) { - throw new PdfParserException( - 'Unable to extract PDF version from file header.', - PdfParserException::PDF_VERSION_NOT_FOUND - ); - } - list(, $major, $minor) = $result; - - $catalog = $this->getCatalog(); - if (isset($catalog->value['Version'])) { - $versionParts = \explode( - '.', - PdfName::unescape(PdfType::resolve($catalog->value['Version'], $this)->value) - ); - if (count($versionParts) === 2) { - list($major, $minor) = $versionParts; - } - } - - return [(int) $major, (int) $minor]; - } - - /** - * Get the catalog dictionary. - * - * @return PdfDictionary - * @throws Type\PdfTypeException - * @throws CrossReferenceException - * @throws PdfParserException - */ - public function getCatalog() - { - $trailer = $this->getCrossReference()->getTrailer(); - - $catalog = PdfType::resolve(PdfDictionary::get($trailer, 'Root'), $this); - - return PdfDictionary::ensure($catalog); - } - - /** - * Get an indirect object by its object number. - * - * @param int $objectNumber - * @param bool $cache - * @return PdfIndirectObject - * @throws CrossReferenceException - * @throws PdfParserException - */ - public function getIndirectObject($objectNumber, $cache = false) - { - $objectNumber = (int) $objectNumber; - if (isset($this->objects[$objectNumber])) { - return $this->objects[$objectNumber]; - } - - $object = $this->getCrossReference()->getIndirectObject($objectNumber); - - if ($cache) { - $this->objects[$objectNumber] = $object; - } - - return $object; - } - - /** - * Read a PDF value. - * - * @param null|bool|string $token - * @param null|string $expectedType - * @return false|PdfArray|PdfBoolean|PdfDictionary|PdfHexString|PdfIndirectObject|PdfIndirectObjectReference|PdfName|PdfNull|PdfNumeric|PdfStream|PdfString|PdfToken - * @throws Type\PdfTypeException - */ - public function readValue($token = null, $expectedType = null) - { - if ($token === null) { - $token = $this->tokenizer->getNextToken(); - } - - if ($token === false) { - if ($expectedType !== null) { - throw new Type\PdfTypeException('Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE); - } - return false; - } - - switch ($token) { - case '(': - $this->ensureExpectedType($token, $expectedType); - return $this->parsePdfString(); - - case '<': - if ($this->streamReader->getByte() === '<') { - $this->ensureExpectedType('<<', $expectedType); - $this->streamReader->addOffset(1); - return $this->parsePdfDictionary(); - } - - $this->ensureExpectedType($token, $expectedType); - return $this->parsePdfHexString(); - - case '/': - $this->ensureExpectedType($token, $expectedType); - return $this->parsePdfName(); - - case '[': - $this->ensureExpectedType($token, $expectedType); - return $this->parsePdfArray(); - - default: - if (\is_numeric($token)) { - if (($token2 = $this->tokenizer->getNextToken()) !== false) { - if (\is_numeric($token2) && ($token3 = $this->tokenizer->getNextToken()) !== false) { - switch ($token3) { - case 'obj': - if ($expectedType !== null && $expectedType !== PdfIndirectObject::class) { - throw new Type\PdfTypeException( - 'Got unexpected token type.', - Type\PdfTypeException::INVALID_DATA_TYPE - ); - } - - return $this->parsePdfIndirectObject((int)$token, (int)$token2); - case 'R': - if ( - $expectedType !== null && - $expectedType !== PdfIndirectObjectReference::class - ) { - throw new Type\PdfTypeException( - 'Got unexpected token type.', - Type\PdfTypeException::INVALID_DATA_TYPE - ); - } - - return PdfIndirectObjectReference::create((int)$token, (int)$token2); - } - - $this->tokenizer->pushStack($token3); - } - - $this->tokenizer->pushStack($token2); - } - - if ($expectedType !== null && $expectedType !== PdfNumeric::class) { - throw new Type\PdfTypeException( - 'Got unexpected token type.', - Type\PdfTypeException::INVALID_DATA_TYPE - ); - } - return PdfNumeric::create($token + 0); - } - - if ($token === 'true' || $token === 'false') { - $this->ensureExpectedType($token, $expectedType); - return PdfBoolean::create($token === 'true'); - } - - if ($token === 'null') { - $this->ensureExpectedType($token, $expectedType); - return new PdfNull(); - } - - if ($expectedType !== null && $expectedType !== PdfToken::class) { - throw new Type\PdfTypeException( - 'Got unexpected token type.', - Type\PdfTypeException::INVALID_DATA_TYPE - ); - } - - $v = new PdfToken(); - $v->value = $token; - - return $v; - } - } - - /** - * @return PdfString - */ - protected function parsePdfString() - { - return PdfString::parse($this->streamReader); - } - - /** - * @return false|PdfHexString - */ - protected function parsePdfHexString() - { - return PdfHexString::parse($this->streamReader); - } - - /** - * @return bool|PdfDictionary - * @throws PdfTypeException - */ - protected function parsePdfDictionary() - { - return PdfDictionary::parse($this->tokenizer, $this->streamReader, $this); - } - - /** - * @return PdfName - */ - protected function parsePdfName() - { - return PdfName::parse($this->tokenizer, $this->streamReader); - } - - /** - * @return false|PdfArray - * @throws PdfTypeException - */ - protected function parsePdfArray() - { - return PdfArray::parse($this->tokenizer, $this); - } - - /** - * @param int $objectNumber - * @param int $generationNumber - * @return false|PdfIndirectObject - * @throws Type\PdfTypeException - */ - protected function parsePdfIndirectObject($objectNumber, $generationNumber) - { - return PdfIndirectObject::parse( - $objectNumber, - $generationNumber, - $this, - $this->tokenizer, - $this->streamReader - ); - } - - /** - * Ensures that the token will evaluate to an expected object type (or not). - * - * @param string $token - * @param string|null $expectedType - * @return bool - * @throws Type\PdfTypeException - */ - protected function ensureExpectedType($token, $expectedType) - { - static $mapping = [ - '(' => PdfString::class, - '<' => PdfHexString::class, - '<<' => PdfDictionary::class, - '/' => PdfName::class, - '[' => PdfArray::class, - 'true' => PdfBoolean::class, - 'false' => PdfBoolean::class, - 'null' => PdfNull::class - ]; - - if ($expectedType === null || $mapping[$token] === $expectedType) { - return true; - } - - throw new Type\PdfTypeException('Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE); - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/PdfParserException.php b/vendor/setasign/fpdi/src/PdfParser/PdfParserException.php deleted file mode 100644 index 0629d9d..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/PdfParserException.php +++ /dev/null @@ -1,49 +0,0 @@ -stream = $stream; - $this->closeStream = $closeStream; - $this->reset(); - } - - /** - * The destructor. - */ - public function __destruct() - { - $this->cleanUp(); - } - - /** - * Closes the file handle. - */ - public function cleanUp() - { - if ($this->closeStream && is_resource($this->stream)) { - \fclose($this->stream); - } - } - - /** - * Returns the byte length of the buffer. - * - * @param bool $atOffset - * @return int - */ - public function getBufferLength($atOffset = false) - { - if ($atOffset === false) { - return $this->bufferLength; - } - - return $this->bufferLength - $this->offset; - } - - /** - * Get the current position in the stream. - * - * @return int - */ - public function getPosition() - { - return $this->position; - } - - /** - * Returns the current buffer. - * - * @param bool $atOffset - * @return string - */ - public function getBuffer($atOffset = true) - { - if ($atOffset === false) { - return $this->buffer; - } - - $string = \substr($this->buffer, $this->offset); - - return (string) $string; - } - - /** - * Gets a byte at a specific position in the buffer. - * - * If the position is invalid the method will return false. - * - * If the $position parameter is set to null the value of $this->offset will be used. - * - * @param int|null $position - * @return string|bool - */ - public function getByte($position = null) - { - $position = (int) ($position !== null ? $position : $this->offset); - if ( - $position >= $this->bufferLength - && (!$this->increaseLength() || $position >= $this->bufferLength) - ) { - return false; - } - - return $this->buffer[$position]; - } - - /** - * Returns a byte at a specific position, and set the offset to the next byte position. - * - * If the position is invalid the method will return false. - * - * If the $position parameter is set to null the value of $this->offset will be used. - * - * @param int|null $position - * @return string|bool - */ - public function readByte($position = null) - { - if ($position !== null) { - $position = (int) $position; - // check if needed bytes are available in the current buffer - if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) { - $this->reset($position); - $offset = $this->offset; - } else { - $offset = $position - $this->position; - } - } else { - $offset = $this->offset; - } - - if ( - $offset >= $this->bufferLength - && ((!$this->increaseLength()) || $offset >= $this->bufferLength) - ) { - return false; - } - - $this->offset = $offset + 1; - return $this->buffer[$offset]; - } - - /** - * Read bytes from the current or a specific offset position and set the internal pointer to the next byte. - * - * If the position is invalid the method will return false. - * - * If the $position parameter is set to null the value of $this->offset will be used. - * - * @param int $length - * @param int|null $position - * @return string|false - */ - public function readBytes($length, $position = null) - { - $length = (int) $length; - if ($position !== null) { - // check if needed bytes are available in the current buffer - if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) { - $this->reset($position, $length); - $offset = $this->offset; - } else { - $offset = $position - $this->position; - } - } else { - $offset = $this->offset; - } - - if ( - ($offset + $length) > $this->bufferLength - && ((!$this->increaseLength($length)) || ($offset + $length) > $this->bufferLength) - ) { - return false; - } - - $bytes = \substr($this->buffer, $offset, $length); - $this->offset = $offset + $length; - - return $bytes; - } - - /** - * Read a line from the current position. - * - * @param int $length - * @return string|bool - */ - public function readLine($length = 1024) - { - if ($this->ensureContent() === false) { - return false; - } - - $line = ''; - while ($this->ensureContent()) { - $char = $this->readByte(); - - if ($char === "\n") { - break; - } - - if ($char === "\r") { - if ($this->getByte() === "\n") { - $this->addOffset(1); - } - break; - } - - $line .= $char; - - if (\strlen($line) >= $length) { - break; - } - } - - return $line; - } - - /** - * Set the offset position in the current buffer. - * - * @param int $offset - */ - public function setOffset($offset) - { - if ($offset > $this->bufferLength || $offset < 0) { - throw new \OutOfRangeException( - \sprintf('Offset (%s) out of range (length: %s)', $offset, $this->bufferLength) - ); - } - - $this->offset = (int) $offset; - } - - /** - * Returns the current offset in the current buffer. - * - * @return int - */ - public function getOffset() - { - return $this->offset; - } - - /** - * Add an offset to the current offset. - * - * @param int $offset - */ - public function addOffset($offset) - { - $this->setOffset($this->offset + $offset); - } - - /** - * Make sure that there is at least one character beyond the current offset in the buffer. - * - * @return bool - */ - public function ensureContent() - { - while ($this->offset >= $this->bufferLength) { - if (!$this->increaseLength()) { - return false; - } - } - return true; - } - - /** - * Returns the stream. - * - * @return resource - */ - public function getStream() - { - return $this->stream; - } - - /** - * Gets the total available length. - * - * @return int - */ - public function getTotalLength() - { - if ($this->totalLength === null) { - $stat = \fstat($this->stream); - $this->totalLength = $stat['size']; - } - - return $this->totalLength; - } - - /** - * Resets the buffer to a position and re-read the buffer with the given length. - * - * If the $pos parameter is negative the start buffer position will be the $pos'th position from - * the end of the file. - * - * If the $pos parameter is negative and the absolute value is bigger then the totalLength of - * the file $pos will set to zero. - * - * @param int|null $pos Start position of the new buffer - * @param int $length Length of the new buffer. Mustn't be negative - */ - public function reset($pos = 0, $length = 200) - { - if ($pos === null) { - $pos = $this->position + $this->offset; - } elseif ($pos < 0) { - $pos = \max(0, $this->getTotalLength() + $pos); - } - - \fseek($this->stream, $pos); - - $this->position = $pos; - $this->buffer = $length > 0 ? \fread($this->stream, $length) : ''; - $this->bufferLength = \strlen($this->buffer); - $this->offset = 0; - - // If a stream wrapper is in use it is possible that - // length values > 8096 will be ignored, so use the - // increaseLength()-method to correct that behavior - if ($this->bufferLength < $length && $this->increaseLength($length - $this->bufferLength)) { - // increaseLength parameter is $minLength, so cut to have only the required bytes in the buffer - $this->buffer = \substr($this->buffer, 0, $length); - $this->bufferLength = \strlen($this->buffer); - } - } - - /** - * Ensures bytes in the buffer with a specific length and location in the file. - * - * @param int $pos - * @param int $length - * @see reset() - */ - public function ensure($pos, $length) - { - if ( - $pos >= $this->position - && $pos < ($this->position + $this->bufferLength) - && ($this->position + $this->bufferLength) >= ($pos + $length) - ) { - $this->offset = $pos - $this->position; - } else { - $this->reset($pos, $length); - } - } - - /** - * Forcefully read more data into the buffer. - * - * @param int $minLength - * @return bool Returns false if the stream reaches the end - */ - public function increaseLength($minLength = 100) - { - $length = \max($minLength, 100); - - if (\feof($this->stream) || $this->getTotalLength() === $this->position + $this->bufferLength) { - return false; - } - - $newLength = $this->bufferLength + $length; - do { - $this->buffer .= \fread($this->stream, $newLength - $this->bufferLength); - $this->bufferLength = \strlen($this->buffer); - } while (($this->bufferLength !== $newLength) && !\feof($this->stream)); - - return true; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Tokenizer.php b/vendor/setasign/fpdi/src/PdfParser/Tokenizer.php deleted file mode 100644 index 5c1ccd8..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Tokenizer.php +++ /dev/null @@ -1,154 +0,0 @@ -streamReader = $streamReader; - } - - /** - * Get the stream reader instance. - * - * @return StreamReader - */ - public function getStreamReader() - { - return $this->streamReader; - } - - /** - * Clear the token stack. - */ - public function clearStack() - { - $this->stack = []; - } - - /** - * Push a token onto the stack. - * - * @param string $token - */ - public function pushStack($token) - { - $this->stack[] = $token; - } - - /** - * Get next token. - * - * @return bool|string - */ - public function getNextToken() - { - $token = \array_pop($this->stack); - if ($token !== null) { - return $token; - } - - if (($byte = $this->streamReader->readByte()) === false) { - return false; - } - - if (\in_array($byte, ["\x20", "\x0A", "\x0D", "\x0C", "\x09", "\x00"], true)) { - if ($this->leapWhiteSpaces() === false) { - return false; - } - $byte = $this->streamReader->readByte(); - } - - switch ($byte) { - case '/': - case '[': - case ']': - case '(': - case ')': - case '{': - case '}': - case '<': - case '>': - return $byte; - case '%': - $this->streamReader->readLine(); - return $this->getNextToken(); - } - - /* This way is faster than checking single bytes. - */ - $bufferOffset = $this->streamReader->getOffset(); - do { - $lastBuffer = $this->streamReader->getBuffer(false); - $pos = \strcspn( - $lastBuffer, - "\x00\x09\x0A\x0C\x0D\x20()<>[]{}/%", - $bufferOffset - ); - } while ( - // Break the loop if a delimiter or white space char is matched - // in the current buffer or increase the buffers length - $lastBuffer !== false && - ( - $bufferOffset + $pos === \strlen($lastBuffer) && - $this->streamReader->increaseLength() - ) - ); - - $result = \substr($lastBuffer, $bufferOffset - 1, $pos + 1); - $this->streamReader->setOffset($bufferOffset + $pos); - - return $result; - } - - /** - * Leap white spaces. - * - * @return boolean - */ - public function leapWhiteSpaces() - { - do { - if (!$this->streamReader->ensureContent()) { - return false; - } - - $buffer = $this->streamReader->getBuffer(false); - $matches = \strspn($buffer, "\x20\x0A\x0C\x0D\x09\x00", $this->streamReader->getOffset()); - if ($matches > 0) { - $this->streamReader->addOffset($matches); - } - } while ($this->streamReader->getOffset() >= $this->streamReader->getBufferLength()); - - return true; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfArray.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfArray.php deleted file mode 100644 index c7981b6..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfArray.php +++ /dev/null @@ -1,85 +0,0 @@ -getNextToken()) !== ']') { - if ($token === false || ($value = $parser->readValue($token)) === false) { - return false; - } - - $result[] = $value; - } - - $v = new self(); - $v->value = $result; - - return $v; - } - - /** - * Helper method to create an instance. - * - * @param PdfType[] $values - * @return self - */ - public static function create(array $values = []) - { - $v = new self(); - $v->value = $values; - - return $v; - } - - /** - * Ensures that the passed array is a PdfArray instance with a (optional) specific size. - * - * @param mixed $array - * @param null|int $size - * @return self - * @throws PdfTypeException - */ - public static function ensure($array, $size = null) - { - $result = PdfType::ensureType(self::class, $array, 'Array value expected.'); - - if ($size !== null && \count($array->value) !== $size) { - throw new PdfTypeException( - \sprintf('Array with %s entries expected.', $size), - PdfTypeException::INVALID_DATA_SIZE - ); - } - - return $result; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfBoolean.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfBoolean.php deleted file mode 100644 index ad7c5d6..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfBoolean.php +++ /dev/null @@ -1,42 +0,0 @@ -value = (bool) $value; - return $v; - } - - /** - * Ensures that the passed value is a PdfBoolean instance. - * - * @param mixed $value - * @return self - * @throws PdfTypeException - */ - public static function ensure($value) - { - return PdfType::ensureType(self::class, $value, 'Boolean value expected.'); - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfDictionary.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfDictionary.php deleted file mode 100644 index 8991322..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfDictionary.php +++ /dev/null @@ -1,134 +0,0 @@ -getNextToken(); - if ($token === '>' && $streamReader->getByte() === '>') { - $streamReader->addOffset(1); - break; - } - - $key = $parser->readValue($token); - if ($key === false) { - return false; - } - - // ensure the first value to be a Name object - if (!($key instanceof PdfName)) { - $lastToken = null; - // ignore all other entries and search for the closing brackets - while (($token = $tokenizer->getNextToken()) !== '>' && $token !== false && $lastToken !== '>') { - $lastToken = $token; - } - - if ($token === false) { - return false; - } - - break; - } - - - $value = $parser->readValue(); - if ($value === false) { - return false; - } - - if ($value instanceof PdfNull) { - continue; - } - - // catch missing value - if ($value instanceof PdfToken && $value->value === '>' && $streamReader->getByte() === '>') { - $streamReader->addOffset(1); - break; - } - - $entries[$key->value] = $value; - } - - $v = new self(); - $v->value = $entries; - - return $v; - } - - /** - * Helper method to create an instance. - * - * @param PdfType[] $entries The keys are the name entries of the dictionary. - * @return self - */ - public static function create(array $entries = []) - { - $v = new self(); - $v->value = $entries; - - return $v; - } - - /** - * Get a value by its key from a dictionary or a default value. - * - * @param mixed $dictionary - * @param string $key - * @param PdfType|null $default - * @return PdfNull|PdfType - * @throws PdfTypeException - */ - public static function get($dictionary, $key, PdfType $default = null) - { - $dictionary = self::ensure($dictionary); - - if (isset($dictionary->value[$key])) { - return $dictionary->value[$key]; - } - - return $default === null - ? new PdfNull() - : $default; - } - - /** - * Ensures that the passed value is a PdfDictionary instance. - * - * @param mixed $dictionary - * @return self - * @throws PdfTypeException - */ - public static function ensure($dictionary) - { - return PdfType::ensureType(self::class, $dictionary, 'Dictionary value expected.'); - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfHexString.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfHexString.php deleted file mode 100644 index cd9d2b6..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfHexString.php +++ /dev/null @@ -1,77 +0,0 @@ -getOffset(); - - while (true) { - $buffer = $streamReader->getBuffer(false); - $pos = \strpos($buffer, '>', $bufferOffset); - if ($pos === false) { - if (!$streamReader->increaseLength()) { - return false; - } - continue; - } - - break; - } - - $result = \substr($buffer, $bufferOffset, $pos - $bufferOffset); - $streamReader->setOffset($pos + 1); - - $v = new self(); - $v->value = $result; - - return $v; - } - - /** - * Helper method to create an instance. - * - * @param string $string The hex encoded string. - * @return self - */ - public static function create($string) - { - $v = new self(); - $v->value = $string; - - return $v; - } - - /** - * Ensures that the passed value is a PdfHexString instance. - * - * @param mixed $hexString - * @return self - * @throws PdfTypeException - */ - public static function ensure($hexString) - { - return PdfType::ensureType(self::class, $hexString, 'Hex string value expected.'); - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfIndirectObject.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfIndirectObject.php deleted file mode 100644 index 72a80e1..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfIndirectObject.php +++ /dev/null @@ -1,103 +0,0 @@ -readValue(); - if ($value === false) { - return false; - } - - $nextToken = $tokenizer->getNextToken(); - if ($nextToken === 'stream') { - $value = PdfStream::parse($value, $reader, $parser); - } elseif ($nextToken !== false) { - $tokenizer->pushStack($nextToken); - } - - $v = new self(); - $v->objectNumber = (int) $objectNumber; - $v->generationNumber = (int) $objectGenerationNumber; - $v->value = $value; - - return $v; - } - - /** - * Helper method to create an instance. - * - * @param int $objectNumber - * @param int $generationNumber - * @param PdfType $value - * @return self - */ - public static function create($objectNumber, $generationNumber, PdfType $value) - { - $v = new self(); - $v->objectNumber = (int) $objectNumber; - $v->generationNumber = (int) $generationNumber; - $v->value = $value; - - return $v; - } - - /** - * Ensures that the passed value is a PdfIndirectObject instance. - * - * @param mixed $indirectObject - * @return self - * @throws PdfTypeException - */ - public static function ensure($indirectObject) - { - return PdfType::ensureType(self::class, $indirectObject, 'Indirect object expected.'); - } - - /** - * The object number. - * - * @var int - */ - public $objectNumber; - - /** - * The generation number. - * - * @var int - */ - public $generationNumber; -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfIndirectObjectReference.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfIndirectObjectReference.php deleted file mode 100644 index 975e9e8..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfIndirectObjectReference.php +++ /dev/null @@ -1,52 +0,0 @@ -value = (int) $objectNumber; - $v->generationNumber = (int) $generationNumber; - - return $v; - } - - /** - * Ensures that the passed value is a PdfIndirectObject instance. - * - * @param mixed $value - * @return self - * @throws PdfTypeException - */ - public static function ensure($value) - { - return PdfType::ensureType(self::class, $value, 'Indirect reference value expected.'); - } - - /** - * The generation number. - * - * @var int - */ - public $generationNumber; -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfName.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfName.php deleted file mode 100644 index 0fbfe52..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfName.php +++ /dev/null @@ -1,82 +0,0 @@ -getByte(), "\x00\x09\x0A\x0C\x0D\x20()<>[]{}/%") === 0) { - $v->value = (string) $tokenizer->getNextToken(); - return $v; - } - - $v->value = ''; - return $v; - } - - /** - * Unescapes a name string. - * - * @param string $value - * @return string - */ - public static function unescape($value) - { - if (strpos($value, '#') === false) { - return $value; - } - - return preg_replace_callback('/#([a-fA-F\d]{2})/', function ($matches) { - return chr(hexdec($matches[1])); - }, $value); - } - - /** - * Helper method to create an instance. - * - * @param string $string - * @return self - */ - public static function create($string) - { - $v = new self(); - $v->value = $string; - - return $v; - } - - /** - * Ensures that the passed value is a PdfName instance. - * - * @param mixed $name - * @return self - * @throws PdfTypeException - */ - public static function ensure($name) - { - return PdfType::ensureType(self::class, $name, 'Name value expected.'); - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfNull.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfNull.php deleted file mode 100644 index 4830564..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfNull.php +++ /dev/null @@ -1,19 +0,0 @@ -value = $value + 0; - - return $v; - } - - /** - * Ensures that the passed value is a PdfNumeric instance. - * - * @param mixed $value - * @return self - * @throws PdfTypeException - */ - public static function ensure($value) - { - return PdfType::ensureType(self::class, $value, 'Numeric value expected.'); - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfStream.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfStream.php deleted file mode 100644 index cfa2cdb..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfStream.php +++ /dev/null @@ -1,352 +0,0 @@ -value = $dictionary; - $v->reader = $reader; - $v->parser = $parser; - - $offset = $reader->getOffset(); - - // Find the first "newline" - while (($firstByte = $reader->getByte($offset)) !== false) { - $offset++; - if ($firstByte === "\n" || $firstByte === "\r") { - break; - } - } - - if ($firstByte === false) { - throw new PdfTypeException( - 'Unable to parse stream data. No newline after the stream keyword found.', - PdfTypeException::NO_NEWLINE_AFTER_STREAM_KEYWORD - ); - } - - $sndByte = $reader->getByte($offset); - if ($sndByte === "\n" && $firstByte !== "\n") { - $offset++; - } - - $reader->setOffset($offset); - // let's only save the byte-offset and read the stream only when needed - $v->stream = $reader->getPosition() + $reader->getOffset(); - - return $v; - } - - /** - * Helper method to create an instance. - * - * @param PdfDictionary $dictionary - * @param string $stream - * @return self - */ - public static function create(PdfDictionary $dictionary, $stream) - { - $v = new self(); - $v->value = $dictionary; - $v->stream = (string) $stream; - - return $v; - } - - /** - * Ensures that the passed value is a PdfStream instance. - * - * @param mixed $stream - * @return self - * @throws PdfTypeException - */ - public static function ensure($stream) - { - return PdfType::ensureType(self::class, $stream, 'Stream value expected.'); - } - - /** - * The stream or its byte-offset position. - * - * @var int|string - */ - protected $stream; - - /** - * The stream reader instance. - * - * @var StreamReader|null - */ - protected $reader; - - /** - * The PDF parser instance. - * - * @var PdfParser - */ - protected $parser; - - /** - * Get the stream data. - * - * @param bool $cache Whether cache the stream data or not. - * @return bool|string - * @throws PdfTypeException - * @throws CrossReferenceException - * @throws PdfParserException - */ - public function getStream($cache = false) - { - if (\is_int($this->stream)) { - $length = PdfDictionary::get($this->value, 'Length'); - if ($this->parser !== null) { - $length = PdfType::resolve($length, $this->parser); - } - - if (!($length instanceof PdfNumeric) || $length->value === 0) { - $this->reader->reset($this->stream, 100000); - $buffer = $this->extractStream(); - } else { - $this->reader->reset($this->stream, $length->value); - $buffer = $this->reader->getBuffer(false); - if ($this->parser !== null) { - $this->reader->reset($this->stream + strlen($buffer)); - $this->parser->getTokenizer()->clearStack(); - $token = $this->parser->readValue(); - if ($token === false || !($token instanceof PdfToken) || $token->value !== 'endstream') { - $this->reader->reset($this->stream, 100000); - $buffer = $this->extractStream(); - $this->reader->reset($this->stream + strlen($buffer)); - } - } - } - - if ($cache === false) { - return $buffer; - } - - $this->stream = $buffer; - $this->reader = null; - } - - return $this->stream; - } - - /** - * Extract the stream "manually". - * - * @return string - * @throws PdfTypeException - */ - protected function extractStream() - { - while (true) { - $buffer = $this->reader->getBuffer(false); - $length = \strpos($buffer, 'endstream'); - if ($length === false) { - if (!$this->reader->increaseLength(100000)) { - throw new PdfTypeException('Cannot extract stream.'); - } - continue; - } - break; - } - - $buffer = \substr($buffer, 0, $length); - $lastByte = \substr($buffer, -1); - - /* Check for EOL marker = - * CARRIAGE RETURN (\r) and a LINE FEED (\n) or just a LINE FEED (\n}, - * and not by a CARRIAGE RETURN (\r) alone - */ - if ($lastByte === "\n") { - $buffer = \substr($buffer, 0, -1); - - $lastByte = \substr($buffer, -1); - if ($lastByte === "\r") { - $buffer = \substr($buffer, 0, -1); - } - } - - // There are streams in the wild, which have only white signs in them but need to be parsed manually due - // to a problem encountered before (e.g. Length === 0). We should set them to empty streams to avoid problems - // in further processing (e.g. applying of filters). - if (trim($buffer) === '') { - $buffer = ''; - } - - return $buffer; - } - - /** - * Get all filters defined for this stream. - * - * @return PdfType[] - * @throws PdfTypeException - */ - public function getFilters() - { - $filters = PdfDictionary::get($this->value, 'Filter'); - if ($filters instanceof PdfNull) { - return []; - } - - if ($filters instanceof PdfArray) { - $filters = $filters->value; - } else { - $filters = [$filters]; - } - - return $filters; - } - - /** - * Get the unfiltered stream data. - * - * @return string - * @throws FilterException - * @throws PdfParserException - */ - public function getUnfilteredStream() - { - $stream = $this->getStream(); - $filters = $this->getFilters(); - if ($filters === []) { - return $stream; - } - - $decodeParams = PdfDictionary::get($this->value, 'DecodeParms'); - if ($decodeParams instanceof PdfArray) { - $decodeParams = $decodeParams->value; - } else { - $decodeParams = [$decodeParams]; - } - - foreach ($filters as $key => $filter) { - if (!($filter instanceof PdfName)) { - continue; - } - - $decodeParam = null; - if (isset($decodeParams[$key])) { - $decodeParam = ($decodeParams[$key] instanceof PdfDictionary ? $decodeParams[$key] : null); - } - - switch ($filter->value) { - case 'FlateDecode': - case 'Fl': - case 'LZWDecode': - case 'LZW': - if (\strpos($filter->value, 'LZW') === 0) { - $filterObject = new Lzw(); - } else { - $filterObject = new Flate(); - } - - $stream = $filterObject->decode($stream); - - if ($decodeParam instanceof PdfDictionary) { - $predictor = PdfDictionary::get($decodeParam, 'Predictor', PdfNumeric::create(1)); - if ($predictor->value !== 1) { - if (!\class_exists(Predictor::class)) { - throw new PdfParserException( - 'This PDF document makes use of features which are only implemented in the ' . - 'commercial "FPDI PDF-Parser" add-on (see https://www.setasign.com/fpdi-pdf-' . - 'parser).', - PdfParserException::IMPLEMENTED_IN_FPDI_PDF_PARSER - ); - } - - $colors = PdfDictionary::get($decodeParam, 'Colors', PdfNumeric::create(1)); - $bitsPerComponent = PdfDictionary::get( - $decodeParam, - 'BitsPerComponent', - PdfNumeric::create(8) - ); - - $columns = PdfDictionary::get($decodeParam, 'Columns', PdfNumeric::create(1)); - - $filterObject = new Predictor( - $predictor->value, - $colors->value, - $bitsPerComponent->value, - $columns->value - ); - - $stream = $filterObject->decode($stream); - } - } - - break; - case 'ASCII85Decode': - case 'A85': - $filterObject = new Ascii85(); - $stream = $filterObject->decode($stream); - break; - - case 'ASCIIHexDecode': - case 'AHx': - $filterObject = new AsciiHex(); - $stream = $filterObject->decode($stream); - break; - - case 'Crypt': - if (!$decodeParam instanceof PdfDictionary) { - break; - } - // Filter is "Identity" - $name = PdfDictionary::get($decodeParam, 'Name'); - if (!$name instanceof PdfName || $name->value !== 'Identity') { - break; - } - - throw new FilterException( - 'Support for Crypt filters other than "Identity" is not implemented.', - FilterException::UNSUPPORTED_FILTER - ); - - default: - throw new FilterException( - \sprintf('Unsupported filter "%s".', $filter->value), - FilterException::UNSUPPORTED_FILTER - ); - } - } - - return $stream; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfString.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfString.php deleted file mode 100644 index dc4ce33..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfString.php +++ /dev/null @@ -1,202 +0,0 @@ -getOffset(); - $openBrackets = 1; - do { - $buffer = $streamReader->getBuffer(false); - for ($length = \strlen($buffer); $openBrackets !== 0 && $pos < $length; $pos++) { - switch ($buffer[$pos]) { - case '(': - $openBrackets++; - break; - case ')': - $openBrackets--; - break; - case '\\': - $pos++; - } - } - } while ($openBrackets !== 0 && $streamReader->increaseLength()); - - $result = \substr($buffer, $startPos, $openBrackets + $pos - $startPos - 1); - $streamReader->setOffset($pos); - - $v = new self(); - $v->value = $result; - - return $v; - } - - /** - * Helper method to create an instance. - * - * @param string $value The string needs to be escaped accordingly. - * @return self - */ - public static function create($value) - { - $v = new self(); - $v->value = $value; - - return $v; - } - - /** - * Ensures that the passed value is a PdfString instance. - * - * @param mixed $string - * @return self - * @throws PdfTypeException - */ - public static function ensure($string) - { - return PdfType::ensureType(self::class, $string, 'String value expected.'); - } - - /** - * Escapes sequences in a string according to the PDF specification. - * - * @param string $s - * @return string - */ - public static function escape($s) - { - // Still a bit faster, than direct replacing - if ( - \strpos($s, '\\') !== false || - \strpos($s, ')') !== false || - \strpos($s, '(') !== false || - \strpos($s, "\x0D") !== false || - \strpos($s, "\x0A") !== false || - \strpos($s, "\x09") !== false || - \strpos($s, "\x08") !== false || - \strpos($s, "\x0C") !== false - ) { - // is faster than strtr(...) - return \str_replace( - ['\\', ')', '(', "\x0D", "\x0A", "\x09", "\x08", "\x0C"], - ['\\\\', '\\)', '\\(', '\r', '\n', '\t', '\b', '\f'], - $s - ); - } - - return $s; - } - - /** - * Unescapes escaped sequences in a PDF string according to the PDF specification. - * - * @param string $s - * @return string - */ - public static function unescape($s) - { - $out = ''; - /** @noinspection ForeachInvariantsInspection */ - for ($count = 0, $n = \strlen($s); $count < $n; $count++) { - if ($s[$count] !== '\\') { - $out .= $s[$count]; - } else { - // A backslash at the end of the string - ignore it - if ($count === ($n - 1)) { - break; - } - - switch ($s[++$count]) { - case ')': - case '(': - case '\\': - $out .= $s[$count]; - break; - - case 'f': - $out .= "\x0C"; - break; - - case 'b': - $out .= "\x08"; - break; - - case 't': - $out .= "\x09"; - break; - - case 'r': - $out .= "\x0D"; - break; - - case 'n': - $out .= "\x0A"; - break; - - case "\r": - if ($count !== $n - 1 && $s[$count + 1] === "\n") { - $count++; - } - break; - - case "\n": - break; - - default: - $actualChar = \ord($s[$count]); - // ascii 48 = number 0 - // ascii 57 = number 9 - if ($actualChar >= 48 && $actualChar <= 57) { - $oct = '' . $s[$count]; - - /** @noinspection NotOptimalIfConditionsInspection */ - if ( - $count + 1 < $n - && \ord($s[$count + 1]) >= 48 - && \ord($s[$count + 1]) <= 57 - ) { - $count++; - $oct .= $s[$count]; - - /** @noinspection NotOptimalIfConditionsInspection */ - if ( - $count + 1 < $n - && \ord($s[$count + 1]) >= 48 - && \ord($s[$count + 1]) <= 57 - ) { - $oct .= $s[++$count]; - } - } - - $out .= \chr(\octdec($oct)); - } else { - // If the character is not one of those defined, the backslash is ignored - $out .= $s[$count]; - } - } - } - } - return $out; - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfToken.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfToken.php deleted file mode 100644 index 8293c28..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfToken.php +++ /dev/null @@ -1,43 +0,0 @@ -value = $token; - - return $v; - } - - /** - * Ensures that the passed value is a PdfToken instance. - * - * @param mixed $token - * @return self - * @throws PdfTypeException - */ - public static function ensure($token) - { - return PdfType::ensureType(self::class, $token, 'Token value expected.'); - } -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfType.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfType.php deleted file mode 100644 index ecd18b3..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfType.php +++ /dev/null @@ -1,106 +0,0 @@ -value, $parser, $stopAtIndirectObject); - } - - if ($value instanceof PdfIndirectObjectReference) { - return self::resolve($parser->getIndirectObject($value->value), $parser, $stopAtIndirectObject); - } - - return $value; - } - - /** - * Ensure that a value is an instance of a specific PDF type. - * - * @param string $type - * @param PdfType $value - * @param string $errorMessage - * @return mixed - * @throws PdfTypeException - */ - protected static function ensureType($type, $value, $errorMessage) - { - if (!($value instanceof $type)) { - throw new PdfTypeException( - $errorMessage, - PdfTypeException::INVALID_DATA_TYPE - ); - } - - return $value; - } - - /** - * Flatten indirect object references to direct objects. - * - * @param PdfType $value - * @param PdfParser $parser - * @return PdfType - * @throws CrossReferenceException - * @throws PdfParserException - */ - public static function flatten(PdfType $value, PdfParser $parser) - { - if ($value instanceof PdfIndirectObjectReference) { - return self::flatten(self::resolve($value, $parser), $parser); - } - - if ($value instanceof PdfDictionary || $value instanceof PdfArray) { - foreach ($value->value as $key => $_value) { - $value->value[$key] = self::flatten($_value, $parser); - } - } - - if ($value instanceof PdfStream) { - throw new PdfTypeException('There is a stream object found which cannot be flattened to a direct object.'); - } - - return $value; - } - - /** - * The value of the PDF type. - * - * @var mixed - */ - public $value; -} diff --git a/vendor/setasign/fpdi/src/PdfParser/Type/PdfTypeException.php b/vendor/setasign/fpdi/src/PdfParser/Type/PdfTypeException.php deleted file mode 100644 index 88d2c20..0000000 --- a/vendor/setasign/fpdi/src/PdfParser/Type/PdfTypeException.php +++ /dev/null @@ -1,24 +0,0 @@ -value; - $ax = PdfNumeric::ensure(PdfType::resolve($array[0], $parser))->value; - $ay = PdfNumeric::ensure(PdfType::resolve($array[1], $parser))->value; - $bx = PdfNumeric::ensure(PdfType::resolve($array[2], $parser))->value; - $by = PdfNumeric::ensure(PdfType::resolve($array[3], $parser))->value; - - return new self($ax, $ay, $bx, $by); - } - - public static function byVectors(Vector $ll, Vector $ur) - { - return new self($ll->getX(), $ll->getY(), $ur->getX(), $ur->getY()); - } - - /** - * Rectangle constructor. - * - * @param float|int $ax - * @param float|int $ay - * @param float|int $bx - * @param float|int $by - */ - public function __construct($ax, $ay, $bx, $by) - { - $this->llx = \min($ax, $bx); - $this->lly = \min($ay, $by); - $this->urx = \max($ax, $bx); - $this->ury = \max($ay, $by); - } - - /** - * Get the width of the rectangle. - * - * @return float|int - */ - public function getWidth() - { - return $this->urx - $this->llx; - } - - /** - * Get the height of the rectangle. - * - * @return float|int - */ - public function getHeight() - { - return $this->ury - $this->lly; - } - - /** - * Get the lower left abscissa. - * - * @return float|int - */ - public function getLlx() - { - return $this->llx; - } - - /** - * Get the lower left ordinate. - * - * @return float|int - */ - public function getLly() - { - return $this->lly; - } - - /** - * Get the upper right abscissa. - * - * @return float|int - */ - public function getUrx() - { - return $this->urx; - } - - /** - * Get the upper right ordinate. - * - * @return float|int - */ - public function getUry() - { - return $this->ury; - } - - /** - * Get the rectangle as an array. - * - * @return array - */ - public function toArray() - { - return [ - $this->llx, - $this->lly, - $this->urx, - $this->ury - ]; - } - - /** - * Get the rectangle as a PdfArray. - * - * @return PdfArray - */ - public function toPdfArray() - { - $array = new PdfArray(); - $array->value[] = PdfNumeric::create($this->llx); - $array->value[] = PdfNumeric::create($this->lly); - $array->value[] = PdfNumeric::create($this->urx); - $array->value[] = PdfNumeric::create($this->ury); - - return $array; - } -} diff --git a/vendor/setasign/fpdi/src/PdfReader/Page.php b/vendor/setasign/fpdi/src/PdfReader/Page.php deleted file mode 100644 index ad3c0c2..0000000 --- a/vendor/setasign/fpdi/src/PdfReader/Page.php +++ /dev/null @@ -1,420 +0,0 @@ -pageObject = $page; - $this->parser = $parser; - } - - /** - * Get the indirect object of this page. - * - * @return PdfIndirectObject - */ - public function getPageObject() - { - return $this->pageObject; - } - - /** - * Get the dictionary of this page. - * - * @return PdfDictionary - * @throws PdfParserException - * @throws PdfTypeException - * @throws CrossReferenceException - */ - public function getPageDictionary() - { - if ($this->pageDictionary === null) { - $this->pageDictionary = PdfDictionary::ensure(PdfType::resolve($this->getPageObject(), $this->parser)); - } - - return $this->pageDictionary; - } - - /** - * Get a page attribute. - * - * @param string $name - * @param bool $inherited - * @return PdfType|null - * @throws PdfParserException - * @throws PdfTypeException - * @throws CrossReferenceException - */ - public function getAttribute($name, $inherited = true) - { - $dict = $this->getPageDictionary(); - - if (isset($dict->value[$name])) { - return $dict->value[$name]; - } - - $inheritedKeys = ['Resources', 'MediaBox', 'CropBox', 'Rotate']; - if ($inherited && \in_array($name, $inheritedKeys, true)) { - if ($this->inheritedAttributes === null) { - $this->inheritedAttributes = []; - $inheritedKeys = \array_filter($inheritedKeys, function ($key) use ($dict) { - return !isset($dict->value[$key]); - }); - - if (\count($inheritedKeys) > 0) { - $parentDict = PdfType::resolve(PdfDictionary::get($dict, 'Parent'), $this->parser); - while ($parentDict instanceof PdfDictionary) { - foreach ($inheritedKeys as $index => $key) { - if (isset($parentDict->value[$key])) { - $this->inheritedAttributes[$key] = $parentDict->value[$key]; - unset($inheritedKeys[$index]); - } - } - - /** @noinspection NotOptimalIfConditionsInspection */ - if (isset($parentDict->value['Parent']) && \count($inheritedKeys) > 0) { - $parentDict = PdfType::resolve(PdfDictionary::get($parentDict, 'Parent'), $this->parser); - } else { - break; - } - } - } - } - - if (isset($this->inheritedAttributes[$name])) { - return $this->inheritedAttributes[$name]; - } - } - - return null; - } - - /** - * Get the rotation value. - * - * @return int - * @throws PdfParserException - * @throws PdfTypeException - * @throws CrossReferenceException - */ - public function getRotation() - { - $rotation = $this->getAttribute('Rotate'); - if ($rotation === null) { - return 0; - } - - $rotation = PdfNumeric::ensure(PdfType::resolve($rotation, $this->parser))->value % 360; - - if ($rotation < 0) { - $rotation += 360; - } - - return $rotation; - } - - /** - * Get a boundary of this page. - * - * @param string $box - * @param bool $fallback - * @return bool|Rectangle - * @throws PdfParserException - * @throws PdfTypeException - * @throws CrossReferenceException - * @see PageBoundaries - */ - public function getBoundary($box = PageBoundaries::CROP_BOX, $fallback = true) - { - $value = $this->getAttribute($box); - - if ($value !== null) { - return Rectangle::byPdfArray($value, $this->parser); - } - - if ($fallback === false) { - return false; - } - - switch ($box) { - case PageBoundaries::BLEED_BOX: - case PageBoundaries::TRIM_BOX: - case PageBoundaries::ART_BOX: - return $this->getBoundary(PageBoundaries::CROP_BOX, true); - case PageBoundaries::CROP_BOX: - return $this->getBoundary(PageBoundaries::MEDIA_BOX, true); - } - - return false; - } - - /** - * Get the width and height of this page. - * - * @param string $box - * @param bool $fallback - * @return array|bool - * @throws PdfParserException - * @throws PdfTypeException - * @throws CrossReferenceException - */ - public function getWidthAndHeight($box = PageBoundaries::CROP_BOX, $fallback = true) - { - $boundary = $this->getBoundary($box, $fallback); - if ($boundary === false) { - return false; - } - - $rotation = $this->getRotation(); - $interchange = ($rotation / 90) % 2; - - return [ - $interchange ? $boundary->getHeight() : $boundary->getWidth(), - $interchange ? $boundary->getWidth() : $boundary->getHeight() - ]; - } - - /** - * Get the raw content stream. - * - * @return string - * @throws PdfReaderException - * @throws PdfTypeException - * @throws FilterException - * @throws PdfParserException - */ - public function getContentStream() - { - $dict = $this->getPageDictionary(); - $contents = PdfType::resolve(PdfDictionary::get($dict, 'Contents'), $this->parser); - if ($contents instanceof PdfNull) { - return ''; - } - - if ($contents instanceof PdfArray) { - $result = []; - foreach ($contents->value as $content) { - $content = PdfType::resolve($content, $this->parser); - if (!($content instanceof PdfStream)) { - continue; - } - $result[] = $content->getUnfilteredStream(); - } - - return \implode("\n", $result); - } - - if ($contents instanceof PdfStream) { - return $contents->getUnfilteredStream(); - } - - throw new PdfReaderException( - 'Array or stream expected.', - PdfReaderException::UNEXPECTED_DATA_TYPE - ); - } - - /** - * Get information of all external links on this page. - * - * All coordinates are normalized in view to rotation and translation of the boundary-box, so that their - * origin is lower-left. - * - * @return array - */ - public function getExternalLinks($box = PageBoundaries::CROP_BOX) - { - try { - $dict = $this->getPageDictionary(); - $annotations = PdfType::resolve(PdfDictionary::get($dict, 'Annots'), $this->parser); - } catch (FpdiException $e) { - return []; - } - - if (!$annotations instanceof PdfArray) { - return []; - } - - $links = []; - - foreach ($annotations->value as $entry) { - try { - $annotation = PdfType::resolve($entry, $this->parser); - - $value = PdfType::resolve(PdfDictionary::get($annotation, 'Subtype'), $this->parser); - if (!$value instanceof PdfName || $value->value !== 'Link') { - continue; - } - - $dest = PdfType::resolve(PdfDictionary::get($annotation, 'Dest'), $this->parser); - if (!$dest instanceof PdfNull) { - continue; - } - - $action = PdfType::resolve(PdfDictionary::get($annotation, 'A'), $this->parser); - if (!$action instanceof PdfDictionary) { - continue; - } - - $actionType = PdfType::resolve(PdfDictionary::get($action, 'S'), $this->parser); - if (!$actionType instanceof PdfName || $actionType->value !== 'URI') { - continue; - } - - $uri = PdfType::resolve(PdfDictionary::get($action, 'URI'), $this->parser); - if ($uri instanceof PdfString) { - $uriValue = PdfString::unescape($uri->value); - } elseif ($uri instanceof PdfHexString) { - $uriValue = \hex2bin($uri->value); - } else { - continue; - } - - $rect = PdfType::resolve(PdfDictionary::get($annotation, 'Rect'), $this->parser); - if (!$rect instanceof PdfArray || count($rect->value) !== 4) { - continue; - } - - $rect = Rectangle::byPdfArray($rect, $this->parser); - if ($rect->getWidth() === 0 || $rect->getHeight() === 0) { - continue; - } - - $bbox = $this->getBoundary($box); - $rotation = $this->getRotation(); - - $gs = new GraphicsState(); - $gs->translate(-$bbox->getLlx(), -$bbox->getLly()); - $gs->rotate($bbox->getLlx(), $bbox->getLly(), -$rotation); - - switch ($rotation) { - case 90: - $gs->translate(-$bbox->getWidth(), 0); - break; - case 180: - $gs->translate(-$bbox->getWidth(), -$bbox->getHeight()); - break; - case 270: - $gs->translate(0, -$bbox->getHeight()); - break; - } - - $normalizedRect = Rectangle::byVectors( - $gs->toUserSpace(new Vector($rect->getLlx(), $rect->getLly())), - $gs->toUserSpace(new Vector($rect->getUrx(), $rect->getUry())) - ); - - $quadPoints = PdfType::resolve(PdfDictionary::get($annotation, 'QuadPoints'), $this->parser); - $normalizedQuadPoints = []; - if ($quadPoints instanceof PdfArray) { - $quadPointsCount = count($quadPoints->value); - if ($quadPointsCount % 8 === 0) { - for ($i = 0; ($i + 1) < $quadPointsCount; $i += 2) { - $x = PdfNumeric::ensure(PdfType::resolve($quadPoints->value[$i], $this->parser)); - $y = PdfNumeric::ensure(PdfType::resolve($quadPoints->value[$i + 1], $this->parser)); - - $v = $gs->toUserSpace(new Vector($x->value, $y->value)); - $normalizedQuadPoints[] = $v->getX(); - $normalizedQuadPoints[] = $v->getY(); - } - } - } - - // we remove unsupported/unneeded values here - unset( - $annotation->value['P'], - $annotation->value['NM'], - $annotation->value['AP'], - $annotation->value['AS'], - $annotation->value['Type'], - $annotation->value['Subtype'], - $annotation->value['Rect'], - $annotation->value['A'], - $annotation->value['QuadPoints'], - $annotation->value['Rotate'], - $annotation->value['M'], - $annotation->value['StructParent'], - $annotation->value['OC'] - ); - - // ...and flatten the PDF object to eliminate any indirect references. - // Indirect references are a problem when writing the output in FPDF - // because FPDF uses pre-calculated object numbers while FPDI creates - // them at runtime. - $annotation = PdfType::flatten($annotation, $this->parser); - - $links[] = [ - 'rect' => $normalizedRect, - 'quadPoints' => $normalizedQuadPoints, - 'uri' => $uriValue, - 'pdfObject' => $annotation - ]; - } catch (FpdiException $e) { - continue; - } - } - - return $links; - } -} diff --git a/vendor/setasign/fpdi/src/PdfReader/PageBoundaries.php b/vendor/setasign/fpdi/src/PdfReader/PageBoundaries.php deleted file mode 100644 index ec24cde..0000000 --- a/vendor/setasign/fpdi/src/PdfReader/PageBoundaries.php +++ /dev/null @@ -1,94 +0,0 @@ -parser = $parser; - } - - /** - * PdfReader destructor. - */ - public function __destruct() - { - if ($this->parser !== null) { - $this->parser->cleanUp(); - } - } - - /** - * Get the pdf parser instance. - * - * @return PdfParser - */ - public function getParser() - { - return $this->parser; - } - - /** - * Get the PDF version. - * - * @return string - * @throws PdfParserException - */ - public function getPdfVersion() - { - return \implode('.', $this->parser->getPdfVersion()); - } - - /** - * Get the page count. - * - * @return int - * @throws PdfTypeException - * @throws CrossReferenceException - * @throws PdfParserException - */ - public function getPageCount() - { - if ($this->pageCount === null) { - $catalog = $this->parser->getCatalog(); - - $pages = PdfType::resolve(PdfDictionary::get($catalog, 'Pages'), $this->parser); - $count = PdfType::resolve(PdfDictionary::get($pages, 'Count'), $this->parser); - - $this->pageCount = PdfNumeric::ensure($count)->value; - } - - return $this->pageCount; - } - - /** - * Get a page instance. - * - * @param int $pageNumber - * @return Page - * @throws PdfTypeException - * @throws CrossReferenceException - * @throws PdfParserException - * @throws \InvalidArgumentException - */ - public function getPage($pageNumber) - { - if (!\is_numeric($pageNumber)) { - throw new \InvalidArgumentException( - 'Page number needs to be a number.' - ); - } - - if ($pageNumber < 1 || $pageNumber > $this->getPageCount()) { - throw new \InvalidArgumentException( - \sprintf( - 'Page number "%s" out of available page range (1 - %s)', - $pageNumber, - $this->getPageCount() - ) - ); - } - - $this->readPages(); - - $page = $this->pages[$pageNumber - 1]; - - if ($page instanceof PdfIndirectObjectReference) { - $readPages = function ($kids) use (&$readPages) { - $kids = PdfArray::ensure($kids); - - /** @noinspection LoopWhichDoesNotLoopInspection */ - foreach ($kids->value as $reference) { - $reference = PdfIndirectObjectReference::ensure($reference); - $object = $this->parser->getIndirectObject($reference->value); - $type = PdfDictionary::get($object->value, 'Type'); - - if ($type->value === 'Pages') { - return $readPages(PdfDictionary::get($object->value, 'Kids')); - } - - return $object; - } - - throw new PdfReaderException( - 'Kids array cannot be empty.', - PdfReaderException::KIDS_EMPTY - ); - }; - - $page = $this->parser->getIndirectObject($page->value); - $dict = PdfType::resolve($page, $this->parser); - $type = PdfDictionary::get($dict, 'Type'); - - if ($type->value === 'Pages') { - $kids = PdfType::resolve(PdfDictionary::get($dict, 'Kids'), $this->parser); - try { - $page = $this->pages[$pageNumber - 1] = $readPages($kids); - } catch (PdfReaderException $e) { - if ($e->getCode() !== PdfReaderException::KIDS_EMPTY) { - throw $e; - } - - // let's reset the pages array and read all page objects - $this->pages = []; - $this->readPages(true); - // @phpstan-ignore-next-line - $page = $this->pages[$pageNumber - 1]; - } - } else { - $this->pages[$pageNumber - 1] = $page; - } - } - - return new Page($page, $this->parser); - } - - /** - * Walk the page tree and resolve all indirect objects of all pages. - * - * @param bool $readAll - * @throws CrossReferenceException - * @throws PdfParserException - * @throws PdfTypeException - */ - protected function readPages($readAll = false) - { - if (\count($this->pages) > 0) { - return; - } - - $expectedPageCount = $this->getPageCount(); - $readPages = function ($kids, $count) use (&$readPages, $readAll, $expectedPageCount) { - $kids = PdfArray::ensure($kids); - $isLeaf = ($count->value === \count($kids->value)); - - foreach ($kids->value as $reference) { - $reference = PdfIndirectObjectReference::ensure($reference); - - if (!$readAll && $isLeaf) { - $this->pages[] = $reference; - continue; - } - - $object = $this->parser->getIndirectObject($reference->value); - $type = PdfDictionary::get($object->value, 'Type'); - - if ($type->value === 'Pages') { - $readPages(PdfDictionary::get($object->value, 'Kids'), PdfDictionary::get($object->value, 'Count')); - } else { - $this->pages[] = $object; - } - - // stop if all pages are read - faulty documents exists with additional entries with invalid data. - if (count($this->pages) === $expectedPageCount) { - break; - } - } - }; - - $catalog = $this->parser->getCatalog(); - $pages = PdfType::resolve(PdfDictionary::get($catalog, 'Pages'), $this->parser); - $count = PdfType::resolve(PdfDictionary::get($pages, 'Count'), $this->parser); - $kids = PdfType::resolve(PdfDictionary::get($pages, 'Kids'), $this->parser); - $readPages($kids, $count); - } -} diff --git a/vendor/setasign/fpdi/src/PdfReader/PdfReaderException.php b/vendor/setasign/fpdi/src/PdfReader/PdfReaderException.php deleted file mode 100644 index 2b3487e..0000000 --- a/vendor/setasign/fpdi/src/PdfReader/PdfReaderException.php +++ /dev/null @@ -1,34 +0,0 @@ -cleanUp(); - } - - /** - * Get the next template id. - * - * @return int - */ - protected function getNextTemplateId() - { - return $this->templateId++; - } - - /** - * Draws an imported page onto the page or another template. - * - * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the - * aspect ratio. - * - * @param mixed $tpl The template id - * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array - * with the keys "x", "y", "width", "height", "adjustPageSize". - * @param float|int $y The ordinate of upper-left corner. - * @param float|int|null $width The width. - * @param float|int|null $height The height. - * @param bool $adjustPageSize - * @return array The size - * @see FpdiTrait::getTemplateSize() - */ - public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false) - { - return $this->useImportedPage($tpl, $x, $y, $width, $height, $adjustPageSize); - } - - /** - * Draws an imported page onto the page. - * - * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the - * aspect ratio. - * - * @param mixed $pageId The page id - * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array - * with the keys "x", "y", "width", "height", "adjustPageSize". - * @param float|int $y The ordinate of upper-left corner. - * @param float|int|null $width The width. - * @param float|int|null $height The height. - * @param bool $adjustPageSize - * @return array The size. - * @see Fpdi::getTemplateSize() - */ - public function useImportedPage($pageId, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false) - { - $size = $this->fpdiUseImportedPage($pageId, $x, $y, $width, $height, $adjustPageSize); - if ($this->inxobj) { - $importedPage = $this->importedPages[$pageId]; - $this->xobjects[$this->xobjid]['importedPages'][$importedPage['id']] = $pageId; - } - - return $size; - } - - /** - * Get the size of an imported page. - * - * Give only one of the size parameters (width, height) to calculate the other one automatically in view to the - * aspect ratio. - * - * @param mixed $tpl The template id - * @param float|int|null $width The width. - * @param float|int|null $height The height. - * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P) - */ - public function getTemplateSize($tpl, $width = null, $height = null) - { - return $this->getImportedPageSize($tpl, $width, $height); - } - - /** - * @inheritdoc - * @return string - */ - protected function _getxobjectdict() - { - $out = parent::_getxobjectdict(); - - foreach ($this->importedPages as $pageData) { - $out .= '/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R '; - } - - return $out; - } - - /** - * @inheritdoc - * @throws CrossReferenceException - * @throws PdfParserException - */ - protected function _putxobjects() - { - foreach ($this->importedPages as $key => $pageData) { - $this->currentObjectNumber = $this->_newobj(); - $this->importedPages[$key]['objectNumber'] = $this->currentObjectNumber; - $this->currentReaderId = $pageData['readerId']; - $this->writePdfType($pageData['stream']); - $this->_put('endobj'); - } - - foreach (\array_keys($this->readers) as $readerId) { - $parser = $this->getPdfReader($readerId)->getParser(); - $this->currentReaderId = $readerId; - - while (($objectNumber = \array_pop($this->objectsToCopy[$readerId])) !== null) { - try { - $object = $parser->getIndirectObject($objectNumber); - } catch (CrossReferenceException $e) { - if ($e->getCode() === CrossReferenceException::OBJECT_NOT_FOUND) { - $object = PdfIndirectObject::create($objectNumber, 0, new PdfNull()); - } else { - throw $e; - } - } - - $this->writePdfType($object); - } - } - - // let's prepare resources for imported pages in templates - foreach ($this->xobjects as $xObjectId => $data) { - if (!isset($data['importedPages'])) { - continue; - } - - foreach ($data['importedPages'] as $id => $pageKey) { - $page = $this->importedPages[$pageKey]; - $this->xobjects[$xObjectId]['xobjects'][$id] = ['n' => $page['objectNumber']]; - } - } - - - parent::_putxobjects(); - $this->currentObjectNumber = null; - } - - /** - * Append content to the buffer of TCPDF. - * - * @param string $s - * @param bool $newLine - */ - protected function _put($s, $newLine = true) - { - if ($newLine) { - $this->setBuffer($s . "\n"); - } else { - $this->setBuffer($s); - } - } - - /** - * Begin a new object and return the object number. - * - * @param int|string $objid Object ID (leave empty to get a new ID). - * @return int object number - */ - protected function _newobj($objid = '') - { - $this->_out($this->_getobj($objid)); - return $this->n; - } - - /** - * Writes a PdfType object to the resulting buffer. - * - * @param PdfType $value - * @throws PdfTypeException - */ - protected function writePdfType(PdfType $value) - { - if (!$this->encrypted) { - $this->fpdiWritePdfType($value); - return; - } - - if ($value instanceof PdfString) { - $string = PdfString::unescape($value->value); - $string = $this->_encrypt_data($this->currentObjectNumber, $string); - $value->value = PdfString::escape($string); - } elseif ($value instanceof PdfHexString) { - $filter = new AsciiHex(); - $string = $filter->decode($value->value); - $string = $this->_encrypt_data($this->currentObjectNumber, $string); - $value->value = $filter->encode($string, true); - } elseif ($value instanceof PdfStream) { - $stream = $value->getStream(); - $stream = $this->_encrypt_data($this->currentObjectNumber, $stream); - $dictionary = $value->value; - $dictionary->value['Length'] = PdfNumeric::create(\strlen($stream)); - $value = PdfStream::create($dictionary, $stream); - } elseif ($value instanceof PdfIndirectObject) { - /** - * @var PdfIndirectObject $value - */ - $this->currentObjectNumber = $this->objectMap[$this->currentReaderId][$value->objectNumber]; - } - - $this->fpdiWritePdfType($value); - } - - /** - * This method will add additional data to the last created link/annotation. - * - * It will copy styling properties (supported by TCPDF) of the imported link. - * - * @param array $externalLink - * @param float|int $xPt - * @param float|int $scaleX - * @param float|int $yPt - * @param float|int $newHeightPt - * @param float|int $scaleY - * @param array $importedPage - * @return void - */ - protected function adjustLastLink($externalLink, $xPt, $scaleX, $yPt, $newHeightPt, $scaleY, $importedPage) - { - $parser = $this->getPdfReader($importedPage['readerId'])->getParser(); - - if ($this->inxobj) { - // store parameters for later use on template - $lastAnnotationKey = count($this->xobjects[$this->xobjid]['annotations']) - 1; - $lastAnnotationOpt = &$this->xobjects[$this->xobjid]['annotations'][$lastAnnotationKey]['opt']; - } else { - $lastAnnotationKey = count($this->PageAnnots[$this->page]) - 1; - $lastAnnotationOpt = &$this->PageAnnots[$this->page][$lastAnnotationKey]['opt']; - } - - // ensure we have a default value - otherwise TCPDF will set it to 4 throughout - $lastAnnotationOpt['f'] = 0; - - // values in this dictonary are all direct objects and we don't need to resolve them here again. - $values = $externalLink['pdfObject']->value; - - foreach ($values as $key => $value) { - try { - switch ($key) { - case 'BS': - $value = PdfDictionary::ensure($value); - $bs = []; - if (isset($value->value['W'])) { - $bs['w'] = PdfNumeric::ensure($value->value['W'])->value; - } - - if (isset($value->value['S'])) { - $bs['s'] = PdfName::ensure($value->value['S'])->value; - } - - if (isset($value->value['D'])) { - $d = []; - foreach (PdfArray::ensure($value->value['D'])->value as $item) { - $d[] = PdfNumeric::ensure($item)->value; - } - $bs['d'] = $d; - } - - $lastAnnotationOpt['bs'] = $bs; - break; - - case 'Border': - $borderArray = PdfArray::ensure($value)->value; - if (count($borderArray) < 3) { - continue 2; - } - - $border = [ - PdfNumeric::ensure($borderArray[0])->value, - PdfNumeric::ensure($borderArray[1])->value, - PdfNumeric::ensure($borderArray[2])->value, - ]; - if (isset($borderArray[3])) { - $dashArray = []; - foreach (PdfArray::ensure($borderArray[3])->value as $item) { - $dashArray[] = PdfNumeric::ensure($item)->value; - } - $border[] = $dashArray; - } - - $lastAnnotationOpt['border'] = $border; - break; - - case 'C': - $c = []; - $colors = PdfArray::ensure(PdfType::resolve($value, $parser))->value; - $m = count($colors) === 4 ? 100 : 255; - foreach ($colors as $item) { - $c[] = PdfNumeric::ensure($item)->value * $m; - } - $lastAnnotationOpt['c'] = $c; - break; - - case 'F': - $lastAnnotationOpt['f'] = $value->value; - break; - - case 'BE': - // is broken in current TCPDF version: "bc" key is checked but "bs" is used. - break; - } - // let's silence invalid/not supported values - } catch (FpdiException $e) { - continue; - } - } - - // QuadPoints are not supported by TCPDF -// if (count($externalLink['quadPoints']) > 0) { -// $quadPoints = []; -// for ($i = 0, $n = count($externalLink['quadPoints']); $i < $n; $i += 2) { -// $quadPoints[] = $xPt + $externalLink['quadPoints'][$i] * $scaleX; -// $quadPoints[] = $this->hPt - $yPt - $newHeightPt + $externalLink['quadPoints'][$i + 1] * $scaleY; -// } -// -// ????? = $quadPoints; -// } - } -} diff --git a/vendor/setasign/fpdi/src/TcpdfFpdi.php b/vendor/setasign/fpdi/src/TcpdfFpdi.php deleted file mode 100644 index 8f3c095..0000000 --- a/vendor/setasign/fpdi/src/TcpdfFpdi.php +++ /dev/null @@ -1,23 +0,0 @@ - -* **copyright** 2002-2024 Nicola Asuni - Tecnick.com LTD +* **copyright** 2002-2025 Nicola Asuni - Tecnick.com LTD * **license** http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT) * **link** http://www.tcpdf.org * **source** https://github.com/tecnickcom/TCPDF diff --git a/vendor/tecnickcom/tcpdf/VERSION b/vendor/tecnickcom/tcpdf/VERSION index c56facf..cf79bf9 100644 --- a/vendor/tecnickcom/tcpdf/VERSION +++ b/vendor/tecnickcom/tcpdf/VERSION @@ -1 +1 @@ -6.7.5 +6.10.0 diff --git a/vendor/tecnickcom/tcpdf/composer.json b/vendor/tecnickcom/tcpdf/composer.json index 7389d09..8d77919 100644 --- a/vendor/tecnickcom/tcpdf/composer.json +++ b/vendor/tecnickcom/tcpdf/composer.json @@ -12,7 +12,7 @@ "barcodes" ], "homepage": "http://www.tcpdf.org/", - "version": "6.7.5", + "version": "6.10.0", "license": "LGPL-3.0-or-later", "authors": [ { @@ -22,15 +22,14 @@ } ], "require": { - "php": ">=5.5.0" + "php": ">=7.1.0", + "ext-curl": "*" }, "autoload": { "classmap": [ "config", "include", "tcpdf.php", - "tcpdf_parser.php", - "tcpdf_import.php", "tcpdf_barcodes_1d.php", "tcpdf_barcodes_2d.php", "include/tcpdf_colors.php", @@ -43,10 +42,5 @@ "include/barcodes/pdf417.php", "include/barcodes/qrcode.php" ] - }, - "archive": { - "exclude": [ - "/examples" - ] } } diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_html.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_html.php deleted file mode 100644 index 6d3233b..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_html.php +++ /dev/null @@ -1,57 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_1d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_1d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.000 - * @group barcode - * @group 1d - * @group html - * @group comparable - */ - -// include 1D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_1d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDFBarcode('http://www.tcpdf.org', 'C128'); - -// output the barcode as HTML object -echo $barcodeobj->getBarcodeHTML(2, 30, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_png.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_png.php deleted file mode 100644 index 02ed166..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_png.php +++ /dev/null @@ -1,56 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_1d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_1d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.000 - * @group barcode - * @group 1d - * @group png - */ - -// include 1D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_1d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDFBarcode('http://www.tcpdf.org', 'C128'); - -// output the barcode as PNG image -$barcodeobj->getBarcodePNG(2, 30, array(0,0,0)); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_svg.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_svg.php deleted file mode 100644 index 5e5ccfc..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_svg.php +++ /dev/null @@ -1,57 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_1d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_1d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.000 - * @group barcode - * @group 1d - * @group svg - * @group comparable - */ - -// include 1D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_1d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDFBarcode('http://www.tcpdf.org', 'C128'); - -// output the barcode as SVG image -$barcodeobj->getBarcodeSVG(2, 30, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_svgi.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_svgi.php deleted file mode 100644 index 8c65b66..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_1d_svgi.php +++ /dev/null @@ -1,57 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_1d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_1d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.000 - * @group barcode - * @group 1d - * @group svg - * @group comparable - */ - -// include 1D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_1d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDFBarcode('http://www.tcpdf.org', 'C128'); - -// output the barcode as SVG inline code -echo $barcodeobj->getBarcodeSVGcode(2, 40, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_html.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_html.php deleted file mode 100644 index e0d200c..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_html.php +++ /dev/null @@ -1,57 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group datamatrix - * @group html - * @group comparable - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'DATAMATRIX'); - -// output the barcode as HTML object -echo $barcodeobj->getBarcodeHTML(6, 6, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_png.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_png.php deleted file mode 100644 index 35d344d..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_png.php +++ /dev/null @@ -1,56 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group datamatrix - * @group png - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'DATAMATRIX'); - -// output the barcode as PNG image -$barcodeobj->getBarcodePNG(6, 6, array(0,0,0)); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_svg.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_svg.php deleted file mode 100644 index 10d0aa3..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_svg.php +++ /dev/null @@ -1,57 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group datamatrix - * @group svg - * @group comparable - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'DATAMATRIX'); - -// output the barcode as SVG image -$barcodeobj->getBarcodeSVG(6, 6, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_svgi.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_svgi.php deleted file mode 100644 index 4262700..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_datamatrix_svgi.php +++ /dev/null @@ -1,57 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group datamatrix - * @group svg - * @group comparable - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'DATAMATRIX'); - -// output the barcode as SVG inline code -echo $barcodeobj->getBarcodeSVGcode(6, 6, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_html.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_html.php deleted file mode 100644 index 3d8de31..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_html.php +++ /dev/null @@ -1,57 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group pdf417 - * @group html - * @group comparable - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'PDF417'); - -// output the barcode as HTML object -echo $barcodeobj->getBarcodeHTML(4, 4, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_png.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_png.php deleted file mode 100644 index b7c9585..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_png.php +++ /dev/null @@ -1,56 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group pdf417 - * @group png - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'PDF417'); - -// output the barcode as PNG image -$barcodeobj->getBarcodePNG(4, 4, array(0,0,0)); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_svg.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_svg.php deleted file mode 100644 index 9017e5b..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_svg.php +++ /dev/null @@ -1,57 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group pdf417 - * @group svg - * @group comparable - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'PDF417'); - -// output the barcode as SVG image -$barcodeobj->getBarcodeSVG(4, 4, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_svgi.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_svgi.php deleted file mode 100644 index 2075da7..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_pdf417_svgi.php +++ /dev/null @@ -1,57 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group pdf417 - * @group svg - * @group comparable - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'PDF417'); - -// output the barcode as SVG inline code -echo $barcodeobj->getBarcodeSVGcode(4, 4, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_html.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_html.php deleted file mode 100644 index 7c05f00..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_html.php +++ /dev/null @@ -1,56 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group qrcode - * @group html - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'QRCODE,H'); - -// output the barcode as HTML object -echo $barcodeobj->getBarcodeHTML(6, 6, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_png.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_png.php deleted file mode 100644 index 75daa7c..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_png.php +++ /dev/null @@ -1,56 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group qrcode - * @group png - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'QRCODE,H'); - -// output the barcode as PNG image -$barcodeobj->getBarcodePNG(6, 6, array(0,0,0)); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_svg.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_svg.php deleted file mode 100644 index c104ca6..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_svg.php +++ /dev/null @@ -1,56 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group qrcode - * @group svg - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'QRCODE,H'); - -// output the barcode as SVG image -$barcodeobj->getBarcodeSVG(6, 6, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_svgi.php b/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_svgi.php deleted file mode 100644 index f31a100..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/example_2d_qrcode_svgi.php +++ /dev/null @@ -1,56 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -// ------------------------------------------------------------------- -// -// Description : Example for tcpdf_barcodes_2d.php class -// -//============================================================+ - -/** - * @file - * Example for tcpdf_barcodes_2d.php class - * @package com.tecnick.tcpdf - * @author Nicola Asuni - * @version 1.0.009 - * @group barcode - * @group qrcode - * @group svg - */ - -// include 2D barcode class (search for installation path) -require_once(dirname(__FILE__).'/tcpdf_barcodes_2d_include.php'); - -// set the barcode content and type -$barcodeobj = new TCPDF2DBarcode('http://www.tcpdf.org', 'QRCODE,H'); - -// output the barcode as SVG inline code -echo $barcodeobj->getBarcodeSVGcode(6, 6, 'black'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/barcodes/tcpdf_barcodes_1d_include.php b/vendor/tecnickcom/tcpdf/examples/barcodes/tcpdf_barcodes_1d_include.php deleted file mode 100644 index 0add5ec..0000000 --- a/vendor/tecnickcom/tcpdf/examples/barcodes/tcpdf_barcodes_1d_include.php +++ /dev/null @@ -1,46 +0,0 @@ -. -// -// See LICENSE.TXT file for more information. -//============================================================+ - -/** - * Example of alternative configuration file for TCPDF. - * @author Nicola Asuni - * @package com.tecnick.tcpdf - * @version 4.9.005 - * @since 2004-10-27 - */ - -/** - * Define the following constant to ignore the default configuration file. - */ -define ('K_TCPDF_EXTERNAL_CONFIG', true); - -/** - * Installation path (/var/www/tcpdf/). - * By default it is automatically calculated but you can also set it as a fixed string to improve performances. - */ -//define ('K_PATH_MAIN', ''); - -/** - * URL path to tcpdf installation folder (http://localhost/tcpdf/). - * By default it is automatically set but you can also set it as a fixed string to improve performances. - */ -//define ('K_PATH_URL', ''); - -/** - * Path for PDF fonts. - * By default it is automatically set but you can also set it as a fixed string to improve performances. - */ -//define ('K_PATH_FONTS', K_PATH_MAIN.'fonts/'); - -/** - * Default images directory. - * By default it is automatically set but you can also set it as a fixed string to improve performances. - */ -define ('K_PATH_IMAGES', dirname(__FILE__).'/../images/'); - -/** - * Deafult image logo used be the default Header() method. - * Please set here your own logo or an empty string to disable it. - */ -define ('PDF_HEADER_LOGO', 'tcpdf_logo.jpg'); - -/** - * Header logo image width in user units. - */ -define ('PDF_HEADER_LOGO_WIDTH', 30); - -/** - * Cache directory for temporary files (full path). - */ -define ('K_PATH_CACHE', sys_get_temp_dir().'/'); - -/** - * Generic name for a blank image. - */ -define ('K_BLANK_IMAGE', '_blank.png'); - -/** - * Page format. - */ -define ('PDF_PAGE_FORMAT', 'A4'); - -/** - * Page orientation (P=portrait, L=landscape). - */ -define ('PDF_PAGE_ORIENTATION', 'P'); - -/** - * Document creator. - */ -define ('PDF_CREATOR', 'TCPDF'); - -/** - * Document author. - */ -define ('PDF_AUTHOR', 'TCPDF'); - -/** - * Header title. - */ -define ('PDF_HEADER_TITLE', 'TCPDF Example'); - -/** - * Header description string. - */ -define ('PDF_HEADER_STRING', "by Nicola Asuni - Tecnick.com\nwww.tcpdf.org"); - -/** - * Document unit of measure [pt=point, mm=millimeter, cm=centimeter, in=inch]. - */ -define ('PDF_UNIT', 'mm'); - -/** - * Header margin. - */ -define ('PDF_MARGIN_HEADER', 5); - -/** - * Footer margin. - */ -define ('PDF_MARGIN_FOOTER', 10); - -/** - * Top margin. - */ -define ('PDF_MARGIN_TOP', 27); - -/** - * Bottom margin. - */ -define ('PDF_MARGIN_BOTTOM', 25); - -/** - * Left margin. - */ -define ('PDF_MARGIN_LEFT', 15); - -/** - * Right margin. - */ -define ('PDF_MARGIN_RIGHT', 15); - -/** - * Default main font name. - */ -define ('PDF_FONT_NAME_MAIN', 'helvetica'); - -/** - * Default main font size. - */ -define ('PDF_FONT_SIZE_MAIN', 10); - -/** - * Default data font name. - */ -define ('PDF_FONT_NAME_DATA', 'helvetica'); - -/** - * Default data font size. - */ -define ('PDF_FONT_SIZE_DATA', 8); - -/** - * Default monospaced font name. - */ -define ('PDF_FONT_MONOSPACED', 'courier'); - -/** - * Ratio used to adjust the conversion of pixels to user units. - */ -define ('PDF_IMAGE_SCALE_RATIO', 1.25); - -/** - * Magnification factor for titles. - */ -define('HEAD_MAGNIFICATION', 1.1); - -/** - * Height of cell respect font height. - */ -define('K_CELL_HEIGHT_RATIO', 1.25); - -/** - * Title magnification respect main font size. - */ -define('K_TITLE_MAGNIFICATION', 1.3); - -/** - * Reduction factor for small font. - */ -define('K_SMALL_RATIO', 2/3); - -/** - * Set to true to enable the special procedure used to avoid the overlappind of symbols on Thai language. - */ -define('K_THAI_TOPCHARS', true); - -/** - * If true allows to call TCPDF methods using HTML syntax - * IMPORTANT: For security reason, disable this feature if you are printing user HTML content. - */ -define('K_TCPDF_CALLS_IN_HTML', true); - -/** - * List of TCPDF methods that are allowed to be called using HTML syntax. - * Note: each method name must end with surrounded with | (pipe) character. - * The constant K_TCPDF_CALLS_IN_HTML must be set to true. - * IMPORTANT: For security reason, disable this feature if you are allowing user HTML content. - */ -define('K_ALLOWED_TCPDF_TAGS', '|AddPage|Rect|SetDrawColor|write1DBarcode|'); - -/** - * If true and PHP version is greater than 5, then the Error() method throw new exception instead of terminating the execution. - */ -define('K_TCPDF_THROW_EXCEPTION_ERROR', false); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/data/cert/tcpdf.crt b/vendor/tecnickcom/tcpdf/examples/data/cert/tcpdf.crt deleted file mode 100644 index f049139..0000000 --- a/vendor/tecnickcom/tcpdf/examples/data/cert/tcpdf.crt +++ /dev/null @@ -1,40 +0,0 @@ -Bag Attributes - localKeyID: 7B AB 1B 7A BE 4C 85 C0 1A A6 DC 59 3F 79 48 C3 93 38 68 9C -subject=/CN=TCPDF DEMO/O=TCPDF/OU=DEMO/emailAddress=you@example.com/C=IT -issuer=/CN=TCPDF DEMO/O=TCPDF/OU=DEMO/emailAddress=you@example.com/C=IT ------BEGIN CERTIFICATE----- -MIIC1TCCAj6gAwIBAgIKkehOL/XGkB5cjjANBgkqhkiG9w0BAQUFADBhMRMwEQYD -VQQDEwpUQ1BERiBERU1PMQ4wDAYDVQQKEwVUQ1BERjENMAsGA1UECxMEREVNTzEe -MBwGCSqGSIb3DQEJARYPeW91QGV4YW1wbGUuY29tMQswCQYDVQQGEwJJVDAeFw0w -OTA4MjExMjU0NDhaFw0xNDA4MjExMjU0NDhaMGExEzARBgNVBAMTClRDUERGIERF -TU8xDjAMBgNVBAoTBVRDUERGMQ0wCwYDVQQLEwRERU1PMR4wHAYJKoZIhvcNAQkB -Fg95b3VAZXhhbXBsZS5jb20xCzAJBgNVBAYTAklUMIGfMA0GCSqGSIb3DQEBAQUA -A4GNADCBiQKBgQDAqIL0uGKmTR98Lxx2vEEE1OGKkMXFo0JViitALe7Onhxxqx0H -XMUDKF5mvEVu1rcvh7/oAnAfrCuEpL/up3u1mQCgBE7WXBnFFE/AE3jCksh9OkS0 -Z0Xj9woN5bzxRDsGoPiOu/4xzk5qSEXt8jf2Ep90QuNkqLIRT4swAzpDbwIDAQAB -o4GTMIGQMDcGA1UdEgQwMC6gEQYDVQQDDApUQ1BERiBERU1PoAwGA1UECgwFVENQ -REagCwYDVQQLDARERU1PMDcGA1UdEQQwMC6gEQYDVQQDDApUQ1BERiBERU1PoAwG -A1UECgwFVENQREagCwYDVQQLDARERU1PMA8GCSqGSIb3LwEBCgQCBQAwCwYDVR0P -BAQDAgSQMA0GCSqGSIb3DQEBBQUAA4GBAEhTQfqX3ZNdHmpTLDbIj22RHXii2roE -OavCbu9WsHoWpva0qSd+yIoD594VHvYAd29sfzDfiN+7W0aiZfDhq5jpaSQMVlN8 -RGYMupbHY/+a9Gz1wqxnR84mlTtIkZVRYAhsfPwy6M1BEjdMqfdh9h40JIdkdjtb -8faTCfXPePWQ ------END CERTIFICATE----- -Bag Attributes - localKeyID: 7B AB 1B 7A BE 4C 85 C0 1A A6 DC 59 3F 79 48 C3 93 38 68 9C -Key Attributes: ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQDAqIL0uGKmTR98Lxx2vEEE1OGKkMXFo0JViitALe7Onhxxqx0H -XMUDKF5mvEVu1rcvh7/oAnAfrCuEpL/up3u1mQCgBE7WXBnFFE/AE3jCksh9OkS0 -Z0Xj9woN5bzxRDsGoPiOu/4xzk5qSEXt8jf2Ep90QuNkqLIRT4swAzpDbwIDAQAB -AoGAXc+wNMmz/5Z+RlIKYia44klmqbplEx+0JULqXI4BQsrqvs67i+I4bJkznoL+ -rEIRYSuQ3sCRKFsFtckjTGpxadnxkB+uwGKc6pZChv99BFX6HFR4hgBlT/BBRAQA -hMDlM2JIRr4S4SMVXR7MHwGMUf9mUeanGLR3ZWtU3aXJrIECQQD7OaYUVYNEEnM9 -uXyjm22CuHyqyEf5gb13sK0uQty67547yJTMUQZd/sQc9KGwhzBbhrob2LO2jAhh -S+f+NSRnAkEAxFHm3fMI5RgXmswxlGm4QW07a/Ueo7ZJG6xjTkFXluJhd+XHswRD -dQIO3zG9nGjNUoeMrPhXhPvKqFc2F9RDuQJAQBEGin74N77gxqfr4ik79y8nE8J5 -oGZ2s/RJZdfFRKLg3mwbjjNHhWb4Ck5UgZkoOt8TzRApXG8/n9hktE5HFwJBALur -M5AueO1Pl5kB489lNJ9OxUQRYUXMxpxuscuoCQwSwmv0O2+0/qtG2WKhUQnI4aYo -L+FV0YwtivBb1jj3T/kCQQDIWOxq8eRowdaMzvJpRUHFgMcf1AVZExKyrugwYOWd -KNsDxC4KaQOsPt8iT/Ulo4g/MJC0HolCOhWibKmR9Ayl ------END RSA PRIVATE KEY----- diff --git a/vendor/tecnickcom/tcpdf/examples/data/cert/tcpdf.fdf b/vendor/tecnickcom/tcpdf/examples/data/cert/tcpdf.fdf deleted file mode 100644 index a8f7c35d9af83d2579adafbe8d2745daa30dfd3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1286 zcmY#&b8*u()HC8$ef0SJWnL~r1p|frq%2+=8-1XRf)S7#r0=WYVr67tU}<1sWNK(+ zVqj{ZZD6EsV4$w4A5vM6s_&dwl9-d9ZfD1vnwJ974blcRB_P0C!5Cx;LKeh@%DVa{ zX69&A=9fC8RwU*YU-uEiZ?;rfP&Z2LK)A;_B7r3kke!oRl4_NlT2$g%0n&r%SOWtqBSU91S93#0P5mGRLt{e&OJj)R zfezFUQ!unNGcz$2Stmap^>SHiA5BUZ)k$Vr=CM% zc=0=>aXxA&FtRc*H}*0ZG&u@?wgf?)m6q&9>l2-|l~g=lrrfT;F~&|0XoQ#OZO$icNz4 z-3H87&iPEtj0}v68z&nyPB1VB2BNGGi-CdO0!TdZAji`JXe{wSVrc;=ieh+KxOu>l z1ko%=ie|W0erW9KGcv~Hu`mIX3dn3(eijyHCYA}PK?qDp%)qeo2zLB6{qE#gxvXFv zvlIQf6J;wF-P*-sx%yDv`>+jFV#~g5S*c!kqKo)Yp8S8-mz=MM)}*_iQ=4k-F>z{O0!L2GAEOs%9fiz&R(?FEV{qfsm&adYyzE04L)pyR%D@jZ)LCJsMoDa#P`pJ37QixnsQIwj-%VnTo z3MAk_!OYau*i-=|0R@I;3SgE(9zx6lQ_K*HIwK1Mgx-=OU}=(C#LERNy@E5VQWZ?~ kgYxrB6hMU zY-rcU0s;sCfPw=!#BpZA(3DXgvNT6SMx2c3M^p9_<~*qj&~k|Wio#7(scRd zVB;~P(%~|g!$#H)Uj>}ly*mK-K*NcRU+dxygiF7$BDFu;YqjVO#_{qT;h+#I>a(HG z6~@vJ%56(B0~rm^94uG!F&Q6%9$*+cPTn~18aH}n`p;SwVUc3}cGnJV33D`@$5Df5 zka5G^;jy@bF*5dt7yj?!On2m{6(lU}`+Ngz(u>-st&u3q&_N{T-$B(t9@HplX>tLm z<+%6V-nXIed*8 zvh$I?cs-H$tTFYyLYOK&N@y0$j3(wM=r+RHOFm_l_Ew3J zjZ|cTFZc?kMLbIRq9#Je0!uHg$!6L_|JSTh4SBDC^kz-4Q$rl0exLwuv7)I*(A$u( z4~P;V(@gR1lQvQ9WUL5F(7#Oi)Yh-}TXhqUaHvL^;{PKZ_!c^5TIaYzP~~Y~ol!Ix zIU5LwA?E0d=I<-Og-X;m1K5(@AtP)n6jQ1{Q2ko=_)Bd{1(bAXe>Coeda=}lih)j$ z2>zQNzJ;fmRdBanq`^ZJV5@Ra+72~oPcmiLf=NHQP1lj<)-k#CXYR9X=4kR3k!;Y8h(}v0#D@^oeB!oK*4q{yIW*$U z&LNQUbi3jQQ0AALA`*I zEfwAUX0Q~1Fi>8l{3I2b+YV;2v~q$Ht)BfYyxTJvSQ3SoAxX}JT3QHXqu~l@W;f&? zvySO921ne7{yT!I)^oWbM&abJwv8?L zM@*J>12l+CQ;QV!Y|w=vINzLD8!z3$e&b>m;bngGpZYK)8EI}digPEz z`_pvC6Afky&Q2vvrHY~v?ZeFbT?m+rp=p=tij1id`L#ceB43YATh$S6RuVfPIPHzQGmAuP1O9==9r!_9t z(XXtxXOZ1gWS49JT5NEHFG{7{?SuzHx8kV3=tHXcRlJ=U+G!T)+ z?jrd2IhsLxK1dcI7Bz#699SwDG5!;?5@oG>vkAutIB1uG5% r0vZJX1QaJKFP)8IPAOWT%X#|+d4hgQWoQHlY+QLorem ipsum dolor sit amet, consectetur adipiscing elit. In sed imperdiet lectus. Phasellus quis velit velit, non condimentum quam. Sed neque urna, ultrices ac volutpat vel, laoreet vitae augue. Sed vel velit erat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras eget velit nulla, eu sagittis elit. Nunc ac arcu est, in lobortis tellus. Praesent condimentum rhoncus sodales. In hac habitasse platea dictumst. Proin porta eros pharetra enim tincidunt dignissim nec vel dolor. Cras sapien elit, ornare ac dignissim eu, ultricies ac eros. Maecenas augue magna, ultrices a congue in, mollis eu nulla. Nunc venenatis massa at est eleifend faucibus. Vivamus sed risus lectus, nec interdum nunc.

- - - -

Fusce et felis vitae diam lobortis sollicitudin. Aenean tincidunt accumsan nisi, id vehicula quam laoreet elementum. Phasellus egestas interdum erat, et viverra ipsum ultricies ac. Praesent sagittis augue at augue volutpat eleifend. Cras nec orci neque. Mauris bibendum posuere blandit. Donec feugiat mollis dui sit amet pellentesque. Sed a enim justo. Donec tincidunt, nisl eget elementum aliquam, odio ipsum ultrices quam, eu porttitor ligula urna at lorem. Donec varius, eros et convallis laoreet, ligula tellus consequat felis, ut ornare metus tellus sodales velit. Duis sed diam ante. Ut rutrum malesuada massa, vitae consectetur ipsum rhoncus sed. Suspendisse potenti. Pellentesque a congue massa.

- -

Integer non sem eget neque mattis accumsan. Maecenas eu nisl mauris, sit amet interdum ipsum. In pharetra erat vel lectus venenatis elementum. Nulla non elit ligula, sit amet mollis urna. Morbi ut gravida est. Mauris tincidunt sem et turpis molestie malesuada. Curabitur vel nulla risus, sed mollis erat. Suspendisse vehicula accumsan purus nec varius. Donec fermentum lorem id felis sodales dictum. Quisque et dolor ipsum. Nam luctus consectetur dui vitae fermentum. Curabitur sodales consequat augue, id ultricies augue tempor ac. Aliquam ac magna id ipsum vehicula bibendum. Sed elementum congue tristique. Phasellus vel lorem eu lectus porta sodales. Etiam neque tortor, sagittis id pharetra quis, laoreet vel arcu.

- -

Cras quam mi, ornare laoreet laoreet vel, vehicula at lacus. Maecenas a lacus accumsan augue convallis sagittis sed quis odio. Morbi sit amet turpis diam, dictum convallis urna. Cras eget interdum augue. Cras eu nisi sit amet dolor faucibus porttitor. Suspendisse potenti. Nunc vitae dolor risus, at cursus libero. Suspendisse bibendum tellus non nibh hendrerit tristique. Mauris eget orci elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam porta libero non ante laoreet semper. Proin volutpat sodales mi, ac fermentum erat sagittis in. Vivamus at viverra felis. Ut pretium facilisis ante et pharetra.

- -

Nulla facilisi. Cras varius quam eget libero aliquam vitae tincidunt leo rutrum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque a nisl massa, quis pretium urna. Proin vel porttitor tortor. Cras rhoncus congue velit in bibendum. Donec pharetra semper augue id lacinia. Quisque magna quam, hendrerit eu aliquam et, pellentesque ut tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas nulla quam, rutrum eu feugiat at, elementum eu libero. Maecenas ullamcorper leo et turpis rutrum ac laoreet eros faucibus. Phasellus condimentum lorem quis neque imperdiet quis molestie enim iaculis. Phasellus risus est, vestibulum ut convallis ultrices, dignissim nec erat. Etiam congue lobortis laoreet. Nulla ut neque sed velit dapibus semper. Quisque nec dolor id nibh eleifend iaculis. Vivamus vitae fermentum odio. Etiam malesuada quam in nulla aliquam sed convallis dui feugiat.

- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sed imperdiet lectus. Phasellus quis velit velit, non condimentum quam. Sed neque urna, ultrices ac volutpat vel, laoreet vitae augue. Sed vel velit erat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras eget velit nulla, eu sagittis elit. Nunc ac arcu est, in lobortis tellus. Praesent condimentum rhoncus sodales. In hac habitasse platea dictumst. Proin porta eros pharetra enim tincidunt dignissim nec vel dolor. Cras sapien elit, ornare ac dignissim eu, ultricies ac eros. Maecenas augue magna, ultrices a congue in, mollis eu nulla. Nunc venenatis massa at est eleifend faucibus. Vivamus sed risus lectus, nec interdum nunc.

- - - -

Fusce et felis vitae diam lobortis sollicitudin. Aenean tincidunt accumsan nisi, id vehicula quam laoreet elementum. Phasellus egestas interdum erat, et viverra ipsum ultricies ac. Praesent sagittis augue at augue volutpat eleifend. Cras nec orci neque. Mauris bibendum posuere blandit. Donec feugiat mollis dui sit amet pellentesque. Sed a enim justo. Donec tincidunt, nisl eget elementum aliquam, odio ipsum ultrices quam, eu porttitor ligula urna at lorem. Donec varius, eros et convallis laoreet, ligula tellus consequat felis, ut ornare metus tellus sodales velit. Duis sed diam ante. Ut rutrum malesuada massa, vitae consectetur ipsum rhoncus sed. Suspendisse potenti. Pellentesque a congue massa.

- -

Integer non sem eget neque mattis accumsan. Maecenas eu nisl mauris, sit amet interdum ipsum. In pharetra erat vel lectus venenatis elementum. Nulla non elit ligula, sit amet mollis urna. Morbi ut gravida est. Mauris tincidunt sem et turpis molestie malesuada. Curabitur vel nulla risus, sed mollis erat. Suspendisse vehicula accumsan purus nec varius. Donec fermentum lorem id felis sodales dictum. Quisque et dolor ipsum. Nam luctus consectetur dui vitae fermentum. Curabitur sodales consequat augue, id ultricies augue tempor ac. Aliquam ac magna id ipsum vehicula bibendum. Sed elementum congue tristique. Phasellus vel lorem eu lectus porta sodales. Etiam neque tortor, sagittis id pharetra quis, laoreet vel arcu.

- -

Cras quam mi, ornare laoreet laoreet vel, vehicula at lacus. Maecenas a lacus accumsan augue convallis sagittis sed quis odio. Morbi sit amet turpis diam, dictum convallis urna. Cras eget interdum augue. Cras eu nisi sit amet dolor faucibus porttitor. Suspendisse potenti. Nunc vitae dolor risus, at cursus libero. Suspendisse bibendum tellus non nibh hendrerit tristique. Mauris eget orci elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam porta libero non ante laoreet semper. Proin volutpat sodales mi, ac fermentum erat sagittis in. Vivamus at viverra felis. Ut pretium facilisis ante et pharetra.

- -

Nulla facilisi. Cras varius quam eget libero aliquam vitae tincidunt leo rutrum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque a nisl massa, quis pretium urna. Proin vel porttitor tortor. Cras rhoncus congue velit in bibendum. Donec pharetra semper augue id lacinia. Quisque magna quam, hendrerit eu aliquam et, pellentesque ut tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas nulla quam, rutrum eu feugiat at, elementum eu libero. Maecenas ullamcorper leo et turpis rutrum ac laoreet eros faucibus. Phasellus condimentum lorem quis neque imperdiet quis molestie enim iaculis. Phasellus risus est, vestibulum ut convallis ultrices, dignissim nec erat. Etiam congue lobortis laoreet. Nulla ut neque sed velit dapibus semper. Quisque nec dolor id nibh eleifend iaculis. Vivamus vitae fermentum odio. Etiam malesuada quam in nulla aliquam sed convallis dui feugiat.

diff --git a/vendor/tecnickcom/tcpdf/examples/data/table_data_demo.txt b/vendor/tecnickcom/tcpdf/examples/data/table_data_demo.txt deleted file mode 100644 index 5a48a42..0000000 --- a/vendor/tecnickcom/tcpdf/examples/data/table_data_demo.txt +++ /dev/null @@ -1,15 +0,0 @@ -Austria;Vienna;83859;8075 -Belgium;Brussels;30518;10192 -Denmark;Copenhagen;43094;5295 -Finland;Helsinki;304529;5147 -France;Paris;543965;58728 -Germany;Berlin;357022;82057 -Greece;Athens;131625;10511 -Ireland;Dublin;70723;3694 -Italy;Roma;301316;57563 -Luxembourg;Luxembourg;2586;424 -Netherlands;Amsterdam;41526;15654 -Portugal;Lisbon;91906;9957 -Spain;Madrid;504790;39348 -Sweden;Stockholm;410934;8839 -United Kingdom;London;243820;58862 diff --git a/vendor/tecnickcom/tcpdf/examples/data/utf8test.txt b/vendor/tecnickcom/tcpdf/examples/data/utf8test.txt deleted file mode 100644 index 291d4e7..0000000 --- a/vendor/tecnickcom/tcpdf/examples/data/utf8test.txt +++ /dev/null @@ -1,128 +0,0 @@ -Sentences that contain all letters commonly used in a language --------------------------------------------------------------- - -This file is UTF-8 encoded. - -Czech (cz) ---------- - - Příšerně žluťoučký kůň úpěl ďábelské ódy. - Hleď, toť přízračný kůň v mátožné póze šíleně úpí. - Zvlášť zákeřný učeň s ďolíčky běží podél zóny úlů. - Loď čeří kýlem tůň obzvlášť v Grónské úžině. - Ó, náhlý déšť již zvířil prach a čilá laň teď běží s houfcem gazel k úkrytům. - -Danish (da) ---------- - - Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen - Wolther spillede på xylofon. - (= Quiz contestants were eating strawbery with cream while Wolther - the circus clown played on xylophone.) - -German (de) ------------ - - Falsches Üben von Xylophonmusik quält jeden größeren Zwerg - (= Wrongful practicing of xylophone music tortures every larger dwarf) - - Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich - (= Twelve boxing fighters hunted Eva across the dike of Sylt) - - Heizölrückstoßabdämpfung - (= fuel oil recoil absorber) - (jqvwxy missing, but all non-ASCII letters in one word) - -English (en) ------------- - - The quick brown fox jumps over the lazy dog - -Spanish (es) ------------- - - El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y - frío, añoraba a su querido cachorro. - (Contains every letter and every accent, but not every combination - of vowel + acute.) - -French (fr) ------------ - - Portez ce vieux whisky au juge blond qui fume sur son île intérieure, à - côté de l'alcôve ovoïde, où les bûches se consument dans l'âtre, ce - qui lui permet de penser à la cænogenèse de l'être dont il est question - dans la cause ambiguë entendue à Moÿ, dans un capharnaüm qui, - pense-t-il, diminue çà et là la qualité de son œuvre. - - l'île exiguë - Où l'obèse jury mûr - Fête l'haï volapük, - Âne ex aéquo au whist, - Ôtez ce vœu déçu. - - Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en - canoë au delà des îles, près du mälström où brûlent les novæ. - -Irish Gaelic (ga) ------------------ - - D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh - -Hungarian (hu) --------------- - - Árvíztűrő tükörfúrógép - (= flood-proof mirror-drilling machine, only all non-ASCII letters) - -Icelandic (is) --------------- - - Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa - - Sævör grét áðan því úlpan var ónýt - (some ASCII letters missing) - -Greek (el) -------------- - - Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο - (= No more shall I see acacias or myrtles in the golden clearing) - - Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία - (= I uncover the soul-destroying abhorrence) - -Hebrew (iw) ------------ - - ? דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה - -Polish (pl) ------------ - - Pchnąć w tę łódź jeża lub osiem skrzyń fig - (= To push a hedgehog or eight bins of figs in this boat) - - Zażółć gęślą jaźń - -Russian (ru) ------------- - - В чащах юга жил бы цитрус? Да, но фальшивый экземпляр! - (= Would a citrus live in the bushes of south? Yes, but only a fake one!) - -Thai (th) ---------- - - [--------------------------|------------------------] - ๏ เป็นมนุษย์สุดประเสริฐเลิศคุณค่า กว่าบรรดาฝูงสัตว์เดรัจฉาน - จงฝ่าฟันพัฒนาวิชาการ อย่าล้างผลาญฤๅเข่นฆ่าบีฑาใคร - ไม่ถือโทษโกรธแช่งซัดฮึดฮัดด่า หัดอภัยเหมือนกีฬาอัชฌาสัย - ปฏิบัติประพฤติกฎกำหนดใจ พูดจาให้จ๊ะๆ จ๋าๆ น่าฟังเอย ฯ - - [The copyright for the Thai example is owned by The Computer - Association of Thailand under the Royal Patronage of His Majesty the - King.] - -Please let me know if you find others! Special thanks to the people -from all over the world who contributed these sentences. diff --git a/vendor/tecnickcom/tcpdf/examples/example_001.php b/vendor/tecnickcom/tcpdf/examples/example_001.php deleted file mode 100644 index 82555e6..0000000 --- a/vendor/tecnickcom/tcpdf/examples/example_001.php +++ /dev/null @@ -1,110 +0,0 @@ -setCreator(PDF_CREATOR); -$pdf->setAuthor('Nicola Asuni'); -$pdf->setTitle('TCPDF Example 001'); -$pdf->setSubject('TCPDF Tutorial'); -$pdf->setKeywords('TCPDF, PDF, example, test, guide'); - -// set default header data -$pdf->setHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE.' 001', PDF_HEADER_STRING, array(0,64,255), array(0,64,128)); -$pdf->setFooterData(array(0,64,0), array(0,64,128)); - -// set header and footer fonts -$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); -$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); - -// set default monospaced font -$pdf->setDefaultMonospacedFont(PDF_FONT_MONOSPACED); - -// set margins -$pdf->setMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); -$pdf->setHeaderMargin(PDF_MARGIN_HEADER); -$pdf->setFooterMargin(PDF_MARGIN_FOOTER); - -// set auto page breaks -$pdf->setAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); - -// set image scale factor -$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); - -// set some language-dependent strings (optional) -if (@file_exists(dirname(__FILE__).'/lang/eng.php')) { - require_once(dirname(__FILE__).'/lang/eng.php'); - $pdf->setLanguageArray($l); -} - -// --------------------------------------------------------- - -// set default font subsetting mode -$pdf->setFontSubsetting(true); - -// Set font -// dejavusans is a UTF-8 Unicode font, if you only need to -// print standard ASCII chars, you can use core fonts like -// helvetica or times to reduce file size. -$pdf->setFont('dejavusans', '', 14, '', true); - -// Add a page -// This method has several options, check the source code documentation for more information. -$pdf->AddPage(); - -// set text shadow effect -$pdf->setTextShadow(array('enabled'=>true, 'depth_w'=>0.2, 'depth_h'=>0.2, 'color'=>array(196,196,196), 'opacity'=>1, 'blend_mode'=>'Normal')); - -// Set some content to print -$html = <<Welcome to
 TCPDF ! -This is the first example of TCPDF library. -

This text is printed using the writeHTMLCell() method but you can also use: Multicell(), writeHTML(), Write(), Cell() and Text().

-

Please check the source code documentation and other examples for further information.

-

TO IMPROVE AND EXPAND TCPDF I NEED YOUR SUPPORT, PLEASE MAKE A DONATION!

-EOD; - -// Print text using writeHTMLCell() -$pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true); - -// --------------------------------------------------------- - -// Close and output PDF document -// This method has several options, check the source code documentation for more information. -$pdf->Output('example_001.pdf', 'I'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/example_002.php b/vendor/tecnickcom/tcpdf/examples/example_002.php deleted file mode 100644 index f40a201..0000000 --- a/vendor/tecnickcom/tcpdf/examples/example_002.php +++ /dev/null @@ -1,91 +0,0 @@ -setCreator(PDF_CREATOR); -$pdf->setAuthor('Nicola Asuni'); -$pdf->setTitle('TCPDF Example 002'); -$pdf->setSubject('TCPDF Tutorial'); -$pdf->setKeywords('TCPDF, PDF, example, test, guide'); - -// remove default header/footer -$pdf->setPrintHeader(false); -$pdf->setPrintFooter(false); - -// set default monospaced font -$pdf->setDefaultMonospacedFont(PDF_FONT_MONOSPACED); - -// set margins -$pdf->setMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); - -// set auto page breaks -$pdf->setAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); - -// set image scale factor -$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); - -// set some language-dependent strings (optional) -if (@file_exists(dirname(__FILE__).'/lang/eng.php')) { - require_once(dirname(__FILE__).'/lang/eng.php'); - $pdf->setLanguageArray($l); -} - -// --------------------------------------------------------- - -// set font -$pdf->setFont('times', 'BI', 20); - -// add a page -$pdf->AddPage(); - -// set some text to print -$txt = <<Write(0, $txt, '', 0, 'C', true, 0, false, false, 0); - -// --------------------------------------------------------- - -//Close and output PDF document -$pdf->Output('example_002.pdf', 'I'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/example_003.php b/vendor/tecnickcom/tcpdf/examples/example_003.php deleted file mode 100644 index eeeb0c8..0000000 --- a/vendor/tecnickcom/tcpdf/examples/example_003.php +++ /dev/null @@ -1,122 +0,0 @@ -Image($image_file, 10, 10, 15, '', 'JPG', '', 'T', false, 300, '', false, false, 0, false, false, false); - // Set font - $this->setFont('helvetica', 'B', 20); - // Title - $this->Cell(0, 15, '<< TCPDF Example 003 >>', 0, false, 'C', 0, '', 0, false, 'M', 'M'); - } - - // Page footer - public function Footer() { - // Position at 15 mm from bottom - $this->setY(-15); - // Set font - $this->setFont('helvetica', 'I', 8); - // Page number - $this->Cell(0, 10, 'Page '.$this->getAliasNumPage().'/'.$this->getAliasNbPages(), 0, false, 'C', 0, '', 0, false, 'T', 'M'); - } -} - -// create new PDF document -$pdf = new MYPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); - -// set document information -$pdf->setCreator(PDF_CREATOR); -$pdf->setAuthor('Nicola Asuni'); -$pdf->setTitle('TCPDF Example 003'); -$pdf->setSubject('TCPDF Tutorial'); -$pdf->setKeywords('TCPDF, PDF, example, test, guide'); - -// set default header data -$pdf->setHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE, PDF_HEADER_STRING); - -// set header and footer fonts -$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); -$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); - -// set default monospaced font -$pdf->setDefaultMonospacedFont(PDF_FONT_MONOSPACED); - -// set margins -$pdf->setMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); -$pdf->setHeaderMargin(PDF_MARGIN_HEADER); -$pdf->setFooterMargin(PDF_MARGIN_FOOTER); - -// set auto page breaks -$pdf->setAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); - -// set image scale factor -$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); - -// set some language-dependent strings (optional) -if (@file_exists(dirname(__FILE__).'/lang/eng.php')) { - require_once(dirname(__FILE__).'/lang/eng.php'); - $pdf->setLanguageArray($l); -} - -// --------------------------------------------------------- - -// set font -$pdf->setFont('times', 'BI', 12); - -// add a page -$pdf->AddPage(); - -// set some text to print -$txt = <<Write(0, $txt, '', 0, 'C', true, 0, false, false, 0); - -// --------------------------------------------------------- - -//Close and output PDF document -$pdf->Output('example_003.pdf', 'I'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/example_004.php b/vendor/tecnickcom/tcpdf/examples/example_004.php deleted file mode 100644 index a1f69a5..0000000 --- a/vendor/tecnickcom/tcpdf/examples/example_004.php +++ /dev/null @@ -1,123 +0,0 @@ -setCreator(PDF_CREATOR); -$pdf->setAuthor('Nicola Asuni'); -$pdf->setTitle('TCPDF Example 004'); -$pdf->setSubject('TCPDF Tutorial'); -$pdf->setKeywords('TCPDF, PDF, example, test, guide'); - -// set default header data -$pdf->setHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE.' 004', PDF_HEADER_STRING); - -// set header and footer fonts -$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); -$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); - -// set default monospaced font -$pdf->setDefaultMonospacedFont(PDF_FONT_MONOSPACED); - -// set margins -$pdf->setMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); -$pdf->setHeaderMargin(PDF_MARGIN_HEADER); -$pdf->setFooterMargin(PDF_MARGIN_FOOTER); - -// set auto page breaks -$pdf->setAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); - -// set image scale factor -$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); - -// set some language-dependent strings (optional) -if (@file_exists(dirname(__FILE__).'/lang/eng.php')) { - require_once(dirname(__FILE__).'/lang/eng.php'); - $pdf->setLanguageArray($l); -} - -// --------------------------------------------------------- - -// set font -$pdf->setFont('times', '', 11); - -// add a page -$pdf->AddPage(); - -//Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') - -// test Cell stretching -$pdf->Cell(0, 0, 'TEST CELL STRETCH: no stretch', 1, 1, 'C', 0, '', 0); -$pdf->Cell(0, 0, 'TEST CELL STRETCH: scaling', 1, 1, 'C', 0, '', 1); -$pdf->Cell(0, 0, 'TEST CELL STRETCH: force scaling', 1, 1, 'C', 0, '', 2); -$pdf->Cell(0, 0, 'TEST CELL STRETCH: spacing', 1, 1, 'C', 0, '', 3); -$pdf->Cell(0, 0, 'TEST CELL STRETCH: force spacing', 1, 1, 'C', 0, '', 4); - -$pdf->Ln(5); - -$pdf->Cell(45, 0, 'TEST CELL STRETCH: scaling', 1, 1, 'C', 0, '', 1); -$pdf->Cell(45, 0, 'TEST CELL STRETCH: force scaling', 1, 1, 'C', 0, '', 2); -$pdf->Cell(45, 0, 'TEST CELL STRETCH: spacing', 1, 1, 'C', 0, '', 3); -$pdf->Cell(45, 0, 'TEST CELL STRETCH: force spacing', 1, 1, 'C', 0, '', 4); - -$pdf->AddPage(); - -// example using general stretching and spacing - -for ($stretching = 90; $stretching <= 110; $stretching += 10) { - for ($spacing = -0.254; $spacing <= 0.254; $spacing += 0.254) { - - // set general stretching (scaling) value - $pdf->setFontStretching($stretching); - - // set general spacing value - $pdf->setFontSpacing($spacing); - - $pdf->Cell(0, 0, 'Stretching '.$stretching.'%, Spacing '.sprintf('%+.3F', $spacing).'mm, no stretch', 1, 1, 'C', 0, '', 0); - $pdf->Cell(0, 0, 'Stretching '.$stretching.'%, Spacing '.sprintf('%+.3F', $spacing).'mm, scaling', 1, 1, 'C', 0, '', 1); - $pdf->Cell(0, 0, 'Stretching '.$stretching.'%, Spacing '.sprintf('%+.3F', $spacing).'mm, force scaling', 1, 1, 'C', 0, '', 2); - $pdf->Cell(0, 0, 'Stretching '.$stretching.'%, Spacing '.sprintf('%+.3F', $spacing).'mm, spacing', 1, 1, 'C', 0, '', 3); - $pdf->Cell(0, 0, 'Stretching '.$stretching.'%, Spacing '.sprintf('%+.3F', $spacing).'mm, force spacing', 1, 1, 'C', 0, '', 4); - - $pdf->Ln(2); - } -} - -// --------------------------------------------------------- - -//Close and output PDF document -$pdf->Output('example_004.pdf', 'I'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/example_005.php b/vendor/tecnickcom/tcpdf/examples/example_005.php deleted file mode 100644 index c27dfea..0000000 --- a/vendor/tecnickcom/tcpdf/examples/example_005.php +++ /dev/null @@ -1,160 +0,0 @@ -setCreator(PDF_CREATOR); -$pdf->setAuthor('Nicola Asuni'); -$pdf->setTitle('TCPDF Example 005'); -$pdf->setSubject('TCPDF Tutorial'); -$pdf->setKeywords('TCPDF, PDF, example, test, guide'); - -// set default header data -$pdf->setHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE.' 005', PDF_HEADER_STRING); - -// set header and footer fonts -$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); -$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); - -// set default monospaced font -$pdf->setDefaultMonospacedFont(PDF_FONT_MONOSPACED); - -// set margins -$pdf->setMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); -$pdf->setHeaderMargin(PDF_MARGIN_HEADER); -$pdf->setFooterMargin(PDF_MARGIN_FOOTER); - -// set auto page breaks -$pdf->setAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); - -// set image scale factor -$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); - -// set some language-dependent strings (optional) -if (@file_exists(dirname(__FILE__).'/lang/eng.php')) { - require_once(dirname(__FILE__).'/lang/eng.php'); - $pdf->setLanguageArray($l); -} - -// --------------------------------------------------------- - -// set font -$pdf->setFont('times', '', 10); - -// add a page -$pdf->AddPage(); - -// set cell padding -$pdf->setCellPaddings(1, 1, 1, 1); - -// set cell margins -$pdf->setCellMargins(1, 1, 1, 1); - -// set color for background -$pdf->setFillColor(255, 255, 127); - -// MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0) - -// set some text for example -$txt = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'; - -// Multicell test -$pdf->MultiCell(55, 5, '[LEFT] '.$txt, 1, 'L', 1, 0, '', '', true); -$pdf->MultiCell(55, 5, '[RIGHT] '.$txt, 1, 'R', 0, 1, '', '', true); -$pdf->MultiCell(55, 5, '[CENTER] '.$txt, 1, 'C', 0, 0, '', '', true); -$pdf->MultiCell(55, 5, '[JUSTIFY] '.$txt."\n", 1, 'J', 1, 2, '' ,'', true); -$pdf->MultiCell(55, 5, '[DEFAULT] '.$txt, 1, '', 0, 1, '', '', true); - -$pdf->Ln(4); - -// set color for background -$pdf->setFillColor(220, 255, 220); - -// Vertical alignment -$pdf->MultiCell(55, 40, '[VERTICAL ALIGNMENT - TOP] '.$txt, 1, 'J', 1, 0, '', '', true, 0, false, true, 40, 'T'); -$pdf->MultiCell(55, 40, '[VERTICAL ALIGNMENT - MIDDLE] '.$txt, 1, 'J', 1, 0, '', '', true, 0, false, true, 40, 'M'); -$pdf->MultiCell(55, 40, '[VERTICAL ALIGNMENT - BOTTOM] '.$txt, 1, 'J', 1, 1, '', '', true, 0, false, true, 40, 'B'); - -$pdf->Ln(4); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// set color for background -$pdf->setFillColor(215, 235, 255); - -// set some text for example -$txt = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sed imperdiet lectus. Phasellus quis velit velit, non condimentum quam. Sed neque urna, ultrices ac volutpat vel, laoreet vitae augue. Sed vel velit erat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras eget velit nulla, eu sagittis elit. Nunc ac arcu est, in lobortis tellus. Praesent condimentum rhoncus sodales. In hac habitasse platea dictumst. Proin porta eros pharetra enim tincidunt dignissim nec vel dolor. Cras sapien elit, ornare ac dignissim eu, ultricies ac eros. Maecenas augue magna, ultrices a congue in, mollis eu nulla. Nunc venenatis massa at est eleifend faucibus. Vivamus sed risus lectus, nec interdum nunc. - -Fusce et felis vitae diam lobortis sollicitudin. Aenean tincidunt accumsan nisi, id vehicula quam laoreet elementum. Phasellus egestas interdum erat, et viverra ipsum ultricies ac. Praesent sagittis augue at augue volutpat eleifend. Cras nec orci neque. Mauris bibendum posuere blandit. Donec feugiat mollis dui sit amet pellentesque. Sed a enim justo. Donec tincidunt, nisl eget elementum aliquam, odio ipsum ultrices quam, eu porttitor ligula urna at lorem. Donec varius, eros et convallis laoreet, ligula tellus consequat felis, ut ornare metus tellus sodales velit. Duis sed diam ante. Ut rutrum malesuada massa, vitae consectetur ipsum rhoncus sed. Suspendisse potenti. Pellentesque a congue massa.'; - -// print a blox of text using multicell() -$pdf->MultiCell(80, 5, $txt."\n", 1, 'J', 1, 1, '' ,'', true); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// AUTO-FITTING - -// set color for background -$pdf->setFillColor(255, 235, 235); - -// Fit text on cell by reducing font size -$pdf->MultiCell(55, 60, '[FIT CELL] '.$txt."\n", 1, 'J', 1, 1, 125, 145, true, 0, false, true, 60, 'M', true); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// CUSTOM PADDING - -// set color for background -$pdf->setFillColor(255, 255, 215); - -// set font -$pdf->setFont('helvetica', '', 8); - -// set cell padding -$pdf->setCellPaddings(2, 4, 6, 8); - -$txt = "CUSTOM PADDING:\nLeft=2, Top=4, Right=6, Bottom=8\nLorem ipsum dolor sit amet, consectetur adipiscing elit. In sed imperdiet lectus. Phasellus quis velit velit, non condimentum quam. Sed neque urna, ultrices ac volutpat vel, laoreet vitae augue.\n"; - -$pdf->MultiCell(55, 5, $txt, 1, 'J', 1, 2, 125, 210, true); - -// move pointer to last page -$pdf->lastPage(); - -// --------------------------------------------------------- - -//Close and output PDF document -$pdf->Output('example_005.pdf', 'I'); - -//============================================================+ -// END OF FILE -//============================================================+ diff --git a/vendor/tecnickcom/tcpdf/examples/example_006.php b/vendor/tecnickcom/tcpdf/examples/example_006.php deleted file mode 100644 index f2c7dbc..0000000 --- a/vendor/tecnickcom/tcpdf/examples/example_006.php +++ /dev/null @@ -1,347 +0,0 @@ -setCreator(PDF_CREATOR); -$pdf->setAuthor('Nicola Asuni'); -$pdf->setTitle('TCPDF Example 006'); -$pdf->setSubject('TCPDF Tutorial'); -$pdf->setKeywords('TCPDF, PDF, example, test, guide'); - -// set default header data -$pdf->setHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE.' 006', PDF_HEADER_STRING); - -// set header and footer fonts -$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); -$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); - -// set default monospaced font -$pdf->setDefaultMonospacedFont(PDF_FONT_MONOSPACED); - -// set margins -$pdf->setMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); -$pdf->setHeaderMargin(PDF_MARGIN_HEADER); -$pdf->setFooterMargin(PDF_MARGIN_FOOTER); - -// set auto page breaks -$pdf->setAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); - -// set image scale factor -$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); - -// set some language-dependent strings (optional) -if (@file_exists(dirname(__FILE__).'/lang/eng.php')) { - require_once(dirname(__FILE__).'/lang/eng.php'); - $pdf->setLanguageArray($l); -} - -// --------------------------------------------------------- - -// set font -$pdf->setFont('dejavusans', '', 10); - -// add a page -$pdf->AddPage(); - -// writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') -// writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) - -// create some HTML content -$html = '

HTML Example

-Some special characters: < € € € & è è © > \\slash \\\\double-slash \\\\\\triple-slash -

List

-List example: -
    -
  1. test alt attribute test image
  2. -
  3. bold text
  4. -
  5. italic text
  6. -
  7. underlined text
  8. -
  9. bbibiubib
  10. -
  11. link to http://www.tecnick.com
  12. -
  13. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
    Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
  14. -
  15. SUBLIST -
      -
    1. row one -
        -
      • sublist
      • -
      -
    2. -
    3. row two
    4. -
    -
  16. -
  17. TEST line through
  18. -
  19. font + 3
  20. -
  21. small text normal small text normal subscript normal superscript normal
  22. -
-
-
Coffee
-
Black hot drink
-
Milk
-
White cold drink
-
-
IMAGES
-test alt attributetest alt attributetest alt attribute -
'; - -// output the HTML content -$pdf->writeHTML($html, true, false, true, false, ''); - - -// output some RTL HTML content -$html = '
The words “מזל [mazel] טוב [tov]” mean “Congratulations!”
'; -$pdf->writeHTML($html, true, false, true, false, ''); - -// test some inline CSS -$html = '

This is just an example of html code to demonstrate some supported CSS inline styles. -bold text -line-trough -underline and line-trough -color -background color -bold -xx-small -x-small -small -medium -large -x-large -xx-large -

'; - -$pdf->writeHTML($html, true, false, true, false, ''); - -// reset pointer to the last page -$pdf->lastPage(); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Print a table - -// add a page -$pdf->AddPage(); - -// create some HTML content -$subtable = '
ab
cd
'; - -$html = '

HTML TABLE:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#RIGHT alignLEFT align4A
1A1 example link column span. One two tree four five six seven eight nine ten.
line after br
small text normal subscript normal superscript normal bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla
  1. first
    1. sublist
    2. sublist
  2. second
small small small small small small small small small small small small small small small small small small small small
4B
'.$subtable.'A2 € € € & è è
A2 € € € & è è
Red Yellow BG4C
1A2AA
2AB
2AC
4D
1B4E
1C2C3C4F
'; - -// output the HTML content -$pdf->writeHTML($html, true, false, true, false, ''); - -// Print some HTML Cells - -$html = 'red green blue
red green blue'; - -$pdf->setFillColor(255,255,0); - -$pdf->writeHTMLCell(0, 0, '', '', $html, 'LRTB', 1, 0, true, 'L', true); -$pdf->writeHTMLCell(0, 0, '', '', $html, 'LRTB', 1, 1, true, 'C', true); -$pdf->writeHTMLCell(0, 0, '', '', $html, 'LRTB', 1, 0, true, 'R', true); - -// reset pointer to the last page -$pdf->lastPage(); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Print a table - -// add a page -$pdf->AddPage(); - -// create some HTML content -$html = '

Image alignments on HTML table

- - - - - - - - -
'; - -// output the HTML content -$pdf->writeHTML($html, true, false, true, false, ''); - -// create some HTML content -$html = '

Embedded Images

- - - -
src="@..."
src="data..."
'; - -$data = base64_encode(file_get_contents("images/logo_example.png")); -$html = str_replace("@DATA1@", "@" . $data, $html); -$html = str_replace("@DATA2@", "data:image/png;base64," . $data, $html); - -// output the HTML content -$pdf->writeHTML($html, true, false, true, false, ''); - -// reset pointer to the last page -$pdf->lastPage(); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Print all HTML colors - -// add a page -$pdf->AddPage(); - -$textcolors = '

HTML Text Colors

'; -$bgcolors = '

HTML Background Colors

'; - -foreach(TCPDF_COLORS::$webcolor as $k => $v) { - $textcolors .= ''.$v.' '; - $bgcolors .= ''.$v.' '; -} - -// output the HTML content -$pdf->writeHTML($textcolors, true, false, true, false, ''); -$pdf->writeHTML($bgcolors, true, false, true, false, ''); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Test word-wrap - -// create some HTML content -$html = '
-

Various tests

-link to page 2
-thisisaverylongword thisisanotherverylongword thisisaverylongword thisisanotherverylongword thisisaverylongword thisisaverylongword thisisanotherverylongword thisisaverylongword thisisanotherverylongword thisisaverylongword thisisaverylongword thisisanotherverylongword thisisaverylongword thisisanotherverylongword thisisaverylongword thisisaverylongword thisisanotherverylongword thisisaverylongword thisisanotherverylongword thisisaverylongword thisisaverylongword thisisanotherverylongword thisisaverylongword thisisanotherverylongword thisisaverylongword'; - -// output the HTML content -$pdf->writeHTML($html, true, false, true, false, ''); - -// Test fonts nesting -$html1 = 'Default Courier Helvetica Times dejavusans Times Helvetica Courier Default'; -$html2 = 'small text normal small text normal subscript normal superscript normal'; -$html3 = 'The quick brown fox jumps over the lazy dog.'; - -$html = $html1.'
'.$html2.'
'.$html3.'
'.$html3.'
'.$html2; - -// output the HTML content -$pdf->writeHTML($html, true, false, true, false, ''); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// test pre tag - -// add a page -$pdf->AddPage(); - -$html = << -Hello World!
-Hello - -
-int main() {
-    printf("HelloWorld");
-    return 0;
-}
-
-Monospace font, normal font, monospace font, normal font. -
-
DIV LEVEL 1
DIV LEVEL 2
DIV LEVEL 1
-
-SPAN LEVEL 1 SPAN LEVEL 2 SPAN LEVEL 1 -EOF; - -// output the HTML content -$pdf->writeHTML($html, true, false, true, false, ''); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// test custom bullet points for list - -// add a page -$pdf->AddPage(); - -$html = <<Test custom bullet image for list items -