diff --git a/.phpunit.result.cache b/.phpunit.result.cache new file mode 100644 index 0000000..c365b02 --- /dev/null +++ b/.phpunit.result.cache @@ -0,0 +1 @@ +{"version":1,"defects":{"SettingsManagerTest::test_logs_retention_days_negative_becomes_zero":3,"ProviderRequestBuilderTest::test_openai_request_payload_respects_settings":4,"ProviderRequestBuilderTest::test_groq_request_payload_uses_default_model_when_missing":4,"ProviderRequestBuilderTest::test_google_request_payload_builds_schema_and_images":4,"TermSaveTest::test_save_term_generation_result_saves_descriptions_and_filtered_meta_key":4},"times":{"ModelExclusionsTest::test_ensure_allowed_blocks_excluded_model":0.002,"ModelExclusionsTest::test_filter_models_removes_excluded_entries":0,"SettingsManagerTest::test_logs_retention_days_sanitized_and_capped":0,"SettingsManagerTest::test_logs_retention_days_allows_zero":0,"SettingsManagerTest::test_logs_retention_days_negative_becomes_zero":0,"SettingsManagerTest::test_sanitize_accepts_all_settings_keys":0,"ProviderRequestBuilderTest::test_openai_request_payload_respects_settings":0,"ProviderRequestBuilderTest::test_groq_request_payload_uses_default_model_when_missing":0,"ProviderRequestBuilderTest::test_google_request_payload_builds_schema_and_images":0,"TermSaveTest::test_save_term_generation_result_saves_descriptions_and_filtered_meta_key":0}} \ No newline at end of file diff --git a/assets/js/admin.js b/assets/js/admin.js index 4ca64a8..1b9786f 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -96,8 +96,35 @@ statusField.setAttribute('data-status', type); } - const loadingText = window.wp && wp.i18n ? wp.i18n.__('AI is bezig met schrijven...', 'siti-ai-product-content-generator') : 'AI is bezig met schrijven...'; - const retryText = window.wp && wp.i18n ? wp.i18n.__('Probeer het opnieuw of pas je prompt/context aan.', 'siti-ai-product-content-generator') : 'Probeer het opnieuw of pas je prompt/context aan.'; + const localized = (window.GroqAIGenerator && GroqAIGenerator.strings) || {}; + const loadingText = localized.loading || (window.wp && wp.i18n ? wp.i18n.__('AI is bezig met schrijven...', 'siti-ai-product-content-generator') : 'AI is bezig met schrijven...'); + const retryText = localized.retry || (window.wp && wp.i18n ? wp.i18n.__('Probeer het opnieuw of pas je prompt/context aan.', 'siti-ai-product-content-generator') : 'Probeer het opnieuw of pas je prompt/context aan.'); + const errorDefaultText = localized.errorDefault || (window.wp && wp.i18n ? wp.i18n.__('Er ging iets mis bij het genereren.', 'siti-ai-product-content-generator') : 'Er ging iets mis bij het genereren.'); + const errorUnknownText = localized.errorUnknown || (window.wp && wp.i18n ? wp.i18n.__('Onbekende fout.', 'siti-ai-product-content-generator') : 'Onbekende fout.'); + const successText = localized.success || (window.wp && wp.i18n ? wp.i18n.__('Structuur gegenereerd. Kopieer of vul velden in.', 'siti-ai-product-content-generator') : 'Structuur gegenereerd. Kopieer of vul velden in.'); + const fieldAppliedText = localized.fieldApplied || (window.wp && wp.i18n ? wp.i18n.__('%s ingevuld.', 'siti-ai-product-content-generator') : '%s ingevuld.'); + const fieldApplyErrorText = localized.fieldApplyError || (window.wp && wp.i18n ? wp.i18n.__('Kon het veld niet automatisch invullen.', 'siti-ai-product-content-generator') : 'Kon het veld niet automatisch invullen.'); + const fieldCopiedText = localized.fieldCopied || (window.wp && wp.i18n ? wp.i18n.__('%s gekopieerd naar het klembord.', 'siti-ai-product-content-generator') : '%s gekopieerd naar het klembord.'); + const jsonCopiedText = localized.jsonCopied || (window.wp && wp.i18n ? wp.i18n.__('JSON gekopieerd naar het klembord.', 'siti-ai-product-content-generator') : 'JSON gekopieerd naar het klembord.'); + const copyFailedText = localized.copyFailed || (window.wp && wp.i18n ? wp.i18n.__('Kopiëren mislukt.', 'siti-ai-product-content-generator') : 'Kopiëren mislukt.'); + + function formatString(template, values) { + if (!template) { + return ''; + } + let autoIndex = 0; + return template.replace(/%(\d+\$)?s/g, (match, position) => { + let valueIndex; + if (position) { + valueIndex = parseInt(position, 10) - 1; + } else { + valueIndex = autoIndex; + autoIndex += 1; + } + const replacement = values[valueIndex]; + return typeof replacement === 'undefined' ? '' : String(replacement); + }); + } function toggleLoading(isLoading) { modal.classList.toggle('is-loading', isLoading); @@ -137,7 +164,7 @@ .then((response) => response.json()) .then((json) => { if (!json.success) { - const errorMessage = json.data && json.data.message ? json.data.message : 'Onbekende fout'; + const errorMessage = json.data && json.data.message ? json.data.message : errorUnknownText; throw new Error(errorMessage); } @@ -154,13 +181,12 @@ if (jsonCopyButton) { jsonCopyButton.disabled = false; } - setStatus('Structuur gegenereerd. Kopieer of vul velden in.', 'success'); + setStatus(successText, 'success'); }) .catch((error) => { - const message = error && error.message ? error.message : 'Er ging iets mis bij het genereren.'; - setStatus(loadingText, 'error'); - const fullMessage = `${loadingText} ${message}. ${retryText}`; - statusField.textContent = fullMessage; + const message = error && error.message ? error.message : errorDefaultText; + const fullMessage = `${errorDefaultText} ${message}. ${retryText}`; + setStatus(fullMessage, 'error'); }) .finally(() => { toggleLoading(false); @@ -308,10 +334,10 @@ } if (applied) { - setStatus(entry.label + ' ingevuld.', 'success'); + setStatus(formatString(fieldAppliedText, [entry.label]), 'success'); setFieldStatus(fieldKey, 'success'); } else { - setStatus('Kon het veld niet automatisch invullen.', 'error'); + setStatus(fieldApplyErrorText, 'error'); setFieldStatus(fieldKey, 'error'); } } @@ -340,10 +366,10 @@ } copyToClipboard(entry.textarea.value) .then(() => { - setStatus(entry.label + ' gekopieerd naar het klembord.', 'success'); + setStatus(formatString(fieldCopiedText, [entry.label]), 'success'); }) .catch(() => { - setStatus('Kopiëren mislukt.', 'error'); + setStatus(copyFailedText, 'error'); }); } @@ -358,10 +384,10 @@ const text = resultField ? resultField.textContent.trim() : ''; copyToClipboard(text) .then(() => { - setStatus('JSON gekopieerd naar het klembord.', 'success'); + setStatus(jsonCopiedText, 'success'); }) .catch(() => { - setStatus('Kopiëren mislukt.', 'error'); + setStatus(copyFailedText, 'error'); }); }); } diff --git a/assets/js/settings.js b/assets/js/settings.js index 628b82f..a74ef5f 100644 --- a/assets/js/settings.js +++ b/assets/js/settings.js @@ -14,6 +14,14 @@ return; } + const strings = data.strings || {}; + const providerUnsupportedText = strings.providerUnsupported || 'Deze aanbieder ondersteunt dit niet.'; + const apiKeyRequiredText = strings.apiKeyRequired || 'Vul eerst de API-sleutel in.'; + const loadingModelsText = strings.loadingModels || 'Modellen worden opgehaald…'; + const errorUnknownText = strings.errorUnknown || 'Onbekende fout'; + const successModelsText = strings.successModels || 'Modellen bijgewerkt.'; + const errorFetchText = strings.errorFetch || 'Ophalen mislukt.'; + const providerSelect = document.querySelector('select[name="' + optionKey + '[provider]"]'); const modelSelect = document.getElementById('groq-ai-model-select'); const refreshButton = document.getElementById('groq-ai-refresh-models'); @@ -164,19 +172,19 @@ const provider = providerSelect ? providerSelect.value : data.currentProvider; const providerData = data.providers && data.providers[provider] ? data.providers[provider] : null; if (!providerData || !providerData.supports_live) { - setRefreshStatus('Deze aanbieder ondersteunt dit niet.', 'error'); + setRefreshStatus(providerUnsupportedText, 'error'); return; } const keyField = document.querySelector('[data-provider-row="' + provider + '"] input'); const apiKey = keyField ? keyField.value.trim() : ''; if (!apiKey) { - setRefreshStatus('Vul eerst de API-sleutel in.', 'error'); + setRefreshStatus(apiKeyRequiredText, 'error'); return; } refreshButton.disabled = true; - setRefreshStatus('Modellen worden opgehaald…', 'loading'); + setRefreshStatus(loadingModelsText, 'loading'); const payload = new URLSearchParams(); payload.append('action', 'groq_ai_refresh_models'); @@ -194,14 +202,14 @@ .then((response) => response.json()) .then((json) => { if (!json.success || !json.data || !Array.isArray(json.data.models)) { - throw new Error((json.data && json.data.message) || 'Onbekende fout'); + throw new Error((json.data && json.data.message) || errorUnknownText); } data.providers[provider].models = json.data.models; buildModelOptions(); - setRefreshStatus('Modellen bijgewerkt.', 'success'); + setRefreshStatus(successModelsText, 'success'); }) .catch((error) => { - setRefreshStatus(error.message || 'Ophalen mislukt.', 'error'); + setRefreshStatus(error.message || errorFetchText, 'error'); }) .finally(() => { refreshButton.disabled = false; diff --git a/assets/js/term-admin.js b/assets/js/term-admin.js index ee4bfa8..56c6c67 100644 --- a/assets/js/term-admin.js +++ b/assets/js/term-admin.js @@ -20,6 +20,14 @@ const includeTopProducts = document.getElementById('groq-ai-term-include-top-products'); const topProductsLimit = document.getElementById('groq-ai-term-top-products-limit'); + const strings = (window.GroqAITermGenerator && GroqAITermGenerator.strings) || {}; + const promptRequiredText = strings.promptRequired || 'Vul eerst een prompt in.'; + const loadingText = strings.loading || 'AI is bezig met schrijven...'; + const successText = strings.success || 'Tekst gegenereerd. Je kunt hem toepassen en opslaan.'; + const applySuccessText = strings.applySuccess || 'Tekst ingevuld. Vergeet niet op "Opslaan" te klikken.'; + const errorDefaultText = strings.errorDefault || 'Er ging iets mis bij het genereren.'; + const errorUnknownText = strings.errorUnknown || 'Onbekende fout'; + function setStatus(message, type) { if (!statusField) { return; @@ -75,7 +83,7 @@ rankmathKeywordsField.value = outputFocusKeywordsField.value || ''; } - setStatus('Tekst ingevuld. Vergeet niet op "Opslaan" te klikken.', 'success'); + setStatus(applySuccessText, 'success'); }); } @@ -83,12 +91,12 @@ event.preventDefault(); const prompt = promptField ? (promptField.value || '').trim() : ''; if (!prompt) { - setStatus('Vul eerst een prompt in.', 'error'); + setStatus(promptRequiredText, 'error'); return; } setLoading(true); - setStatus('AI is bezig met schrijven...', 'loading'); + setStatus(loadingText, 'loading'); if (rawField) { rawField.textContent = ''; } @@ -109,7 +117,7 @@ .then((response) => response.json()) .then((json) => { if (!json.success) { - const errorMessage = json.data && json.data.message ? json.data.message : 'Onbekende fout'; + const errorMessage = json.data && json.data.message ? json.data.message : errorUnknownText; throw new Error(errorMessage); } @@ -137,10 +145,10 @@ rawField.textContent = (json.data && json.data.raw ? String(json.data.raw) : '').trim(); } - setStatus('Tekst gegenereerd. Je kunt hem toepassen en opslaan.', 'success'); + setStatus(successText, 'success'); }) .catch((error) => { - setStatus(error && error.message ? error.message : 'Er ging iets mis bij het genereren.', 'error'); + setStatus(error && error.message ? error.message : errorDefaultText, 'error'); }) .finally(() => { setLoading(false); diff --git a/assets/js/term-bulk.js b/assets/js/term-bulk.js index 390d3f5..c2cacfb 100644 --- a/assets/js/term-bulk.js +++ b/assets/js/term-bulk.js @@ -11,6 +11,13 @@ const strings = data.strings || {}; const allowRegenerate = !!data.allowRegenerate; + const unknownErrorText = strings.unknownError || 'Onbekende fout'; + const unknownTermText = strings.unknownTerm || 'Onbekende term.'; + const confirmStopFallbackText = strings.confirmStopFallback || 'Stoppen?'; + const logErrorDefaultText = strings.logErrorDefault || '%1$s: %2$s'; + const logSuccessDefaultText = strings.logSuccessDefault || '%1$s gevuld.'; + const regenerateErrorDefaultText = strings.regenerateErrorDefault || '%1$s mislukt: %2$s'; + const regenerateDoneDefaultText = strings.regenerateDoneDefault || '%s is bijgewerkt.'; const terms = (Array.isArray(data.terms) ? data.terms : []) .map((term) => { const id = parseInt(term.id, 10); @@ -146,19 +153,19 @@ function handleResponse(term, json, context) { if (!json || !json.success) { - const errorMessage = (json && json.data && json.data.message) || 'Onbekende fout'; - appendLog(formatString(strings.logError || '%1$s: %2$s', [term.name || term.id, errorMessage]), 'error'); + const errorMessage = (json && json.data && json.data.message) || unknownErrorText; + appendLog(formatString(strings.logError || logErrorDefaultText, [term.name || term.id, errorMessage]), 'error'); if (context === 'single') { - setStatus(formatString(strings.regenerateError || '%1$s mislukt: %2$s', [term.name || term.id, errorMessage]), 'error'); + setStatus(formatString(strings.regenerateError || regenerateErrorDefaultText, [term.name || term.id, errorMessage]), 'error'); } return false; } const words = json.data && typeof json.data.words !== 'undefined' ? parseInt(json.data.words, 10) : term.words; markTermCompleted(term, Number.isFinite(words) ? words : term.words); - appendLog(formatString(strings.logSuccess || '%1$s gevuld.', [term.name || term.id, term.words]), 'success'); + appendLog(formatString(strings.logSuccess || logSuccessDefaultText, [term.name || term.id, term.words]), 'success'); if (context === 'single') { - setStatus(formatString(strings.regenerateDone || '%s is bijgewerkt.', [term.name || term.id]), 'success'); + setStatus(formatString(strings.regenerateDone || regenerateDoneDefaultText, [term.name || term.id]), 'success'); } return true; } @@ -233,7 +240,7 @@ if (!isRunning) { return; } - const confirmation = strings.confirmStop ? window.confirm(strings.confirmStop) : window.confirm('Stoppen?'); + const confirmation = strings.confirmStop ? window.confirm(strings.confirmStop) : window.confirm(confirmStopFallbackText); if (confirmation) { abortRequested = true; } @@ -251,7 +258,7 @@ const termId = parseInt(button.getAttribute('data-term-id'), 10); const term = termMap.get(termId); if (!term) { - setStatus('Onbekende term.', 'error'); + setStatus(unknownTermText, 'error'); return; } if (strings.confirmRegenerate) { @@ -270,9 +277,9 @@ handleResponse(term, json, 'single'); }) .catch((error) => { - const message = error && error.message ? error.message : 'Onbekende fout'; - appendLog(formatString(strings.logError || '%1$s: %2$s', [term.name || term.id, message]), 'error'); - setStatus(formatString(strings.regenerateError || '%1$s mislukt: %2$s', [term.name || term.id, message]), 'error'); + const message = error && error.message ? error.message : unknownErrorText; + appendLog(formatString(strings.logError || logErrorDefaultText, [term.name || term.id, message]), 'error'); + setStatus(formatString(strings.regenerateError || regenerateErrorDefaultText, [term.name || term.id, message]), 'error'); }) .finally(() => { button.disabled = false; diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..c2fb1a9 --- /dev/null +++ b/composer.json @@ -0,0 +1,16 @@ +{ + "name": "sitiweb/siti-ai-product-content-generator", + "description": "SitiAI Product Teksten WordPress plugin", + "type": "wordpress-plugin", + "require-dev": { + "phpunit/phpunit": "^9.6" + }, + "autoload-dev": { + "classmap": [ + "includes/" + ] + }, + "scripts": { + "test": "phpunit" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..7f66cee --- /dev/null +++ b/composer.lock @@ -0,0 +1,1815 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "589964c1d168d0193d3244292a056025", + "packages": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/23da848e1a2308728fe5fdddabf4be17ff9720c7", + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7", + "shasum": "" + }, + "require": { + "php": "^8.4" + }, + "require-dev": { + "doctrine/coding-standard": "^14", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5.58" + }, + "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.1.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": "2026-01-05T06:47:08+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "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.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "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.x-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.7.0" + }, + "time": "2025-12-06T11:56:16+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": "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.34", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "b36f02317466907a230d3aa1d34467041271ef4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a", + "reference": "b36f02317466907a230d3aa1d34467041271ef4a", + "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.4", + "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.10", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", + "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.34" + }, + "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": "2026-01-27T05:45: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.10", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d", + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d", + "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.10" + }, + "funding": [ + { + "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/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-01-24T09:22:56+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.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "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.8" + }, + "funding": [ + { + "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/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:03:27+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "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.8" + }, + "funding": [ + { + "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/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2025-08-10T07:10:35+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.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "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": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" + }, + "funding": [ + { + "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/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-10T06:57:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-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 types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.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": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "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.3.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-11-17T20:03:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/groq-ai-product-text.php b/groq-ai-product-text.php index 3f38034..e6d9bae 100644 --- a/groq-ai-product-text.php +++ b/groq-ai-product-text.php @@ -2,8 +2,9 @@ /** * Plugin Name: SitiAI Product Teksten * Description: Genereer productteksten met diverse AI-aanbieders rechtstreeks vanuit WooCommerce. - * Version: 1.8.0 - * Author: SitiAI + * Version: 1.9.0 + * Author: Roberto Guagliardo | SitiWeb + * Author URI: https://sitiweb.nl/ * Text Domain: siti-ai-product-content-generator * Domain Path: /languages */ @@ -44,6 +45,9 @@ if ( ! defined( 'GROQ_AI_DEBUG_TRACE_ADDED' ) && defined( 'WP_DEBUG' ) && WP_DEB require_once __DIR__ . '/includes/Core/class-groq-ai-service-container.php'; require_once __DIR__ . '/includes/Core/class-groq-ai-model-exclusions.php'; require_once __DIR__ . '/includes/Core/class-groq-ai-ajax-controller.php'; +require_once __DIR__ . '/includes/Core/class-groq-ai-compatibility-service.php'; +require_once __DIR__ . '/includes/Core/class-groq-ai-model-service.php'; +require_once __DIR__ . '/includes/Core/class-groq-ai-log-scheduler.php'; require_once __DIR__ . '/includes/Contracts/interface-groq-ai-provider.php'; require_once __DIR__ . '/includes/Providers/class-groq-ai-abstract-openai-provider.php'; require_once __DIR__ . '/includes/Providers/class-groq-ai-provider-groq.php'; @@ -106,8 +110,14 @@ final class Groq_AI_Product_Text_Plugin { /** @var Groq_AI_Product_Text_Product_UI */ private $product_ui; - /** @var bool */ - private $missing_wc_notice = false; + /** @var Groq_AI_Compatibility_Service */ + private $compatibility_service; + + /** @var Groq_AI_Model_Service */ + private $model_service; + + /** @var Groq_AI_Log_Scheduler */ + private $log_scheduler; public static function instance() { if ( null === self::$instance ) { @@ -119,6 +129,9 @@ final class Groq_AI_Product_Text_Plugin { private function __construct() { $this->register_services(); + $this->compatibility_service = new Groq_AI_Compatibility_Service(); + $this->model_service = new Groq_AI_Model_Service(); + $this->log_scheduler = new Groq_AI_Log_Scheduler( $this->get_settings_manager(), $this->get_generation_logger() ); $this->settings_page = new Groq_AI_Product_Text_Settings_Page( $this, $this->get_provider_manager() ); $this->categories_admin = new Groq_AI_Categories_Admin( $this ); @@ -127,8 +140,11 @@ final class Groq_AI_Product_Text_Plugin { $this->product_ui = new Groq_AI_Product_Text_Product_UI( $this ); add_action( 'init', [ $this, 'load_textdomain' ] ); - add_action( 'plugins_loaded', [ $this, 'maybe_create_logs_table' ] ); - add_action( 'load-plugins.php', [ $this, 'maybe_deactivate_if_woocommerce_missing' ] ); + $logger = $this->container->get( 'generation_logger' ); + add_action( 'plugins_loaded', [ $logger, 'maybe_create_table' ] ); + add_action( 'load-plugins.php', [ $this->compatibility_service, 'maybe_deactivate_if_woocommerce_missing' ] ); + add_action( 'init', [ $this->log_scheduler, 'ensure_logs_cleanup_schedule' ] ); + add_action( 'groq_ai_cleanup_logs', [ $this->log_scheduler, 'cleanup_logs' ] ); add_filter( 'groq_ai_term_google_context', [ $this, 'inject_google_term_context' ], 10, 3 ); } @@ -238,60 +254,84 @@ final class Groq_AI_Product_Text_Plugin { return self::OPTION_KEY; } - public function get_provider_manager() { - return $this->container->get( 'provider_manager' ); - } - - public function get_settings_manager() { - return $this->container->get( 'settings_manager' ); - } - - public function get_prompt_builder() { - return $this->container->get( 'prompt_builder' ); - } - - public function get_conversation_manager() { - return $this->container->get( 'conversation_manager' ); - } - - public function get_generation_logger() { - return $this->container->get( 'generation_logger' ); - } - - public function get_settings() { - return $this->get_settings_manager()->all(); - } - - public function sanitize_settings( $input ) { - return $this->get_settings_manager()->sanitize( $input ); - } - - public function maybe_deactivate_if_woocommerce_missing() { - if ( $this->is_woocommerce_active() ) { - return; + public function __call( $name, $arguments ) { + switch ( $name ) { + case 'get_provider_manager': + return $this->container->get( 'provider_manager' ); + case 'get_settings_manager': + return $this->container->get( 'settings_manager' ); + case 'get_prompt_builder': + return $this->container->get( 'prompt_builder' ); + case 'get_conversation_manager': + return $this->container->get( 'conversation_manager' ); + case 'get_generation_logger': + return $this->container->get( 'generation_logger' ); + case 'get_settings': + return $this->container->get( 'settings_manager' )->all(); + case 'sanitize_settings': + return $this->container->get( 'settings_manager' )->sanitize( $arguments[0] ?? [] ); + case 'get_context_field_definitions': + return $this->container->get( 'settings_manager' )->get_context_field_definitions(); + case 'get_default_modules_settings': + return $this->container->get( 'settings_manager' )->get_default_modules_settings(); + case 'get_default_context_fields': + return $this->container->get( 'settings_manager' )->get_default_context_fields(); + case 'normalize_context_fields': + return $this->container->get( 'settings_manager' )->normalize_context_fields( $arguments[0] ?? [] ); + case 'get_module_config': + return $this->container->get( 'settings_manager' )->get_module_config( $arguments[0] ?? '', $arguments[1] ?? null ); + case 'is_module_enabled': + return $this->container->get( 'settings_manager' )->is_module_enabled( $arguments[0] ?? '', $arguments[1] ?? null ); + case 'get_rankmath_focus_keyword_limit': + return $this->container->get( 'settings_manager' )->get_rankmath_focus_keyword_limit( $arguments[0] ?? null ); + case 'get_rankmath_meta_title_pixel_limit': + return $this->container->get( 'settings_manager' )->get_rankmath_meta_title_pixel_limit( $arguments[0] ?? null ); + case 'get_rankmath_meta_description_pixel_limit': + return $this->container->get( 'settings_manager' )->get_rankmath_meta_description_pixel_limit( $arguments[0] ?? null ); + case 'is_response_format_compat_enabled': + return $this->container->get( 'settings_manager' )->is_response_format_compat_enabled( $arguments[0] ?? null ); + case 'get_image_context_mode': + return $this->container->get( 'settings_manager' )->get_image_context_mode( $arguments[0] ?? null ); + case 'get_image_context_limit': + return $this->container->get( 'settings_manager' )->get_image_context_limit( $arguments[0] ?? null ); + case 'get_term_top_description_char_limit': + return $this->container->get( 'settings_manager' )->get_term_top_description_char_limit( $arguments[0] ?? null ); + case 'get_term_bottom_description_char_limit': + return $this->container->get( 'settings_manager' )->get_term_bottom_description_char_limit( $arguments[0] ?? null ); + case 'get_google_safety_settings': + return $this->container->get( 'settings_manager' )->get_google_safety_settings( $arguments[0] ?? null ); + case 'get_google_safety_categories': + return $this->container->get( 'settings_manager' )->get_google_safety_categories(); + case 'get_google_safety_thresholds': + return $this->container->get( 'settings_manager' )->get_google_safety_thresholds(); + case 'get_loggable_settings_snapshot': + return $this->container->get( 'settings_manager' )->get_loggable_settings_snapshot( $arguments[0] ?? null ); + case 'create_settings_renderer': + $values = $arguments[0] ?? null; + if ( null === $values ) { + $values = $this->container->get( 'settings_manager' )->all(); + } + return new Groq_AI_Settings_Renderer( self::OPTION_KEY, $values ); + case 'should_use_response_format': + $provider = $arguments[0] ?? null; + $settings = $arguments[1] ?? null; + if ( ! $provider instanceof Groq_AI_Provider_Interface ) { + return false; + } + return ! $this->container->get( 'settings_manager' )->is_response_format_compat_enabled( $settings ) && $provider->supports_response_format(); + case 'is_rankmath_active': + return $this->compatibility_service->is_rankmath_active(); + case 'is_woocommerce_active': + return $this->compatibility_service->is_woocommerce_active(); + case 'get_selected_model': + return $this->model_service->get_selected_model( $arguments[0], $arguments[1] ?? [] ); + case 'get_cached_models_for_provider': + return $this->model_service->get_cached_models_for_provider( $arguments[0] ?? '' ); + case 'update_cached_models_for_provider': + return $this->model_service->update_cached_models_for_provider( $arguments[0] ?? '', $arguments[1] ?? [] ); } - if ( ! function_exists( 'deactivate_plugins' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - } - - deactivate_plugins( plugin_basename( GROQ_AI_PRODUCT_TEXT_FILE ) ); - $this->missing_wc_notice = true; - - add_action( 'admin_notices', [ $this, 'render_missing_wc_notice' ] ); - } - - public function render_missing_wc_notice() { - if ( ! $this->missing_wc_notice ) { - return; - } - ?> -
- -
-- +
@@ -155,12 +145,7 @@ class Groq_AI_Product_Text_Settings_Page extends Groq_AI_Admin_Base {
- -+ +
+Dit is een test.
', + 'bottom_description' => 'Onderste tekst.
', + ]; + + $settings = $plugin->get_settings(); + + $method = $controller_ref->getMethod( 'save_term_generation_result' ); + $method->setAccessible( true ); + $saved = $method->invoke( $controller, $term, $result, $settings ); + + $this->assertIsArray( $saved ); + $this->assertSame( 4, $saved['words'] ); + + $this->assertCount( 1, $GLOBALS['wp_term_updates'] ); + $this->assertSame( 12, $GLOBALS['wp_term_updates'][0]['term_id'] ); + $this->assertSame( 'product_cat', $GLOBALS['wp_term_updates'][0]['taxonomy'] ); + $this->assertSame( 'Dit is een test.
', $GLOBALS['wp_term_updates'][0]['args']['description'] ); + + $this->assertArrayHasKey( 12, $GLOBALS['wp_term_meta_updates'] ); + $this->assertArrayHasKey( 'customkey', $GLOBALS['wp_term_meta_updates'][12] ); + $this->assertSame( 'Onderste tekst.
', $GLOBALS['wp_term_meta_updates'][12]['customkey'] ); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..b404e14 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,258 @@ +message = (string) $message; + } + + public function get_error_message() { + return $this->message; + } + } +} + +if ( ! function_exists( 'is_wp_error' ) ) { + function is_wp_error( $thing ) { + return $thing instanceof WP_Error; + } +} + +if ( ! function_exists( '__' ) ) { + function __( $text ) { + return $text; + } +} + +if ( ! function_exists( 'wp_parse_args' ) ) { + function wp_parse_args( $args, $defaults = [] ) { + if ( is_object( $args ) ) { + $args = get_object_vars( $args ); + } + if ( ! is_array( $args ) ) { + $args = []; + } + return array_merge( $defaults, $args ); + } +} + +if ( ! function_exists( 'sanitize_text_field' ) ) { + function sanitize_text_field( $text ) { + return trim( (string) $text ); + } +} + +if ( ! function_exists( 'sanitize_textarea_field' ) ) { + function sanitize_textarea_field( $text ) { + return trim( (string) $text ); + } +} + +if ( ! function_exists( 'sanitize_key' ) ) { + function sanitize_key( $key ) { + $key = strtolower( (string) $key ); + return preg_replace( '/[^a-z0-9_\-]/', '', $key ); + } +} + +if ( ! function_exists( 'absint' ) ) { + function absint( $number ) { + return abs( (int) $number ); + } +} + +if ( ! function_exists( 'esc_url_raw' ) ) { + function esc_url_raw( $url ) { + return (string) $url; + } +} + +if ( ! function_exists( 'add_filter' ) ) { + function add_filter( $tag, $callback, $priority = 10, $accepted_args = 1 ) { + $GLOBALS['wp_filters'][ $tag ][ $priority ][] = [ + 'callback' => $callback, + 'accepted_args' => (int) $accepted_args, + ]; + } +} + +if ( ! function_exists( 'apply_filters' ) ) { + function apply_filters( $tag, $value ) { + $args = func_get_args(); + if ( empty( $GLOBALS['wp_filters'][ $tag ] ) ) { + return $value; + } + ksort( $GLOBALS['wp_filters'][ $tag ] ); + foreach ( $GLOBALS['wp_filters'][ $tag ] as $callbacks ) { + foreach ( $callbacks as $filter ) { + $accepted = isset( $filter['accepted_args'] ) ? (int) $filter['accepted_args'] : 1; + $call_args = array_slice( $args, 0, max( 1, $accepted ) ); + $call_args[0] = $value; + $value = call_user_func_array( $filter['callback'], $call_args ); + $args[0] = $value; + } + } + return $value; + } +} + +if ( ! function_exists( 'wp_kses_post' ) ) { + function wp_kses_post( $content ) { + return (string) $content; + } +} + +if ( ! function_exists( 'wp_strip_all_tags' ) ) { + function wp_strip_all_tags( $text ) { + return strip_tags( (string) $text ); + } +} + +if ( ! function_exists( 'wp_update_term' ) ) { + function wp_update_term( $term_id, $taxonomy, $args = [] ) { + $GLOBALS['wp_term_updates'][] = [ + 'term_id' => (int) $term_id, + 'taxonomy' => (string) $taxonomy, + 'args' => $args, + ]; + return [ 'term_id' => (int) $term_id ]; + } +} + +if ( ! function_exists( 'update_term_meta' ) ) { + function update_term_meta( $term_id, $meta_key, $meta_value ) { + $term_id = (int) $term_id; + if ( ! isset( $GLOBALS['wp_term_meta_updates'][ $term_id ] ) ) { + $GLOBALS['wp_term_meta_updates'][ $term_id ] = []; + } + $GLOBALS['wp_term_meta_updates'][ $term_id ][ (string) $meta_key ] = $meta_value; + return true; + } +} + +if ( ! function_exists( 'wp_json_encode' ) ) { + function wp_json_encode( $data, $options = 0, $depth = 512 ) { + return json_encode( $data, $options, $depth ); + } +} + +if ( ! function_exists( 'add_query_arg' ) ) { + function add_query_arg( $args, $url = '' ) { + if ( is_string( $args ) ) { + return $url; + } + $query = http_build_query( (array) $args ); + $separator = strpos( $url, '?' ) === false ? '?' : '&'; + return $url . $separator . $query; + } +} + +if ( ! function_exists( 'wp_remote_post' ) ) { + function wp_remote_post( $url, $args = [] ) { + $GLOBALS['wp_last_http_request'] = [ + 'url' => $url, + 'args' => $args, + ]; + + $body = json_encode( + [ + 'choices' => [ + [ + 'message' => [ + 'content' => 'ok', + ], + 'finish_reason' => 'stop', + ], + ], + 'usage' => [ + 'prompt_tokens' => 10, + 'completion_tokens' => 20, + 'total_tokens' => 30, + ], + 'candidates' => [ + [ + 'content' => [ + 'parts' => [ + [ 'text' => 'ok' ], + ], + ], + 'finishReason' => 'STOP', + ], + ], + 'usageMetadata' => [ + 'promptTokenCount' => 10, + 'candidatesTokenCount' => 20, + 'totalTokenCount' => 30, + ], + ] + ); + + return [ + 'body' => $body, + 'response' => [ 'code' => 200 ], + ]; + } +} + +if ( ! function_exists( 'wp_remote_get' ) ) { + function wp_remote_get( $url, $args = [] ) { + $GLOBALS['wp_last_http_request'] = [ + 'url' => $url, + 'args' => $args, + ]; + + return [ + 'body' => json_encode( [ 'data' => [], 'models' => [] ] ), + 'response' => [ 'code' => 200 ], + ]; + } +} + +if ( ! function_exists( 'wp_remote_retrieve_body' ) ) { + function wp_remote_retrieve_body( $response ) { + return isset( $response['body'] ) ? $response['body'] : ''; + } +} + +if ( ! function_exists( 'wp_remote_retrieve_response_code' ) ) { + function wp_remote_retrieve_response_code( $response ) { + return isset( $response['response']['code'] ) ? (int) $response['response']['code'] : 0; + } +} + +if ( ! function_exists( 'get_option' ) ) { + function get_option( $key, $default = false ) { + return isset( $GLOBALS['wp_options'][ $key ] ) ? $GLOBALS['wp_options'][ $key ] : $default; + } +} + +if ( ! function_exists( 'update_option' ) ) { + function update_option( $key, $value ) { + $GLOBALS['wp_options'][ $key ] = $value; + return true; + } +} + +require_once __DIR__ . '/../includes/Core/class-groq-ai-model-exclusions.php'; +require_once __DIR__ . '/../includes/Contracts/interface-groq-ai-provider.php'; +require_once __DIR__ . '/../includes/Providers/class-groq-ai-abstract-openai-provider.php'; +require_once __DIR__ . '/../includes/Providers/class-groq-ai-provider-groq.php'; +require_once __DIR__ . '/../includes/Providers/class-groq-ai-provider-openai.php'; +require_once __DIR__ . '/../includes/Providers/class-groq-ai-provider-google.php'; +require_once __DIR__ . '/../includes/Providers/class-groq-ai-provider-manager.php'; +require_once __DIR__ . '/../includes/Services/Settings/class-groq-ai-settings-manager.php'; +require_once __DIR__ . '/../includes/Core/class-groq-ai-ajax-controller.php';