diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index c308ed0c..00000000 --- a/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -# http://editorconfig.org -root = true - -[*] -indent_style = space -indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index ec5a0a92..00000000 --- a/.gitattributes +++ /dev/null @@ -1,10 +0,0 @@ -* text=auto - -/tests export-ignore -/.editorconfig export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore -/.travis.yml export-ignore -/.php_cs export-ignore -/.php-cs-fixer.php export-ignore -/phpunit.xml.dist export-ignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3400b507..00000000 --- a/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -phpunit.xml -composer.lock -vendor -bin -.php_cs.cache -.php-cs-fixer.cache -.phpunit.result.cache -.phpunit.cache diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php deleted file mode 100644 index 0aa32e23..00000000 --- a/.php-cs-fixer.php +++ /dev/null @@ -1,189 +0,0 @@ -in(__DIR__.'/src') - ->in(__DIR__.'/tests') -; - -return (new PhpCsFixer\Config()) - ->setFinder($finder) - ->setRiskyAllowed(true) - ->registerCustomFixers(new PedroTroller\CS\Fixer\Fixers()) - ->setRules([ - '@PHP71Migration' => true, - '@PSR1' => true, - '@PSR2' => true, - 'array_indentation' => true, - 'array_syntax' => [ 'syntax' => 'short' ], - 'align_multiline_comment' => [ - 'comment_type' => 'all_multiline', - ], - 'array_syntax' => [ - 'syntax' => 'short', - ], - 'binary_operator_spaces' => true, - 'blank_line_after_opening_tag' => true, - 'blank_line_before_statement' => true, - 'cast_spaces' => true, - 'class_attributes_separation' => true, - 'combine_consecutive_issets' => true, - 'combine_consecutive_unsets' => true, - 'compact_nullable_typehint' => true, - 'concat_space' => ['spacing' => 'one'], - 'date_time_immutable' => true, - 'declare_equal_normalize' => [ - 'space' => 'single', - ], - 'dir_constant' => true, - 'ereg_to_preg' => true, - 'escape_implicit_backslashes' => true, - 'explicit_indirect_variable' => true, - 'explicit_string_variable' => true, - 'fopen_flag_order' => true, - 'fopen_flags' => true, - 'fully_qualified_strict_types' => true, - 'function_to_constant' => [ - 'functions' => [ - 'get_class', - 'php_sapi_name', - 'phpversion', - 'pi', - ] - ], - 'function_typehint_space' => true, - 'global_namespace_import' => true, - 'heredoc_to_nowdoc' => true, - 'implode_call' => true, - 'include' => true, - 'is_null' => true, - 'linebreak_after_opening_tag' => true, - 'list_syntax' => [ - 'syntax' => 'long', - ], - 'logical_operators' => true, - 'lowercase_cast' => true, - 'lowercase_static_reference' => true, - 'magic_constant_casing' => true, - 'magic_method_casing' => true, - 'method_chaining_indentation' => true, - 'modernize_types_casting' => true, - 'multiline_comment_opening_closing' => true, - 'multiline_whitespace_before_semicolons' => [ - 'strategy' => 'new_line_for_chained_calls', - ], - 'native_constant_invocation' => [ - 'include' => ['@internal'], - 'scope' => 'all', - ], - 'native_function_casing' => true, - 'native_function_invocation' => ['include' => ['@all']], - 'native_function_type_declaration_casing' => true, - 'new_with_braces' => true, - 'no_alternative_syntax' => true, - 'no_binary_string' => true, - 'no_blank_lines_after_class_opening' => true, - 'no_blank_lines_after_phpdoc' => true, - 'no_empty_comment' => true, - 'no_empty_phpdoc' => true, - 'no_empty_statement' => true, - 'no_extra_blank_lines' => [ - 'tokens' => ['extra'] - ], - 'no_leading_import_slash' => true, - 'no_leading_namespace_whitespace' => true, - 'no_mixed_echo_print' => true, - 'no_multiline_whitespace_around_double_arrow' => true, - 'no_short_bool_cast' => true, - 'echo_tag_syntax' => ['format' => 'long'], - 'no_singleline_whitespace_before_semicolons' => true, - 'no_spaces_around_offset' => [ - 'positions' => ['inside', 'outside'], - ], - 'no_superfluous_elseif' => true, - 'no_superfluous_phpdoc_tags' => false, - 'no_trailing_comma_in_list_call' => true, - 'no_trailing_comma_in_singleline_array' => true, - 'no_unneeded_curly_braces' => true, - 'no_unused_imports' => true, - 'no_useless_else' => true, - 'no_useless_return' => true, - 'no_whitespace_before_comma_in_array' => true, - 'no_whitespace_in_blank_line' => true, - 'normalize_index_brace' => true, - 'object_operator_without_whitespace' => true, - 'ordered_class_elements' => [ - 'order' => [ - 'use_trait', - 'constant_public', - 'constant_protected', - 'constant_private', - 'property_public', - 'property_protected', - 'property_private', - 'construct', - 'destruct', - 'magic', - 'phpunit', - 'method_public', - 'method_protected', - 'method_private' - ], - ], - 'php_unit_construct' => [ - 'assertions' => [ - 'assertEquals', - 'assertSame', - 'assertNotEquals', - 'assertNotSame' - ] - ], - // Check on other phpunit stuff - 'phpdoc_align' => [ - 'align' => 'vertical' - ], - 'phpdoc_indent' => true, - 'general_phpdoc_tag_rename' => ['fix_inline' => true], - 'phpdoc_no_access' => true, - 'phpdoc_no_alias_tag' => true, - 'phpdoc_no_empty_return' => false, - 'phpdoc_no_package' => true, - 'phpdoc_no_useless_inheritdoc' => true, - 'phpdoc_order' => true, - 'phpdoc_return_self_reference' => true, - 'phpdoc_scalar' => true, - 'phpdoc_separation' => true, - 'phpdoc_single_line_var_spacing' => true, - 'phpdoc_summary' => true, - 'phpdoc_to_comment' => true, - 'phpdoc_trim' => true, - 'phpdoc_trim_consecutive_blank_line_separation' => true, - 'phpdoc_types' => true, - 'phpdoc_types_order' => true, - 'phpdoc_var_annotation_correct_order' => true, - 'phpdoc_var_without_name' => true, - 'psr_autoloading' => ['dir' => './src'], - 'return_assignment' => true, - 'return_type_declaration' => true, - 'semicolon_after_instruction' => true, - 'short_scalar_cast' => true, - 'single_blank_line_before_namespace' => true, - 'single_line_comment_style' => true, - 'single_line_throw' => true, - 'single_quote' => true, - 'single_trait_insert_per_statement' => true, - 'space_after_semicolon' => true, - 'standardize_increment' => true, - 'standardize_not_equals' => true, - 'strict_comparison' => true, - 'ternary_operator_spaces' => true, - 'trailing_comma_in_multiline' => ['elements' => ['arrays']], - 'trim_array_spaces' => true, - 'unary_operator_spaces' => true, - 'whitespace_after_comma_in_array' => true, - 'PedroTroller/exceptions_punctuation' => true, - 'PedroTroller/line_break_between_method_arguments' => [ - 'max-args' => 5, - ], - 'PedroTroller/useless_code_after_return' => true, - ]) -; diff --git a/.php_cs b/.php_cs deleted file mode 100644 index fffc0bf7..00000000 --- a/.php_cs +++ /dev/null @@ -1,189 +0,0 @@ -in(__DIR__.'/src') - ->in(__DIR__.'/tests') -; - -return PhpCsFixer\Config::create() - ->setFinder($finder) - ->setRiskyAllowed(true) - ->registerCustomFixers(new PedroTroller\CS\Fixer\Fixers()) - ->setRules([ - '@PHP71Migration' => true, - '@PSR1' => true, - '@PSR2' => true, - 'array_indentation' => true, - 'array_syntax' => [ 'syntax' => 'short' ], - 'align_multiline_comment' => [ - 'comment_type' => 'all_multiline', - ], - 'array_syntax' => [ - 'syntax' => 'short', - ], - 'binary_operator_spaces' => true, - 'blank_line_after_opening_tag' => true, - 'blank_line_before_statement' => true, - 'cast_spaces' => true, - 'class_attributes_separation' => true, - 'combine_consecutive_issets' => true, - 'combine_consecutive_unsets' => true, - 'compact_nullable_typehint' => true, - 'concat_space' => ['spacing' => 'one'], - 'date_time_immutable' => true, - 'declare_equal_normalize' => [ - 'space' => 'single', - ], - 'dir_constant' => true, - 'ereg_to_preg' => true, - 'escape_implicit_backslashes' => true, - 'explicit_indirect_variable' => true, - 'explicit_string_variable' => true, - 'fopen_flag_order' => true, - 'fopen_flags' => true, - 'fully_qualified_strict_types' => true, - 'function_to_constant' => [ - 'functions' => [ - 'get_class', - 'php_sapi_name', - 'phpversion', - 'pi', - ] - ], - 'function_typehint_space' => true, - 'global_namespace_import' => true, - 'heredoc_to_nowdoc' => true, - 'implode_call' => true, - 'include' => true, - 'is_null' => true, - 'linebreak_after_opening_tag' => true, - 'list_syntax' => [ - 'syntax' => 'long', - ], - 'logical_operators' => true, - 'lowercase_cast' => true, - 'lowercase_static_reference' => true, - 'magic_constant_casing' => true, - 'magic_method_casing' => true, - 'method_chaining_indentation' => true, - 'modernize_types_casting' => true, - 'multiline_comment_opening_closing' => true, - 'multiline_whitespace_before_semicolons' => [ - 'strategy' => 'new_line_for_chained_calls', - ], - 'native_constant_invocation' => [ - 'include' => ['@internal'], - 'scope' => 'all', - ], - 'native_function_casing' => true, - 'native_function_invocation' => true, - 'native_function_type_declaration_casing' => true, - 'new_with_braces' => true, - 'no_alternative_syntax' => true, - 'no_binary_string' => true, - 'no_blank_lines_after_class_opening' => true, - 'no_blank_lines_after_phpdoc' => true, - 'no_empty_comment' => true, - 'no_empty_phpdoc' => true, - 'no_empty_statement' => true, - 'no_extra_blank_lines' => [ - 'tokens' => ['extra'] - ], - 'no_leading_import_slash' => true, - 'no_leading_namespace_whitespace' => true, - 'no_mixed_echo_print' => true, - 'no_multiline_whitespace_around_double_arrow' => true, - 'no_short_bool_cast' => true, - 'no_short_echo_tag' => true, - 'no_singleline_whitespace_before_semicolons' => true, - 'no_spaces_around_offset' => [ - 'positions' => ['inside', 'outside'], - ], - 'no_superfluous_elseif' => true, - 'no_superfluous_phpdoc_tags' => false, - 'no_trailing_comma_in_list_call' => true, - 'no_trailing_comma_in_singleline_array' => true, - 'no_unneeded_curly_braces' => true, - 'no_unused_imports' => true, - 'no_useless_else' => true, - 'no_useless_return' => true, - 'no_whitespace_before_comma_in_array' => true, - 'no_whitespace_in_blank_line' => true, - 'normalize_index_brace' => true, - 'object_operator_without_whitespace' => true, - 'ordered_class_elements' => [ - 'order' => [ - 'use_trait', - 'constant_public', - 'constant_protected', - 'constant_private', - 'property_public', - 'property_protected', - 'property_private', - 'construct', - 'destruct', - 'magic', - 'phpunit', - 'method_public', - 'method_protected', - 'method_private' - ], - ], - 'php_unit_construct' => [ - 'assertions' => [ - 'assertEquals', - 'assertSame', - 'assertNotEquals', - 'assertNotSame' - ] - ], - // Check on other phpunit stuff - 'phpdoc_align' => [ - 'align' => 'vertical' - ], - 'phpdoc_indent' => true, - 'phpdoc_inline_tag' => true, - 'phpdoc_no_access' => true, - 'phpdoc_no_alias_tag' => true, - 'phpdoc_no_empty_return' => false, - 'phpdoc_no_package' => true, - 'phpdoc_no_useless_inheritdoc' => true, - 'phpdoc_order' => true, - 'phpdoc_return_self_reference' => true, - 'phpdoc_scalar' => true, - 'phpdoc_separation' => true, - 'phpdoc_single_line_var_spacing' => true, - 'phpdoc_summary' => true, - 'phpdoc_to_comment' => true, - 'phpdoc_trim' => true, - 'phpdoc_trim_consecutive_blank_line_separation' => true, - 'phpdoc_types' => true, - 'phpdoc_types_order' => true, - 'phpdoc_var_annotation_correct_order' => true, - 'phpdoc_var_without_name' => true, - 'psr4' => true, - 'return_assignment' => true, - 'return_type_declaration' => true, - 'semicolon_after_instruction' => true, - 'short_scalar_cast' => true, - 'single_blank_line_before_namespace' => true, - 'single_line_comment_style' => true, - 'single_line_throw' => true, - 'single_quote' => true, - 'single_trait_insert_per_statement' => true, - 'space_after_semicolon' => true, - 'standardize_increment' => true, - 'standardize_not_equals' => true, - 'strict_comparison' => true, - 'ternary_operator_spaces' => true, - 'trailing_comma_in_multiline_array' => true, - 'trim_array_spaces' => true, - 'unary_operator_spaces' => true, - 'whitespace_after_comma_in_array' => true, - 'PedroTroller/exceptions_punctuation' => true, - 'PedroTroller/line_break_between_method_arguments' => [ - 'max-args' => 5, - ], - 'PedroTroller/useless_code_after_return' => true, - ]) -; diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a0ffa0db..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,45 +0,0 @@ -## 1.1.0 - -* Add bypass-proxy-for option added in 0.12.3 (see [#302](https://github.com/KnpLabs/snappy/pull/302)) -* Fix symfony/process 4.2 deprecation notice (see [#331](https://github.com/KnpLabs/snappy/pull/331)) -* Drop suppor for unmaintained PHP versions (5.6 and 7.0, see [#337](https://github.com/KnpLabs/snappy/pull/337) -* Drop support for unmaintained symfony/process versions (see [#337](https://github.com/KnpLabs/snappy/pull/337)) -* Pass on error code in checkProcessStatus (see [#328](https://github.com/KnpLabs/snappy/pull/328)) - -Thanks to @joshpme, @drigani, @fbourigault, @NiR- and @leimd for their work. - -## 1.0.4 - -* Support cache-dir for Image generation (see [#297](https://github.com/KnpLabs/snappy/pull/297)). - -Thank you @dimitrilahaye for their work. - -## 1.0.3 - -* Add support to Symfony 4 ([#290](https://github.com/KnpLabs/snappy/pull/290)) -* Use PHPUnit\Framework\TestCase instead of PHPUnit_Framework_TestCase ([#287](https://github.com/KnpLabs/snappy/pull/287)) - -Credits go to @michaelperrin and @carusogabriel. - -## 1.0.2 - -*A BC break was introduced in v1.0.0: using objects castable to string with a cyclic dependency to the generator -as option value would break `setOption()` / `setOptions()` methods.* - -* Use logger context rather than `var_export` to log option values (see [#283](https://github.com/KnpLabs/snappy/pull/283)) - -Credits go to: @barryvdh. - -## 1.0.1 - -* Fix `Call to a member function debug() on null` logger (see [#270](https://github.com/KnpLabs/snappy/pull/270)) - -## 1.0.0 - -* Don't check if it's a file when the path is bigger than `PHP_MAXPATHLEN` (see [#224](https://github.com/KnpLabs/snappy/pull/224)) -* Pass `image-dpi` and `image-quality` options as integer (see [#251](https://github.com/KnpLabs/snappy/pull/251)) -* Improve documentation readability (see [#255](https://github.com/KnpLabs/snappy/pull/255)) -* Add logging capabilities to generators (see [#264](https://github.com/KnpLabs/snappy/pull/264)) -* Add some more frequent questions/issues to the FAQ (see [#263](https://github.com/KnpLabs/snappy/pull/263), [#265](https://github.com/KnpLabs/snappy/pull/265), [#266](https://github.com/KnpLabs/snappy/pull/266)) - -Credits go to: @wouterbulten, @martinssipenko, @Herz3h, @akovalyov, @NiR-. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 45ac77b9..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,64 +0,0 @@ - -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to make participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies within all project spaces, and it also applies when -an individual is representing the project or its community in public spaces. -Examples of representing a project or community include using an official -project e-mail address, posting via an official social media account, or acting -as an appointed representative at an online or offline event. Representation of -a project may be further defined and clarified by project maintainers. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -[homepage]: https://www.contributor-covenant.org - -## Contact -If you have any questions or feedback, [please ping us](https://twitter.com/KNPLabs) ! diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 472c0515..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,85 +0,0 @@ - -# Thanks for contributing! - -:+1: First of all, thanks for contributing! The team is happy to help if you -have any questions. Have a look to this contributing guide and also to the -[FAQ section](https://github.com/KnpLabs/snappy/blob/master/doc/faq.md). :feet: -The following is a set of guidelines for contributing to Snappy, which is hosted -by the [KNP Labs Organization](https://github.com/KnpLabs) on GitHub. These are -mostly guidelines, not rules. Use your best judgment, and feel free to propose -changes to this document opening a pull request. :shipit: - -## Code of Conduct - -This project and everyone participating in it is governed by the following -[Code of Conduct](https://github.com/KnpLabs/snappy/blob/master/CODE_OF_CONDUCT.md). -By participating, you are expected to uphold this code. - -## Reporting a bug - -#### Before submitting a bug -- Verify that you are using the latest Snappy version; -- Double-check the [documentation](https://github.com/KnpLabs/snappy/blob/master/README.md) -and the [FAQ section](https://github.com/KnpLabs/snappy/blob/master/doc/faq.md) -to see if you're not misusing the library; -- Check if the issue is a Snappy issue and not a wkhtmltopdf issue (see [how to](#how-to-verify-if-the-issue-is-a-snappy-issue)); -- Check if the issue has already been reported. If it has and the issue is still -open, add a comment to the existing issue instead of opening a new one. - -##### How to verify if the issue is a Snappy issue -In order to verify that the issue is a Snappy issue and not a wkhtmltopdf issue, -simply copy paste the command displayed in the error message in your command prompt. -If the same error appears on the command line, then it's a wkhtmltopdf issue and -you'll have more chance to resolve your issue [there](https://github.com/wkhtmltopdf/wkhtmltopdf/issues). - -#### How to submit a (good) bug report -To report a Snappy bug please open a [GitHub issue](https://github.com/KnpLabs/snappy/issues) -following the rules below. - -- Use a clear and descriptive title for the issue to identify the problem; -- Describe the steps needed to reproduce the bug including a code example when -possible; -- Give as much detail as possible about your environment (OS, PHP version, -Snappy configuration, ...); - -## Suggesting enhancements - -To suggest Snappy enhancements please open a [GitHub issue](https://github.com/KnpLabs/snappy/issues) -following the rules below. - -- Use a clear and descriptive title for the issue to identify the problem; -- Provide a step-by-step description of the suggested enhancement in as many -details as possible; -- Explain why this enhancement would be useful with one or more use cases; - -## Contributing to the code - -A pull request, is the best way to provide a bug fix or to propose enhancements to Snappy. - -When submitting a pull request please be sure to follow the same rules described -above in [Reporting a bug](#reporting-a-bug) and [Suggesting enhancements](suggesting-enhancements) -sections depending on the nature of your change. - -> Before starting to work on a large change please open an issue to ask the -maintainers if they are fine with it (no one likes to work for nothing!). - -1. Fork the repository -2. Once the repository has been forked clone it locally -``` -git clone git@github.com:USERNAME/snappy.git -``` -3. Create a new branch -``` -git checkout -b BRANCH_NAME master -``` -4. Code!!! -5. Add/Update tests (if needed) -6. Update documentation (if needed) -7. Run the tests and make sure that they are passing -``` -composer unit-tests -composer static-analysis -``` -8. Squash your commits -9. Rebase your branch on master and fix merge conflicts -10. Open the pull request diff --git a/LICENSE b/LICENSE deleted file mode 100644 index cbdf8a91..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2010 Matthieu Bontemps - -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. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 421d2e07..00000000 --- a/README.md +++ /dev/null @@ -1,169 +0,0 @@ -# Snappy - -![Build Status](https://github.com/KnpLabs/snappy/actions/workflows/build.yaml/badge.svg) -[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/github/KnpLabs/snappy?branch=master&svg=true)](https://ci.appveyor.com/project/NiR-/snappy) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/KnpLabs/Gaufrette/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/KnpLabs/Gaufrette/?branch=master) - -Snappy is a PHP library allowing thumbnail, snapshot or PDF generation from a url or a html page. -It uses the excellent webkit-based [wkhtmltopdf and wkhtmltoimage](http://wkhtmltopdf.org/) -available on OSX, linux, windows. - -You will have to download wkhtmltopdf `0.12.x` in order to use Snappy. - -Please, check [FAQ](doc/faq.md) before opening a new issue. Snappy is a tiny wrapper around wkhtmltox, so lots of issues are already answered, resolved or wkhtmltox ones. - -Following integrations are available: -* [`knplabs/knp-snappy-bundle`](https://github.com/KnpLabs/KnpSnappyBundle), for Symfony -* [`barryvdh/laravel-snappy`](https://github.com/barryvdh/laravel-snappy), for Laravel -* [`mvlabs/mvlabs-snappy`](https://github.com/mvlabs/MvlabsSnappy), for Zend Framework - -## Installation using [Composer](http://getcomposer.org/) - -```bash -composer require knplabs/knp-snappy -``` - -## Usage - -### Initialization -```php -setBinary('/usr/local/bin/wkhtmltopdf'); -``` - -### Display the pdf in the browser - -```php -$snappy = new Pdf('/usr/local/bin/wkhtmltopdf'); -header('Content-Type: application/pdf'); -echo $snappy->getOutput('http://www.github.com'); -``` - -### Download the pdf from the browser - -```php -$snappy = new Pdf('/usr/local/bin/wkhtmltopdf'); -header('Content-Type: application/pdf'); -header('Content-Disposition: attachment; filename="file.pdf"'); -echo $snappy->getOutput('http://www.github.com'); -``` - -### Merge multiple urls into one pdf -```php -$snappy = new Pdf('/usr/local/bin/wkhtmltopdf'); -header('Content-Type: application/pdf'); -header('Content-Disposition: attachment; filename="file.pdf"'); -echo $snappy->getOutput(array('http://www.github.com','http://www.knplabs.com','http://www.php.net')); -``` - -### Generate local pdf file -```php -$snappy = new Pdf('/usr/local/bin/wkhtmltopdf'); -$snappy->generateFromHtml('

