diff --git a/.github/actions/verify-generated-files/action.yml b/.github/actions/verify-generated-files/action.yml index 79c49dbfcfffb..3f3dea73c4dcd 100644 --- a/.github/actions/verify-generated-files/action.yml +++ b/.github/actions/verify-generated-files/action.yml @@ -13,4 +13,5 @@ runs: ext/tokenizer/tokenizer_data_gen.php build/gen_stub.php -f --generate-optimizer-info --verify ext/phar/makestub.php + .github/scripts/download-bundled/make-workflow-file.php .github/scripts/test-directory-unchanged.sh . diff --git a/.github/scripts/download-bundled/make-workflow-file.php b/.github/scripts/download-bundled/make-workflow-file.php new file mode 100755 index 0000000000000..e536d3bd69bd7 --- /dev/null +++ b/.github/scripts/download-bundled/make-workflow-file.php @@ -0,0 +1,212 @@ +#!/usr/bin/env php + $directories + */ + public function __construct( + public string $name, + public array $directories + ) {} + + public function getNameForPath(): string + { + return preg_replace('~\W+~', '-', strtolower($this->name)); + } +} + +class Generator +{ + /** + * @param list $bundles + */ + public function __construct( + public array $bundles + ) {} + + protected function getRepoDirectory(): string + { + return dirname(__DIR__, 3); + } + + protected function indentString(string $value, int $levels, bool $inclFirstLine): string + { + return preg_replace( + '~' . ($inclFirstLine ? '^|' : '') . '(?<=\n)~', + str_repeat(' ', $levels), + $value + ); + } + + /** + * @param mixed $data + */ + protected function encodeYml($data): string + { + if (is_array($data)) { + $isList = array_is_list($data); + $resParts = []; + foreach ($data as $k => $v) { + $kEncoded = $isList + ? '-' + : $this->encodeYml($k) . ':'; + $vEncoded = $this->encodeYml($v); + + $resParts[] = $kEncoded + . (!$isList && is_array($v) && $v !== [] ? "\n " : ' ') + . (is_array($v) ? $this->indentString($vEncoded, 1, false) : $vEncoded); + } + + return implode("\n", $resParts); + } + + if (preg_match('~^(\w+|\$\{\{[^\}]+\}\})$~', $data)) { + return $data; + } + + return strpos($data, "\n") !== false + ? '|' . "\n" . $this->indentString($data, 1, true) + : '\'' . str_replace('\'', '\'\'', $data) . '\''; + } + + public function makeWorkflowFile(): void + { + $content = <<<'EOD' + name: Verify Bundled Files + + on: + push: + paths: &paths + %paths% + pull_request: + paths: *paths + schedule: + - cron: "0 1 * * *" + workflow_dispatch: ~ + + permissions: + contents: read + + jobs: + VERIFY_BUNDLED_FILES: + if: github.repository == 'php/php-src' || github.event_name == 'workflow_dispatch' + name: Verify Bundled Files + runs-on: ubuntu-24.04 + steps: + - name: git checkout + uses: actions/checkout@v6 + + - name: Detect changed files + if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} + uses: dorny/paths-filter@v3 + id: changes + with: + filters: %filters% + + %steps% + + EOD; + + $paths = [ + '.github/scripts/download-bundled/**', + ]; + foreach ($this->bundles as $bundle) { + foreach ($this->makeDornyPathsFilterFilters($bundle) as $p) { + if (str_starts_with($p, '.github/scripts/download-bundled/')) { + continue; + } + + $paths[] = $p; + } + } + $content = str_replace('%paths%', $this->indentString($this->encodeYml($paths), 3, false), $content); + + $filters = []; + foreach ($this->bundles as $bundle) { + $filters[$bundle->getNameForPath()] = $this->makeDornyPathsFilterFilters($bundle); + } + $content = str_replace('%filters%', $this->indentString($this->encodeYml($this->encodeYml($filters)), 5, false), $content); + + $steps = []; + foreach ($this->bundles as $bundle) { + $steps[] = [ + 'name' => $bundle->name, + 'if' => '${{ !cancelled() && (steps.changes.outputs.' . $bundle->getNameForPath() . ' == \'true\' || github.event_name == \'schedule\' || github.event_name == \'workflow_dispatch\') }}', + 'run' => implode("\n", [ + 'echo "::group::Download"', + '.github/scripts/download-bundled/' . $bundle->getNameForPath() . '.sh', + 'echo "::endgroup::"', + 'echo "::group::Verify files"', + ...array_map(static fn ($v) => '.github/scripts/test-directory-unchanged.sh \'' . $v . '\'', $bundle->directories), + 'echo "::endgroup::"', + ]), + ]; + } + $content = str_replace('%steps%', $this->indentString($this->encodeYml($steps), 3, false), $content); + + file_put_contents($this->getRepoDirectory() . '/.github/workflows/verify-bundled-files.yml', $content); + } + + protected function makeDornyPathsFilterFilters(Bundle $bundle): array + { + return [ + '.github/scripts/download-bundled/' . $bundle->getNameForPath() . '.*', + ...array_map(fn ($v) => is_file($this->getRepoDirectory() . '/' . $v) ? $v : $v . '/**', $bundle->directories), + ]; + } + + public function makeDownloadScriptHeaders(): void + { + foreach ($this->bundles as $bundle) { + $this->makeDownloadScriptHeader($bundle); + } + } + + protected function makeDownloadScriptHeader(Bundle $bundle): void + { + $scriptPath = $this->getRepoDirectory() . '/.github/scripts/download-bundled/' . $bundle->getNameForPath() . '.sh'; + + $content = !file_exists($scriptPath) + ? "# TODO\n" + : file_get_contents($scriptPath); + + $header = <<<'EOD' + #!/bin/sh + set -ex + cd "$(dirname "$0")/../../.." + + tmp_dir=%tmp_dir% + rm -rf "$tmp_dir" + + + EOD; + + $header = str_replace('%tmp_dir%', '/tmp/php-src-download-bundled/' . $bundle->getNameForPath(), $header); + + if (!str_starts_with($content, $header)) { + $content = $header . $content; + } + + file_put_contents($scriptPath, $content); + } +} + +$generator = new Generator($bundles); +$generator->makeWorkflowFile(); +$generator->makeDownloadScriptHeaders(); diff --git a/.github/scripts/download-bundled/timelib.parse_date.patch b/.github/scripts/download-bundled/timelib.parse_date.patch new file mode 100644 index 0000000000000..0df8503ae3923 --- /dev/null +++ b/.github/scripts/download-bundled/timelib.parse_date.patch @@ -0,0 +1,12 @@ +diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re +index 2f05f03..893ce16 100644 +--- a/ext/date/lib/parse_date.re ++++ b/ext/date/lib/parse_date.re +@@ -1281,7 +1281,6 @@ weekdayof = (reltextnumber|reltexttext) space (dayfulls|dayfull|dayabbr) + DEBUG_OUTPUT("firstdayof | lastdayof"); + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); +- TIMELIB_UNHAVE_TIME(); // Don't merge into PHP + + /* skip "last day of" or "first day of" */ + if (*ptr == 'l' || *ptr == 'L') { diff --git a/.github/scripts/download-bundled/timelib.sh b/.github/scripts/download-bundled/timelib.sh new file mode 100755 index 0000000000000..a7cf0aaaab68f --- /dev/null +++ b/.github/scripts/download-bundled/timelib.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -ex +cd "$(dirname "$0")/../../.." + +tmp_dir=/tmp/php-src-download-bundled/timelib +rm -rf "$tmp_dir" + +revision=refs/tags/2022.16 + +git clone --depth 1 --revision="$revision" https://github.com/derickr/timelib.git "$tmp_dir" + +rm -rf ext/date/lib +cp -R "$tmp_dir" ext/date/lib + +cd ext/date/lib + +# remove unneeded files +rm -r docs +rm -r tests +rm -r zones +rm .gitignore +rm gettzmapping.php +rm parse_zoneinfo.c +rm win_dirent.h + +# add extra files +rm -r .git +git restore parse_date.c +git restore parse_iso_intervals.c + +# patch customized files +git apply -v ../../../.github/scripts/download-bundled/timelib.parse_date.patch diff --git a/.github/scripts/download-bundled/unicode-character-database.sh b/.github/scripts/download-bundled/unicode-character-database.sh new file mode 100755 index 0000000000000..59dfe2733fe1f --- /dev/null +++ b/.github/scripts/download-bundled/unicode-character-database.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -ex +cd "$(dirname "$0")/../../.." + +tmp_dir=/tmp/php-src-download-bundled/unicode-character-database +rm -rf "$tmp_dir" + +version=17.0.0 + +mkdir -p "$tmp_dir" +curl --fail https://www.unicode.org/Public/$version/ucd/UCD.zip -o "$tmp_dir/data.zip" +unzip "$tmp_dir/data.zip" -d "$tmp_dir" + +cd ext/mbstring + +rm libmbfl/mbfl/eaw_table.h +rm unicode_data.h + +./ucgendat/ucgendat.php "$tmp_dir" diff --git a/.github/scripts/download-bundled/xsse.sh b/.github/scripts/download-bundled/xsse.sh new file mode 100755 index 0000000000000..1e2c94d9366f3 --- /dev/null +++ b/.github/scripts/download-bundled/xsse.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -ex +cd "$(dirname "$0")/../../.." + +tmp_dir=/tmp/php-src-download-bundled/xsse +rm -rf "$tmp_dir" + +revision=refs/tags/xsse-1.0.0 + +git clone --depth 1 --revision="$revision" https://github.com/SakiTakamachi/xsse.git "$tmp_dir" + +cp -f "$tmp_dir"/src/xsse.h Zend/zend_simd.h diff --git a/.github/scripts/download-bundled/xxhash.sh b/.github/scripts/download-bundled/xxhash.sh new file mode 100755 index 0000000000000..fc0c7eb7b3029 --- /dev/null +++ b/.github/scripts/download-bundled/xxhash.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -ex +cd "$(dirname "$0")/../../.." + +tmp_dir=/tmp/php-src-download-bundled/xxhash +rm -rf "$tmp_dir" + +revision=refs/tags/v0.8.2 + +git clone --depth 1 --revision="$revision" https://github.com/Cyan4973/xxHash.git "$tmp_dir" + +cp -f "$tmp_dir"/xxhash.h ext/hash/xxhash/xxhash.h diff --git a/.github/workflows/verify-bundled-files.yml b/.github/workflows/verify-bundled-files.yml index 473d6e2d8f4df..6f5310a9382eb 100644 --- a/.github/workflows/verify-bundled-files.yml +++ b/.github/workflows/verify-bundled-files.yml @@ -5,6 +5,10 @@ on: paths: &paths - '.github/scripts/download-bundled/**' - 'Zend/asm/**' + - 'Zend/zend_simd.h' + - 'ext/date/lib/**' + - 'ext/hash/xxhash/**' + - 'ext/mbstring/**' - 'ext/pcre/pcre2lib/**' - 'ext/uri/uriparser/**' pull_request: @@ -34,6 +38,18 @@ jobs: 'boost-context': - '.github/scripts/download-bundled/boost-context.*' - 'Zend/asm/**' + xsse: + - '.github/scripts/download-bundled/xsse.*' + - 'Zend/zend_simd.h' + timelib: + - '.github/scripts/download-bundled/timelib.*' + - 'ext/date/lib/**' + xxhash: + - '.github/scripts/download-bundled/xxhash.*' + - 'ext/hash/xxhash/**' + 'unicode-character-database': + - '.github/scripts/download-bundled/unicode-character-database.*' + - 'ext/mbstring/**' pcre2: - '.github/scripts/download-bundled/pcre2.*' - 'ext/pcre/pcre2lib/**' @@ -48,7 +64,43 @@ jobs: .github/scripts/download-bundled/boost-context.sh echo "::endgroup::" echo "::group::Verify files" - .github/scripts/test-directory-unchanged.sh Zend/asm + .github/scripts/test-directory-unchanged.sh 'Zend/asm' + echo "::endgroup::" + - name: XSSE + if: ${{ !cancelled() && (steps.changes.outputs.xsse == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} + run: | + echo "::group::Download" + .github/scripts/download-bundled/xsse.sh + echo "::endgroup::" + echo "::group::Verify files" + .github/scripts/test-directory-unchanged.sh 'Zend/zend_simd.h' + echo "::endgroup::" + - name: timelib + if: ${{ !cancelled() && (steps.changes.outputs.timelib == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} + run: | + echo "::group::Download" + .github/scripts/download-bundled/timelib.sh + echo "::endgroup::" + echo "::group::Verify files" + .github/scripts/test-directory-unchanged.sh 'ext/date/lib' + echo "::endgroup::" + - name: xxHash + if: ${{ !cancelled() && (steps.changes.outputs.xxhash == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} + run: | + echo "::group::Download" + .github/scripts/download-bundled/xxhash.sh + echo "::endgroup::" + echo "::group::Verify files" + .github/scripts/test-directory-unchanged.sh 'ext/hash/xxhash' + echo "::endgroup::" + - name: 'Unicode Character Database' + if: ${{ !cancelled() && (steps.changes.outputs.unicode-character-database == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} + run: | + echo "::group::Download" + .github/scripts/download-bundled/unicode-character-database.sh + echo "::endgroup::" + echo "::group::Verify files" + .github/scripts/test-directory-unchanged.sh 'ext/mbstring' echo "::endgroup::" - name: PCRE2 if: ${{ !cancelled() && (steps.changes.outputs.pcre2 == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} @@ -57,7 +109,7 @@ jobs: .github/scripts/download-bundled/pcre2.sh echo "::endgroup::" echo "::group::Verify files" - .github/scripts/test-directory-unchanged.sh ext/pcre/pcre2lib + .github/scripts/test-directory-unchanged.sh 'ext/pcre/pcre2lib' echo "::endgroup::" - name: uriparser if: ${{ !cancelled() && (steps.changes.outputs.uriparser == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} @@ -66,5 +118,5 @@ jobs: .github/scripts/download-bundled/uriparser.sh echo "::endgroup::" echo "::group::Verify files" - .github/scripts/test-directory-unchanged.sh ext/uri/uriparser + .github/scripts/test-directory-unchanged.sh 'ext/uri/uriparser' echo "::endgroup::" diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re index ffb3e8e359137..08302e0b2bb11 100644 --- a/ext/date/lib/parse_date.re +++ b/ext/date/lib/parse_date.re @@ -555,7 +555,6 @@ static timelib_ull timelib_get_signed_nr(Scanner *s, const char **ptr, int max_l int len = 0; /* Skip over non-numeric chars */ - while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) { if (**ptr == '\0') { add_error(s, TIMELIB_ERR_UNEXPECTED_DATA, "Found unexpected data"); @@ -569,6 +568,7 @@ static timelib_ull timelib_get_signed_nr(Scanner *s, const char **ptr, int max_l str[0] = '+'; /* First position is the sign */ str_ptr = str + 1; + while ((**ptr == '+') || (**ptr == '-')) { if (**ptr == '-') { str[0] = str[0] == '+' ? '-' : '+'; diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index acae8e32533db..cb032ec60574b 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -30,9 +30,9 @@ # include "timelib_config.h" #endif -#define TIMELIB_VERSION 202215 -#define TIMELIB_EXTENDED_VERSION 20221501 -#define TIMELIB_ASCII_VERSION "2022.15" +#define TIMELIB_VERSION 202216 +#define TIMELIB_EXTENDED_VERSION 20221601 +#define TIMELIB_ASCII_VERSION "2022.16" #include #include diff --git a/ext/date/lib/timelib_private.h b/ext/date/lib/timelib_private.h index 3c5f9b22147c0..23419c8f44193 100644 --- a/ext/date/lib/timelib_private.h +++ b/ext/date/lib/timelib_private.h @@ -135,6 +135,7 @@ #ifndef TIMELIB_HAVE_BUILTIN_SADDLL_OVERFLOW # define TIMELIB_HAVE_BUILTIN_SADDLL_OVERFLOW 0 #endif + struct _ttinfo { int32_t offset;