diff --git a/.circleci/config.yml b/.circleci/config.yml index 921b8ae..4716bd5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: - newspack: newspack/newspack@1.4.3 + newspack: newspack/newspack@1.5.6 jobs: release_wporg: @@ -51,3 +51,6 @@ workflows: branches: only: - release + php: + jobs: + - newspack/test-php diff --git a/.gitignore b/.gitignore index 3e693ce..bda5e68 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ node_modules/ *.zip release/ vendor +.phpunit.result.cache diff --git a/CHANGELOG.md b/CHANGELOG.md index 859e626..99f96cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# [2.6.0](https://github.com/Automattic/republication-tracker-tool/compare/v2.5.2...v2.6.0) (2025-09-08) + + +### Bug Fixes + +* displaying the values ([e4c83fd](https://github.com/Automattic/republication-tracker-tool/commit/e4c83fdbf28718b31e072a5ca94ce5f1c07a0543)) +* escaping ([76eb893](https://github.com/Automattic/republication-tracker-tool/commit/76eb8930080d0cf85e6a855c5905411efdecbdb2)) + + +### Features + +* Add plain text option ([#268](https://github.com/Automattic/republication-tracker-tool/issues/268)) ([b484c57](https://github.com/Automattic/republication-tracker-tool/commit/b484c57b47cf6436cc86b0d22fdbf8d1c8cd144b)) +* gracefully support no licenses ([#270](https://github.com/Automattic/republication-tracker-tool/issues/270)) ([733b088](https://github.com/Automattic/republication-tracker-tool/commit/733b08899c487c6d058c5c95755516b39cda1a6c)) + ## [2.5.2](https://github.com/Automattic/republication-tracker-tool/compare/v2.5.1...v2.5.2) (2025-07-30) diff --git a/assets/clipboard-utils.js b/assets/clipboard-utils.js new file mode 100644 index 0000000..4454881 --- /dev/null +++ b/assets/clipboard-utils.js @@ -0,0 +1,86 @@ +/** + * Clipboard utility for copying text to clipboard + * + * uses Clipboard API. + */ +window.ClipboardUtils = { + /** + * Copy text to clipboard + * @param {string} text - Text to copy + * @returns {Promise} - Promise resolving to success status + */ + async copyText(text) { + if (!navigator.clipboard) { + console.warn('Clipboard API not available'); + return false; + } + + try { + await navigator.clipboard.writeText(text); + return true; + } catch (err) { + console.error('Failed to copy text:', err); + return false; + } + }, + + /** + * Get text content from an element + * @param {string|Element} elementOrSelector - Element or selector + * @returns {string} - Text content + */ + getElementText(elementOrSelector) { + let element; + + if (typeof elementOrSelector === 'string') { + element = document.querySelector(elementOrSelector); + } else { + element = elementOrSelector; + } + + if (!element) { + return ''; + } + + const tagName = element.tagName.toLowerCase(); + if (tagName === 'input' || tagName === 'textarea') { + return element.value || ''; + } else { + return element.textContent || element.innerText || ''; + } + }, + + /** + * Copy content from an element + * @param {string|Element} elementOrSelector - Source element + * @param {Element} button - Button element for feedback + * @returns {Promise} - Promise resolving to success status + */ + async copyFromElement(elementOrSelector, button) { + const text = this.getElementText(elementOrSelector); + if (!text) { + return false; + } + + const success = await this.copyText(text); + + if (success && button) { + this.showButtonFeedback(button); + } + + return success; + }, + + /** + * Show temporary "Copied!" feedback on button + * @param {Element} button - Button element + */ + showButtonFeedback(button) { + const originalText = button.textContent || button.innerText; + + button.textContent = 'Copied!'; + setTimeout(() => { + button.textContent = originalText; + }, 2000); + } +}; diff --git a/assets/republish-template.css b/assets/republish-template.css index 7d15062..552f19e 100644 --- a/assets/republish-template.css +++ b/assets/republish-template.css @@ -20,18 +20,127 @@ font-family: monospace; font-size: smaller; text-align: left; - border: 2px solid #c7c7c7; - height: 50vh; + border: 1px solid #ccc; + height: 40vh; line-height: 1.6em; overflow: scroll; padding: 16px; } .republish-article__info textarea:focus { - border-color: #a7a7a7; - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2); + border-color: #1e1e1e; } .republish-article__copy-button { margin-top: 20px; } + +.republish-format-tabs { + display: flex; + margin-bottom: 1.5rem; + border-bottom: 1px solid #e0e0e0; +} + +.republish-format-tabs__button { + background: none; + border: none; + padding: 0.75rem 1.5rem; + cursor: pointer; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-weight: 600; + color: #6c6c6c; + position: relative; + transition: color 0.2s ease; + border-bottom: 3px solid transparent; + border-radius: 0; + margin-bottom: -1px; + font-size: 20px; + + &:hover { + color: #e0e0e0; + } + + &:focus { + outline: none; + color: #e0e0e0; + } +} + +.republish-format-tabs__button--active { + color: #1e1e1e; + border-bottom-color: currentcolor; +} + +.republish-content { + display: none; +} + +.republish-content--active { + display: block; +} + +.republish-content-container { + position: relative; +} + +.republish-content__input, +.republish-content__textarea { + margin: 1em 0 1em 0; + width: 100%; +} + +.republish-content__input { + display: block; +} + +.plain-text-field { + margin-bottom: 1.5rem; +} + +.plain-text-field__label { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; + color: #1e1e1e; + font-size: 16px; +} + +.plain-text-field__input { + width: 100%; + padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 4px; + font-family: monospace; + font-size: 14px; + background-color: white; + margin-bottom: 0.5rem; + + &:focus { + outline: none; + border-color: #1e1e1e; + } +} + +.plain-text-field__button { + background: #2a7ac2; + border: 1px solid #255b98; + color: white; + padding: 0.5rem 1rem; + border-radius: 0.25em; + cursor: pointer; + font-size: 16px; + text-shadow: 1px 1px 1px #255b98; + + &:hover { + background: #2863a7; + text-decoration: none; + } +} + +.republish-article__copy-button--main { + display: none; +} + +.republish-article__copy-button--main.show-for-html { + display: inline-block; +} diff --git a/assets/republish-template.js b/assets/republish-template.js index b7e5696..d4e4c33 100644 --- a/assets/republish-template.js +++ b/assets/republish-template.js @@ -7,56 +7,74 @@ */ document.addEventListener("DOMContentLoaded", () => { /** - * Selects the text in the textarea when it is focused. + * Handle tab switching for format selection. */ - document - .querySelector(".republish-article .republish-article__info textarea") - ?.addEventListener("focus", (event) => { + const tabButtons = document.querySelectorAll(".republish-format-tabs__button"); + const tabContents = document.querySelectorAll(".republish-content"); + + tabButtons.forEach((button) => { + button.addEventListener("click", (event) => { + event.preventDefault(); + const targetTab = button.getAttribute("data-tab"); + + tabButtons.forEach((btn) => btn.classList.remove("republish-format-tabs__button--active")); + tabContents.forEach((content) => content.classList.remove("republish-content--active")); + + button.classList.add("republish-format-tabs__button--active"); + const targetContent = document.querySelector(`[data-tab-content="${targetTab}"]`); + if (targetContent) { + targetContent.classList.add("republish-content--active"); + } + + // Show/hide main copy button based on active tab + const mainCopyButton = document.querySelector(".republish-article__copy-button--main"); + if (mainCopyButton) { + if (targetTab === "html") { + mainCopyButton.classList.add("show-for-html"); + } else { + mainCopyButton.classList.remove("show-for-html"); + } + } + }); + }); + + /** + * Selects the text in the active textarea when it is focused. + */ + const textareas = document.querySelectorAll(".republish-content__textarea"); + textareas.forEach((textarea) => { + textarea.addEventListener("focus", (event) => { event.target.select(); }); + }); /** - * Copies the text in the textarea to the clipboard when the copy button is clicked. + * Copies the text in the active textarea to the clipboard when the copy button is clicked. */ document - .querySelector(".republish-article .republish-article__copy-button") + .querySelector(".republish-article__copy-button") ?.addEventListener("click", (event) => { event.preventDefault(); - const textarea = document.querySelector( - ".republish-article .republish-article__info textarea" - ); - const success = copyTextToClipboard(textarea.value); - - if (success) { - event.target.innerText = __("Copied!", "the-city-features"); - - setTimeout(() => { - event.target.innerText = __( - "Copy to clipboard", - "the-city-features" - ); - }, 2000); + + const activeTextarea = document.querySelector(".republish-content.republish-content--active.republish-content__textarea"); + + if (!activeTextarea) { + return; } + + ClipboardUtils.copyFromElement(activeTextarea, event.target); }); /** - * Copies the given text to the clipboard. - * - * @param {string} text The text to copy to the clipboard. - * - * @return {boolean} True if the text was copied to the clipboard, false otherwise. + * Handle individual field copy buttons for plain text format. */ - const copyTextToClipboard = (text) => { - // Check if the clipboard API is available. - if (!navigator.clipboard) { - return false; - } - // Copy the text to the clipboard. - navigator.clipboard - .writeText(text) - .then(() => true) - .catch(() => false); - - return true; - }; + const copyFieldButtons = document.querySelectorAll(".plain-text-field__button"); + copyFieldButtons.forEach((button) => { + button.addEventListener("click", (event) => { + event.preventDefault(); + + const targetSelector = button.getAttribute("data-target"); + ClipboardUtils.copyFromElement(targetSelector, button); + }); + }); }); diff --git a/assets/widget.css b/assets/widget.css index 8ea7356..eeaf479 100644 --- a/assets/widget.css +++ b/assets/widget.css @@ -5,140 +5,258 @@ */ .side-widget.republication_tracker_tool, .widget.republication_tracker_tool { - text-align: center; - max-width: 300px; - margin: 0 auto; + text-align: center; + max-width: 300px; + margin: 0 auto; } .side-widget.republication_tracker_tool p, .widget.republication_tracker_tool p { - margin-bottom: 1em; + margin-bottom: 1em; } .side-widget.republication_tracker_tool a.license, .widget.republication_tracker_tool a.license { - border-top: 3px double #ddd; - border-bottom: 3px double #ddd; - padding: 1em 0; - min-width: 100%; - display: block; + border-top: 3px double #ddd; + border-bottom: 3px double #ddd; + padding: 1em 0; + min-width: 100%; + display: block; } -.side-widget.republication_tracker_tool - button.republication-tracker-tool-button, +.side-widget.republication_tracker_tool button.republication-tracker-tool-button, .widget.republication_tracker_tool button.republication-tracker-tool-button { - width: 100%; - background: #2a7ac2; - border: 1px solid #255b98; - color: #fff; - padding: 1em; - font-size: 1.25em; - margin: 0 0 1em 0; - display: block; - border-radius: 0.25em; - font-weight: bold; - text-shadow: 1px 1px 1px #255b98; -} - -.side-widget.republication_tracker_tool - button.republication-tracker-tool-button:hover, -.widget.republication_tracker_tool - button.republication-tracker-tool-button:hover { - background-color: #2863a7; - text-decoration: none; - cursor: pointer; + width: 100%; + background: #2a7ac2; + border: 1px solid #255b98; + color: #fff; + padding: 1em; + font-size: 1.25em; + margin: 0 0 1em 0; + display: block; + border-radius: 0.25em; + font-weight: bold; + text-shadow: 1px 1px 1px #255b98; +} + +.side-widget.republication_tracker_tool button.republication-tracker-tool-button:hover, +.widget.republication_tracker_tool button.republication-tracker-tool-button:hover { + background-color: #2863a7; + text-decoration: none; + cursor: pointer; } #republication-tracker-tool-modal { - position: fixed; - z-index: 999999999; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.75); + position: fixed; + z-index: 999999999; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.75); } #republication-tracker-tool-modal-content { - position: absolute; - width: 90%; - max-width: 800px; - max-height: 90%; - overflow: auto; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: #fff; - padding: 2em; - text-align: left; + position: absolute; + width: 90%; + max-width: 800px; + max-height: 90%; + overflow: auto; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #fff; + padding: 2em; + text-align: left; + border-radius: 0.25em; } #republication-tracker-tool-modal-content textarea { - font-family: monospace; - font-size: smaller; - box-sizing: border-box; - width: 100%; - margin: 0.5em 0 2em 0; - cursor: auto; - background-color: inherit; - border-color: inherit; + font-family: monospace; + font-size: smaller; + box-sizing: border-box; + width: 100%; + margin: 0.5em 0; + cursor: auto; + background-color: inherit; + border-color: inherit; } #republication-tracker-tool-modal-content h2, #republication-tracker-tool-modal-content .cc-license, #republication-tracker-tool-modal-content .cc-policy { - border-bottom: 1px solid #ccc; - padding-bottom: 1em; + border-bottom: 1px solid #ccc; + padding-bottom: 1em; } #republication-tracker-tool-modal-content .cc-license, #republication-tracker-tool-modal-content .cc-policy { - margin-bottom: 2em; + margin-bottom: 2em; } #republication-tracker-tool-modal-content .cc-license img { - margin-bottom: 1em; + margin-bottom: 1em; } #republication-tracker-tool-modal-content .article-info h1 { - margin-bottom: 0; + margin-bottom: 0; +} + +#republication-tracker-tool-modal-content .republish-modal-label { + margin-top: 0; } .republication-tracker-tool-close { - background: transparent !important; - border: 0 !important; - color: #000 !important; - padding: 0.5em !important; - position: absolute; - top: 0; - right: 0.5em; - font-family: sans-serif; - text-transform: lowercase; - font-size: 2em; + background: transparent !important; + border: 0 !important; + color: #000 !important; + font-size: 1rem; + padding: 0 !important; + position: absolute; + top: 0; + transition: all 125ms ease-in-out; + right: 0; } .republication-tracker-tool-close:hover { - cursor: pointer; - opacity: 0.8; + cursor: pointer; + opacity: 0.8; +} + +.republication-tracker-tool-close-icon { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='M13.0597 12L19.5297 5.52997L18.4697 4.46997L11.9997 10.94L5.52973 4.46997L4.46973 5.52997L10.9397 12L4.46973 18.47L5.52973 19.53L11.9997 13.06L18.4697 19.53L19.5297 18.47L13.0597 12Z' fill='%231E1E1E'/%3E%3C/svg%3E"); + background-position: center; + background-size: 24px 24px; + background-repeat: no-repeat; + display: block; + height: 2em; + width: 2em; } .modal-open-disallow-scrolling { - overflow: hidden; + overflow: hidden; } .republication_tracker_tool .license img { - width: 88px; + width: 88px; } .screen-reader-text { - border: 0; - clip: rect( 1px, 1px, 1px, 1px ); - clip-path: inset( 50% ); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute !important; - width: 1px; - word-wrap: normal !important; /* Many screen reader and browser combinations announce broken words as they would appear visually. */ + border: 0; + clip: rect( 1px, 1px, 1px, 1px ); + clip-path: inset( 50% ); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute !important; + width: 1px; + word-wrap: normal !important; /* Many screen reader and browser combinations announce broken words as they would appear visually. */ +} + +.republish-format-tabs { + display: flex; + margin-bottom: 1.5rem; + border-bottom: 1px solid #e0e0e0; +} + +.republish-format-tabs__button { + background: none; + border: none; + padding: 1rem 1.5rem; + cursor: pointer; + font-weight: 500; + color: #666; + position: relative; + transition: color 0.2s ease; + border-bottom: 2px solid transparent; + border-radius: 0; + font-size: 20px; + + &:hover { + color: #e0e0e0; + } + + &:focus { + outline: none; + color: #e0e0e0; + } +} + +.republish-format-tabs__button--active { + color: #333; + border-bottom: 3px solid #333; +} + +.republish-content { + display: none; +} + +.republish-content--active { + display: block; +} + +.republish-content-container { + position: relative; +} + +.republish-content__textarea { + width: 100%; +} + +.plain-text-field { + margin-bottom: 1.5rem; +} + +.plain-text-field__label { + display: block; + margin-bottom: 0.75rem; + font-weight: 600; + color: #1e1e1e; + font-size: 16px; +} + +.plain-text-field__input { + width: 100%; + padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 4px; + font-family: monospace; + font-size: 14px; + background-color: #fff; + margin-bottom: 0.5rem; + + &:focus { + outline: none; + border-color: #007cba; + box-shadow: 0 0 0 1px #007cba; + } +} + +.plain-text-field__button { + background: #007cba; + border: none; + color: white; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s; + + &:hover { + background: #005a87; + } + + &:focus { + outline: 2px solid #007cba; + outline-offset: 2px; + } +} + +.republication-tracker-tool__copy-button--main { + display: none; +} + +.republication-tracker-tool__copy-button--main.show-for-html { + display: inline-block; } diff --git a/assets/widget.js b/assets/widget.js index 6cd847d..84c4b97 100644 --- a/assets/widget.js +++ b/assets/widget.js @@ -1,10 +1,41 @@ -function copyToClipboard( element, button ) { - var $temp = jQuery( '' ); - jQuery( 'body' ).append( $temp ); - $temp.val( jQuery( element ).text() ).select(); - document.execCommand( 'copy' ); - $temp.remove(); - button.focus(); +function getActiveTextarea() { + const activeTextarea = document.querySelector('.republish-content.republish-content--active textarea'); + if (activeTextarea) { + return activeTextarea; + } + // Fallback to the original textarea if no tabs are present + return document.querySelector('#republication-tracker-tool-shareable-content'); +} + +function initTabSwitching() { + const $ = jQuery; + $( '.republish-format-tabs__button' ).on( 'click', function(e) { + e.preventDefault(); + const targetTab = $(this).attr('data-tab'); + + $( '.republish-format-tabs__button' ).removeClass( 'republish-format-tabs__button--active' ); + $( '.republish-content' ).removeClass( 'republish-content--active' ); + + $( this ).addClass( 'republish-format-tabs__button--active' ); + $('[data-tab-content="' + targetTab + '"]').addClass( 'republish-content--active' ); + + // Show/hide main copy button based on active tab + const $mainCopyButton = $( '.republication-tracker-tool__copy-button--main' ); + if ( $mainCopyButton.length ) { + if ( targetTab === 'html' ) { + $mainCopyButton.addClass( 'show-for-html' ); + } else { + $mainCopyButton.removeClass( 'show-for-html' ); + } + } + } ); + + // Initialize copy buttons for individual fields + $( '.plain-text-field__button' ).on( 'click', function(e) { + e.preventDefault(); + const target = $( this ).attr( 'data-target' ); + ClipboardUtils.copyFromElement( target, this ); + } ); } function modal_actions(){ @@ -59,9 +90,12 @@ function show_modal( $modal, $close ) { $modal.show(); $modal_content.show(); $('body').addClass('modal-open-disallow-scrolling'); - $('#republication-tracker-tool-modal-content').unbind().click(function(e) { + $modal_content.unbind().click(function(e) { e.stopPropagation(); }); + + initTabSwitching(); + trapFocus( $modal ); $close.focus(); } diff --git a/composer.json b/composer.json index 5c260d7..0a6690f 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,9 @@ "brainmaestro/composer-git-hooks": "^2.6", "wp-coding-standards/wpcs": "*", "dealerdirect/phpcodesniffer-composer-installer": "*", - "phpcompatibility/phpcompatibility-wp": "*" + "phpcompatibility/phpcompatibility-wp": "*", + "phpunit/phpunit": "^9.6", + "yoast/phpunit-polyfills": "^4.0" }, "license": "GPL-2.0-or-later", "scripts": { @@ -28,11 +30,11 @@ }, "config": { "platform": { - "php": "8.0" + "php": "8.3" }, "allow-plugins": { "composer/installers": true, "dealerdirect/phpcodesniffer-composer-installer": true } } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 3b9990f..cfa86b4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "99bbcc27a227adbe1320408f2cf8899c", + "content-hash": "08ee5aa2ed591da1635cd521d22288f6", "packages": [], "packages-dev": [ { @@ -89,7 +89,6 @@ "type": "library", "extra": { "hooks": { - "pre-commit": "composer check-style", "pre-push": [ "composer test", "appver=$(grep -o -E '\\d.\\d.\\d' cghooks)", @@ -100,7 +99,8 @@ "sed -i -E \"s/$appver/$tag/\" cghooks", "exit 1", "fi" - ] + ], + "pre-commit": "composer check-style" } }, "autoload": { @@ -286,28 +286,28 @@ }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.0.0", + "version": "v1.1.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "4be43904336affa5c2f70744a348312336afd0da" + "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", - "reference": "4be43904336affa5c2f70744a348312336afd0da", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1", + "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0", + "composer-plugin-api": "^2.2", "php": ">=5.4", "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" }, "require-dev": { - "composer/composer": "*", + "composer/composer": "^2.2", "ext-json": "*", "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", + "php-parallel-lint/php-parallel-lint": "^1.4.0", "phpcompatibility/php-compatibility": "^9.0", "yoast/phpunit-polyfills": "^1.0" }, @@ -327,9 +327,9 @@ "authors": [ { "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" }, { "name": "Contributors", @@ -337,7 +337,6 @@ } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", "keywords": [ "PHPCodeSniffer", "PHP_CodeSniffer", @@ -358,9 +357,334 @@ ], "support": { "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", "source": "https://github.com/PHPCSStandards/composer-installer" }, - "time": "2023-01-05T11:28:13+00:00" + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-07-17T20:45:56+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.3", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36", + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-07-05T12:25:42+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.5.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + }, + "time": "2025-05-31T08:24:38+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -386,246 +710,1649 @@ "require-dev": { "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, + "time": "2019-12-27T09:44:58+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-paragonie", + "version": "1.3.3", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", + "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/293975b465e0e709b571cbf0c957c6c0a7b9a2ac", + "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "paragonie/random_compat": "dev-master", + "paragonie/sodium_compat": "dev-master" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "paragonie", + "phpcs", + "polyfill", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-04-24T21:30:46+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-wp", + "version": "2.1.7", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/5bfbbfbabb3df2b9a83e601de9153e4a7111962c", + "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/phpcompatibility-paragonie": "^1.0", + "squizlabs/php_codesniffer": "^3.3" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-05-12T16:38:37+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.32", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:23:01+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.23", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.5.0 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-05-02T06:40:34+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] }, - "type": "phpcodesniffer-standard", "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0-or-later" + "BSD-3-Clause" ], "authors": [ { - "name": "Wim Godden", - "homepage": "https://github.com/wimg", - "role": "lead" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Juliette Reinders Folmer", - "homepage": "https://github.com/jrfnl", - "role": "lead" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" }, { - "name": "Contributors", - "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", - "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", - "keywords": [ - "compatibility", - "phpcs", - "standards" - ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibility" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, - "time": "2019-12-27T09:44:58+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" }, { - "name": "phpcompatibility/phpcompatibility-paragonie", - "version": "1.3.3", + "name": "sebastian/resource-operations", + "version": "3.0.4", "source": { "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac" + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/293975b465e0e709b571cbf0c957c6c0a7b9a2ac", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { - "phpcompatibility/php-compatibility": "^9.0" + "php": ">=7.3" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "paragonie/random_compat": "dev-master", - "paragonie/sodium_compat": "dev-master" + "phpunit/phpunit": "^9.0" }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] }, - "type": "phpcodesniffer-standard", "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0-or-later" + "BSD-3-Clause" ], "authors": [ { - "name": "Wim Godden", - "role": "lead" - }, - { - "name": "Juliette Reinders Folmer", - "role": "lead" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", - "homepage": "http://phpcompatibility.com/", - "keywords": [ - "compatibility", - "paragonie", - "phpcs", - "polyfill", - "standards", - "static analysis" - ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", - "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { - "url": "https://github.com/PHPCompatibility", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" } ], - "time": "2024-04-24T21:30:46+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { - "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.5", + "name": "sebastian/type", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "01c1ff2704a58e46f0cb1ca9d06aee07b3589082" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/01c1ff2704a58e46f0cb1ca9d06aee07b3589082", - "reference": "01c1ff2704a58e46f0cb1ca9d06aee07b3589082", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { - "phpcompatibility/php-compatibility": "^9.0", - "phpcompatibility/phpcompatibility-paragonie": "^1.0" + "php": ">=7.3" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0" + "phpunit/phpunit": "^9.5" }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] }, - "type": "phpcodesniffer-standard", "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0-or-later" + "BSD-3-Clause" ], "authors": [ { - "name": "Wim Godden", - "role": "lead" - }, - { - "name": "Juliette Reinders Folmer", + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", - "homepage": "http://phpcompatibility.com/", - "keywords": [ - "compatibility", - "phpcs", - "standards", - "static analysis", - "wordpress" - ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", - "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { - "url": "https://github.com/PHPCompatibility", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" } ], - "time": "2024-04-24T21:37:59+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { - "name": "psr/container", - "version": "1.1.2", + "name": "sebastian/version", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": ">=7.3" }, "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" }, { "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.11.18", + "version": "v2.12.0", "source": { "type": "git", "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "ca242a0b7309e0f9d1f73b236e04ecf4ca3248d0" + "reference": "4debf5383d9ade705e0a25121f16c3fecaf433a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/ca242a0b7309e0f9d1f73b236e04ecf4ca3248d0", - "reference": "ca242a0b7309e0f9d1f73b236e04ecf4ca3248d0", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/4debf5383d9ade705e0a25121f16c3fecaf433a7", + "reference": "4debf5383d9ade705e0a25121f16c3fecaf433a7", "shasum": "" }, "require": { @@ -636,9 +2363,8 @@ "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", "phpcsstandards/phpcsdevcs": "^1.1", "phpstan/phpstan": "^1.7", - "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0", - "sirbrillig/phpcs-import-detection": "^1.1", - "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0@beta" + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0 || ^10.5.32 || ^11.3.3", + "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0" }, "type": "phpcodesniffer-standard", "autoload": { @@ -670,20 +2396,20 @@ "source": "https://github.com/sirbrillig/phpcs-variable-analysis", "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" }, - "time": "2024-04-13T16:42:46+00:00" + "time": "2025-03-17T16:17:38+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.10.1", + "version": "3.13.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877" + "reference": "5b5e3821314f947dd040c70f7992a64eac89025c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8f90f7a53ce271935282967f53d0894f8f1ff877", - "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c", + "reference": "5b5e3821314f947dd040c70f7992a64eac89025c", "shasum": "" }, "require": { @@ -748,22 +2474,26 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2024-05-22T21:24:41+00:00" + "time": "2025-06-17T22:17:01+00:00" }, { "name": "symfony/console", - "version": "v5.4.40", + "version": "v5.4.47", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "aa73115c0c24220b523625bfcfa655d7d73662dd" + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/aa73115c0c24220b523625bfcfa655d7d73662dd", - "reference": "aa73115c0c24220b523625bfcfa655d7d73662dd", + "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", "shasum": "" }, "require": { @@ -833,7 +2563,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.40" + "source": "https://github.com/symfony/console/tree/v5.4.47" }, "funding": [ { @@ -849,33 +2579,33 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-11-06T11:30:55+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.3", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "80d075412b557d41002320b96a096ca65aa2c98d" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/80d075412b557d41002320b96a096ca65aa2c98d", - "reference": "80d075412b557d41002320b96a096ca65aa2c98d", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { @@ -900,7 +2630,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.3" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -916,24 +2646,24 @@ "type": "tidelift" } ], - "time": "2023-01-24T14:02:46+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.29.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -944,8 +2674,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -979,7 +2709,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" }, "funding": [ { @@ -995,24 +2725,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -1020,8 +2750,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1057,7 +2787,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" }, "funding": [ { @@ -1073,24 +2803,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -1098,8 +2828,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1138,7 +2868,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" }, "funding": [ { @@ -1154,24 +2884,25 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -1182,8 +2913,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1218,7 +2949,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -1234,30 +2965,30 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.29.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "21bd091060673a1177ae842c0ef8fe30893114d2" + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/21bd091060673a1177ae842c0ef8fe30893114d2", - "reference": "21bd091060673a1177ae842c0ef8fe30893114d2", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1294,7 +3025,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.32.0" }, "funding": [ { @@ -1310,30 +3041,30 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.29.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1374,7 +3105,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" }, "funding": [ { @@ -1390,47 +3121,47 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.3", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3" + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a2329596ddc8fd568900e3fc76cba42489ecc7f3", - "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1457,7 +3188,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.3" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -1473,38 +3204,38 @@ "type": "tidelift" } ], - "time": "2023-04-21T15:04:16+00:00" + "time": "2025-04-25T09:37:31+00:00" }, { "name": "symfony/string", - "version": "v5.4.40", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "142877285aa974a6f7685e292ab5ba9aae86b143" + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/142877285aa974a6f7685e292ab5ba9aae86b143", - "reference": "142877285aa974a6f7685e292ab5ba9aae86b143", + "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": ">=3.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -1543,7 +3274,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.40" + "source": "https://github.com/symfony/string/tree/v6.4.21" }, "funding": [ { @@ -1559,7 +3290,57 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2025-04-18T15:23:29+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" }, { "name": "wp-coding-standards/wpcs", @@ -1611,17 +3392,80 @@ "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" }, "time": "2020-05-13T23:57:56+00:00" + }, + { + "name": "yoast/phpunit-polyfills", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", + "reference": "134921bfca9b02d8f374c48381451da1d98402f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/134921bfca9b02d8f374c48381451da1d98402f9", + "reference": "134921bfca9b02d8f374c48381451da1d98402f9", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0 || ^11.0 || ^12.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "yoast/yoastcs": "^3.1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.x-dev" + } + }, + "autoload": { + "files": [ + "phpunitpolyfills-autoload.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Team Yoast", + "email": "support@yoast.com", + "homepage": "https://yoast.com" + }, + { + "name": "Contributors", + "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" + } + ], + "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", + "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", + "keywords": [ + "phpunit", + "polyfill", + "testing" + ], + "support": { + "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", + "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", + "source": "https://github.com/Yoast/PHPUnit-Polyfills" + }, + "time": "2025-02-09T18:58:54+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "platform-overrides": { - "php": "8.0" + "php": "8.3" }, "plugin-api-version": "2.6.0" } diff --git a/includes/class-content.php b/includes/class-content.php index d47dffa..f431dd7 100644 --- a/includes/class-content.php +++ b/includes/class-content.php @@ -17,8 +17,9 @@ class Republication_Tracker_Tool_Content { * * @param string $content The post content. * @param int $post_id Optional. Current post ID by default. + * @param bool $escape_html Optional. Whether to escape HTML for final output. Default true. */ - public static function get_republishable_content( $content, $post_id = false ) { + public static function get_republishable_content( $content, $post_id = false, $escape_html = true ) { if ( ! $post_id ) { $post_id = get_the_ID(); } @@ -72,8 +73,10 @@ public static function get_republishable_content( $content, $post_id = false ) { // Remove non-distributable images. $content = self::remove_non_distributable_images( $content, 'RTT removed image' ); - // Force the content to be UTF-8 escaped HTML. - $content = htmlspecialchars( $content, ENT_HTML5, 'UTF-8', true ); + // Force the content to be UTF-8 escaped HTML only if requested. + if ( $escape_html ) { + $content = htmlspecialchars( $content, ENT_HTML5, 'UTF-8', true ); + } $post_object = get_post( $post_id ); @@ -119,4 +122,128 @@ public static function remove_non_distributable_images( $content ) { return $content; } + + /** + * Generate plain text version of republishable content. + * + * @param WP_Post $post_object The post object. + * @return string The plain text content. + */ + public static function get_republishable_plain_text_content( $post_object ) { + if ( ! $post_object instanceof WP_Post ) { + return ''; + } + + // Start building the plain text content + $plain_text_content = ''; + + // Get article metadata + $article_title = get_the_title( $post_object ); + $author_byline = sprintf( '%1$s, %2$s', apply_filters( 'republication_tracker_tool_byline', __( 'By ', 'republication-tracker-tool' ) . get_the_author_meta( 'display_name', $post_object->post_author ) ), get_bloginfo( 'name' ) ); + $article_date = date( 'F j, Y', strtotime( $post_object->post_date ) ); + + // Add the article title + if ( ! empty( $article_title ) ) { + $plain_text_content .= $article_title . "\n\n"; + } + + // Add the author byline (strip HTML tags) + if ( ! empty( $author_byline ) ) { + $plain_text_content .= wp_strip_all_tags( $author_byline ) . "\n"; + } + + // Add the article date + if ( ! empty( $article_date ) ) { + $plain_text_content .= $article_date . "\n\n"; + } + + // Process the main content + if ( ! empty( $post_object->post_content ) ) { + // Get HTML content without escaping for plain text conversion + $html_content = self::get_republishable_content( $post_object->post_content, $post_object->ID, false ); + + // Convert HTML to plain text + $plain_content = self::convert_html_to_plain_text( $html_content ); + + $plain_text_content .= $plain_content . "\n\n"; + } + + // Add attribution. + $display_attribution = get_option( 'republication_tracker_tool_display_attribution', 'on' ); + if ( 'on' === $display_attribution ) { + $plain_text_content .= Republication_Tracker_Tool::get_attribution( $post_object, true ) . "\n\n"; + } + + /** + * Filters the plain text content of the republished post. + * + * @param string $plain_text_content The plain text content of the post. + * @param WP_Post $post_object The post object. + * @return string The filtered plain text content. + */ + $plain_text_content = apply_filters( 'republication_tracker_tool_republish_plain_text_content', $plain_text_content, $post_object ); + + return trim( $plain_text_content ); + } + + /** + * Convert HTML content to formatted plain text. + * + * @param string $html_content The HTML content to convert. + * @return string The converted plain text content. + */ + private static function convert_html_to_plain_text( $html_content ) { + $html_content = html_entity_decode( $html_content, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); + + // Filter tags and clean up the content. + $html_content = preg_replace( '/]*>.*?<\/figure>/is', '', $html_content ); + $html_content = preg_replace( '/]*>/i', '', $html_content ); + $html_content = preg_replace( '/]*>(.*?)<\/h[1-6]>/is', "\n$1\n\n", $html_content ); + $html_content = preg_replace( '/]*>(.*?)<\/p>/is', "$1\n\n", $html_content ); + $html_content = preg_replace( '//i', "\n", $html_content ); + + // ul to bullet points + $html_content = preg_replace_callback( + '/]*>(.*?)<\/ul>/is', + function( $matches ) { + $list_content = $matches[1]; + // Convert each list item to a bullet point + $list_content = preg_replace( '/]*>(.*?)<\/li>/is', "• $1\n", $list_content ); + return "\n" . $list_content . "\n"; + }, + $html_content + ); + + // ol to numbered points + $html_content = preg_replace_callback( + '/]*>(.*?)<\/ol>/is', + function( $matches ) { + $list_content = $matches[1]; + $counter = 1; + // Convert each list item to a numbered point + $list_content = preg_replace_callback( + '/]*>(.*?)<\/li>/is', + function( $item_matches ) use ( &$counter ) { + return $counter++ . ". " . $item_matches[1] . "\n"; + }, + $list_content + ); + return "\n" . $list_content . "\n"; + }, + $html_content + ); + + // Handle tags containing information + $html_content = preg_replace( '/]*>(.*?)<\/blockquote>/is', "\n> $1\n\n", $html_content ); + $html_content = preg_replace( '/]*>(.*?)<\/a>/is', '$1', $html_content ); + $html_content = preg_replace( '/<(strong|b)[^>]*>(.*?)<\/\1>/is', '$2', $html_content ); + $html_content = preg_replace( '/<(em|i)[^>]*>(.*?)<\/\1>/is', '$2', $html_content ); + + // Remove all remaining HTML tags + $plain_text = wp_strip_all_tags( $html_content ); + $plain_text = preg_replace( '/\n\s*\n\s*\n/', "\n\n", $plain_text ); + $plain_text = preg_replace( '/[ \t]+/', ' ', $plain_text ); + + return trim( $plain_text ); + } } diff --git a/includes/class-republication-rewrite.php b/includes/class-republication-rewrite.php index 20e3de4..1810bcf 100644 --- a/includes/class-republication-rewrite.php +++ b/includes/class-republication-rewrite.php @@ -161,11 +161,20 @@ public function enqueue_scripts_and_styles() { REPUBLICATION_TRACKER_TOOL_VERSION ); + // Enqueue the clipboard utility first + wp_enqueue_script( + 'republication-tracker-tool-clipboard-utils', + REPUBLICATION_TRACKER_TOOL_URL . 'assets/clipboard-utils.js', + array(), + REPUBLICATION_TRACKER_TOOL_VERSION, + true + ); + // Enqueue the republish page scripts. wp_enqueue_script( 'republication-tracker-tool-republish-template', REPUBLICATION_TRACKER_TOOL_URL . 'assets/republish-template.js', - array(), + array( 'republication-tracker-tool-clipboard-utils' ), REPUBLICATION_TRACKER_TOOL_VERSION, true ); diff --git a/includes/class-settings.php b/includes/class-settings.php index 7840265..1fd6136 100644 --- a/includes/class-settings.php +++ b/includes/class-settings.php @@ -92,6 +92,11 @@ public function create_settings() { 'label' => esc_html__( 'Hide republication widgets on posts by default', 'republication-tracker-tool' ), 'callback' => array( $this, 'republication_tracker_tool_default_post_distribution_callback' ), ], + [ + 'key' => 'republication_tracker_tool_enable_plain_text', + 'label' => esc_html__( 'Enable plain text option', 'republication-tracker-tool' ), + 'callback' => array( $this, 'republication_tracker_tool_enable_plain_text_callback' ), + ], ]; foreach ( $settings as $setting ) { add_settings_field( @@ -252,21 +257,37 @@ public function republication_tracker_tool_license_callback() { ?>

