From 08b77a482133e3b074f84162b6e2cbd985ce790c Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Thu, 13 Jul 2023 20:30:35 +0200 Subject: [PATCH 01/14] Fix #11 --- Classes/Server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Server.php b/Classes/Server.php index 4f068f1..0daf4c6 100644 --- a/Classes/Server.php +++ b/Classes/Server.php @@ -289,7 +289,7 @@ class Server { if ($datetime === false) { $datetime = \DateTime::createFromFormat('Y-m-d', $date); } - return ($datetime !== false) && !array_sum($datetime->getLastErrors()); + return ($datetime !== false) && ($datetime->getLastErrors() !== false); } } From 06ab2d11af786feda81a4ccbe579bede3363807e Mon Sep 17 00:00:00 2001 From: scottlimmer Date: Tue, 22 Aug 2023 13:42:10 +1000 Subject: [PATCH 02/14] Update composer.json for PHP8.x Local testing with https://github.com/PHPCompatibility/PHPCompatibility shows no compat issues with PHP8.x. Bumping composer dependency accordingly. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ade45fb..da49535 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,7 @@ "docs": "https://github.com/opencultureconsulting/oai_pmh/blob/master/README.md" }, "require": { - "php": "^7.0", + "php": "^7.0|^8.0", "ext-xml": "*" }, "autoload": { From 78466667a0bb89595f09662f76b1ab3b55298c39 Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Fri, 29 Sep 2023 20:29:44 +0200 Subject: [PATCH 03/14] Add EditorConfig file --- .editorconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b9ab33e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true From 252999576d91872635d2eddd8c4b1f4701c33f93 Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Fri, 29 Sep 2023 20:29:54 +0200 Subject: [PATCH 04/14] Add .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57872d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/vendor/ From b5ec6ea083cfc1327238b5b4def77fc9b4b0eaa4 Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Fri, 29 Sep 2023 20:30:53 +0200 Subject: [PATCH 05/14] Update README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 288ddee..bcfaaa8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Simple OAI-PMH 2.0 Data Provider -This is a stand-alone and easy to install data provider implementing the [Open Archives Initiative's Protocol for Metadata Harvesting (OAI-PMH)](https://openarchives.org/pmh/). It serves records in any metadata format from directories of XML files using the directory name as `metadataPrefix`, the filename as `identifier` and the filemtime as datestamp. 0-byte files are considered deleted records and handled accordingly. Resumption tokens are managed using files. Sets are currently not supported. +This is a stand-alone and easy to install data provider implementing the [Open Archives Initiative's Protocol for Metadata Harvesting (OAI-PMH)](https://openarchives.org/pmh/). It serves records in any metadata format from directories of XML files using the directory name as `metadataPrefix`, the filename as `identifier` and the filemtime as timestamp. 0-byte files are considered deleted records and handled accordingly. Resumption tokens are managed using files. Sets are currently not supported. Just put the records as XML files in the data directory, adjust a few configuration settings and you are ready to go! @@ -12,9 +12,9 @@ A demo installation can be found [here](https://demo.opencultureconsulting.com/o 1. Run `composer create-project opencultureconsulting/oai_pmh `. -2. Create a data directory in a not publicly accessible location outside of ``. Create a subdirectory inside the specified data directory for every format (i. e. `metadataPrefix`) you want to provide. +2. Create a data directory in a location not publicly accessible (i. e. outside of ``). Create a subdirectory inside the specified data directory for every format (i. e. `metadataPrefix`) you want to provide. -3. Copy `Configuration/Main.template.php` to `Configuration/Main.php` and adjust the settings according to your preferences. Don't forget pointing `$config['dataDirectory']` to your newly created data directory. +3. Copy `Configuration/Main.template.php` to `Configuration/Main.php` and edit the settings according to your preferences. Don't forget pointing `$config['dataDirectory']` to your newly created data directory. 4. Put the records into the respective directories according to their format. Each record has to be a separate XML file with its `identifier` as filename (e. g. the file *12345678.xml* can be adressed using the `identifier` *12345678*). Optionally you can maintain deletions by keeping 0-byte files for deleted records. From 6cf4b53289423babd30735c0b383b5605066e313 Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Fri, 29 Sep 2023 21:08:00 +0200 Subject: [PATCH 06/14] Rename GitHub repository --- README.md | 2 +- Resources/Stylesheet.xsl | 2 +- composer.json | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bcfaaa8..57c13b5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Just put the records as XML files in the data directory, adjust a few configurat A demo installation can be found [here](https://demo.opencultureconsulting.com/oai_pmh/?verb=Identify). -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/7a12022611d047ad9ef9a0c3aadb986a)](https://www.codacy.com/gh/opencultureconsulting/oai_pmh) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/7a12022611d047ad9ef9a0c3aadb986a)](https://www.codacy.com/gh/opencultureconsulting/simple-oai-pmh) ## Installation diff --git a/Resources/Stylesheet.xsl b/Resources/Stylesheet.xsl index 116375d..b432597 100644 --- a/Resources/Stylesheet.xsl +++ b/Resources/Stylesheet.xsl @@ -164,7 +164,7 @@