Bill

You owe me money, dude.

', '/tmp/bill-123.pdf'); -``` - -### Pass options to snappy -```php -// Type wkhtmltopdf -H to see the list of options -$snappy = new Pdf('/usr/local/bin/wkhtmltopdf'); -$snappy->setOption('disable-javascript', true); -$snappy->setOption('no-background', true); -$snappy->setOption('allow', array('/path1', '/path2')); -$snappy->setOption('cookie', array('key' => 'value', 'key2' => 'value2')); -$snappy->setOption('post', array('key' => 'value')); -$snappy->setOption('cover', 'pathToCover.html'); -// .. or pass a cover as html -$snappy->setOption('cover', '

Bill cover

'); -$snappy->setOption('toc', true); -$snappy->setOption('cache-dir', '/path/to/cache/dir'); -``` - -### Reset options -Options can be reset to their initial values with `resetOptions()` method. -```php -$snappy = new Pdf('/usr/local/bin/wkhtmltopdf'); -// Set some options -$snappy->setOption('copies' => 4); -// .. -// Reset options -$snappy->resetOptions(); -``` - -## wkhtmltopdf binary as composer dependencies - -If you want to download wkhtmltopdf and wkhtmltoimage with composer you add to `composer.json`: - -```bash -composer require h4cc/wkhtmltopdf-i386 0.12.x -composer require h4cc/wkhtmltoimage-i386 0.12.x -``` - -or this if you are in 64 bit based system: - -```bash -composer require h4cc/wkhtmltopdf-amd64 0.12.x -composer require h4cc/wkhtmltoimage-amd64 0.12.x -``` - -And then you can use it - -```php -setOption('toc', true); -$snappy->setOption('xsl-style-sheet', 'http://path/to/stylesheet.xsl') //or local file; - -$snappy->generateFromHtml('