- $license_values ) : ?> + $license_values ) : ?> + +
+ +
- + +

+ + checked + + /> + +

+ ID, 'republication-tracker-tool-hide-widget', true ), $post ); @@ -55,7 +59,8 @@ public function widget( $args, $instance ) { $is_amp = self::is_amp(); if ( ! $is_amp ) { - wp_enqueue_script( 'republication-tracker-tool-js', plugins_url( 'assets/widget.js', dirname( __FILE__ ) ), array( 'jquery' ), filemtime( plugin_dir_path( __FILE__ ) ), false ); + wp_enqueue_script( 'republication-tracker-tool-clipboard-utils', plugins_url( 'assets/clipboard-utils.js', dirname( __FILE__ ) ), array(), filemtime( plugin_dir_path( __FILE__ ) ), false ); + wp_enqueue_script( 'republication-tracker-tool-js', plugins_url( 'assets/widget.js', dirname( __FILE__ ) ), array( 'jquery', 'republication-tracker-tool-clipboard-utils' ), filemtime( plugin_dir_path( __FILE__ ) ), false ); } wp_enqueue_style( 'republication-tracker-tool-css', plugins_url( 'assets/widget.css', dirname( __FILE__ ) ), array(), filemtime( plugin_dir_path(__FILE__) ) ); @@ -82,12 +87,16 @@ public function widget( $args, $instance ) { ); } - echo sprintf( - '

%s

', - REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['url'], - esc_html__( 'Creative Commons License', 'republication-tracker-tool' ), - esc_url( plugin_dir_url( dirname( __FILE__ ) ) ) . 'assets/img/' . $license_key . '.png' - ); + if ( $using_license ) { + echo sprintf( + '

%s

', + REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['url'], + esc_html__( 'Creative Commons License', 'republication-tracker-tool' ), + esc_url( plugin_dir_url( dirname( __FILE__ ) ) ) . 'assets/img/' . $license_key . '.png' + ); + } + + echo "
$license_statement
"; echo ''; diff --git a/includes/licenses.php b/includes/licenses.php index a8e74c0..11ea703 100644 --- a/includes/licenses.php +++ b/includes/licenses.php @@ -40,7 +40,7 @@ ], 'cc-zero-1.0' => [ 'label' => 'CC0 1.0', - 'description' => 'CC0 1.0 Universal', + 'description' => 'CC0 1.0 Universal License', 'url' => 'https://creativecommons.org/publicdomain/zero/1.0/', 'badge' => 'https://licensebuttons.net/l/zero/1.0/88x31.png' ], diff --git a/includes/shareable-content.php b/includes/shareable-content.php index d523161..6f87a33 100644 --- a/includes/shareable-content.php +++ b/includes/shareable-content.php @@ -50,6 +50,15 @@ // strip empty tags after automatically applying p tags. $article_info = str_replace( '

', '', wpautop( $article_info ) ); +// Check if plain text feature is enabled +$plain_text_enabled = Republication_Tracker_Tool_Settings::is_plain_text_enabled(); +$plain_text_content = ''; +if ( $plain_text_enabled ) { + $canonical_tag = sprintf( '', esc_url( get_permalink( $post->ID ) ) ); + $plain_text_content = Republication_Tracker_Tool_Content::get_republishable_plain_text_content( $post ); + $additional_tracking_html = Republication_Tracker_Tool::create_additional_tracking_code_markup( $post->ID ); +} + /** * The licensing statement from this plugin * @@ -57,15 +66,17 @@ */ $license_statement = wp_kses_post( get_option( 'republication_tracker_tool_policy' ) ); $license_key = get_option( 'republication_tracker_tool_license', REPUBLICATION_TRACKER_TOOL_DEFAULT_LICENSE ); +$using_license = isset( REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ] ); echo '
'; echo ''; + echo '' . esc_html( 'Close window', 'republication-tracker-tool' ) . ''; printf( '

%s

', esc_html__( 'Republish this article', 'republication-tracker-tool' ) ); // Explain Creative Commons echo '
'; - echo '
'; + echo '
'; + if ( $using_license ) { printf( '%s', REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['url'], REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['description'], REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['badge'] ); echo wp_kses_post( wpautop( @@ -77,9 +88,10 @@ ) ) ); - echo '
'; // .cc-license - echo wp_kses_post( $license_statement ); - echo '
'; // .cc-policy + } + echo '
'; // .cc-license + echo "

" . wp_kses_post( $license_statement ) . "

"; + echo '
'; // .cc-policy // what we display to the embedder echo ' + - + - + ./tests/ diff --git a/republication-tracker-tool.php b/republication-tracker-tool.php index 0fd3be0..668bdc3 100644 --- a/republication-tracker-tool.php +++ b/republication-tracker-tool.php @@ -6,7 +6,7 @@ * Author URI: https://labs.inn.org * Text Domain: republication-tracker-tool * Domain Path: /languages - * Version: 2.5.2 + * Version: 2.6.0 * * @package Republication_Tracker_Tool */ @@ -276,7 +276,7 @@ public static function create_additional_tracking_code_markup( $post_id ) { } /** - * Get attribution text, which will be inserted at the end of the copyable content. + * Get attribution text and tracking code to be added to the content footer * * @param $post The shared post. */ @@ -286,35 +286,94 @@ public static function create_content_footer( $post = null ) { $additional_tracking_code = self::create_additional_tracking_code_markup( $post->ID ); $tracking_html = htmlentities( $pixel ) . htmlentities( $parsely_tracking ) . htmlentities( $additional_tracking_code ); - $license_key = get_option( 'republication_tracker_tool_license', 'cc-by-nd-4.0' ); - $license_url = REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['url']; - $license_description = REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['description']; - $display_attribution = get_option( 'republication_tracker_tool_display_attribution', 'on' ); + + $attribution = ''; + if ( 'on' === $display_attribution && null !== $post ) { - $site_icon_markup = ''; - $site_icon_url = get_site_icon_url( 150 ); - if ( ! empty( $site_icon_url ) ) { - $site_icon_markup = sprintf( - '', - esc_attr( $site_icon_url ), - ); - } + $attribution = self::get_attribution( $post ); + } + + return $attribution . $tracking_html; + } + + /** + * Get attribution text and tracking code to be added to the content footer + * + * @param \WP_Post $post The shared post. + * @param bool $plain_text Whether to return plain text. + */ + public static function get_attribution( \WP_Post $post, $plain_text = false ) { + $license_key = get_option( 'republication_tracker_tool_license', 'cc-by-nd-4.0' ); - return wpautop( - sprintf( - // translators: %1$s is a URL, %2$s is the site home URL, %3$s is the site title. %4$s is the license URL, %5$s is the license description. - esc_html__( 'This article first appeared on %3$s and is republished here under a %5$s.', 'republication-tracker-tool' ), - get_permalink( $post ), - home_url(), + $site_icon_markup = ''; + $site_icon_url = get_site_icon_url( 150 ); + if ( ! empty( $site_icon_url ) ) { + $site_icon_markup = sprintf( + '', + esc_attr( $site_icon_url ), + ); + } + + if ( isset( REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ] ) ) { + $license_url = REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['url']; + $license_description = REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['description']; + + if ( $plain_text ) { + $attribution = sprintf( + // translators: %1$s is the site title, %2$s is the license description. + esc_html__( 'This article first appeared on %1$s and is republished here under a %2$s.', 'republication-tracker-tool' ), esc_html( get_bloginfo() ), - $license_url, $license_description - ) . htmlentities( $site_icon_markup ) . $tracking_html - ); + ); + } else { + $attribution = wpautop( + sprintf( + // translators: %1$s is a URL, %2$s is the site home URL, %3$s is the site title. %4$s is the license URL, %5$s is the license description. + esc_html__( 'This article first appeared on %3$s and is republished here under a %5$s.', 'republication-tracker-tool' ), + get_permalink( $post ), + home_url(), + esc_html( get_bloginfo() ), + $license_url, + $license_description + ) . htmlentities( $site_icon_markup ) + ); + } + + + } else { + + if ( $plain_text ) { + $attribution = sprintf( + // translators: %s is the site title. + esc_html__( 'This article first appeared on %s.', 'republication-tracker-tool' ), + esc_html( get_bloginfo() ) + ); + } else { + + $attribution = wpautop( + sprintf( + // translators: %1$s is a URL, %2$s is the site home URL, %3$s is the site title. + esc_html__( 'This article first appeared on %3$s.', 'republication-tracker-tool' ), + get_permalink( $post ), + home_url(), + esc_html( get_bloginfo() ), + ) . htmlentities( $site_icon_markup ) + ); + } } - return $tracking_html; + + /** + * Filters the attribution HTML for the given post. + * + * @param string $attribution The attribution HTML. + * @param \WP_Post $post The post object. + * @param bool $plain_text Whether the attribution is plain text. + */ + return apply_filters( 'republication_tracker_tool_attribution', $attribution, $post, $plain_text ); + } + } /** diff --git a/templates/republish-template.php b/templates/republish-template.php index 5533c7b..093dbb8 100644 --- a/templates/republish-template.php +++ b/templates/republish-template.php @@ -24,12 +24,17 @@ $license_statement = get_option( 'republication_tracker_tool_policy' ); $license_key = get_option( 'republication_tracker_tool_license', REPUBLICATION_TRACKER_TOOL_DEFAULT_LICENSE ); -$license_badge = sprintf( - '%s', - REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['url'], - REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['description'], - REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['badge'] -); + +$using_license = isset( REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ] ); + +if ( $using_license ) { + $license_badge = sprintf( + '%s', + REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['url'], + REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['description'], + REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['badge'] + ); +} // Article title. $article_title = get_the_title( $republish_post_id ); @@ -38,7 +43,14 @@ $article_subtitle = get_post_meta( $republish_post_id, 'newspack_post_subtitle', true ); // Get article author byline. -$author_byline = apply_filters( 'republication_tracker_tool_author_byline', '', $republish_post_id ); +$author_byline = apply_filters( + 'republication_tracker_tool_author_byline', + sprintf( + '%1$s, %2$s', + __( 'By ', 'republication-tracker-tool' ) . get_the_author_meta( 'display_name', $post_object->post_author ), + get_bloginfo( 'name' ) + ) +); // Get article date and time in current timezone. $article_date_gmt = get_post_time( 'U', true, $republish_post_id ); // Get post date in GMT. @@ -95,7 +107,7 @@ // Add the article author to the republish content. if ( ! empty( $author_byline ) ) { $republish_content .= sprintf( - "\n\n
%s
", + "\n\n", $author_byline ); } @@ -151,6 +163,14 @@ // Filter the republish content. $republish_content = apply_filters( 'republication_tracker_tool_republish_article_markup', $republish_content, $post_object ); + +// Generate plain text content if the feature is enabled. +$plain_text_enabled = Republication_Tracker_Tool_Settings::is_plain_text_enabled(); +$republish_plain_text_content = ''; +if ( $plain_text_enabled ) { + $republish_plain_text_content = Republication_Tracker_Tool_Content::get_republishable_plain_text_content( $post_object ); + $additional_tracking_html = Republication_Tracker_Tool::create_additional_tracking_code_markup( $post_object->ID ); +} ?>
@@ -162,21 +182,23 @@
-
- %2$s.', 'republication-tracker-tool' ), - REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['url'], - REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['description'], + +
+ %2$s.', 'republication-tracker-tool' ), + REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['url'], + REPUBLICATION_TRACKER_TOOL_LICENSES[ $license_key ]['description'], + ) ) - ) - ); - ?> -
+ ); + ?> +
+
@@ -185,8 +207,84 @@
+ + +
+ + + +
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+
+ +
+ +
diff --git a/tests/test-sample.php b/tests/test-sample.php deleted file mode 100644 index c666e10..0000000 --- a/tests/test-sample.php +++ /dev/null @@ -1,20 +0,0 @@ -assertTrue( true ); - } -} diff --git a/tests/test-widget.php b/tests/test-widget.php new file mode 100644 index 0000000..612400a --- /dev/null +++ b/tests/test-widget.php @@ -0,0 +1,164 @@ +widget = new Republication_Tracker_Tool_Widget(); + + $this->test_post = $this->factory->post->create_and_get( array( + 'post_title' => 'Test Post for Widget', + 'post_content' => '

Test content for widget display.

', + 'post_status' => 'publish', + ) ); + } + + /** + * Clean up after tests. + */ + public function tear_down() { + wp_delete_post( $this->test_post->ID, true ); + parent::tear_down(); + } + + /** + * Test widget displays on single posts. + */ + function test_widget_displays_on_single_post() { + global $post, $wp_query; + + $post = $this->test_post; + $wp_query->is_single = true; + $wp_query->queried_object = $this->test_post; + $wp_query->queried_object_id = $this->test_post->ID; + + $args = array( + 'before_widget' => '
', + 'after_widget' => '
', + 'before_title' => '

', + 'after_title' => '

', + ); + + $instance = array( + 'title' => 'Republish This Story', + 'text' => 'Test widget text', + ); + + ob_start(); + $this->widget->widget( $args, $instance ); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'Republish This Story', $output ); + $this->assertStringContainsString( 'Test widget text', $output ); + + $this->assertStringStartsWith( $args['before_widget'], $output ); + $this->assertStringContainsString( $args['after_widget'], $output ); + $this->assertStringContainsString( $args['before_title'], $output ); + $this->assertStringContainsString( $args['after_title'], $output ); + $this->assertStringContainsString( 'republication-tracker-tool-button', $output ); + $this->assertStringContainsString( 'republication-tracker-tool-modal', $output ); + } + + /** + * Test widget respects hide widget meta. + */ + function test_widget_respects_hide_meta() { + global $post, $wp_query; + + $post = $this->test_post; + $wp_query->is_single = true; + $wp_query->queried_object = $this->test_post; + $wp_query->queried_object_id = $this->test_post->ID; + + update_post_meta( $this->test_post->ID, 'republication-tracker-tool-hide-widget', true ); + + ob_start(); + $this->widget->widget( array(), array() ); + $output = ob_get_clean(); + + $this->assertEmpty( $output ); + + delete_post_meta( $this->test_post->ID, 'republication-tracker-tool-hide-widget' ); + } + + /** + * Test widget does not display on hide_republication_widget filter set to true. + */ + function test_widget_does_not_display_on_hide_filter() { + global $post, $wp_query; + $post = $this->test_post; + $wp_query->is_single = true; + $wp_query->queried_object = $this->test_post; + $wp_query->queried_object_id = $this->test_post->ID; + + add_filter( 'hide_republication_widget', '__return_true' ); + + ob_start(); + $this->widget->widget( array(), array() ); + $output = ob_get_clean(); + + $this->assertEmpty( $output ); + remove_filter( 'hide_republication_widget', '__return_true' ); + } + + /** + * Test even if multiple instances of the widget are created, it should only contain one modal. + */ + function test_multiple_widget_instances() { + global $post, $wp_query; + $post = $this->test_post; + $wp_query->is_single = true; + $wp_query->queried_object = $this->test_post; + $wp_query->queried_object_id = $this->test_post->ID; + + $instance = array( + 'title' => 'Republish This Story', + 'text' => 'Test widget text', + ); + + $args = array( + 'before_widget' => '
', + 'after_widget' => '
', + 'before_title' => '

', + 'after_title' => '

', + ); + + ob_start(); + $this->widget->widget( $args, $instance ); + $this->widget->widget( $args, $instance ); + $this->widget->widget( $args, $instance ); + $output = ob_get_clean(); + $this->assertStringContainsString( 'republication-tracker-tool-modal', $output ); + $this->assertStringContainsString( 'republication-tracker-tool-button', $output ); + + // Check only one modal is present + $this->assertEquals( substr_count( $output, 'republication-tracker-tool-modal' ), 1, 'Single modal found in output.' ); + $this->assertEquals( substr_count( $output, 'republication-tracker-tool-button' ), 3, 'Multiple buttons found in output.' ); + } +}