You are viewing an HTML version of the XML OAI-PMH response. To see the underlying XML as it appears to any OAI-PMH harvester use your web browser's view source option or disable XSLT processing.

-

This XSL script was originally written by Christopher Gutteridge at University of Southampton for the EPrints project and was later adapted by Sebastian Meyer at Open Culture Consulting to be more generally applicable to other OAI-PMH interfaces. It is available on GitHub for free!

+

This XSL script was originally written by Christopher Gutteridge at University of Southampton for the EPrints project and was later adapted by Sebastian Meyer at Open Culture Consulting to be more generally applicable to other OAI-PMH interfaces. It is available on GitHub for free!

diff --git a/composer.json b/composer.json index da49535..6b6917e 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "code4lib", "repository" ], - "homepage": "https://github.com/opencultureconsulting/oai_pmh", + "homepage": "https://github.com/opencultureconsulting/simple-oai-pmh", "readme": "README.md", "license": ["GPL-3.0-or-later"], "authors": [ @@ -41,9 +41,9 @@ } ], "support": { - "issues": "https://github.com/opencultureconsulting/oai_pmh/issues", - "source": "https://github.com/opencultureconsulting/oai_pmh", - "docs": "https://github.com/opencultureconsulting/oai_pmh/blob/master/README.md" + "issues": "https://github.com/opencultureconsulting/simple-oai-pmh/issues", + "source": "https://github.com/opencultureconsulting/simple-oai-pmh", + "docs": "https://github.com/opencultureconsulting/simple-oai-pmh/blob/master/README.md" }, "require": { "php": "^7.0|^8.0", From db76e3ba10f23c438bd5f702a4dc1fb3265cd462 Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Fri, 29 Sep 2023 21:22:22 +0200 Subject: [PATCH 07/14] Rename composer project --- README.md | 4 ++-- composer.json | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 57c13b5..84fc173 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A demo installation can be found [here](https://demo.opencultureconsulting.com/o ## Installation -1. Run `composer create-project opencultureconsulting/oai_pmh `. +1. Run `composer create-project opencultureconsulting/simple-oai-pmh `. 2. Create a data directory in a location not publicly accessible (i. e. outside of ``). Create a subdirectory inside the specified data directory for every format (i. e. `metadataPrefix`) you want to provide. @@ -24,7 +24,7 @@ A demo installation can be found [here](https://demo.opencultureconsulting.com/o 1. Backup `Configuration/Main.php`! -2. Delete `` and re-install by running `composer create-project opencultureconsulting/oai_pmh `. +2. Delete `` and re-install by running `composer create-project opencultureconsulting/simple-oai-pmh `. 3. Move your configuration back into `Configuration/Main.php`. diff --git a/composer.json b/composer.json index 6b6917e..2f1b2ad 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "opencultureconsulting/oai_pmh", + "name": "opencultureconsulting/simple-oai-pmh", "description": "This is a stand-alone and easy to install data provider implementing the Open Archives Initiative's Protocol for Metadata Harvesting (OAI-PMH).", "type": "project", "keywords": [ @@ -49,6 +49,9 @@ "php": "^7.0|^8.0", "ext-xml": "*" }, + "replace": { + "opencultureconsulting/oai_pmh": "self.version" + }, "autoload": { "psr-4": { "OCC\\OAI2\\": "Classes/" From 11d8aa411a9afc294d1bfa837cfd8901132e598e Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Fri, 29 Sep 2023 21:40:31 +0200 Subject: [PATCH 08/14] Add composer.lock to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 57872d0..3a9875b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /vendor/ +composer.lock From 53670681b1a03b02faded3ae57ca476d0fa461d0 Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Wed, 8 May 2024 15:29:44 +0200 Subject: [PATCH 09/14] Update metadata --- .gitattributes | 121 +++++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 9 +++- composer.lock | 21 +++++++++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 .gitattributes create mode 100644 composer.lock diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..deb86b8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,121 @@ +### +# https://github.com/gitattributes/gitattributes/blob/master/Common.gitattributes +### + +# Auto detect text files and perform LF normalization +* text=auto + +# +# The above will handle all files NOT found below +# + +# Documents +*.bibtex text diff=bibtex +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain +*.md text diff=markdown +*.mdx text diff=markdown +*.tex text diff=tex +*.adoc text +*.textile text +*.mustache text +*.csv text eol=crlf +*.tab text +*.tsv text +*.txt text +*.sql text +*.epub diff=astextplain + +# Graphics +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.tif binary +*.tiff binary +*.ico binary +# SVG treated as text by default. +*.svg text +# If you want to treat it as binary, +# use the following line instead. +# *.svg binary +*.eps binary + +# Scripts +*.bash text eol=lf +*.fish text eol=lf +*.ksh text eol=lf +*.sh text eol=lf +*.zsh text eol=lf +# These are explicitly windows files and should use crlf +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf + +# Serialisation +*.json text +*.toml text +*.xml text +*.yaml text +*.yml text + +# Archives +*.7z binary +*.gz binary +*.tar binary +*.tgz binary +*.zip binary + +# Text files where line endings should be preserved +*.patch -text + +# +# Exclude files from exporting +# + +.gitattributes export-ignore +.gitignore export-ignore +.gitkeep export-ignore + +### +# https://github.com/gitattributes/gitattributes/blob/master/PHP.gitattributes +### + +# PHP files +*.php text eol=lf diff=php +*.phpt text eol=lf diff=php +*.phtml text eol=lf diff=html +*.twig text eol=lf +*.phar binary + +# Configuration +phpcs.xml text eol=lf +phpunit.xml text eol=lf +phpstan.neon text eol=lf +psalm.xml text eol=lf + +### +# Open Culture Consulting custom additions +### + +# Configuration +.editorconfig text eol=lf +*.dist.xml text eol=lf +*.xml.dist text eol=lf +*.neon text eol=lf + +# Generated documentation +doc/* linguist-generated=true + +# Exclude files from exporting +.github/* export-ignore +.phpdoc/* export-ignore +phpdoc.dist.xml export-ignore diff --git a/.gitignore b/.gitignore index 3a9875b..1f17302 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,9 @@ +/.phpdoc/cache/ +/.vscode/ /vendor/ -composer.lock +.php-cs-fixer.php +phpcs.xml +phpdoc.xml +phpstan.neon +psalm.xml +TODO diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..39a781d --- /dev/null +++ b/composer.lock @@ -0,0 +1,21 @@ +{ + "_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": "202dff7c3944cd862680ea4668a67650", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.0|^8.0", + "ext-xml": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} From beeef31d9123528833ebe695ac720c1d24d7dcd1 Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Wed, 8 May 2024 15:29:58 +0200 Subject: [PATCH 10/14] Fix deprecation warnings for PHP 8.1 --- Classes/Exception.php | 53 ++++++++++++++++++++++--------------------- Classes/Response.php | 6 ++++- Classes/Server.php | 14 ++++++++---- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/Classes/Exception.php b/Classes/Exception.php index c4d5b58..5d0d1a2 100644 --- a/Classes/Exception.php +++ b/Classes/Exception.php @@ -24,33 +24,34 @@ namespace OCC\OAI2; class Exception extends \Exception { + private array $errorTable = [ + 'badArgument' => [ + 'text' => 'The request includes illegal arguments, is missing required arguments, includes a repeated argument, or values for arguments have an illegal syntax.', + ], + 'badResumptionToken' => [ + 'text' => 'The value of the resumptionToken argument is invalid or expired.', + ], + 'badVerb' => [ + 'text' => 'Value of the verb argument is not a legal OAI-PMH verb, the verb argument is missing, or the verb argument is repeated.', + ], + 'cannotDisseminateFormat' => [ + 'text' => 'The metadata format identified by the value given for the metadataPrefix argument is not supported by the item or by the repository.', + ], + 'idDoesNotExist' => [ + 'text' => 'The value of the identifier argument is unknown or illegal in this repository.', + ], + 'noRecordsMatch' => [ + 'text' => 'The combination of the values of the from, until, set and metadataPrefix arguments results in an empty list.', + ], + 'noMetadataFormats' => [ + 'text' => 'There are no metadata formats available for the specified item.', + ], + 'noSetHierarchy' => [ + 'text' => 'The repository does not support sets.', + ] + ]; + public function __construct($code) { - $this->errorTable = [ - 'badArgument' => [ - 'text' => 'The request includes illegal arguments, is missing required arguments, includes a repeated argument, or values for arguments have an illegal syntax.', - ], - 'badResumptionToken' => [ - 'text' => 'The value of the resumptionToken argument is invalid or expired.', - ], - 'badVerb' => [ - 'text' => 'Value of the verb argument is not a legal OAI-PMH verb, the verb argument is missing, or the verb argument is repeated.', - ], - 'cannotDisseminateFormat' => [ - 'text' => 'The metadata format identified by the value given for the metadataPrefix argument is not supported by the item or by the repository.', - ], - 'idDoesNotExist' => [ - 'text' => 'The value of the identifier argument is unknown or illegal in this repository.', - ], - 'noRecordsMatch' => [ - 'text' => 'The combination of the values of the from, until, set and metadataPrefix arguments results in an empty list.', - ], - 'noMetadataFormats' => [ - 'text' => 'There are no metadata formats available for the specified item.', - ], - 'noSetHierarchy' => [ - 'text' => 'The repository does not support sets.', - ], - ]; parent::__construct($this->errorTable[$code]['text']); $this->code = $code; } diff --git a/Classes/Response.php b/Classes/Response.php index d2d3c1f..17d1dfd 100644 --- a/Classes/Response.php +++ b/Classes/Response.php @@ -24,7 +24,11 @@ namespace OCC\OAI2; class Response { - public $doc; // DOMDocument. Handle of current XML Document object + public \DOMDocument $doc; // DOMDocument. Handle of current XML Document object + + private string $verb = ''; + + private \DOMElement $verbNode; public function __construct($uri, $verb, $request_args) { if (substr($uri, -1, 1) == '/') { diff --git a/Classes/Server.php b/Classes/Server.php index 0daf4c6..2a5eada 100644 --- a/Classes/Server.php +++ b/Classes/Server.php @@ -34,6 +34,12 @@ class Server { private $max_records = 100; private $token_prefix = '/tmp/oai2-'; private $token_valid = 86400; + private $uri = ''; + private $identifyResponse; + private $listMetadataFormatsCallback; + private $listRecordsCallback; + private $getRecordCallback; + private Response $response; public function __construct($uri, $args, $identifyResponse, $callbacks, $config) { $this->uri = $uri; @@ -222,7 +228,7 @@ class Server { if ($records_count - $deliveredRecords > $maxItems) { $deliveredRecords += $maxItems; $restoken = $this->createResumptionToken($deliveredRecords, $metadataPrefix, $from, $until); - $expirationDatetime = gmstrftime('%Y-%m-%dT%TZ', time()+$this->token_valid); + $expirationDatetime = date('Y-m-d\TH:i:s\Z', time()+$this->token_valid); } elseif (isset($this->args['resumptionToken'])) { // Last delivery, return empty resumptionToken $restoken = null; @@ -277,8 +283,8 @@ class Server { } private function formatTimestamp($datestamp) { - if (is_array($time = strptime($datestamp, '%Y-%m-%dT%H:%M:%SZ')) || is_array($time = strptime($datestamp, '%Y-%m-%d'))) { - return gmmktime($time['tm_hour'], $time['tm_min'], $time['tm_sec'], $time['tm_mon'] + 1, $time['tm_mday'], $time['tm_year']+1900); + if (is_array($time = date_parse_from_format('Y-m-d\TH:i:s\Z', $datestamp)) || is_array($time = date_parse_from_format('Y-m-d\TH:i:s\Z', $datestamp))) { + return gmmktime($time['hour'], $time['minute'], $time['second'], $time['month'] + 1, $time['day'], $time['year']); } else { return null; } @@ -289,7 +295,7 @@ class Server { if ($datetime === false) { $datetime = \DateTime::createFromFormat('Y-m-d', $date); } - return ($datetime !== false) && ($datetime->getLastErrors() !== false); + return ($datetime !== false); } } From c4a5a0920568170927ad621bdb24dd744e3e0e1f Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Wed, 8 May 2024 15:39:09 +0200 Subject: [PATCH 11/14] Avoid null values for XML elements --- Classes/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Response.php b/Classes/Response.php index 17d1dfd..6a7a37c 100644 --- a/Classes/Response.php +++ b/Classes/Response.php @@ -78,7 +78,7 @@ class Response { * @param string $nodeName The name of appending node. * @param string $value The content of appending node. */ - public function addToVerbNode($nodeName, $value = null) { + public function addToVerbNode($nodeName, $value = '') { if (!isset($this->verbNode) && !empty($this->verb)) { $this->verbNode = $this->addChild($this->doc->documentElement, $this->verb); } From a142f9fdfb723fc8649fd6ba9cd796b2fcef64fc Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Fri, 10 May 2024 15:02:28 +0200 Subject: [PATCH 12/14] Fix date parsing error --- Classes/Server.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Classes/Server.php b/Classes/Server.php index 2a5eada..4e39332 100644 --- a/Classes/Server.php +++ b/Classes/Server.php @@ -283,10 +283,14 @@ class Server { } private function formatTimestamp($datestamp) { - if (is_array($time = date_parse_from_format('Y-m-d\TH:i:s\Z', $datestamp)) || is_array($time = date_parse_from_format('Y-m-d\TH:i:s\Z', $datestamp))) { - return gmmktime($time['hour'], $time['minute'], $time['second'], $time['month'] + 1, $time['day'], $time['year']); - } else { + $time = date_parse_from_format('Y-m-d\TH:i:s\Z', $datestamp); + if ($time['error_count'] > 0) { + $time = date_parse_from_format('Y-m-d', $datestamp); + } + if ($time['error_count'] > 0) { return null; + } else { + return gmmktime($time['hour'], $time['minute'], $time['second'], $time['month'] + 1, $time['day'], $time['year']); } } From 6272d964346464a73ddaddcf6cc7f5509fa46983 Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Thu, 16 May 2024 10:53:47 +0200 Subject: [PATCH 13/14] Stricter PHP version constraints --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2f1b2ad..ef9aab7 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,7 @@ "docs": "https://github.com/opencultureconsulting/simple-oai-pmh/blob/master/README.md" }, "require": { - "php": "^7.0|^8.0", + "php": "7.4.*|8.0.*|8.1.*|8.2.*", "ext-xml": "*" }, "replace": { From d305f8a9bba8650154963253f53c657896f223b4 Mon Sep 17 00:00:00 2001 From: Sebastian Meyer Date: Thu, 16 May 2024 14:04:37 +0200 Subject: [PATCH 14/14] Update composer.lock --- composer.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.lock b/composer.lock index 39a781d..7639c60 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": "202dff7c3944cd862680ea4668a67650", + "content-hash": "af0a24a6f06b7a8dc4ecd3084647403e", "packages": [], "packages-dev": [], "aliases": [], @@ -13,7 +13,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.0|^8.0", + "php": "7.4.*|8.0.*|8.1.*|8.2.*", "ext-xml": "*" }, "platform-dev": [],