Some content

', 'test.pdf'); -``` - -## Bugs & Support - -If you found a bug please fill a detailed issue with all the following points. -If you need some help, please at least provide a complete reproducer so we could help you based on facts rather than assumptions. - -* OS and its version -* Wkhtmltopdf, its version and how you installed it -* A complete reproducer with relevant php and html/css/js code - -If your reproducer is big, please try to shrink it. It will help everyone to narrow the bug. - -## Maintainers - -KNPLabs is looking for maintainers ([see why](https://knplabs.com/en/blog/news-for-our-foss-projects-maintenance)). - -If you are interested, feel free to open a PR to ask to be added as a maintainer. - -We’ll be glad to hear from you :) - -## Credits - -Snappy has been originally developed by the [KnpLabs](http://knplabs.com) team. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 22b52e0d..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,12 +0,0 @@ -# Security Policy - -## Supported Versions - -| Version | Supported | -| ------- | ------------------ | -| 1.4.x | :white_check_mark: | -| 1.3.x | :x: | - -## Reporting a Vulnerability - -You can send an email to if you spot any security issue in this library. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 2d7cfaaa..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,53 +0,0 @@ -build: false -platform: x64 -clone_folder: c:\projects\snappy - -environment: - matrix: - - dependencies: lowest - php_ver_target: 7.1 - - dependencies: current - php_ver_target: 7.2 - - dependencies: highest - php_ver_target: 7.3 - -cache: - - composer.phar - - c:\ProgramData\chocolatey\bin -> appveyor.yml - - c:\ProgramData\chocolatey\lib -> appveyor.yml - - c:\php -> appveyor.yml - -init: - - SET PATH=c:\Program Files\OpenSSL;c:\tools\php;%PATH% - - SET COMPOSER_NO_INTERACTION=1 - - SET PHP=1 - - SET ANSICON=121x90 (121x90) - -install: - - IF EXIST c:\tools\php (SET PHP=0) - - ps: Set-Service wuauserv -StartupType Manual - # In order to be able to list all the avialable PHP packages we have to - # downgrade Chocolatey to version 0.10.13. - # See https://github.com/chocolatey/choco/issues/1843 - - ps: choco install chocolatey -y --version 0.10.13 --allow-downgrade --force - - ps: choco search php --exact --all-versions -r - - ps: echo ((choco search php --exact --all-versions -r | select-string -pattern $env:php_ver_target | sort { [version]($_ -split '\|' | select -last 1) } -Descending | Select-Object -first 1) -replace '[php|]','') - - ps: appveyor-retry cinst php --params '""/InstallDir:c:\tools\php""' --ignore-checksums -y --force --version ((choco search php --exact --all-versions -r | select-string -pattern $env:php_ver_target | sort { [version]($_ -split '\|' | select -last 1) } -Descending | Select-Object -first 1) -replace '[php|]','') - - cd c:\tools\php - - IF %PHP%==1 copy php.ini-production php.ini /Y - - IF %PHP%==1 echo date.timezone="UTC" >> php.ini - - IF %PHP%==1 echo extension_dir=ext >> php.ini - - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini - - IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini - - IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini - - IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat - - appveyor-retry appveyor DownloadFile https://getcomposer.org/composer.phar - - cd c:\projects\snappy - - IF %dependencies%==lowest appveyor-retry composer update --prefer-lowest --no-progress --profile -n - - IF %dependencies%==current appveyor-retry composer install --no-progress --profile - - IF %dependencies%==highest appveyor-retry composer update --no-progress --profile -n - - composer show - -test_script: - - cd c:\projects\snappy - - vendor/bin/phpunit diff --git a/composer.json b/composer.json index 88ebc293..e6babd5f 100644 --- a/composer.json +++ b/composer.json @@ -16,43 +16,20 @@ } ], "require": { - "php": ">=7.1", - "symfony/process": "~3.4||~4.3||~5.0||~6.0", - "psr/log": "^1.0||^2.0||^3.0" - }, - "require-dev": { - "phpunit/phpunit": "~7.4||~8.5", - "phpstan/phpstan": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.0.0", - "friendsofphp/php-cs-fixer": "^2.16||^3.0", - "pedrotroller/php-cs-custom-fixer": "^2.19" - }, - "suggest": { - "h4cc/wkhtmltopdf-amd64": "Provides wkhtmltopdf-amd64 binary for Linux-compatible machines, use version `~0.12` as dependency", - "h4cc/wkhtmltopdf-i386": "Provides wkhtmltopdf-i386 binary for Linux-compatible machines, use version `~0.12` as dependency", - "h4cc/wkhtmltoimage-amd64": "Provides wkhtmltoimage-amd64 binary for Linux-compatible machines, use version `~0.12` as dependency", - "h4cc/wkhtmltoimage-i386": "Provides wkhtmltoimage-i386 binary for Linux-compatible machines, use version `~0.12` as dependency", - "wemersonjanuario/wkhtmltopdf-windows": "Provides wkhtmltopdf executable for Windows, use version `~0.12` as dependency" + "php": ">=8.1", + "symfony/process": "~5.0||~6.0", + "psr/log": "^2.0||^3.0", + "symfony/http-client": "^6.2", + "psr/http-message": "^2.0" }, "autoload": { "psr-4": { - "Knp\\Snappy\\": "src/Knp/Snappy" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests/" + "KnpLabs\\Snappy\\": "src/" } }, - "scripts": { - "unit-tests": "vendor/bin/phpunit", - "static-analysis": "vendor/bin/phpstan analyse --ansi", - "check-cs": "vendor/bin/php-cs-fixer fix --diff --dry-run --verbose", - "fix-cs": "vendor/bin/php-cs-fixer fix --verbose" - }, "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" } } } diff --git a/doc/faq.md b/doc/faq.md deleted file mode 100644 index e66bc202..00000000 --- a/doc/faq.md +++ /dev/null @@ -1,179 +0,0 @@ -# Frequently asked questions - -###### *Q*: It does not work and everything is broken. - -*A*: Please, try to execute the command manually in your shell. Snappy is a thin PHP wrapper and most likely your issue is with wkhtmltopdf itself or is already described in this FAQ. If not, feel free to open the issue in Snappy issue tracker. - -Please, note that wkhtmltopdf takes only input URL(s) or file name(s) as source. - - -###### *Q*: How to get the command executed by Snappy? - -*A*: You need to install any PSR-3 compliant logging library and call `setLogger()` method on the generator. It will -log every command executed, its env vars and timeout. It will also log stdout and stderr whenever a command finishes, even if it fails. - - -###### *Q*: My tables are broken when it is rendered on multiple pages with break. - -*A*: Add ```thead``` and ```tbody``` tags. Add the following css -```css -table { page-break-inside:auto; } -tr { page-break-inside:avoid; page-break-after:auto; } -thead { display:table-header-group; } -tfoot { display:table-footer-group; } -``` - - -###### *Q*: I have a PNG with a transparent background. When generating a PDF, the background turns black. - -*A*: It is wkhtmltopdf bug as described in https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2214. You should update wkhtmltopdf to at least 0.12.3-dev - - -###### *Q*: Is there a way to secure the pdf so it can't be edited? - -*A*: There is no way to add a password via wkhtmltopdf, but there is a way via other linux tools like pdftk - - -###### *Q*: We are using wkhtmltopdf to export html to pdf. It breaks the HTML in two pages of pdf. Can we add a break? - -*A*: It is known problem of `wkhtmltopdf`. You can use css `page-break-after`, like: -```html - - -
- new page -
-``` - - -###### *Q*: It says `wkhtmltopdf: cannot connect to X server` or `xvfb-run: error: Xvfb failed to start.` - -*A*: Please, check your `wkhtmltopdf` version. It is recommended to use at least `0.12.2.1` and what is important - starting from `wkhtmltopdf >= 0.12.2` it doesn't require X server or emulation anymore. You can download new version from http://wkhtmltopdf.org/downloads.html or install via composer for Linux servers as stated in [README](https://github.com/KnpLabs/snappy#wkhtmltopdf-binary-as-composer-dependencies). If there is no possibility to update `wkhtmltopdf`, please check http://stackoverflow.com/questions/9604625/wkhtmltopdf-cannot-connect-to-x-server - -###### *Q*: PDF generation failed with wkhtmltopdf returning error code 1 due to ContentNotFoundError, how do I deal with that? -*A*: This is a known problem with wkhtmltopdf. Several issues has been raised: [issue 1855](https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1855), [issue 2051](https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2051). To catch that error, `generate` method will throw a `RuntimeException` with error code equals to the error code returned with wkhtmltopdf, catch this exception and check for the error code and then deal with this exception in appropriate ways. - -###### *Q*: My PDF is always generated for a small screen resolution\I always receive a mobile version. - -*A*: It is well-known issue of wkhtmltopdf, you can check https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1508. One of solutions is to use xvfb and to setup xvfb resolution to desired one though a simple bit of css such as `zoom: .75;` would be sufficient in most cases. - -###### *Q*: My chars with accents in HTML document are not correctly rendered. - -*A*: Make sure that you have set `` in your HTML document, and you used the option `"encoding" => "utf-8"`. - -###### *Q*: My document text is not correctly rendered, it is just black squares - -*A*: Make sure you have installed `xfonts-base`, `xfonts-75dpi` and `urw-fonts` - -###### *Q*: How to convert page with relative links from stdin / How to use relative media URLs - -*A*: When you convert an HTML file with relative links/media URLs into PDF, you need to either: -* Switch to absolute links/media URLs -* Or use `` tag to specify what's the base URL of those relative links. - -###### *Q*: How to generate a single PDF from multiple sources? - -*A*: Snappy and wkhtmltopdf both support generating a single PDF from multiple sources. To do so, you need to provide an array of input rather than a string. - -```php -generate(['https://google.com', 'https://google.jp'], '/tmp/out/test.pdf'); -// or -$pdf->generateFromHtml(['Doc 1', 'Doc 2'], '/tmp/out/test.pdf'); -``` - -###### *Q*: My chars with accents passed to wkhtmltopdf options are not correctly rendered, i.e. `footer-right => 'Página [page] de [toPage]'` is converted to 'Página 1 de 1'. - -*A*: The answer is long here. We use `escapeshellarg` function to escape all the option value passed to `wkhtmltox`. `escapeshellarg` makes its escape based on server locale, so if you are experiencing this issue - you can set -```php -setlocale(LC_CTYPE, 'es_ES.UTF-8') -``` - -or any locale which is suitable for you. You should take into account that if given locale is not configured on the server - you will still have an issue. Check your locales installed via running -```bash -locale -a -``` -If the needed locale is missing on the server - you should install/configure it. - -###### *Q*: How to put an header/footer on every page of the PDF? - -*A*: You need to provide either a valid file path or some HTML content. Note that your HTML document(s) needs to start with a valid doctype and have html, head and body tags, or wkhtmltopdf will fail to render the PDF properly. - -*Note that this feature does not work with wkhtmltopdf compiled against unpatched Qt. Most of the time, wkhtmltopdf packages from Linux distributions are not fine. You should rather rely on the -official version available on [wkhtmltopdf.org](https://wkhtmltopdf.org) or the version available from `h4cc/wkhtmltopdf` package.* - -```php - - - -

Lorem ipsum

- -HTML; - -$footer = << - - -

Lorem ipsum

- -HTML; - -// Without html extension you might face following error: -// Exit with code 1, due to unknown error. -$footerPath = tempnam('/tmp', 'footer') . '.html'; -file_put_contents($footerPath, $footer); - -$pdf = new \Knp\Snappy\Pdf(__DIR__ . '/vendor/bin/wkhtmltopdf-amd64'); -$pdf->generateFromHtml('', '/tmp/out/test.pdf', ['header-html' => $header, 'footer-html' => $footerPath], true); -``` - -###### *Q*: Is it possible to include an header and/or footer only on some specific pages? - -*A*: No, wkhtmtopdf does not allow this. - -###### *Q*: When running wkhtmltopdf through Snappy, I got an exit code 5 or 6 - -*A*: It's usually due to bad environment variables. For example, on MacOS, you need to check the value of `DYLD_LIBRARY_PATH` (see [#27](https://github.com/KnpLabs/snappy/issues/27#issuecomment-7199659)). -On Linux, you should check the value of `LD_LIBRARY_PATH`. Also note that, depending on the way you execute PHP, your environment variables might be reset for security reasons (for instance, look at `clear_env` on php-fpm). - -###### *Q*: On Windows, when I generate a PDF nothing happens (there's no PDF file written) - -*A*: You should check with sysinternals procmon if you experience `ACCESS_DENIED` error. If that's the case, you need to give execution permission to IIS users on wkhtmltopdf binary. Also, your user(s) should have write permissions on the temporary folder. - -For more details see [#123](https://github.com/KnpLabs/snappy/issues/123). - -###### *Q*: Snappy takes an endless amount of time to generate a PDF and eventually fails due to timeout - -*A*: This is generally indicating some networking issues. It might be bad DNS record(s), some sporadic packet losses, unresponsive HTTP server ... - -Note that if you use the PHP embedded server, you can't generate a PDF from an HTML page accessible from the same embedded server. -Indeed, the embedded server never forks and does not use threads. That means it's not able to process two requests -at the same time: it processes the first one, send the first response and only then starts to process the second one. - -###### *Q*: How to proceed when experiencing `ContentNotFound`, `ConnectionRefusedError` or timeouts? - -*A*: When you experience errors like `ContentNotFound` or `ConnectionRefusedError`, try to turn off `quiet` option and -look at Snappy logs (you have to set up a logger first). - -If you experience timeouts, it might be hard to know what is failing. The best you can do to narrow the scope of the bug -is to slightly change your HTML code until you found the culprit. Start by removing whole parts, like document body to -know if it comes from something in the body or something in the head. If that's now working, re-add it but now remove -one half of its content. And repeat again and again until you find which URLs is buggy. - -There's one more (better) way though: fire up tcpdump or wireshark and listen for http requests. You should see which -request(s) is failing, and you can even check the content of the request/response. - -###### *Q*: My custom fonts aren't smooth - -According to #326, you shall prefer using SVG versions of your custom fonts to have a better font smoothing. diff --git a/phpstan.neon b/phpstan.neon deleted file mode 100644 index f99e8107..00000000 --- a/phpstan.neon +++ /dev/null @@ -1,14 +0,0 @@ -includes: - - vendor/phpstan/phpstan-phpunit/extension.neon - -parameters: - level: 8 - paths: - - src/ - - tests/ - inferPrivatePropertyTypeFromConstructor: true - reportUnmatchedIgnoredErrors: false - checkMissingIterableValueType: false - ignoreErrors: - - "#^Call to an undefined static method #" - - "#^Parameter \\#1 \\$command of class Symfony\\\\Component\\\\Process\\\\Process constructor expects array, string given\\.$#" diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index c9e576be..00000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - ./tests - - - diff --git a/src/Backend/WkHtmlToPdf/WkHtmlToPdf.php b/src/Backend/WkHtmlToPdf/WkHtmlToPdf.php new file mode 100644 index 00000000..a17fda7e --- /dev/null +++ b/src/Backend/WkHtmlToPdf/WkHtmlToPdf.php @@ -0,0 +1,17 @@ +stringToPdf->generate( + file_get_contents($file->getPathname()), + $options, + ); + } +} diff --git a/src/Core/Bridge/FromStringToFileToPdf.php b/src/Core/Bridge/FromStringToFileToPdf.php new file mode 100644 index 00000000..b63a7685 --- /dev/null +++ b/src/Core/Bridge/FromStringToFileToPdf.php @@ -0,0 +1,34 @@ +fileToPdf->generate($file, $options); + } finally { + unlink($path); + } + + return $stream; + } +} diff --git a/src/Core/FileToPdf.php b/src/Core/FileToPdf.php new file mode 100644 index 00000000..b252e0d5 --- /dev/null +++ b/src/Core/FileToPdf.php @@ -0,0 +1,12 @@ + - * @author Antoine Hérault - */ -abstract class AbstractGenerator implements GeneratorInterface, LoggerAwareInterface -{ - use LoggerAwareTrait; - - /** - * @var array - */ - public $temporaryFiles = []; - - /** - * @var string - */ - protected $temporaryFolder; - - /** - * @var null|string - */ - private $binary; - - /** - * @var array - */ - private $options = []; - - /** - * @var null|array - */ - private $env; - - /** - * @var null|int - */ - private $timeout; - - /** - * @var string - */ - private $defaultExtension; - - /** - * @param null|string $binary - * @param array $options - * @param null|array $env - */ - public function __construct($binary, array $options = [], array $env = null) - { - $this->configure(); - - $this->setBinary($binary); - $this->setOptions($options); - $this->env = empty($env) ? null : $env; - - if (\is_callable([$this, 'removeTemporaryFiles'])) { - \register_shutdown_function([$this, 'removeTemporaryFiles']); - } - } - - public function __destruct() - { - $this->removeTemporaryFiles(); - } - - /** - * Sets the default extension. - * Useful when letting Snappy deal with file creation. - * - * @param string $defaultExtension - * - * @return $this - */ - public function setDefaultExtension($defaultExtension) - { - $this->defaultExtension = $defaultExtension; - - return $this; - } - - /** - * Gets the default extension. - * - * @return string - */ - public function getDefaultExtension(): string - { - return $this->defaultExtension; - } - - /** - * Sets an option. Be aware that option values are NOT validated and that - * it is your responsibility to validate user inputs. - * - * @param string $name The option to set - * @param mixed $value The value (NULL to unset) - * - * @throws InvalidArgumentException - * - * @return $this - */ - public function setOption($name, $value) - { - if (!\array_key_exists($name, $this->options)) { - throw new InvalidArgumentException(\sprintf('The option \'%s\' does not exist.', $name)); - } - - $this->options[$name] = $value; - - if (null !== $this->logger) { - $this->logger->debug(\sprintf('Set option "%s".', $name), ['value' => $value]); - } - - return $this; - } - - /** - * Sets the timeout. - * - * @param null|int $timeout The timeout to set - * - * @return $this - */ - public function setTimeout($timeout) - { - $this->timeout = $timeout; - - return $this; - } - - /** - * Sets an array of options. - * - * @param array $options An associative array of options as name/value - * - * @return $this - */ - public function setOptions(array $options) - { - foreach ($options as $name => $value) { - $this->setOption($name, $value); - } - - return $this; - } - - /** - * Returns all the options. - * - * @return array - */ - public function getOptions() - { - return $this->options; - } - - /** - * {@inheritdoc} - */ - public function generate($input, $output, array $options = [], $overwrite = false) - { - $this->prepareOutput($output, $overwrite); - - $command = $this->getCommand($input, $output, $options); - - $inputFiles = \is_array($input) ? \implode('", "', $input) : $input; - - if (null !== $this->logger) { - $this->logger->info(\sprintf('Generate from file(s) "%s" to file "%s".', $inputFiles, $output), [ - 'command' => $command, - 'env' => $this->env, - 'timeout' => $this->timeout, - ]); - } - - try { - list($status, $stdout, $stderr) = $this->executeCommand($command); - $this->checkProcessStatus($status, $stdout, $stderr, $command); - $this->checkOutput($output, $command); - } catch (Exception $e) { - if (null !== $this->logger) { - $this->logger->error(\sprintf('An error happened while generating "%s".', $output), [ - 'command' => $command, - 'status' => $status ?? null, - 'stdout' => $stdout ?? null, - 'stderr' => $stderr ?? null, - ]); - } - - throw $e; - } - - if (null !== $this->logger) { - $this->logger->info(\sprintf('File "%s" has been successfully generated.', $output), [ - 'command' => $command, - 'stdout' => $stdout, - 'stderr' => $stderr, - ]); - } - } - - /** - * {@inheritdoc} - */ - public function generateFromHtml($html, $output, array $options = [], $overwrite = false) - { - $fileNames = []; - if (\is_array($html)) { - foreach ($html as $htmlInput) { - $fileNames[] = $this->createTemporaryFile($htmlInput, 'html'); - } - } else { - $fileNames[] = $this->createTemporaryFile($html, 'html'); - } - - $this->generate($fileNames, $output, $options, $overwrite); - } - - /** - * {@inheritdoc} - */ - public function getOutput($input, array $options = []) - { - $filename = $this->createTemporaryFile(null, $this->getDefaultExtension()); - - $this->generate($input, $filename, $options); - - return $this->getFileContents($filename); - } - - /** - * {@inheritdoc} - */ - public function getOutputFromHtml($html, array $options = []) - { - $fileNames = []; - if (\is_array($html)) { - foreach ($html as $htmlInput) { - $fileNames[] = $this->createTemporaryFile($htmlInput, 'html'); - } - } else { - $fileNames[] = $this->createTemporaryFile($html, 'html'); - } - - return $this->getOutput($fileNames, $options); - } - - /** - * Defines the binary. - * - * @param null|string $binary The path/name of the binary - * - * @return $this - */ - public function setBinary($binary) - { - $this->binary = $binary; - - return $this; - } - - /** - * Returns the binary. - * - * @return null|string - */ - public function getBinary() - { - return $this->binary; - } - - /** - * Returns the command for the given input and output files. - * - * @param array|string $input The input file - * @param string $output The ouput file - * @param array $options An optional array of options that will be used - * only for this command - * - * @return string - */ - public function getCommand($input, $output, array $options = []) - { - if (null === $this->binary) { - throw new LogicException('You must define a binary prior to conversion.'); - } - - $options = $this->mergeOptions($options); - - return $this->buildCommand($this->binary, $input, $output, $options); - } - - /** - * Removes all temporary files. - * - * @return void - */ - public function removeTemporaryFiles() - { - foreach ($this->temporaryFiles as $file) { - $this->unlink($file); - } - } - - /** - * Get TemporaryFolder. - * - * @return string - */ - public function getTemporaryFolder() - { - if ($this->temporaryFolder === null) { - return \sys_get_temp_dir(); - } - - return $this->temporaryFolder; - } - - /** - * Set temporaryFolder. - * - * @param string $temporaryFolder - * - * @return $this - */ - public function setTemporaryFolder($temporaryFolder) - { - $this->temporaryFolder = $temporaryFolder; - - return $this; - } - - /** - * Reset all options to their initial values. - * - * @return void - */ - public function resetOptions() - { - $this->options = []; - $this->configure(); - } - - /** - * This method must configure the media options. - * - * @return void - * - * @see AbstractGenerator::addOption() - */ - abstract protected function configure(); - - /** - * Adds an option. - * - * @param string $name The name - * @param mixed $default An optional default value - * - * @throws InvalidArgumentException - * - * @return $this - */ - protected function addOption($name, $default = null) - { - if (\array_key_exists($name, $this->options)) { - throw new InvalidArgumentException(\sprintf('The option \'%s\' already exists.', $name)); - } - - $this->options[$name] = $default; - - return $this; - } - - /** - * Adds an array of options. - * - * @param array $options - * - * @return $this - */ - protected function addOptions(array $options) - { - foreach ($options as $name => $default) { - $this->addOption($name, $default); - } - - return $this; - } - - /** - * Merges the given array of options to the instance options and returns - * the result options array. It does NOT change the instance options. - * - * @param array $options - * - * @throws InvalidArgumentException - * - * @return array - */ - protected function mergeOptions(array $options) - { - $mergedOptions = $this->options; - - foreach ($options as $name => $value) { - if (!\array_key_exists($name, $mergedOptions)) { - throw new InvalidArgumentException(\sprintf('The option \'%s\' does not exist.', $name)); - } - - $mergedOptions[$name] = $value; - } - - return $mergedOptions; - } - - /** - * Checks the specified output. - * - * @param string $output The output filename - * @param string $command The generation command - * - * @throws RuntimeException if the output file generation failed - * - * @return void - */ - protected function checkOutput($output, $command) - { - // the output file must exist - if (!$this->fileExists($output)) { - throw new RuntimeException(\sprintf('The file \'%s\' was not created (command: %s).', $output, $command)); - } - - // the output file must not be empty - if (0 === $this->filesize($output)) { - throw new RuntimeException(\sprintf('The file \'%s\' was created but is empty (command: %s).', $output, $command)); - } - } - - /** - * Checks the process return status. - * - * @param int $status The exit status code - * @param string $stdout The stdout content - * @param string $stderr The stderr content - * @param string $command The run command - * - * @throws RuntimeException if the output file generation failed - * - * @return void - */ - protected function checkProcessStatus($status, $stdout, $stderr, $command) - { - if (0 !== $status && '' !== $stderr) { - throw new RuntimeException(\sprintf('The exit status code \'%s\' says something went wrong:' . "\n" . 'stderr: "%s"' . "\n" . 'stdout: "%s"' . "\n" . 'command: %s.', $status, $stderr, $stdout, $command), $status); - } - } - - /** - * Creates a temporary file. - * The file is not created if the $content argument is null. - * - * @param null|string $content Optional content for the temporary file - * @param null|string $extension An optional extension for the filename - * - * @return string The filename - */ - protected function createTemporaryFile($content = null, $extension = null) - { - $dir = \rtrim($this->getTemporaryFolder(), \DIRECTORY_SEPARATOR); - - if (!\is_dir($dir)) { - if (false === @\mkdir($dir, 0777, true) && !\is_dir($dir)) { - throw new RuntimeException(\sprintf("Unable to create directory: %s\n", $dir)); - } - } elseif (!\is_writable($dir)) { - throw new RuntimeException(\sprintf("Unable to write in directory: %s\n", $dir)); - } - - $filename = $dir . \DIRECTORY_SEPARATOR . \uniqid('knp_snappy', true); - - if (null !== $extension) { - $filename .= '.' . $extension; - } - - if (null !== $content) { - \file_put_contents($filename, $content); - } - - $this->temporaryFiles[] = $filename; - - return $filename; - } - - /** - * Builds the command string. - * - * @param string $binary The binary path/name - * @param array|string $input Url(s) or file location(s) of the page(s) to process - * @param string $output File location to the image-to-be - * @param array $options An array of options - * - * @return string - */ - protected function buildCommand($binary, $input, $output, array $options = []) - { - $command = $binary; - $escapedBinary = \escapeshellarg($binary); - if (\is_executable($escapedBinary)) { - $command = $escapedBinary; - } - - foreach ($options as $key => $option) { - if (null !== $option && false !== $option) { - if (true === $option) { - // Dont't put '--' if option is 'toc'. - if ($key === 'toc') { - $command .= ' ' . $key; - } else { - $command .= ' --' . $key; - } - } elseif (\is_array($option)) { - if ($this->isAssociativeArray($option)) { - foreach ($option as $k => $v) { - $command .= ' --' . $key . ' ' . \escapeshellarg($k) . ' ' . \escapeshellarg($v); - } - } else { - foreach ($option as $v) { - $command .= ' --' . $key . ' ' . \escapeshellarg($v); - } - } - } else { - // Dont't add '--' if option is "cover" or "toc". - if (\in_array($key, ['toc', 'cover'])) { - $command .= ' ' . $key . ' ' . \escapeshellarg($option); - } elseif (\in_array($key, ['image-dpi', 'image-quality'])) { - $command .= ' --' . $key . ' ' . (int) $option; - } else { - $command .= ' --' . $key . ' ' . \escapeshellarg($option); - } - } - } - } - - if (\is_array($input)) { - foreach ($input as $i) { - $command .= ' ' . \escapeshellarg($i) . ' '; - } - $command .= \escapeshellarg($output); - } else { - $command .= ' ' . \escapeshellarg($input) . ' ' . \escapeshellarg($output); - } - - return $command; - } - - /** - * Return true if the array is an associative array - * and not an indexed array. - * - * @param array $array - * - * @return bool - */ - protected function isAssociativeArray(array $array) - { - return (bool) \count(\array_filter(\array_keys($array), 'is_string')); - } - - /** - * Executes the given command via shell and returns the complete output as - * a string. - * - * @param string $command - * - * @return array [status, stdout, stderr] - */ - protected function executeCommand($command) - { - if (\method_exists(Process::class, 'fromShellCommandline')) { - $process = Process::fromShellCommandline($command, null, $this->env); - } else { - $process = new Process($command, null, $this->env); - } - - if (null !== $this->timeout) { - $process->setTimeout($this->timeout); - } - - $process->run(); - - return [ - $process->getExitCode(), - $process->getOutput(), - $process->getErrorOutput(), - ]; - } - - /** - * Prepares the specified output. - * - * @param string $filename The output filename - * @param bool $overwrite Whether to overwrite the file if it already - * exist - * - * @throws FileAlreadyExistsException - * @throws RuntimeException - * @throws InvalidArgumentException - * - * @return void - */ - protected function prepareOutput($filename, $overwrite) - { - if (\strpos($filename, 'phar://') === 0) { - throw new InvalidArgumentException('The output file cannot be a phar archive.'); - } - - $directory = \dirname($filename); - - if ($this->fileExists($filename)) { - if (!$this->isFile($filename)) { - throw new InvalidArgumentException(\sprintf('The output file \'%s\' already exists and it is a %s.', $filename, $this->isDir($filename) ? 'directory' : 'link')); - } - if (false === $overwrite) { - throw new FileAlreadyExistsException(\sprintf('The output file \'%s\' already exists.', $filename)); - } - if (!$this->unlink($filename)) { - throw new RuntimeException(\sprintf('Could not delete already existing output file \'%s\'.', $filename)); - } - } elseif (!$this->isDir($directory) && !$this->mkdir($directory)) { - throw new RuntimeException(\sprintf('The output file\'s directory \'%s\' could not be created.', $directory)); - } - } - - /** - * Wrapper for the "file_get_contents" function. - * - * @param string $filename - * - * @return string - */ - protected function getFileContents($filename) - { - $fileContent = \file_get_contents($filename); - - if (false === $fileContent) { - throw new RuntimeException(\sprintf('Could not read file \'%s\' content.', $filename)); - } - - return $fileContent; - } - - /** - * Wrapper for the "file_exists" function. - * - * @param string $filename - * - * @return bool - */ - protected function fileExists($filename) - { - return \file_exists($filename); - } - - /** - * Wrapper for the "is_file" method. - * - * @param string $filename - * - * @return bool - */ - protected function isFile($filename) - { - return \strlen($filename) <= \PHP_MAXPATHLEN && \is_file($filename); - } - - /** - * Wrapper for the "filesize" function. - * - * @param string $filename - * - * @return int - */ - protected function filesize($filename) - { - $filesize = \filesize($filename); - - if (false === $filesize) { - throw new RuntimeException(\sprintf('Could not read file \'%s\' size.', $filename)); - } - - return $filesize; - } - - /** - * Wrapper for the "unlink" function. - * - * @param string $filename - * - * @return bool - */ - protected function unlink($filename) - { - return $this->fileExists($filename) ? \unlink($filename) : false; - } - - /** - * Wrapper for the "is_dir" function. - * - * @param string $filename - * - * @return bool - */ - protected function isDir($filename) - { - return \is_dir($filename); - } - - /** - * Wrapper for the mkdir function. - * - * @param string $pathname - * - * @return bool - */ - protected function mkdir($pathname) - { - return \mkdir($pathname, 0777, true); - } -} diff --git a/src/Knp/Snappy/Exception/FileAlreadyExistsException.php b/src/Knp/Snappy/Exception/FileAlreadyExistsException.php deleted file mode 100644 index a22275cd..00000000 --- a/src/Knp/Snappy/Exception/FileAlreadyExistsException.php +++ /dev/null @@ -1,9 +0,0 @@ - - * @author Antoine Hérault * - */ -interface GeneratorInterface -{ - /** - * Generates the output media file from the specified input HTML file. - * - * @param array|string $input The input HTML filename or URL - * @param string $output The output media filename - * @param array $options An array of options for this generation only - * @param bool $overwrite Overwrite the file if it exists. If not, throw a FileAlreadyExistsException - * - * @return void - */ - public function generate($input, $output, array $options = [], $overwrite = false); - - /** - * Generates the output media file from the given HTML. - * - * @param array|string $html The HTML to be converted - * @param string $output The output media filename - * @param array $options An array of options for this generation only - * @param bool $overwrite Overwrite the file if it exists. If not, throw a FileAlreadyExistsException - * - * @return void - */ - public function generateFromHtml($html, $output, array $options = [], $overwrite = false); - - /** - * Returns the output of the media generated from the specified input HTML - * file. - * - * @param array|string $input The input HTML filename or URL - * @param array $options An array of options for this output only - * - * @return string - */ - public function getOutput($input, array $options = []); - - /** - * Returns the output of the media generated from the given HTML. - * - * @param array|string $html The HTML to be converted - * @param array $options An array of options for this output only - * - * @return string - */ - public function getOutputFromHtml($html, array $options = []); -} diff --git a/src/Knp/Snappy/Image.php b/src/Knp/Snappy/Image.php deleted file mode 100644 index a66310be..00000000 --- a/src/Knp/Snappy/Image.php +++ /dev/null @@ -1,82 +0,0 @@ - - * @author Antoine Hérault - */ -class Image extends AbstractGenerator -{ - /** - * {@inheritdoc} - */ - public function __construct($binary = null, array $options = [], array $env = null) - { - $this->setDefaultExtension('jpg'); - - parent::__construct($binary, $options, $env); - } - - /** - * {@inheritdoc} - */ - protected function configure() - { - $this->addOptions([ - 'allow' => null, // Allow the file or files from the specified folder to be loaded (repeatable) - 'bypass-proxy-for' => null, // Bypass proxy for host (repeatable) - 'cache-dir' => null, // Web cache directory - 'checkbox-checked-svg' => null, // Use this SVG file when rendering checked checkboxes - 'checked-svg' => null, // Use this SVG file when rendering unchecked checkboxes - 'cookie' => [], // Set an additional cookie (repeatable) - 'cookie-jar' => null, // Read and write cookies from and to the supplied cookie jar file - 'crop-h' => null, // Set height for cropping - 'crop-w' => null, // Set width for cropping - 'crop-x' => null, // Set x coordinate for cropping (default 0) - 'crop-y' => null, // Set y coordinate for cropping (default 0) - 'custom-header' => [], // Set an additional HTTP header (repeatable) - 'custom-header-propagation' => null, // Add HTTP headers specified by --custom-header for each resource request. - 'no-custom-header-propagation' => null, // Do not add HTTP headers specified by --custom-header for each resource request. - 'debug-javascript' => null, // Show javascript debugging output - 'no-debug-javascript' => null, // Do not show javascript debugging output (default) - 'encoding' => null, // Set the default text encoding, for input - 'format' => $this->getDefaultExtension(), // Output format - 'height' => null, // Set screen height (default is calculated from page content) (default 0) - 'images' => null, // Do load or print images (default) - 'no-images' => null, // Do not load or print images - 'disable-javascript' => null, // Do not allow web pages to run javascript - 'enable-javascript' => null, // Do allow web pages to run javascript (default) - 'javascript-delay' => null, // Wait some milliseconds for javascript finish (default 200) - 'load-error-handling' => null, // Specify how to handle pages that fail to load: abort, ignore or skip (default abort) - 'load-media-error-handling' => null, // Specify how to handle media files that fail to load: abort, ignore or skip (default ignore) - 'disable-local-file-access' => null, // Do not allowed conversion of a local file to read in other local files, unless explicitly allowed with allow - 'enable-local-file-access' => null, // Allowed conversion of a local file to read in other local files. (default) - 'minimum-font-size' => null, // Minimum font size - 'password' => null, // HTTP Authentication password - 'disable-plugins' => null, // Disable installed plugins (default) - 'enable-plugins' => null, // Enable installed plugins (plugins will likely not work) - 'post' => [], // Add an additional post field - 'post-file' => [], // Post an additional file - 'proxy' => null, // Use a proxy - 'quality' => null, // Output image quality (between 0 and 100) (default 94) - 'quiet' => null, // Be less verbose - 'radiobutton-checked-svg' => null, // Use this SVG file when rendering checked radio-buttons - 'radiobutton-svg' => null, // Use this SVG file when rendering unchecked radio-buttons - 'run-script' => null, // Run this additional javascript after the page is done loading (repeatable) - 'disable-smart-width' => null, // Use the specified width even if it is not large enough for the content - 'enable-smart-width' => null, // Extend --width to fit unbreakable content (default) - 'stop-slow-scripts' => null, // Stop slow running javascript - 'no-stop-slow-scripts' => null, // Do not stop slow running javascript (default) - 'transparent' => null, // Make the background transparent in pngs * - 'use-xserver' => null, // Use the X server (some plugins and other stuff might not work without X11) - 'user-style-sheet' => null, // Specify a user style sheet, to load with every page - 'username' => null, // HTTP Authentication username - 'width' => null, // Set screen width (default is 1024) - 'window-status' => null, // Wait until window.status is equal to this string before rendering page - 'zoom' => null, // Use this zoom factor (default 1) - ]); - } -} diff --git a/src/Knp/Snappy/Pdf.php b/src/Knp/Snappy/Pdf.php deleted file mode 100644 index b3a51ade..00000000 --- a/src/Knp/Snappy/Pdf.php +++ /dev/null @@ -1,245 +0,0 @@ - - * @author Antoine Hérault - */ -class Pdf extends AbstractGenerator -{ - /** - * @var array - */ - protected $optionsWithContentCheck = []; - - /** - * {@inheritdoc} - */ - public function __construct($binary = null, array $options = [], array $env = null) - { - $this->setDefaultExtension('pdf'); - $this->setOptionsWithContentCheck(); - - parent::__construct($binary, $options, $env); - } - - /** - * {@inheritdoc} - */ - public function generate($input, $output, array $options = [], $overwrite = false) - { - $options = $this->handleOptions($this->mergeOptions($options)); - - parent::generate($input, $output, $options, $overwrite); - } - - /** - * Handle options to transform HTML strings into temporary files containing HTML. - * - * @param array $options - * - * @return array $options Transformed options - */ - protected function handleOptions(array $options = []) - { - foreach ($options as $option => $value) { - if (null === $value) { - unset($options[$option]); - - continue; - } - - if (!empty($value) && \array_key_exists($option, $this->optionsWithContentCheck)) { - $saveToTempFile = !$this->isFile($value) && !$this->isOptionUrl($value); - $fetchUrlContent = $option === 'xsl-style-sheet' && $this->isOptionUrl($value); - - if ($saveToTempFile || $fetchUrlContent) { - $fileContent = $fetchUrlContent ? \file_get_contents($value) : $value; - $options[$option] = $this->createTemporaryFile($fileContent, $this->optionsWithContentCheck[$option]); - } - } - } - - return $options; - } - - /** - * Convert option content or url to file if it is needed. - * - * @param mixed $option - * - * @return bool - */ - protected function isOptionUrl($option) - { - return (bool) \filter_var($option, \FILTER_VALIDATE_URL); - } - - /** - * {@inheritdoc} - */ - protected function configure() - { - $this->addOptions([ - // Global options - 'collate' => null, - 'no-collate' => null, - 'cookie-jar' => null, - 'copies' => null, - 'dpi' => null, - 'extended-help' => null, - 'grayscale' => null, - 'help' => null, - 'htmldoc' => null, - 'ignore-load-errors' => null, // old v0.9 - 'image-dpi' => null, - 'image-quality' => null, - 'license' => null, - 'log-level' => null, - 'lowquality' => true, - 'manpage' => null, - 'margin-bottom' => null, - 'margin-left' => null, - 'margin-right' => null, - 'margin-top' => null, - 'orientation' => null, - 'page-height' => null, - 'page-size' => null, - 'page-width' => null, - 'no-pdf-compression' => null, - 'quiet' => null, - 'read-args-from-stdin' => null, - 'readme' => null, - 'title' => null, - 'use-xserver' => null, - 'version' => null, - // Outline options - 'dump-default-toc-xsl' => null, - 'dump-outline' => null, - 'outline' => null, - 'no-outline' => null, - 'outline-depth' => null, - 'output-format' => null, - // Page options - 'allow' => null, - 'background' => null, - 'no-background' => null, - 'bypass-proxy-for' => null, - 'cache-dir' => null, - 'checkbox-checked-svg' => null, - 'checkbox-svg' => null, - 'cookie' => null, - 'custom-header' => null, - 'custom-header-propagation' => null, - 'no-custom-header-propagation' => null, - 'debug-javascript' => null, - 'no-debug-javascript' => null, - 'default-header' => null, - 'encoding' => null, - 'disable-external-links' => null, - 'enable-external-links' => null, - 'disable-forms' => null, - 'enable-forms' => null, - 'images' => null, - 'no-images' => null, - 'disable-internal-links' => null, - 'enable-internal-links' => null, - 'disable-javascript' => null, - 'enable-javascript' => null, - 'javascript-delay' => null, - 'keep-relative-links' => null, - 'load-error-handling' => null, - 'load-media-error-handling' => null, - 'disable-local-file-access' => null, - 'enable-local-file-access' => null, - 'minimum-font-size' => null, - 'exclude-from-outline' => null, - 'include-in-outline' => null, - 'page-offset' => null, - 'password' => null, - 'disable-plugins' => null, - 'enable-plugins' => null, - 'post' => null, - 'post-file' => null, - 'print-media-type' => null, - 'no-print-media-type' => null, - 'proxy' => null, - 'proxy-hostname-lookup' => null, - 'radiobutton-checked-svg' => null, - 'radiobutton-svg' => null, - 'redirect-delay' => null, // old v0.9 - 'resolve-relative-links' => null, - 'run-script' => null, - 'disable-smart-shrinking' => null, - 'enable-smart-shrinking' => null, - 'ssl-crt-path' => null, - 'ssl-key-password' => null, - 'ssl-key-path' => null, - 'stop-slow-scripts' => null, - 'no-stop-slow-scripts' => null, - 'disable-toc-back-links' => null, - 'enable-toc-back-links' => null, - 'user-style-sheet' => null, - 'username' => null, - 'viewport-size' => null, - 'window-status' => null, - 'zoom' => null, - // Headers and footer options - 'footer-center' => null, - 'footer-font-name' => null, - 'footer-font-size' => null, - 'footer-html' => null, - 'footer-left' => null, - 'footer-line' => null, - 'no-footer-line' => null, - 'footer-right' => null, - 'footer-spacing' => null, - 'header-center' => null, - 'header-font-name' => null, - 'header-font-size' => null, - 'header-html' => null, - 'header-left' => null, - 'header-line' => null, - 'no-header-line' => null, - 'header-right' => null, - 'header-spacing' => null, - 'replace' => null, - // Cover object - 'cover' => null, - // TOC object - 'toc' => null, - // TOC options - 'disable-dotted-lines' => null, - 'toc-depth' => null, // old v0.9 - 'toc-font-name' => null, // old v0.9 - 'toc-l1-font-size' => null, // old v0.9 - 'toc-header-text' => null, - 'toc-header-font-name' => null, // old v0.9 - 'toc-header-font-size' => null, // old v0.9 - 'toc-level-indentation' => null, - 'disable-toc-links' => null, - 'toc-text-size-shrink' => null, - 'xsl-style-sheet' => null, - ]); - } - - /** - * Array with options which require to store the content of the option before passing it to wkhtmltopdf. - * - * @return $this - */ - protected function setOptionsWithContentCheck() - { - $this->optionsWithContentCheck = [ - 'header-html' => 'html', - 'footer-html' => 'html', - 'cover' => 'html', - 'xsl-style-sheet' => 'xsl', - ]; - - return $this; - } -} diff --git a/tests/Knp/Snappy/AbstractGeneratorTest.php b/tests/Knp/Snappy/AbstractGeneratorTest.php deleted file mode 100644 index 1ab6a900..00000000 --- a/tests/Knp/Snappy/AbstractGeneratorTest.php +++ /dev/null @@ -1,999 +0,0 @@ -getMockForAbstractClass(AbstractGenerator::class, [], '', false); - - $this->assertEquals([], $media->getOptions()); - - $r = new ReflectionMethod($media, 'addOption'); - $r->setAccessible(true); - $r->invokeArgs($media, ['foo', 'bar']); - - $this->assertEquals(['foo' => 'bar'], $media->getOptions(), '->addOption() adds an option'); - - $r->invokeArgs($media, ['baz', 'bat']); - - $this->assertEquals( - [ - 'foo' => 'bar', - 'baz' => 'bat', - ], - $media->getOptions(), - '->addOption() appends the option to the existing ones' - ); - - $message = '->addOption() raises an exception when the specified option already exists'; - - try { - $r->invokeArgs($media, ['baz', 'bat']); - $this->fail($message); - // @phpstan-ignore-next-line - } catch (InvalidArgumentException $e) { - $this->anything(); - } - } - - public function testAddOptions(): void - { - $media = $this->getMockForAbstractClass(AbstractGenerator::class, [], '', false); - - $this->assertEquals([], $media->getOptions()); - - $r = new ReflectionMethod($media, 'addOptions'); - $r->setAccessible(true); - $r->invokeArgs($media, [['foo' => 'bar', 'baz' => 'bat']]); - - $this->assertEquals( - [ - 'foo' => 'bar', - 'baz' => 'bat', - ], - $media->getOptions(), - '->addOptions() adds all the given options' - ); - - $r->invokeArgs($media, [['ban' => 'bag', 'bal' => 'bac']]); - - $this->assertEquals( - [ - 'foo' => 'bar', - 'baz' => 'bat', - 'ban' => 'bag', - 'bal' => 'bac', - ], - $media->getOptions(), - '->addOptions() adds the given options to the existing ones' - ); - - $message = '->addOptions() raises an exception when one of the given options already exists'; - - try { - $r->invokeArgs($media, [['bak' => 'bam', 'bah' => 'bap', 'baz' => 'bat']]); - $this->fail($message); - // @phpstan-ignore-next-line - } catch (InvalidArgumentException $e) { - $this->anything(); - } - } - - public function testSetOption(): void - { - $media = $this - ->getMockBuilder(AbstractGenerator::class) - ->setConstructorArgs(['/usr/local/bin/wkhtmltopdf']) - ->getMockForAbstractClass() - ; - - $logger = $this - ->getMockBuilder(LoggerInterface::class) - ->getMock() - ; - $media->setLogger($logger); - $logger->expects($this->once())->method('debug'); - - $r = new ReflectionMethod($media, 'addOption'); - $r->setAccessible(true); - $r->invokeArgs($media, ['foo', 'bar']); - - $media->setOption('foo', 'abc'); - - $this->assertEquals( - [ - 'foo' => 'abc', - ], - $media->getOptions(), - '->setOption() defines the value of an option' - ); - - $message = '->setOption() raises an exception when the specified option does not exist'; - - try { - $media->setOption('bad', 'def'); - $this->fail($message); - } catch (InvalidArgumentException $e) { - $this->anything(); - } - } - - public function testSetOptions(): void - { - $media = $this - ->getMockBuilder(AbstractGenerator::class) - ->setConstructorArgs(['/usr/local/bin/wkhtmltopdf']) - ->getMockForAbstractClass() - ; - - $logger = $this - ->getMockBuilder(LoggerInterface::class) - ->getMock() - ; - $media->setLogger($logger); - $logger->expects($this->exactly(4))->method('debug'); - - $r = new ReflectionMethod($media, 'addOptions'); - $r->setAccessible(true); - $r->invokeArgs($media, [['foo' => 'bar', 'baz' => 'bat']]); - - $media->setOptions(['foo' => 'abc', 'baz' => 'def']); - - $this->assertEquals( - [ - 'foo' => 'abc', - 'baz' => 'def', - ], - $media->getOptions(), - '->setOptions() defines the values of all the specified options' - ); - - $message = '->setOptions() raises an exception when one of the specified options does not exist'; - - try { - $media->setOptions(['foo' => 'abc', 'baz' => 'def', 'bad' => 'ghi']); - $this->fail($message); - } catch (InvalidArgumentException $e) { - $this->anything(); - } - } - - public function testGenerate(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'prepareOutput', - 'getCommand', - 'executeCommand', - 'checkOutput', - 'checkProcessStatus', - ]) - ->setConstructorArgs(['the_binary', []]) - ->getMock() - ; - - $logger = $this - ->getMockBuilder(LoggerInterface::class) - ->getMock() - ; - $media->setLogger($logger); - $logger - ->expects($this->exactly(2)) - ->method('info') - ->with( - $this->logicalOr( - 'Generate from file(s) "the_input_file" to file "the_output_file".', - 'File "the_output_file" has been successfully generated.' - ), - $this->logicalOr( - ['command' => 'the command', 'env' => null, 'timeout' => false], - ['command' => 'the command', 'stdout' => 'stdout', 'stderr' => 'stderr'] - ) - ) - ; - - $media - ->expects($this->once()) - ->method('prepareOutput') - ->with($this->equalTo('the_output_file')) - ; - $media - ->expects($this->any()) - ->method('getCommand') - ->with( - $this->equalTo('the_input_file'), - $this->equalTo('the_output_file'), - $this->equalTo(['foo' => 'bar']) - ) - ->will($this->returnValue('the command')) - ; - $media - ->expects($this->once()) - ->method('executeCommand') - ->with($this->equalTo('the command')) - ->willReturn([0, 'stdout', 'stderr']) - ; - $media - ->expects($this->once()) - ->method('checkProcessStatus') - ->with(0, 'stdout', 'stderr', 'the command') - ; - $media - ->expects($this->once()) - ->method('checkOutput') - ->with( - $this->equalTo('the_output_file'), - $this->equalTo('the command') - ) - ; - - $media->generate('the_input_file', 'the_output_file', ['foo' => 'bar']); - } - - public function testFailingGenerate(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'prepareOutput', - 'getCommand', - 'executeCommand', - 'checkOutput', - 'checkProcessStatus', - ]) - ->setConstructorArgs(['the_binary', [], ['PATH' => '/usr/bin']]) - ->getMock() - ; - - $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); - $media->setLogger($logger); - $media->setTimeout(2000); - - $logger - ->expects($this->once()) - ->method('info') - ->with( - $this->equalTo('Generate from file(s) "the_input_file" to file "the_output_file".'), - $this->equalTo(['command' => 'the command', 'env' => ['PATH' => '/usr/bin'], 'timeout' => 2000]) - ) - ; - - $logger - ->expects($this->once()) - ->method('error') - ->with( - $this->equalTo('An error happened while generating "the_output_file".'), - $this->equalTo(['command' => 'the command', 'status' => 1, 'stdout' => 'stdout', 'stderr' => 'stderr']) - ) - ; - - $media - ->expects($this->once()) - ->method('prepareOutput') - ->with($this->equalTo('the_output_file')) - ; - $media - ->expects($this->any()) - ->method('getCommand') - ->with( - $this->equalTo('the_input_file'), - $this->equalTo('the_output_file') - ) - ->will($this->returnValue('the command')) - ; - $media - ->expects($this->once()) - ->method('executeCommand') - ->with($this->equalTo('the command')) - ->willReturn([1, 'stdout', 'stderr']) - ; - $media - ->expects($this->once()) - ->method('checkProcessStatus') - ->with(1, 'stdout', 'stderr', 'the command') - ->willThrowException(new RuntimeException()) - ; - - $this->expectException(RuntimeException::class); - - $media->generate('the_input_file', 'the_output_file', ['foo' => 'bar']); - } - - public function testGenerateFromHtml(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'generate', - 'createTemporaryFile', - ]) - ->setConstructorArgs(['the_binary']) - ->disableOriginalConstructor() - ->getMock() - ; - - $media - ->expects($this->once()) - ->method('createTemporaryFile') - ->with( - $this->equalTo('foo'), - $this->equalTo('html') - ) - ->will($this->returnValue('the_temporary_file')) - ; - $media - ->expects($this->once()) - ->method('generate') - ->with( - $this->equalTo(['the_temporary_file']), - $this->equalTo('the_output_file'), - $this->equalTo(['foo' => 'bar']) - ) - ; - - $media->generateFromHtml('foo', 'the_output_file', ['foo' => 'bar']); - } - - public function testGenerateFromHtmlWithHtmlArray(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'generate', - 'createTemporaryFile', - ]) - ->setConstructorArgs(['the_binary']) - ->disableOriginalConstructor() - ->getMock() - ; - $media - ->expects($this->once()) - ->method('createTemporaryFile') - ->with( - $this->equalTo('foo'), - $this->equalTo('html') - ) - ->will($this->returnValue('the_temporary_file')) - ; - $media - ->expects($this->once()) - ->method('generate') - ->with( - $this->equalTo(['the_temporary_file']), - $this->equalTo('the_output_file'), - $this->equalTo(['foo' => 'bar']) - ) - ; - - $media->generateFromHtml(['foo'], 'the_output_file', ['foo' => 'bar']); - } - - public function testGetOutput(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'getDefaultExtension', - 'createTemporaryFile', - 'generate', - 'getFileContents', - 'unlink', - ]) - ->disableOriginalConstructor() - ->getMock() - ; - $media - ->expects($this->any()) - ->method('getDefaultExtension') - ->will($this->returnValue('ext')) - ; - $media - ->expects($this->any()) - ->method('createTemporaryFile') - ->with( - $this->equalTo(null), - $this->equalTo('ext') - ) - ->will($this->returnValue('the_temporary_file')) - ; - $media - ->expects($this->once()) - ->method('generate') - ->with( - $this->equalTo('the_input_file'), - $this->equalTo('the_temporary_file'), - $this->equalTo(['foo' => 'bar']) - ) - ; - $media - ->expects($this->once()) - ->method('getFileContents') - ->will($this->returnValue('the file contents')) - ; - - $media - ->expects($this->any()) - ->method('unlink') - ->with($this->equalTo('the_temporary_file')) - ->will($this->returnValue(true)) - ; - - $this->assertEquals('the file contents', $media->getOutput('the_input_file', ['foo' => 'bar'])); - } - - public function testGetOutputFromHtml(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'getOutput', - 'createTemporaryFile', - ]) - ->disableOriginalConstructor() - ->getMock() - ; - $media - ->expects($this->once()) - ->method('createTemporaryFile') - ->with( - $this->equalTo('foo'), - $this->equalTo('html') - ) - ->will($this->returnValue('the_temporary_file')) - ; - $media - ->expects($this->once()) - ->method('getOutput') - ->with( - $this->equalTo(['the_temporary_file']), - $this->equalTo(['foo' => 'bar']) - ) - ->will($this->returnValue('the output')) - ; - - $this->assertEquals('the output', $media->getOutputFromHtml('foo', ['foo' => 'bar'])); - } - - public function testGetOutputFromHtmlWithHtmlArray(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'getOutput', - 'createTemporaryFile', - ]) - ->disableOriginalConstructor() - ->getMock() - ; - $media - ->expects($this->once()) - ->method('createTemporaryFile') - ->with( - $this->equalTo('foo'), - $this->equalTo('html') - ) - ->will($this->returnValue('the_temporary_file')) - ; - $media - ->expects($this->once()) - ->method('getOutput') - ->with( - $this->equalTo(['the_temporary_file']), - $this->equalTo(['foo' => 'bar']) - ) - ->will($this->returnValue('the output')) - ; - - $this->assertEquals('the output', $media->getOutputFromHtml(['foo'], ['foo' => 'bar'])); - } - - public function testMergeOptions(): void - { - $media = $this->getMockForAbstractClass(AbstractGenerator::class, [], '', false); - - $originalOptions = ['foo' => 'bar', 'baz' => 'bat']; - - $addOptions = new ReflectionMethod($media, 'addOptions'); - $addOptions->setAccessible(true); - $addOptions->invokeArgs($media, [$originalOptions]); - - $r = new ReflectionMethod($media, 'mergeOptions'); - $r->setAccessible(true); - - $mergedOptions = $r->invokeArgs($media, [['foo' => 'ban']]); - - $this->assertEquals( - [ - 'foo' => 'ban', - 'baz' => 'bat', - ], - $mergedOptions, - '->mergeOptions() merges an option to the instance ones and returns the result options array' - ); - - $this->assertEquals( - $originalOptions, - $media->getOptions(), - '->mergeOptions() does NOT change the instance options' - ); - - $mergedOptions = $r->invokeArgs($media, [['foo' => 'ban', 'baz' => 'bag']]); - - $this->assertEquals( - [ - 'foo' => 'ban', - 'baz' => 'bag', - ], - $mergedOptions, - '->mergeOptions() merges many options to the instance ones and returns the result options array' - ); - - $message = '->mergeOptions() throws an InvalidArgumentException once there is an undefined option in the given array'; - - try { - $r->invokeArgs($media, [['foo' => 'ban', 'bad' => 'bah']]); - $this->fail($message); - // @phpstan-ignore-next-line - } catch (InvalidArgumentException $e) { - $this->anything(); - } - } - - /** - * @dataProvider dataForBuildCommand - */ - public function testBuildCommand(string $binary, string $url, string $path, array $options, string $expected): void - { - $media = $this->getMockForAbstractClass(AbstractGenerator::class, [], '', false); - - $r = new ReflectionMethod($media, 'buildCommand'); - $r->setAccessible(true); - - $this->assertEquals($expected, $r->invokeArgs($media, [$binary, $url, $path, $options])); - } - - public function dataForBuildCommand(): array - { - $theBinary = $this->getPHPExecutableFromPath() . ' -v'; // i.e.: '/usr/bin/php -v' - - return [ - [ - $theBinary, - 'http://the.url/', - '/the/path', - [], - $theBinary . ' ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'), - ], - [ - $theBinary, - 'http://the.url/', - '/the/path', - [ - 'foo' => null, - 'bar' => false, - 'baz' => [], - ], - $theBinary . ' ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'), - ], - [ - $theBinary, - 'http://the.url/', - '/the/path', - [ - 'foo' => 'foovalue', - 'bar' => ['barvalue1', 'barvalue2'], - 'baz' => true, - ], - $theBinary . ' --foo ' . \escapeshellarg('foovalue') . ' --bar ' . \escapeshellarg('barvalue1') . ' --bar ' . \escapeshellarg('barvalue2') . ' --baz ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'), - ], - [ - $theBinary, - 'http://the.url/', - '/the/path', - [ - 'cookie' => ['session' => 'bla', 'phpsess' => 12], - 'no-background' => '1', - ], - $theBinary . ' --cookie ' . \escapeshellarg('session') . ' ' . \escapeshellarg('bla') . ' --cookie ' . \escapeshellarg('phpsess') . ' ' . \escapeshellarg('12') . ' --no-background ' . \escapeshellarg('1') . ' ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'), - ], - [ - $theBinary, - 'http://the.url/', - '/the/path', - [ - 'allow' => ['/path1', '/path2'], - 'no-background' => '1', - ], - $theBinary . ' --allow ' . \escapeshellarg('/path1') . ' --allow ' . \escapeshellarg('/path2') . ' --no-background ' . \escapeshellarg('1') . ' ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'), - ], - [ - $theBinary, - 'http://the.url/', - '/the/path', - [ - 'image-dpi' => 100, - 'image-quality' => 50, - ], - $theBinary . ' ' . '--image-dpi 100 --image-quality 50 ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'), - ], - ]; - } - - public function testCheckOutput(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'fileExists', - 'filesize', - ]) - ->disableOriginalConstructor() - ->getMock() - ; - $media - ->expects($this->once()) - ->method('fileExists') - ->with($this->equalTo('the_output_file')) - ->will($this->returnValue(true)) - ; - $media - ->expects($this->once()) - ->method('filesize') - ->with($this->equalTo('the_output_file')) - ->will($this->returnValue(123)) - ; - - $r = new ReflectionMethod($media, 'checkOutput'); - $r->setAccessible(true); - - $message = '->checkOutput() checks both file existence and size'; - - try { - $r->invokeArgs($media, ['the_output_file', 'the command']); - $this->anything(); - } catch (RuntimeException $e) { - $this->fail($message); - } - } - - public function testCheckOutputWhenTheFileDoesNotExist(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'fileExists', - 'filesize', - ]) - ->disableOriginalConstructor() - ->getMock() - ; - $media - ->expects($this->once()) - ->method('fileExists') - ->with($this->equalTo('the_output_file')) - ->will($this->returnValue(false)) - ; - - $r = new ReflectionMethod($media, 'checkOutput'); - $r->setAccessible(true); - - $message = '->checkOutput() throws an InvalidArgumentException when the file does not exist'; - - try { - $r->invokeArgs($media, ['the_output_file', 'the command']); - $this->fail($message); - } catch (RuntimeException $e) { - $this->anything(); - } - } - - public function testCheckOutputWhenTheFileIsEmpty(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'fileExists', - 'filesize', - ]) - ->disableOriginalConstructor() - ->getMock() - ; - - $media - ->expects($this->once()) - ->method('fileExists') - ->with($this->equalTo('the_output_file')) - ->will($this->returnValue(true)) - ; - $media - ->expects($this->once()) - ->method('filesize') - ->with($this->equalTo('the_output_file')) - ->will($this->returnValue(0)) - ; - - $r = new ReflectionMethod($media, 'checkOutput'); - $r->setAccessible(true); - - $message = '->checkOutput() throws an InvalidArgumentException when the file is empty'; - - try { - $r->invokeArgs($media, ['the_output_file', 'the command']); - $this->fail($message); - } catch (RuntimeException $e) { - $this->anything(); - } - } - - public function testCheckProcessStatus(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods(['configure']) - ->disableOriginalConstructor() - ->getMock() - ; - - $r = new ReflectionMethod($media, 'checkProcessStatus'); - $r->setAccessible(true); - - try { - $r->invokeArgs($media, [0, '', '', 'the command']); - $this->anything(); - } catch (RuntimeException $e) { - $this->fail('0 status means success'); - } - - try { - $r->invokeArgs($media, [1, '', '', 'the command']); - $this->anything(); - } catch (RuntimeException $e) { - $this->fail('1 status means failure, but no stderr content'); - } - - try { - $r->invokeArgs($media, [1, '', 'Could not connect to X', 'the command']); - $this->fail('1 status means failure'); - } catch (RuntimeException $e) { - $this->assertEquals(1, $e->getCode(), 'Exception thrown by checkProcessStatus should pass on the error code'); - } - } - - /** - * @dataProvider dataForIsAssociativeArray - */ - public function testIsAssociativeArray(array $array, bool $isAssociativeArray): void - { - $generator = $this->getMockForAbstractClass(AbstractGenerator::class, [], '', false); - - $r = new ReflectionMethod($generator, 'isAssociativeArray'); - $r->setAccessible(true); - $this->assertEquals($isAssociativeArray, $r->invokeArgs($generator, [$array])); - } - - public function testItThrowsTheProperExceptionWhenFileExistsAndNotOverwritting(): void - { - $this->expectException(FileAlreadyExistsException::class); - - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'fileExists', - 'isFile', - ]) - ->disableOriginalConstructor() - ->getMock() - ; - - $media - ->expects($this->any()) - ->method('fileExists') - ->will($this->returnValue(true)) - ; - $media - ->expects($this->any()) - ->method('isFile') - ->will($this->returnValue(true)) - ; - $r = new ReflectionMethod($media, 'prepareOutput'); - $r->setAccessible(true); - - $r->invokeArgs($media, ['', false]); - } - - public function dataForIsAssociativeArray(): array - { - return [ - [ - ['key' => 'value'], - true, - ], - [ - ['key' => 2], - true, - ], - [ - ['key' => 'value', 'key2' => 'value2'], - true, - ], - [ - [0 => 'value', 1 => 'value2', 'deux' => 'value3'], - true, - ], - [ - [0 => 'value'], - false, - ], - [ - [0 => 'value', 1 => 'value2', 3 => 'value3'], - false, - ], - [ - ['0' => 'value', '1' => 'value2', '3' => 'value3'], - false, - ], - [ - [], - false, - ], - ]; - } - - public function testCleanupEmptyTemporaryFiles(): void - { - $generator = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'unlink', - ]) - ->setConstructorArgs(['the_binary']) - ->getMock() - ; - - $generator - ->expects($this->once()) - ->method('unlink') - ; - - $create = new ReflectionMethod($generator, 'createTemporaryFile'); - $create->setAccessible(true); - $create->invoke($generator, null, null); - - $files = new ReflectionProperty($generator, 'temporaryFiles'); - $files->setAccessible(true); - $this->assertCount(1, $files->getValue($generator)); - - $remove = new ReflectionMethod($generator, 'removeTemporaryFiles'); - $remove->setAccessible(true); - $remove->invoke($generator); - } - - public function testleanupTemporaryFiles(): void - { - $generator = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'unlink', - ]) - ->setConstructorArgs(['the_binary']) - ->getMock() - ; - - $generator - ->expects($this->once()) - ->method('unlink') - ; - - $create = new ReflectionMethod($generator, 'createTemporaryFile'); - $create->setAccessible(true); - $create->invoke($generator, '', 'html'); - - $files = new ReflectionProperty($generator, 'temporaryFiles'); - $files->setAccessible(true); - $this->assertCount(1, $files->getValue($generator)); - - $remove = new ReflectionMethod($generator, 'removeTemporaryFiles'); - $remove->setAccessible(true); - $remove->invoke($generator); - } - - public function testResetOptions(): void - { - $media = new class('/usr/local/bin/wkhtmltopdf') extends AbstractGenerator { - protected function configure(): void - { - $this->addOptions([ - 'optionA' => null, - 'optionB' => 'abc', - ]); - } - }; - - $media->setOption('optionA', 'bar'); - - $this->assertEquals( - [ - 'optionA' => 'bar', - 'optionB' => 'abc', - ], - $media->getOptions() - ); - - $media->resetOptions(); - - $this->assertEquals( - [ - 'optionA' => null, - 'optionB' => 'abc', - ], - $media->getOptions() - ); - } - - public function testFailingGenerateWithOutputContainingPharPrefix(): void - { - $media = $this->getMockBuilder(AbstractGenerator::class) - ->setMethods([ - 'configure', - 'prepareOutput', - ]) - ->setConstructorArgs(['the_binary', [], ['PATH' => '/usr/bin']]) - ->getMock() - ; - - $media->setTimeout(2000); - - $media - ->expects($this->once()) - ->method('prepareOutput') - ->with($this->equalTo('phar://the_output_file')) - ; - - $this->expectException(InvalidArgumentException::class); - - $media->generate('the_input_file', 'phar://the_output_file', ['foo' => 'bar']); - } - - /** - * @return null|string - */ - private function getPHPExecutableFromPath(): ?string - { - if (isset($_SERVER['_'])) { - return $_SERVER['_']; - } - - if (@\defined(\PHP_BINARY)) { - return \PHP_BINARY; - } - - if (false === \getenv('PATH')) { - return null; - } - - $paths = \explode(\PATH_SEPARATOR, \getenv('PATH')); - foreach ($paths as $path) { - // we need this for XAMPP (Windows) - if (\strstr($path, 'php.exe') && isset($_SERVER['WINDIR']) && \file_exists($path) && \is_file($path)) { - return $path; - } - $php_executable = $path . \DIRECTORY_SEPARATOR . 'php' . (isset($_SERVER['WINDIR']) ? '.exe' : ''); - if (\file_exists($php_executable) && \is_file($php_executable)) { - return $php_executable; - } - } - - return null; // not found - } -} diff --git a/tests/Knp/Snappy/ImageTest.php b/tests/Knp/Snappy/ImageTest.php deleted file mode 100644 index 71bd1ca4..00000000 --- a/tests/Knp/Snappy/ImageTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertInstanceOf(Image::class, $testObject); - } -} diff --git a/tests/Knp/Snappy/PdfTest.php b/tests/Knp/Snappy/PdfTest.php deleted file mode 100644 index 76ef84aa..00000000 --- a/tests/Knp/Snappy/PdfTest.php +++ /dev/null @@ -1,201 +0,0 @@ -getPathname()); - } - } - - public function testCreateInstance(): void - { - $testObject = new Pdf(); - $this->assertInstanceOf(Pdf::class, $testObject); - } - - public function testThatSomethingUsingTmpFolder(): void - { - $q = self::SHELL_ARG_QUOTE_REGEX; - $testObject = new PdfSpy(); - $testObject->setTemporaryFolder(__DIR__); - - $testObject->getOutputFromHtml('', ['footer-html' => 'footer']); - $this->assertRegExp('/emptyBinary --lowquality --footer-html ' . $q . '.*' . $q . ' ' . $q . '.*' . $q . ' ' . $q . '.*' . $q . '/', $testObject->getLastCommand()); - } - - public function testThatSomethingUsingNonexistentTmpFolder(): void - { - $temporaryFolder = \sys_get_temp_dir() . '/i-dont-exist'; - - $testObject = new PdfSpy(); - $testObject->setTemporaryFolder($temporaryFolder); - - $testObject->getOutputFromHtml('', ['footer-html' => 'footer']); - - $this->assertDirectoryExists($temporaryFolder); - } - - public function testRemovesLocalFilesOnError(): void - { - $pdf = new PdfSpy(); - $method = new ReflectionMethod($pdf, 'createTemporaryFile'); - $method->setAccessible(true); - $method->invoke($pdf, 'test', $pdf->getDefaultExtension()); - $this->assertEquals(1, \count($pdf->temporaryFiles)); - $this->expectException(Error::class); - \trigger_error('test error', \E_USER_ERROR); - // @phpstan-ignore-next-line See https://github.com/phpstan/phpstan/issues/7799 - $this->assertFileNotExists(\reset($pdf->temporaryFiles)); - } - - /** - * @dataProvider dataOptions - */ - public function testOptions(array $options, string $expectedRegex): void - { - $testObject = new PdfSpy(); - $testObject->getOutputFromHtml('', $options); - $this->assertRegExp($expectedRegex, $testObject->getLastCommand()); - } - - public function dataOptions(): array - { - $q = self::SHELL_ARG_QUOTE_REGEX; - - return [ - // no options - [ - [], - '/emptyBinary --lowquality ' . $q . '.*\.html' . $q . ' ' . $q . '.*\.pdf' . $q . '/', - ], - // just pass the given footer URL - [ - ['footer-html' => 'http://google.com'], - '/emptyBinary --lowquality --footer-html ' . $q . 'http:\/\/google\.com' . $q . ' ' . $q . '.*\.html' . $q . ' ' . $q . '.*\.pdf' . $q . '/', - ], - // just pass the given footer file - [ - ['footer-html' => __FILE__], - '/emptyBinary --lowquality --footer-html ' . $q . \preg_quote(__FILE__, '/') . $q . ' ' . $q . '.*\.html' . $q . ' ' . $q . '.*\.pdf' . $q . '/', - ], - // save the given footer HTML string into a temporary file and pass that filename - [ - ['footer-html' => 'footer'], - '/emptyBinary --lowquality --footer-html ' . $q . '.*\.html' . $q . ' ' . $q . '.*\.html' . $q . ' ' . $q . '.*\.pdf' . $q . '/', - ], - // save the content of the given XSL URL to a file and pass that filename - [ - ['xsl-style-sheet' => 'http://google.com'], - '/emptyBinary --lowquality --xsl-style-sheet ' . $q . '.*\.xsl' . $q . ' ' . $q . '.*\.html' . $q . ' ' . $q . '.*\.pdf' . $q . '/', - ], - // set toc options after toc argument - [ - [ - 'grayscale' => true, - 'toc' => true, - 'disable-dotted-lines' => true, - ], - '/emptyBinary --grayscale --lowquality toc --disable-dotted-lines ' . $q . '.*\.html' . $q . ' ' . $q . '.*\.pdf' . $q . '/', - ], - ]; - } - - public function testRemovesLocalFilesOnDestruct(): void - { - $pdf = new PdfSpy(); - $method = new ReflectionMethod($pdf, 'createTemporaryFile'); - $method->setAccessible(true); - $method->invoke($pdf, 'test', $pdf->getDefaultExtension()); - $this->assertEquals(1, \count($pdf->temporaryFiles)); - $this->assertFileExists(\reset($pdf->temporaryFiles)); - $pdf->__destruct(); - $this->assertFileNotExists(\reset($pdf->temporaryFiles)); - } -} - -class PdfSpy extends Pdf -{ - /** - * @var string - */ - private $lastCommand; - - public function __construct() - { - parent::__construct('emptyBinary'); - } - - /** - * @return string - */ - public function getLastCommand() - { - return $this->lastCommand; - } - - /** - * {@inheritdoc} - */ - public function getOutput($input, array $options = []) - { - $filename = $this->createTemporaryFile(null, $this->getDefaultExtension()); - $this->generate($input, $filename, $options, true); - - return 'output'; - } - - /** - * {@inheritdoc} - */ - protected function executeCommand($command) - { - $this->lastCommand = $command; - - return [0, 'output', 'errorOutput']; - } - - /** - * {@inheritdoc} - */ - protected function checkOutput($output, $command) - { - //let's say everything went right - } -}