diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cbcc0d5..6d6a92d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: - '!main' env: - BUILDER_VERSION: v0.8.9 + BUILDER_VERSION: v0.8.18 BUILDER_SOURCE: releases BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net PACKAGE_NAME: aws-crt-php @@ -15,38 +15,71 @@ env: RUN: ${{ github.run_id }}-${{ github.run_number }} jobs: - php7-linux-x64: + php-5_5-linux-x64: + name: php-linux-x64 (5.5) runs-on: ubuntu-latest steps: - - name: Build PHP 7.4 with ffi - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u awslabs --password-stdin - export DOCKER_IMAGE=docker.pkg.github.com/awslabs/aws-crt-builder/aws-crt-php7-linux-x64:${{ env.BUILDER_VERSION }} - docker pull $DOCKER_IMAGE - docker run --env GITHUB_REF --env LC_ALL=C.UTF-8 $DOCKER_IMAGE --version=${{env.BUILDER_VERSION}} build -p ${{ env.PACKAGE_NAME }} --spec=downstream --skip-install + - name: Setup PHP with Xdebug + uses: shivammathur/setup-php@v2 + with: + coverage: xdebug + php-version: 5.5 + ini-values: xdebug.overload_var_dump=0, memory_limit=4G, phar.readonly=false - php5-linux-x64: + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install ancient PHPUnit + run: composer require --dev --ignore-platform-reqs phpunit/phpunit "4.8.36" + + - name: Install depedencies + run: composer update --no-interaction + + - name: Run tests + env: + CC: clang + CXX: clang++ + run: | + phpize + ./configure + make test + + php-linux-x64: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + version: + - "5.6" + - "7.0" + - "7.1" + - "7.2" + - "7.3" + - "7.4" + - "8.0" steps: - - name: Build PHP 5.6 extension - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u awslabs --password-stdin - export DOCKER_IMAGE=docker.pkg.github.com/awslabs/aws-crt-builder/aws-crt-php5-linux-x64:${{ env.BUILDER_VERSION }} - docker pull $DOCKER_IMAGE - docker run --env GITHUB_REF --env LC_ALL=C.UTF-8 $DOCKER_IMAGE --version=${{env.BUILDER_VERSION}} build -p ${{ env.PACKAGE_NAME }} --spec=downstream --skip-install + - name: PHP ${{ matrix.version}} Extension + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u awslabs --password-stdin + export DOCKER_IMAGE=docker.pkg.github.com/awslabs/aws-crt-builder/aws-crt-php${{ matrix.version }}-linux-x64:${{ env.BUILDER_VERSION }} + docker pull $DOCKER_IMAGE + docker run --env GITHUB_REF --env LC_ALL=C.UTF-8 $DOCKER_IMAGE --version=${{env.BUILDER_VERSION}} build -p ${{ env.PACKAGE_NAME }} --spec=downstream --skip-install -# linux-arm: -# name: ARM (${{ matrix.arch }}) -# runs-on: ubuntu-latest -# strategy: -# matrix: -# arch: [armv6, armv7, arm64] -# steps: -# - name: Build ${{ env.PACKAGE_NAME }} -# run: | -# python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" -# chmod a+x builder -# ./builder build -p ${{ env.PACKAGE_NAME }} --target=linux-${{ matrix.arch }} --spec=downstream + + # linux-arm: + # name: ARM (${{ matrix.arch }}) + # runs-on: ubuntu-latest + # strategy: + # matrix: + # arch: [armv6, armv7, arm64] + # steps: + # - name: Build ${{ env.PACKAGE_NAME }} + # run: | + # python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" + # chmod a+x builder + # ./builder build -p ${{ env.PACKAGE_NAME }} --target=linux-${{ matrix.arch }} --spec=downstream # windows-vc16: # runs-on: windows-latest @@ -87,8 +120,8 @@ jobs: matrix: version: [10.15] steps: - - name: Build PHP 8 extension and test - run: | - python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" - chmod a+x builder - ./builder build -p ${{ env.PACKAGE_NAME }} --spec=downstream + - name: Build PHP 8 extension and test + run: | + python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" + chmod a+x builder + ./builder build -p ${{ env.PACKAGE_NAME }} --spec=downstream diff --git a/Makefile.frag b/Makefile.frag index dadf724..4a01adb 100644 --- a/Makefile.frag +++ b/Makefile.frag @@ -3,7 +3,7 @@ BUILD_DIR=$(shell pwd)/build DEPS_DIR=$(BUILD_DIR)/deps INT_DIR=$(BUILD_DIR)/install INSTALL_DIR=$(shell pwd) -AT_LEAST_PHP7=$(shell expr `php --version | head -1 | cut -f 2 -d' '` \>= 7) +GENERATE_STUBS=$(shell expr `php --version | head -1 | cut -f 2 -d' '` \>= 7.1) CMAKE = cmake3 ifeq (, $(shell which cmake3)) @@ -32,20 +32,17 @@ $(BUILD_DIR)/aws-crt-ffi-static/libaws-crt-ffi.a: $(BUILD_DIR)/aws-crt-ffi-stati $(CMAKE_BUILD) build/aws-crt-ffi-static $(CMAKE_TARGET) # PHP extension target -extension: ext/crt.lo +awscrt: ext/awscrt.lo # Force the crt object target to depend on the CRT static library -ext/crt.lo: $(BUILD_DIR)/aws-crt-ffi-static/libaws-crt-ffi.a ext/api.h ext/awscrt_arginfo.h +ext/awscrt.lo: ext/awscrt.c -# borrow the gen_stub script from PHP's build process -GEN_STUB=build/gen_stub.php -$(GEN_STUB): - curl -o $(GEN_STUB) -sSL https://raw.githubusercontent.com/php/php-src/bbb86ba7e2fe8ae365294d1834c6a392570a9dcd/build/gen_stub.php +ext/awscrt.c: $(BUILD_DIR)/aws-crt-ffi-static/libaws-crt-ffi.a ext/api.h ext/awscrt_arginfo.h -ext/awscrt_arginfo.h: ext/awscrt.stub.php $(GEN_STUB) -ifeq ($(AT_LEAST_PHP7),1) +ext/awscrt_arginfo.h: ext/awscrt.stub.php gen_stub.php +ifeq ($(GENERATE_STUBS),1) # generate awscrt_arginfo.h - php $(GEN_STUB) ext/awscrt.stub.php + php gen_stub.php --minimal-arginfo ext/awscrt.stub.php endif # transform/install api.h from FFI lib @@ -56,14 +53,16 @@ src/api.h: crt/aws-crt-ffi/src/api.h ext/api.h : src/api.h cp -v src/api.h ext/api.h +ext/php_aws_crt.h: ext/awscrt_arginfo.h ext/api.h + # FFI target ffi: src/libaws-crt-ffi.so # copy the lib into the src folder -src/libaws-crt-ffi.so: $(BUILD_DIR)/aws-crt-ffi-shared/libaws-crt-ffi.so src/api.h - cp -v $(BUILD_DIR)/aws-crt-ffi-shared/libaws-crt-ffi.so src/libaws-crt-ffi.so +modules/libaws-crt-ffi.so: $(BUILD_DIR)/aws-crt-ffi-shared/libaws-crt-ffi.so src/api.h + cp -v $(BUILD_DIR)/aws-crt-ffi-shared/libaws-crt-ffi.so modules/libaws-crt-ffi.so # Use PHPUnit to run tests -test: ext/api.h ext/awscrt_arginfo.h ext/crt.lo +test: ext/api.h ext/awscrt_arginfo.h ext/awscrt.lo composer update composer run test diff --git a/composer.json b/composer.json index 0d9b878..e00c270 100644 --- a/composer.json +++ b/composer.json @@ -15,9 +15,10 @@ }, "minimum-stability": "alpha", "require": { + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "^5" + "phpunit/phpunit":"^4.8.35|^5.4.3" }, "conflict": { "phpunit/php-timer": ">=2" diff --git a/composer.lock b/composer.lock deleted file mode 100644 index b475aab..0000000 --- a/composer.lock +++ /dev/null @@ -1,1539 +0,0 @@ -{ - "_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": "d1e4715f1331f5ef3a9a1966360c6cd6", - "packages": [], - "packages-dev": [ - { - "name": "doctrine/instantiator", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", - "shasum": "" - }, - "require": { - "php": ">=5.3,<8.0-DEV" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "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": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/master" - }, - "time": "2015-06-14T21:17:01+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.7.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "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.x" - }, - "time": "2017-10-19T19:58:43+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" - }, - "time": "2017-09-11T18:02:19+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/3.x" - }, - "time": "2017-11-10T14:09:06+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/master" - }, - "time": "2017-07-14T14:27:02+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.10.3", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" - }, - "time": "2020-03-05T15:02:03+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "^1.3", - "phpunit/php-text-template": "^1.2", - "phpunit/php-token-stream": "^1.4.2 || ^2.0", - "sebastian/code-unit-reverse-lookup": "^1.0", - "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "^1.0 || ^2.0" - }, - "require-dev": { - "ext-xdebug": "^2.1.4", - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-xdebug": "^2.5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.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": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/4.0" - }, - "time": "2017-04-02T07:44:40+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.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": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5" - }, - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "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/1.2.1" - }, - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "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": "sb@sebastian-bergmann.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/master" - }, - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.4.12", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/1.4" - }, - "abandoned": true, - "time": "2017-12-04T08:55:13+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "5.7.27", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", - "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "~1.3", - "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.4", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "^1.2.4", - "sebastian/diff": "^1.4.3", - "sebastian/environment": "^1.3.4 || ^2.0", - "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "~2.0", - "sebastian/resource-operations": "~1.0", - "sebastian/version": "^1.0.6|^2.0.1", - "symfony/yaml": "~2.1|~3.0|~4.0" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "~1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.7.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": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/5.7.27" - }, - "time": "2018-02-01T05:50:59+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "3.4.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.6 || ^7.0", - "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2 || ^2.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.4" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "support": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues", - "source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/3.4" - }, - "abandoned": true, - "time": "2017-06-30T09:13:00+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-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/1.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:15:22+00:00" - }, - { - "name": "sebastian/comparator", - "version": "1.2.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/1.2" - }, - "time": "2017-01-29T09:50:25+00:00" - }, - { - "name": "sebastian/diff", - "version": "1.4.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/1.4" - }, - "time": "2017-05-22T07:24:03+00:00" - }, - { - "name": "sebastian/environment", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.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": "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/master" - }, - "time": "2016-11-26T07:53:53+00:00" - }, - { - "name": "sebastian/exporter", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~2.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" - }, - "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": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/master" - }, - "time": "2016-11-19T08:54:04+00:00" - }, - { - "name": "sebastian/global-state", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" - }, - "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" - } - ], - "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/1.1.1" - }, - "time": "2015-10-12T03:26:01+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", - "shasum": "" - }, - "require": { - "php": ">=5.6", - "sebastian/recursion-context": "~2.0" - }, - "require-dev": { - "phpunit/phpunit": "~5" - }, - "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": "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/master" - }, - "time": "2017-02-18T15:18:39+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "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": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/master" - }, - "time": "2016-11-19T07:33:16+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-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": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/master" - }, - "time": "2015-07-28T20:34:47+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "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": "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/master" - }, - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.19.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b", - "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.19-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.19.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-23T09:01:57+00:00" - }, - { - "name": "symfony/yaml", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "88289caa3c166321883f67fe5130188ebbb47094" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094", - "reference": "88289caa3c166321883f67fe5130188ebbb47094", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.9.1", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" - }, - "type": "library", - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.9.1" - }, - "time": "2020-07-08T17:02:28+00:00" - } - ], - "aliases": [], - "minimum-stability": "alpha", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "platform-overrides": { - "php": "5.6" - }, - "plugin-api-version": "2.0.0" -} diff --git a/config.m4 b/config.m4 index b056b33..27e55ba 100644 --- a/config.m4 +++ b/config.m4 @@ -3,7 +3,8 @@ dnl * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. dnl * SPDX-License-Identifier: Apache-2.0. dnl -PHP_ARG_WITH(awscrt) +PHP_ARG_WITH(awscrt, for AWS Common Runtime support, + [ --with-awscrt Include awscrt support]) if test "$PHP_AWSCRT" != "no"; then # force lib paths to be absolute, or PHP will mangle them @@ -23,8 +24,7 @@ if test "$PHP_AWSCRT" != "no"; then PHP_SUBST(AWSCRT_SHARED_LIBADD) # Sources for the PHP extension itself - AWSCRT_SOURCES=ext/*.c + AWSCRT_SOURCES=ext/awscrt.c PHP_NEW_EXTENSION(awscrt, $AWSCRT_SOURCES, $ext_shared) + PHP_ADD_MAKEFILE_FRAGMENT fi - -PHP_ADD_MAKEFILE_FRAGMENT diff --git a/crt/aws-crt-ffi b/crt/aws-crt-ffi index 0cfb701..61f7a7a 160000 --- a/crt/aws-crt-ffi +++ b/crt/aws-crt-ffi @@ -1 +1 @@ -Subproject commit 0cfb701a7574bc1528f75085901580d76fa32177 +Subproject commit 61f7a7adcc5ffc143f515dc6030150acb7ce3632 diff --git a/ext/awscrt.c b/ext/awscrt.c new file mode 100644 index 0000000..55ba77e --- /dev/null +++ b/ext/awscrt.c @@ -0,0 +1,13 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +/* This is a unity-build style source file, as PHP's build system is simplest with 1 source file per extension */ + +#include "credentials.c" +#include "crt.c" +#include "event_loop.c" +#include "http.c" +#include "signing.c" +#include "stream.c" diff --git a/ext/awscrt.stub.php b/ext/awscrt.stub.php index 8e96360..1f72189 100644 --- a/ext/awscrt.stub.php +++ b/ext/awscrt.stub.php @@ -103,6 +103,8 @@ function aws_crt_signing_config_aws_set_signed_body_header_type(int $config, int function aws_crt_signing_config_aws_set_expiration_in_seconds(int $config, int $expiration_in_seconds): void {} +function aws_crt_signing_config_aws_set_date(int $config, int $timestamp): void {} + function aws_crt_signable_new_from_http_request(int $http_message): int {} function aws_crt_signable_new_from_chunk(int $input_stream, string $previous_signature): int {} @@ -111,4 +113,8 @@ function aws_crt_signable_new_from_canonical_request(string $request): int {} function aws_crt_signable_release(int $signable): void {} +function aws_crt_signing_result_release(int $signing_result): void {} + +function aws_crt_signing_result_apply_to_http_request(object $signing_result, object $http_request): void {} + function aws_crt_sign_request_aws(int $signable, int $signing_config, object $on_complete, object $user_data): int {} diff --git a/ext/awscrt_arginfo.h b/ext/awscrt_arginfo.h index 1446152..10b90d2 100644 --- a/ext/awscrt_arginfo.h +++ b/ext/awscrt_arginfo.h @@ -1,11 +1,11 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8694d01fdcf4c8ffa26a6462b6c4546671ce7aa5 */ + * Stub hash: 8ee94b778eb6d2eeabc8c2695aaded9fb18de88f */ -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_last_error, 0, 0, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_last_error, 0, 0, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_error_name, 0, 1, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, error_code, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_error_name, 0, 0, 1) + ZEND_ARG_INFO(0, error_code) ZEND_END_ARG_INFO() #define arginfo_aws_crt_error_str arginfo_aws_crt_error_name @@ -14,104 +14,104 @@ ZEND_END_ARG_INFO() #define arginfo_aws_crt_event_loop_group_options_new arginfo_aws_crt_last_error -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_event_loop_group_options_release, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, elg_options, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_event_loop_group_options_release, 0, 0, 1) + ZEND_ARG_INFO(0, elg_options) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_event_loop_group_options_set_max_threads, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, elg_options, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, max_threads, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_event_loop_group_options_set_max_threads, 0, 0, 2) + ZEND_ARG_INFO(0, elg_options) + ZEND_ARG_INFO(0, max_threads) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_event_loop_group_new, 0, 1, IS_OBJECT, 0) - ZEND_ARG_TYPE_INFO(0, options, IS_OBJECT, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_event_loop_group_new, 0, 0, 1) + ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_event_loop_group_release, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, event_loop_group, IS_OBJECT, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_event_loop_group_release, 0, 0, 1) + ZEND_ARG_INFO(0, event_loop_group) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_input_stream_options_new, 0, 0, IS_OBJECT, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_input_stream_options_new, 0, 0, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_input_stream_options_release, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, options, IS_OBJECT, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_input_stream_options_release, 0, 0, 1) + ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_input_stream_options_set_user_data, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, options, IS_OBJECT, 0) - ZEND_ARG_TYPE_INFO(0, user_data, IS_OBJECT, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_input_stream_options_set_user_data, 0, 0, 2) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, user_data) ZEND_END_ARG_INFO() #define arginfo_aws_crt_input_stream_new arginfo_aws_crt_event_loop_group_new -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_input_stream_release, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, stream, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_input_stream_release, 0, 0, 1) + ZEND_ARG_INFO(0, stream) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_input_stream_seek, 0, 3, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, stream, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, basis, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_input_stream_seek, 0, 0, 3) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, basis) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_input_stream_read, 0, 2, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, stream, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_input_stream_read, 0, 0, 2) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO(0, length) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_input_stream_eof, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, stream, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_input_stream_eof, 0, 0, 1) + ZEND_ARG_INFO(0, stream) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_input_stream_get_length, 0, 1, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, stream, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_input_stream_get_length, 0, 0, 1) + ZEND_ARG_INFO(0, stream) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_http_message_new_from_blob, 0, 1, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, blob, IS_STRING, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_http_message_new_from_blob, 0, 0, 1) + ZEND_ARG_INFO(0, blob) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_http_message_to_blob, 0, 1, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, message, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_http_message_to_blob, 0, 0, 1) + ZEND_ARG_INFO(0, message) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_http_message_release, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, message, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_http_message_release, 0, 0, 1) + ZEND_ARG_INFO(0, message) ZEND_END_ARG_INFO() #define arginfo_aws_crt_credentials_options_new arginfo_aws_crt_input_stream_options_new #define arginfo_aws_crt_credentials_options_release arginfo_aws_crt_input_stream_options_release -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_credentials_options_set_access_key_id, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, options, IS_OBJECT, 0) - ZEND_ARG_TYPE_INFO(0, access_key_id, IS_STRING, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_credentials_options_set_access_key_id, 0, 0, 2) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, access_key_id) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_credentials_options_set_secret_access_key, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, options, IS_OBJECT, 0) - ZEND_ARG_TYPE_INFO(0, secret_access_key, IS_STRING, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_credentials_options_set_secret_access_key, 0, 0, 2) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, secret_access_key) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_credentials_options_set_session_token, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, options, IS_OBJECT, 0) - ZEND_ARG_TYPE_INFO(0, session_token, IS_STRING, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_credentials_options_set_session_token, 0, 0, 2) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, session_token) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_credentials_options_set_expiration_timepoint_seconds, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, options, IS_OBJECT, 0) - ZEND_ARG_TYPE_INFO(0, expiration_timepoint_seconds, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_credentials_options_set_expiration_timepoint_seconds, 0, 0, 2) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, expiration_timepoint_seconds) ZEND_END_ARG_INFO() #define arginfo_aws_crt_credentials_new arginfo_aws_crt_event_loop_group_new -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_credentials_release, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, credentials, IS_OBJECT, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_credentials_release, 0, 0, 1) + ZEND_ARG_INFO(0, credentials) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_credentials_provider_release, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, credentials, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_credentials_provider_release, 0, 0, 1) + ZEND_ARG_INFO(0, credentials) ZEND_END_ARG_INFO() #define arginfo_aws_crt_credentials_provider_static_options_new arginfo_aws_crt_input_stream_options_new @@ -128,87 +128,101 @@ ZEND_END_ARG_INFO() #define arginfo_aws_crt_signing_config_aws_new arginfo_aws_crt_last_error -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_release, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_release, 0, 0, 1) + ZEND_ARG_INFO(0, config) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_algorithm, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, algorithm, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_algorithm, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, algorithm) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_signature_type, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, signature_type, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_signature_type, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, signature_type) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_credentials_provider, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, credentials_provider, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_credentials_provider, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, credentials_provider) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_region, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, region, IS_STRING, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_region, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, region) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_service, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, service, IS_STRING, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_service, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, service) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_use_double_uri_encode, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, use_double_uri_encode, _IS_BOOL, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_use_double_uri_encode, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, use_double_uri_encode) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_should_normalize_uri_path, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, should_normalize_uri_path, _IS_BOOL, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_should_normalize_uri_path, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, should_normalize_uri_path) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_omit_session_token, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, omit_session_token, _IS_BOOL, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_omit_session_token, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, omit_session_token) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_signed_body_value, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, signed_body_value, IS_STRING, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_signed_body_value, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, signed_body_value) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_signed_body_header_type, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, signed_body_header_type, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_signed_body_header_type, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, signed_body_header_type) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signing_config_aws_set_expiration_in_seconds, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, expiration_in_seconds, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_expiration_in_seconds, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, expiration_in_seconds) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signable_new_from_http_request, 0, 1, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, http_message, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_config_aws_set_date, 0, 0, 2) + ZEND_ARG_INFO(0, config) + ZEND_ARG_INFO(0, timestamp) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signable_new_from_chunk, 0, 2, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, input_stream, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, previous_signature, IS_STRING, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signable_new_from_http_request, 0, 0, 1) + ZEND_ARG_INFO(0, http_message) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signable_new_from_canonical_request, 0, 1, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, request, IS_STRING, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signable_new_from_chunk, 0, 0, 2) + ZEND_ARG_INFO(0, input_stream) + ZEND_ARG_INFO(0, previous_signature) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_signable_release, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, signable, IS_LONG, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signable_new_from_canonical_request, 0, 0, 1) + ZEND_ARG_INFO(0, request) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_aws_crt_sign_request_aws, 0, 4, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, signable, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, signing_config, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, on_complete, IS_OBJECT, 0) - ZEND_ARG_TYPE_INFO(0, user_data, IS_OBJECT, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signable_release, 0, 0, 1) + ZEND_ARG_INFO(0, signable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_result_release, 0, 0, 1) + ZEND_ARG_INFO(0, signing_result) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_signing_result_apply_to_http_request, 0, 0, 2) + ZEND_ARG_INFO(0, signing_result) + ZEND_ARG_INFO(0, http_request) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_aws_crt_sign_request_aws, 0, 0, 4) + ZEND_ARG_INFO(0, signable) + ZEND_ARG_INFO(0, signing_config) + ZEND_ARG_INFO(0, on_complete) + ZEND_ARG_INFO(0, user_data) ZEND_END_ARG_INFO() @@ -261,10 +275,13 @@ ZEND_FUNCTION(aws_crt_signing_config_aws_set_omit_session_token); ZEND_FUNCTION(aws_crt_signing_config_aws_set_signed_body_value); ZEND_FUNCTION(aws_crt_signing_config_aws_set_signed_body_header_type); ZEND_FUNCTION(aws_crt_signing_config_aws_set_expiration_in_seconds); +ZEND_FUNCTION(aws_crt_signing_config_aws_set_date); ZEND_FUNCTION(aws_crt_signable_new_from_http_request); ZEND_FUNCTION(aws_crt_signable_new_from_chunk); ZEND_FUNCTION(aws_crt_signable_new_from_canonical_request); ZEND_FUNCTION(aws_crt_signable_release); +ZEND_FUNCTION(aws_crt_signing_result_release); +ZEND_FUNCTION(aws_crt_signing_result_apply_to_http_request); ZEND_FUNCTION(aws_crt_sign_request_aws); @@ -318,10 +335,13 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(aws_crt_signing_config_aws_set_signed_body_value, arginfo_aws_crt_signing_config_aws_set_signed_body_value) ZEND_FE(aws_crt_signing_config_aws_set_signed_body_header_type, arginfo_aws_crt_signing_config_aws_set_signed_body_header_type) ZEND_FE(aws_crt_signing_config_aws_set_expiration_in_seconds, arginfo_aws_crt_signing_config_aws_set_expiration_in_seconds) + ZEND_FE(aws_crt_signing_config_aws_set_date, arginfo_aws_crt_signing_config_aws_set_date) ZEND_FE(aws_crt_signable_new_from_http_request, arginfo_aws_crt_signable_new_from_http_request) ZEND_FE(aws_crt_signable_new_from_chunk, arginfo_aws_crt_signable_new_from_chunk) ZEND_FE(aws_crt_signable_new_from_canonical_request, arginfo_aws_crt_signable_new_from_canonical_request) ZEND_FE(aws_crt_signable_release, arginfo_aws_crt_signable_release) + ZEND_FE(aws_crt_signing_result_release, arginfo_aws_crt_signing_result_release) + ZEND_FE(aws_crt_signing_result_apply_to_http_request, arginfo_aws_crt_signing_result_apply_to_http_request) ZEND_FE(aws_crt_sign_request_aws, arginfo_aws_crt_sign_request_aws) ZEND_FE_END }; diff --git a/ext/credentials.c b/ext/credentials.c new file mode 100644 index 0000000..f361f08 --- /dev/null +++ b/ext/credentials.c @@ -0,0 +1,154 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include "php_aws_crt.h" + +PHP_FUNCTION(aws_crt_credentials_options_new) { + aws_crt_credentials_options *options = aws_crt_credentials_options_new(); + RETURN_LONG((zend_ulong)options); +} + +PHP_FUNCTION(aws_crt_credentials_options_release) { + zend_ulong php_options = 0; + + aws_php_parse_parameters("l", &php_options); + + aws_crt_credentials_options *options = (void *)php_options; + aws_crt_credentials_options_release(options); +} + +PHP_FUNCTION(aws_crt_credentials_options_set_access_key_id) { + zend_ulong php_options = 0; + const char *access_key_id = NULL; + size_t access_key_id_len = 0; + + aws_php_parse_parameters("ls", &php_options, &access_key_id, &access_key_id_len); + + aws_crt_credentials_options *options = (void *)php_options; + aws_crt_credentials_options_set_access_key_id(options, (uint8_t *)access_key_id, access_key_id_len); +} + +PHP_FUNCTION(aws_crt_credentials_options_set_secret_access_key) { + zend_ulong php_options = 0; + const char *secret_access_key = NULL; + size_t secret_access_key_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &secret_access_key, &secret_access_key_len) == + FAILURE) { + RETURN_NULL(); + } + + aws_crt_credentials_options *options = (void *)php_options; + aws_crt_credentials_options_set_secret_access_key(options, (uint8_t *)secret_access_key, secret_access_key_len); +} + +PHP_FUNCTION(aws_crt_credentials_options_set_session_token) { + zend_ulong php_options = 0; + const char *session_token = NULL; + size_t session_token_len = 0; + + aws_php_parse_parameters("ls", &php_options, &session_token, &session_token_len); + + aws_crt_credentials_options *options = (void *)php_options; + aws_crt_credentials_options_set_session_token(options, (uint8_t *)session_token, session_token_len); +} + +PHP_FUNCTION(aws_crt_credentials_options_set_expiration_timepoint_seconds) { + zend_ulong php_options = 0; + zend_ulong expiration_timepoint_seconds = 0; + aws_php_parse_parameters("ll", &php_options, &expiration_timepoint_seconds); + + aws_crt_credentials_options *options = (void *)php_options; + aws_crt_credentials_options_set_expiration_timepoint_seconds(options, expiration_timepoint_seconds); +} + +PHP_FUNCTION(aws_crt_credentials_new) { + zend_ulong php_options = 0; + + aws_php_parse_parameters("l", &php_options); + + aws_crt_credentials_options *options = (void *)php_options; + aws_crt_credentials *credentials = aws_crt_credentials_new(options); + RETURN_LONG((zend_ulong)credentials); +} + +PHP_FUNCTION(aws_crt_credentials_release) { + zend_ulong php_credentials = 0; + + aws_php_parse_parameters("l", &php_credentials); + + aws_crt_credentials *credentials = (void *)php_credentials; + aws_crt_credentials_release(credentials); +} + +PHP_FUNCTION(aws_crt_credentials_provider_release) { + zend_ulong php_creds_provider = 0; + + aws_php_parse_parameters("l", &php_creds_provider); + + aws_crt_credentials_provider *provider = (void *)php_creds_provider; + aws_crt_credentials_provider_release(provider); +} + +PHP_FUNCTION(aws_crt_credentials_provider_static_options_new) { + aws_crt_credentials_provider_static_options *options = aws_crt_credentials_provider_static_options_new(); + RETURN_LONG((zend_ulong)options); +} + +PHP_FUNCTION(aws_crt_credentials_provider_static_options_release) { + zend_ulong php_options = 0; + + aws_php_parse_parameters("l", &php_options); + + aws_crt_credentials_provider_static_options *options = (void *)php_options; + aws_crt_credentials_provider_static_options_release(options); +} + +PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_access_key_id) { + zend_ulong php_options = 0; + const char *access_key_id = NULL; + size_t access_key_id_len = 0; + + aws_php_parse_parameters("ls", &php_options, &access_key_id, &access_key_id_len); + + aws_crt_credentials_provider_static_options *options = (void *)php_options; + aws_crt_credentials_provider_static_options_set_access_key_id(options, (uint8_t *)access_key_id, access_key_id_len); +} + +PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_secret_access_key) { + zend_ulong php_options = 0; + const char *secret_access_key = NULL; + size_t secret_access_key_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &secret_access_key, &secret_access_key_len) == + FAILURE) { + RETURN_NULL(); + } + + aws_crt_credentials_provider_static_options *options = (void *)php_options; + aws_crt_credentials_provider_static_options_set_secret_access_key( + options, (uint8_t *)secret_access_key, secret_access_key_len); +} + +PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_session_token) { + zend_ulong php_options = 0; + const char *session_token = NULL; + size_t session_token_len = 0; + + aws_php_parse_parameters("ls", &php_options, &session_token, &session_token_len); + + aws_crt_credentials_provider_static_options *options = (void *)php_options; + aws_crt_credentials_provider_static_options_set_session_token(options, (uint8_t *)session_token, session_token_len); +} + +PHP_FUNCTION(aws_crt_credentials_provider_static_new) { + zend_ulong php_options = 0; + + aws_php_parse_parameters("l", &php_options); + + aws_crt_credentials_provider_static_options *options = (void *)php_options; + aws_crt_credentials_provider *provider = aws_crt_credentials_provider_static_new(options); + RETURN_LONG((zend_ulong)provider); +} diff --git a/ext/crt.c b/ext/crt.c index 816d80f..0a37d40 100644 --- a/ext/crt.c +++ b/ext/crt.c @@ -6,82 +6,22 @@ #include "php_aws_crt.h" -#include "api.h" - -#include "awscrt_arginfo.h" - -/* Helpful references for this file: +/* Helpful references for this extension: * zend_parse_parameters and friends - * https://git.php.net/?p=php-src.git;a=blob;f=docs/parameter-parsing-api.md;h=c962fc6ee58cc756aaac9e65759b7d5ea5c18fc4;hb=HEAD * https://git.php.net/?p=php-src.git;a=blob;f=docs/self-contained-extensions.md;h=47f4c636baca8ca195118e2cc234ac7fd2842c1b;hb=HEAD + * Threads: + * http://blog.jpauli.tech/2017-01-12-threads-and-php-html/ * Examples: * Curl extension: https://github.com/php/php-src/blob/PHP-5.6/ext/curl/interface.c * libuv extension: https://github.com/amphp/ext-uv/blob/master/php_uv.c */ -/* Macros borrowed from aws-c-common */ -#define GLUE(x, y) x y - -#define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count -#define EXPAND_ARGS(args) RETURN_ARG_COUNT args -#define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0)) - -#define OVERLOAD_MACRO2(name, count) name##count -#define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count) -#define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count) - -#define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__)) - -#define VARIABLE_LENGTH_ARRAY(type, name, length) type *name = alloca(sizeof(type) * (length)) - -/* - * Exception throwing mechanism, will never return - */ -#define aws_php_throw_exception(...) CALL_OVERLOAD(_AWS_PHP_THROW_EXCEPTION, __VA_ARGS__); -#define _AWS_PHP_THROW_EXCEPTION5(format, ...) zend_error_noreturn(E_ERROR, format, __VA_ARGS__) -#define _AWS_PHP_THROW_EXCEPTION4(format, ...) zend_error_noreturn(E_ERROR, format, __VA_ARGS__) -#define _AWS_PHP_THROW_EXCEPTION3(format, ...) zend_error_noreturn(E_ERROR, format, __VA_ARGS__) -#define _AWS_PHP_THROW_EXCEPTION2(format, ...) zend_error_noreturn(E_ERROR, format, __VA_ARGS__) -#define _AWS_PHP_THROW_EXCEPTION1(format) zend_error_noreturn(E_ERROR, format) - -/** - * throws an exception resulting from argument parsing, notes the current function name in the exception - */ -#define aws_php_argparse_fail() \ - do { \ - aws_php_throw_exception("Failed to parse arguments to %s", __func__); \ - } while (0) - -/** - * calls zend_parse_parameters() with the arguments and throws an exception if parsing fails - */ -#define aws_php_parse_parameters(type_spec, ...) \ - do { \ - if (zend_parse_parameters(ZEND_NUM_ARGS(), type_spec, __VA_ARGS__) == FAILURE) { \ - aws_php_argparse_fail(); \ - } \ - } while (0) - -/** - * calls zend_parse_parameters_none() and throws an exception if parsing fails - */ -#define aws_php_parse_parameters_none() \ - do { \ - if (zend_parse_parameters_none() == FAILURE) { \ - aws_php_argparse_fail(); \ - } \ - } while (0) - -/** - * generic dispatch mechanism to call a callback provided as a zval with arguments - * that are converted to zvals based on the arg_types format string - * Uses the same format string as zend_parse_parameters - */ -static zval *aws_php_invoke_callback(zval *callback, const char *arg_types, ...) { +zval aws_php_invoke_callback(zval *callback, const char *arg_types, ...) { char *error = NULL; zend_fcall_info fci = {0}; - zend_fcall_info_cache fcc = {0}; + zend_fcall_info_cache fcc = empty_fcall_info_cache; if (zend_fcall_info_init(callback, IS_CALLABLE_CHECK_SYNTAX_ONLY, &fci, &fcc, NULL, &error) == FAILURE) { aws_php_throw_exception("Unable to initialize callback from callable via zend_fcall_info_init: %s", error); } @@ -89,7 +29,6 @@ static zval *aws_php_invoke_callback(zval *callback, const char *arg_types, ...) /* Allocate the stack frame of zval arguments and fill them in */ const size_t num_args = strlen(arg_types); zval *stack = alloca(sizeof(zval) * num_args); - zval *retval = NULL; int arg_idx = 0; va_list va; va_start(va, arg_types); @@ -159,38 +98,132 @@ static zval *aws_php_invoke_callback(zval *callback, const char *arg_types, ...) zend_fcall_info_argp(&fci, num_args, stack); #else /* PHP5.6 may mutate the arguments due to coercion */ - zval **args = alloca(sizeof(zval *) * num_args); + zval **arg_ptrs = alloca(sizeof(zval *) * num_args); + zval ***args = alloca(sizeof(zval **) * num_args); for (int arg_idx = 0; arg_idx < num_args; ++arg_idx) { - args[arg_idx] = &stack[arg_idx]; + arg_ptrs[arg_idx] = &stack[arg_idx]; + args[arg_idx] = &arg_ptrs[arg_idx]; } - zend_fcall_info_argp(&fci, num_args, &args); + fci.param_count = num_args; + fci.params = args; #endif - /* PHP5 requires us to have a retval on the stack that zend fills out */ + zval retval; + /* PHP5 allocates its own return value, 7+ uses an existing one we provide */ #if !AWS_PHP_AT_LEAST_7 - fci.retval_ptr_ptr = &retval; + zval *retval5 = NULL; + fci.retval_ptr_ptr = &retval5; +#else + fci.retval = &retval; #endif if (zend_call_function(&fci, &fcc) == FAILURE) { aws_php_throw_exception("zend_call_function failed in aws_php_invoke_callback"); } - /* PHP7+ allocates its own retval, so we need to copy it out */ -#if AWS_PHP_AT_LEAST_7 - retval = fci.retval; +#if !AWS_PHP_AT_LEAST_7 + /* initialize the local retval from the retval in retval_ptr_ptr above */ + if (retval5) { + ZVAL_ZVAL(&retval, retval5, 1, 1); + } #endif /* Clean up arguments */ for (int arg_idx = 0; arg_idx < num_args; ++arg_idx) { #if !AWS_PHP_AT_LEAST_7 zval_ptr_dtor(args[arg_idx]); -#endif +#else zval_ptr_dtor(&stack[arg_idx]); +#endif } return retval; } +aws_php_thread_queue s_aws_php_main_thread_queue; + +bool aws_php_is_main_thread(void) { + return s_aws_php_main_thread_queue.thread_id == aws_thread_current_thread_id(); +} + +void aws_php_thread_queue_init(aws_php_thread_queue *queue) { + aws_mutex_init(&queue->mutex); + memset(queue->queue, 0, sizeof(aws_php_task) * AWS_PHP_THREAD_QUEUE_MAX_DEPTH); + queue->write_slot = 0; + queue->thread_id = aws_thread_current_thread_id(); +} + +void aws_php_thread_queue_clean_up(aws_php_thread_queue *queue) { + assert(queue->write_slot == 0 && "aws_php_thread_queue cannot be cleaned up while queue is not empty"); + aws_mutex_clean_up(&queue->mutex); +} + +void aws_php_thread_queue_push(aws_php_thread_queue *queue, aws_php_task task) { + aws_mutex_lock(&queue->mutex); + assert(queue->write_slot < AWS_PHP_THREAD_QUEUE_MAX_DEPTH && "thread queue is full"); + queue->queue[queue->write_slot++] = task; + aws_mutex_unlock(&queue->mutex); +} + +bool aws_php_thread_queue_drain(aws_php_thread_queue *queue) { + assert( + queue->thread_id == aws_thread_current_thread_id() && + "thread queue cannot be drained from a thread other than its home"); + aws_php_task drain_queue[AWS_PHP_THREAD_QUEUE_MAX_DEPTH]; + aws_mutex_lock(&queue->mutex); + /* copy any queued tasks into the drain queue, then reset the queue */ + memcpy(drain_queue, queue->queue, sizeof(aws_php_task) * AWS_PHP_THREAD_QUEUE_MAX_DEPTH); + memset(queue->queue, 0, sizeof(aws_php_task) * AWS_PHP_THREAD_QUEUE_MAX_DEPTH); + queue->write_slot = 0; + aws_mutex_unlock(&queue->mutex); + + bool did_work = false; + for (int idx = 0; idx < AWS_PHP_THREAD_QUEUE_MAX_DEPTH; ++idx) { + aws_php_task *task = &drain_queue[idx]; + if (!task->callback) { + break; + } + did_work = true; + task->callback(task->data); + if (task->dtor) { + task->dtor(task->data); + } + } + + return did_work; +} + +/* called on main thread after delivery */ +static void s_thread_queue_complete_promise(void *data) { + struct aws_promise *promise = data; + aws_promise_complete(promise, NULL, NULL); +} + +/* called from worker thread to wait for the main thread to execute any queued work in PHP */ +void aws_php_thread_queue_yield(aws_php_thread_queue *queue) { + /* If on the main thread, then just drain the queue */ + if (aws_php_is_main_thread()) { + aws_php_thread_queue_drain(queue); + } else { + /* push a task onto the end of the queue, we will return once this task completes our promise */ + struct aws_promise *queue_drained = aws_promise_new(aws_crt_default_allocator()); + aws_php_task queue_drained_task = { + .callback = s_thread_queue_complete_promise, + .data = queue_drained, + }; + aws_php_thread_queue_push(queue, queue_drained_task); + aws_promise_wait(queue_drained); + aws_promise_release(queue_drained); + } +} + +/* called from PHP thread to wait on async queued jobs, one of which should complete the promise */ +void aws_php_thread_queue_wait(aws_php_thread_queue *queue, struct aws_promise *promise) { + while (!aws_promise_is_complete(promise)) { + aws_php_thread_queue_drain(queue); + } +} + ZEND_DECLARE_MODULE_GLOBALS(awscrt); PHP_INI_BEGIN() @@ -208,11 +241,13 @@ static PHP_MINIT_FUNCTION(awscrt) { REGISTER_INI_ENTRIES(); aws_crt_init(); + aws_php_thread_queue_init(&s_aws_php_main_thread_queue); return SUCCESS; } static PHP_MSHUTDOWN_FUNCTION(awscrt) { UNREGISTER_INI_ENTRIES(); + aws_php_thread_queue_clean_up(&s_aws_php_main_thread_queue); aws_crt_clean_up(); return SUCCESS; } @@ -224,595 +259,6 @@ static PHP_GINIT_FUNCTION(awscrt) { awscrt_globals->log_level = 0; } -/* aws_crt_last_error() */ -PHP_FUNCTION(aws_crt_last_error) { - RETURN_LONG(aws_crt_last_error()); -} - -/* aws_crt_error_str(int error_code) */ -PHP_FUNCTION(aws_crt_error_str) { - zend_ulong error_code = 0; - aws_php_parse_parameters("l", &error_code); - - AWS_RETURN_STRING(aws_crt_error_str(error_code)); -} - -/* aws_crt_error_name(int error_code) */ -PHP_FUNCTION(aws_crt_error_name) { - zend_ulong error_code = 0; - aws_php_parse_parameters("l", &error_code); - - AWS_RETURN_STRING(aws_crt_error_name(error_code)); -} - -/* aws_crt_error_debug_str(int error_code) */ -PHP_FUNCTION(aws_crt_error_debug_str) { - zend_ulong error_code = 0; - aws_php_parse_parameters("l", &error_code); - - AWS_RETURN_STRING(aws_crt_error_debug_str(error_code)); -} - -PHP_FUNCTION(aws_crt_event_loop_group_options_new) { - aws_php_parse_parameters_none(); - aws_crt_event_loop_group_options *options = aws_crt_event_loop_group_options_new(); - RETURN_LONG((zend_ulong)options); -} - -PHP_FUNCTION(aws_crt_event_loop_group_options_release) { - zend_ulong php_options = 0; - aws_php_parse_parameters("l", &php_options); - - aws_crt_event_loop_group_options *options = (void *)php_options; - aws_crt_event_loop_group_options_release(options); -} - -PHP_FUNCTION(aws_crt_event_loop_group_options_set_max_threads) { - zend_ulong php_options = 0; - zend_ulong num_threads = 0; - aws_php_parse_parameters("ll", &php_options, &num_threads); - - aws_crt_event_loop_group_options *options = (void *)php_options; - aws_crt_event_loop_group_options_set_max_threads(options, num_threads); -} - -PHP_FUNCTION(aws_crt_event_loop_group_new) { - zend_ulong php_options = 0; - - aws_php_parse_parameters("l", &php_options); - - aws_crt_event_loop_group_options *options = (void *)php_options; - aws_crt_event_loop_group *elg = aws_crt_event_loop_group_new(options); - RETURN_LONG((zend_ulong)elg); -} - -PHP_FUNCTION(aws_crt_event_loop_group_release) { - zend_ulong php_elg = 0; - - aws_php_parse_parameters("l", &php_elg); - - struct aws_event_loop_group *elg = (void *)php_elg; - aws_crt_event_loop_group_release(elg); -} - -PHP_FUNCTION(aws_crt_input_stream_options_new) { - if (zend_parse_parameters_none() == FAILURE) { - aws_php_argparse_fail(); - } - - aws_crt_input_stream_options *options = aws_crt_input_stream_options_new(); - RETURN_LONG((zend_ulong)options); -} - -PHP_FUNCTION(aws_crt_input_stream_options_release) { - zend_ulong php_options = 0; - - aws_php_parse_parameters("l", &php_options); - - aws_crt_input_stream_options *options = (void *)php_options; - aws_crt_input_stream_options_release(options); -} - -/* PHP streams info: - * https://git.php.net/?p=php-src.git;a=blob;f=docs/streams.md;h=0ec3846d68bf70067297d8a6c691d2591c49b48a;hb=HEAD - * https://github.com/php/php-src/blob/PHP-5.6.0/main/php_streams.h - */ - -PHP_FUNCTION(aws_crt_input_stream_options_set_user_data) { - zend_ulong php_options = 0; - zval *user_data = NULL; - - aws_php_parse_parameters("lz", &php_options, &user_data); - - aws_crt_input_stream_options *options = (void *)php_options; - php_stream *stream = NULL; - AWS_PHP_STREAM_FROM_ZVAL(stream, user_data); - aws_crt_input_stream_options_set_user_data(options, stream); -} - -static int s_php_stream_seek(void *user_data, int64_t offset, aws_crt_input_stream_seek_basis basis) { - php_stream *stream = user_data; - return php_stream_seek(stream, offset, basis); -} - -static int s_php_stream_read(void *user_data, uint8_t *dest, size_t dest_length) { - php_stream *stream = user_data; - return php_stream_read(stream, (char *)dest, dest_length) != 0; -} - -static int s_php_stream_get_length(void *user_data, int64_t *out_length) { - php_stream *stream = user_data; - size_t pos = php_stream_tell(stream); - php_stream_seek(stream, 0, SEEK_END); - *out_length = php_stream_tell(stream); - php_stream_seek(stream, pos, SEEK_SET); - return 0; -} - -static int s_php_stream_get_status(void *user_data, aws_crt_input_stream_status *out_status) { - php_stream *stream = user_data; - out_status->is_valid = stream != NULL; - /* We would like to use php_stream_eof here, but certain streams (notably php://memory) - * are not actually capable of EOF, so we get to do it the hard way */ - int64_t length = 0; - int64_t pos = 0; - s_php_stream_get_length(stream, &length); - pos = php_stream_tell(stream); - out_status->is_end_of_stream = pos == length; - return 0; -} - -static void s_php_stream_destroy(void *user_data) { - (void)user_data; - /* no op, stream will be freed by PHP refcount dropping from InputStream::stream */ -} - -PHP_FUNCTION(aws_crt_input_stream_new) { - zend_ulong php_options = 0; - - aws_php_parse_parameters("l", &php_options); - - aws_crt_input_stream_options *options = (void *)php_options; - aws_crt_input_stream_options_set_seek(options, s_php_stream_seek); - aws_crt_input_stream_options_set_read(options, s_php_stream_read); - aws_crt_input_stream_options_set_get_status(options, s_php_stream_get_status); - aws_crt_input_stream_options_set_get_length(options, s_php_stream_get_length); - aws_crt_input_stream_options_set_destroy(options, s_php_stream_destroy); - aws_crt_input_stream *stream = aws_crt_input_stream_new(options); - RETURN_LONG((zend_ulong)stream); -} - -PHP_FUNCTION(aws_crt_input_stream_release) { - zend_ulong php_stream = 0; - - aws_php_parse_parameters("l", &php_stream); - - aws_crt_input_stream *stream = (void *)php_stream; - aws_crt_input_stream_release(stream); -} - -PHP_FUNCTION(aws_crt_input_stream_seek) { - zend_ulong php_stream = 0; - zend_ulong offset = 0; - zend_ulong basis = 0; - - aws_php_parse_parameters("lll", &php_stream, &offset, &basis); - - aws_crt_input_stream *stream = (void *)php_stream; - RETURN_LONG(aws_crt_input_stream_seek(stream, offset, basis)); -} - -PHP_FUNCTION(aws_crt_input_stream_read) { - zend_ulong php_stream = 0; - zend_ulong length = 0; - - aws_php_parse_parameters("ll", &php_stream, &length); - - aws_crt_input_stream *stream = (void *)php_stream; - uint8_t *buf = emalloc(length); - int ret = aws_crt_input_stream_read(stream, buf, length); - AWS_RETURN_STRINGL((const char *)buf, length); - efree(buf); -} - -PHP_FUNCTION(aws_crt_input_stream_eof) { - zend_ulong php_stream = 0; - - aws_php_parse_parameters("l", &php_stream); - - aws_crt_input_stream *stream = (void *)php_stream; - aws_crt_input_stream_status status = {0}; - aws_crt_input_stream_get_status(stream, &status); - RETURN_BOOL(status.is_end_of_stream); -} - -PHP_FUNCTION(aws_crt_input_stream_get_length) { - zend_ulong php_stream = 0; - - aws_php_parse_parameters("l", &php_stream); - - aws_crt_input_stream *stream = (void *)php_stream; - int64_t length = 0; - aws_crt_input_stream_get_length(stream, &length); - RETURN_LONG(length); -} - -PHP_FUNCTION(aws_crt_http_message_new_from_blob) { - const char *blob = NULL; - size_t blob_len = 0; - - aws_php_parse_parameters("s", &blob, &blob_len); - - aws_crt_http_message *message = aws_crt_http_message_new_from_blob((uint8_t *)blob, blob_len); - RETURN_LONG((zend_ulong)message); -} - -PHP_FUNCTION(aws_crt_http_message_to_blob) { - zend_ulong php_msg = 0; - - aws_php_parse_parameters("l", &php_msg); - - aws_crt_http_message *message = (void *)php_msg; - uint8_t *blob = NULL; - size_t blob_len = 0; - aws_crt_http_message_to_blob(message, &blob, &blob_len); - AWS_RETURN_STRINGL((const char *)blob, blob_len); - aws_crt_mem_release(blob); -} - -PHP_FUNCTION(aws_crt_http_message_release) { - zend_ulong php_msg = 0; - - aws_php_parse_parameters("l", &php_msg); - - aws_crt_http_message *message = (void *)php_msg; - aws_crt_http_message_release(message); -} - -PHP_FUNCTION(aws_crt_credentials_options_new) { - aws_crt_credentials_options *options = aws_crt_credentials_options_new(); - RETURN_LONG((zend_ulong)options); -} - -PHP_FUNCTION(aws_crt_credentials_options_release) { - zend_ulong php_options = 0; - - aws_php_parse_parameters("l", &php_options); - - aws_crt_credentials_options *options = (void *)php_options; - aws_crt_credentials_options_release(options); -} - -PHP_FUNCTION(aws_crt_credentials_options_set_access_key_id) { - zend_ulong php_options = 0; - const char *access_key_id = NULL; - size_t access_key_id_len = 0; - - aws_php_parse_parameters("ls", &php_options, &access_key_id, &access_key_id_len); - - aws_crt_credentials_options *options = (void *)php_options; - aws_crt_credentials_options_set_access_key_id(options, (uint8_t *)access_key_id, access_key_id_len); -} - -PHP_FUNCTION(aws_crt_credentials_options_set_secret_access_key) { - zend_ulong php_options = 0; - const char *secret_access_key = NULL; - size_t secret_access_key_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &secret_access_key, &secret_access_key_len) == - FAILURE) { - RETURN_NULL(); - } - - aws_crt_credentials_options *options = (void *)php_options; - aws_crt_credentials_options_set_secret_access_key(options, (uint8_t *)secret_access_key, secret_access_key_len); -} - -PHP_FUNCTION(aws_crt_credentials_options_set_session_token) { - zend_ulong php_options = 0; - const char *session_token = NULL; - size_t session_token_len = 0; - - aws_php_parse_parameters("ls", &php_options, &session_token, &session_token_len); - - aws_crt_credentials_options *options = (void *)php_options; - aws_crt_credentials_options_set_session_token(options, (uint8_t *)session_token, session_token_len); -} - -PHP_FUNCTION(aws_crt_credentials_options_set_expiration_timepoint_seconds) { - zend_ulong php_options = 0; - zend_ulong expiration_timepoint_seconds = 0; - aws_php_parse_parameters("ll", &php_options, &expiration_timepoint_seconds); - - aws_crt_credentials_options *options = (void *)php_options; - aws_crt_credentials_options_set_expiration_timepoint_seconds(options, expiration_timepoint_seconds); -} - -PHP_FUNCTION(aws_crt_credentials_new) { - zend_ulong php_options = 0; - - aws_php_parse_parameters("l", &php_options); - - aws_crt_credentials_options *options = (void *)php_options; - aws_crt_credentials *credentials = aws_crt_credentials_new(options); - RETURN_LONG((zend_ulong)credentials); -} - -PHP_FUNCTION(aws_crt_credentials_release) { - zend_ulong php_credentials = 0; - - aws_php_parse_parameters("l", &php_credentials); - - aws_crt_credentials *credentials = (void *)php_credentials; - aws_crt_credentials_release(credentials); -} - -PHP_FUNCTION(aws_crt_credentials_provider_release) { - zend_ulong php_creds_provider = 0; - - aws_php_parse_parameters("l", &php_creds_provider); - - aws_crt_credentials_provider *provider = (void *)php_creds_provider; - aws_crt_credentials_provider_release(provider); -} - -PHP_FUNCTION(aws_crt_credentials_provider_static_options_new) { - aws_crt_credentials_provider_static_options *options = aws_crt_credentials_provider_static_options_new(); - RETURN_LONG((zend_ulong)options); -} - -PHP_FUNCTION(aws_crt_credentials_provider_static_options_release) { - zend_ulong php_options = 0; - - aws_php_parse_parameters("l", &php_options); - - aws_crt_credentials_provider_static_options *options = (void *)php_options; - aws_crt_credentials_provider_static_options_release(options); -} - -PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_access_key_id) { - zend_ulong php_options = 0; - const char *access_key_id = NULL; - size_t access_key_id_len = 0; - - aws_php_parse_parameters("ls", &php_options, &access_key_id, &access_key_id_len); - - aws_crt_credentials_provider_static_options *options = (void *)php_options; - aws_crt_credentials_provider_static_options_set_access_key_id(options, (uint8_t *)access_key_id, access_key_id_len); -} - -PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_secret_access_key) { - zend_ulong php_options = 0; - const char *secret_access_key = NULL; - size_t secret_access_key_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &php_options, &secret_access_key, &secret_access_key_len) == - FAILURE) { - RETURN_NULL(); - } - - aws_crt_credentials_provider_static_options *options = (void *)php_options; - aws_crt_credentials_provider_static_options_set_secret_access_key( - options, (uint8_t *)secret_access_key, secret_access_key_len); -} - -PHP_FUNCTION(aws_crt_credentials_provider_static_options_set_session_token) { - zend_ulong php_options = 0; - const char *session_token = NULL; - size_t session_token_len = 0; - - aws_php_parse_parameters("ls", &php_options, &session_token, &session_token_len); - - aws_crt_credentials_provider_static_options *options = (void *)php_options; - aws_crt_credentials_provider_static_options_set_session_token(options, (uint8_t *)session_token, session_token_len); -} - -PHP_FUNCTION(aws_crt_credentials_provider_static_new) { - zend_ulong php_options = 0; - - aws_php_parse_parameters("l", &php_options); - - aws_crt_credentials_provider_static_options *options = (void *)php_options; - aws_crt_credentials_provider *provider = aws_crt_credentials_provider_static_new(options); - RETURN_LONG((zend_ulong)provider); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_new) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_NULL(); - } - - aws_crt_signing_config_aws *signing_config = aws_crt_signing_config_aws_new(); - RETURN_LONG((zend_ulong)signing_config); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_release) { - zend_ulong php_signing_config = 0; - - aws_php_parse_parameters("l", &php_signing_config); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signing_config_aws_release(signing_config); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_algorithm) { - zend_ulong php_signing_config = 0; - zend_ulong php_algorithm = 0; - - aws_php_parse_parameters("ll", &php_signing_config, &php_algorithm); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signing_algorithm algorithm = php_algorithm; - aws_crt_signing_config_aws_set_algorithm(signing_config, algorithm); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_signature_type) { - zend_ulong php_signing_config = 0; - zend_ulong php_signature_type = 0; - - aws_php_parse_parameters("ll", &php_signing_config, &php_signature_type); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signature_type signature_type = php_signature_type; - aws_crt_signing_config_aws_set_signature_type(signing_config, signature_type); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_credentials_provider) { - zend_ulong php_signing_config = 0; - zend_ulong php_credentials_provider = 0; - - aws_php_parse_parameters("ll", &php_signing_config, &php_credentials_provider); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_credentials_provider *credentials_provider = (void *)php_credentials_provider; - aws_crt_signing_config_aws_set_credentials_provider(signing_config, credentials_provider); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_region) { - zend_ulong php_signing_config = 0; - const char *region = NULL; - size_t region_len = 0; - - aws_php_parse_parameters("ls", &php_signing_config, ®ion, ®ion_len); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signing_config_aws_set_region(signing_config, (uint8_t *)region, region_len); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_service) { - zend_ulong php_signing_config = 0; - const char *service = NULL; - size_t service_len = 0; - - aws_php_parse_parameters("ls", &php_signing_config, &service, &service_len); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signing_config_aws_set_service(signing_config, (uint8_t *)service, service_len); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_use_double_uri_encode) { - zend_ulong php_signing_config = 0; - zend_bool php_use_double_uri_encode = 0; - - aws_php_parse_parameters("lb", &php_signing_config, &php_use_double_uri_encode); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signing_config_aws_set_use_double_uri_encode(signing_config, php_use_double_uri_encode); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_should_normalize_uri_path) { - zend_ulong php_signing_config = 0; - zend_bool php_should_normalize_uri_path = 0; - - aws_php_parse_parameters("lb", &php_signing_config, &php_should_normalize_uri_path); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signing_config_aws_set_should_normalize_uri_path(signing_config, php_should_normalize_uri_path); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_omit_session_token) { - zend_ulong php_signing_config = 0; - zend_bool php_omit_session_token = 0; - - aws_php_parse_parameters("lb", &php_signing_config, &php_omit_session_token); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signing_config_aws_set_omit_session_token(signing_config, php_omit_session_token); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_signed_body_value) { - zend_ulong php_signing_config = 0; - const char *signed_body_value = NULL; - size_t signed_body_value_len = 0; - - aws_php_parse_parameters("ls", &php_signing_config, &signed_body_value, &signed_body_value_len); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signing_config_aws_set_signed_body_value( - signing_config, (uint8_t *)signed_body_value, signed_body_value_len); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_signed_body_header_type) { - zend_ulong php_signing_config = 0; - zend_ulong php_signed_body_header_type = 0; - - aws_php_parse_parameters("ll", &php_signing_config, &php_signed_body_header_type); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signed_body_header_type signed_body_header_type = php_signed_body_header_type; - aws_crt_signing_config_aws_set_signed_body_header_type(signing_config, signed_body_header_type); -} - -PHP_FUNCTION(aws_crt_signing_config_aws_set_expiration_in_seconds) { - zend_ulong php_signing_config = 0; - zend_ulong php_expiration_in_seconds = 0; - - aws_php_parse_parameters("ll", &php_signing_config, &php_expiration_in_seconds); - - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - aws_crt_signing_config_aws_set_expiration_in_seconds(signing_config, php_expiration_in_seconds); -} - -PHP_FUNCTION(aws_crt_signable_new_from_http_request) { - zend_ulong php_http_message = 0; - - aws_php_parse_parameters("l", &php_http_message); - - const aws_crt_http_message *http_message = (void *)php_http_message; - aws_crt_signable *signable = aws_crt_signable_new_from_http_request(http_message); - RETURN_LONG((zend_ulong)signable); -} - -PHP_FUNCTION(aws_crt_signable_new_from_chunk) { - zend_ulong php_input_stream = 0; - const char *previous_signature = NULL; - size_t previous_signature_len = 0; - - aws_php_parse_parameters("ls", &php_input_stream, &previous_signature, &previous_signature_len); - - aws_crt_input_stream *input_stream = (void *)php_input_stream; - aws_crt_signable *signable = - aws_crt_signable_new_from_chunk(input_stream, (uint8_t *)previous_signature, previous_signature_len); - RETURN_LONG((zend_ulong)signable); -} - -PHP_FUNCTION(aws_crt_signable_new_from_canonical_request) { - const char *canonical_request = NULL; - size_t canonical_request_len = 0; - - aws_crt_signable *signable = - aws_crt_signable_new_from_canonical_request((uint8_t *)canonical_request, canonical_request_len); - RETURN_LONG((zend_ulong)signable); -} - -PHP_FUNCTION(aws_crt_signable_release) { - zend_ulong php_signable = 0; - - aws_php_parse_parameters("l", &php_signable); - - aws_crt_signable *signable = (void *)php_signable; - aws_crt_signable_release(signable); -} - -static void s_on_sign_request_aws_complete(aws_crt_signing_result *result, int error_code, void *user_data) { - zval *on_complete = user_data; - - aws_php_invoke_callback(on_complete, "ll", (zend_ulong)result, (zend_ulong)error_code); -} - -PHP_FUNCTION(aws_crt_sign_request_aws) { - zend_ulong php_signable = 0; - zend_ulong php_signing_config = 0; - zval *php_on_complete = 0; - - aws_php_parse_parameters("llz", &php_signable, &php_signing_config, &php_on_complete); - - aws_crt_signable *signable = (void *)php_signable; - aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; - int ret = aws_crt_sign_request_aws(signable, signing_config, s_on_sign_request_aws_complete, php_on_complete); - RETURN_LONG(ret); -} - zend_module_entry awscrt_module_entry = { STANDARD_MODULE_HEADER, "awscrt", @@ -833,3 +279,32 @@ zend_module_entry awscrt_module_entry = { #ifdef COMPILE_DL_AWSCRT ZEND_GET_MODULE(awscrt) #endif + +/* aws_crt_last_error() */ +PHP_FUNCTION(aws_crt_last_error) { + RETURN_LONG(aws_crt_last_error()); +} + +/* aws_crt_error_str(int error_code) */ +PHP_FUNCTION(aws_crt_error_str) { + zend_ulong error_code = 0; + aws_php_parse_parameters("l", &error_code); + + RETURN_STRING(aws_crt_error_str(error_code)); +} + +/* aws_crt_error_name(int error_code) */ +PHP_FUNCTION(aws_crt_error_name) { + zend_ulong error_code = 0; + aws_php_parse_parameters("l", &error_code); + + RETURN_STRING(aws_crt_error_name(error_code)); +} + +/* aws_crt_error_debug_str(int error_code) */ +PHP_FUNCTION(aws_crt_error_debug_str) { + zend_ulong error_code = 0; + aws_php_parse_parameters("l", &error_code); + + RETURN_STRING(aws_crt_error_debug_str(error_code)); +} diff --git a/ext/event_loop.c b/ext/event_loop.c new file mode 100644 index 0000000..7e78bef --- /dev/null +++ b/ext/event_loop.c @@ -0,0 +1,48 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include "php_aws_crt.h" + +PHP_FUNCTION(aws_crt_event_loop_group_options_new) { + aws_php_parse_parameters_none(); + aws_crt_event_loop_group_options *options = aws_crt_event_loop_group_options_new(); + RETURN_LONG((zend_ulong)options); +} + +PHP_FUNCTION(aws_crt_event_loop_group_options_release) { + zend_ulong php_options = 0; + aws_php_parse_parameters("l", &php_options); + + aws_crt_event_loop_group_options *options = (void *)php_options; + aws_crt_event_loop_group_options_release(options); +} + +PHP_FUNCTION(aws_crt_event_loop_group_options_set_max_threads) { + zend_ulong php_options = 0; + zend_ulong num_threads = 0; + aws_php_parse_parameters("ll", &php_options, &num_threads); + + aws_crt_event_loop_group_options *options = (void *)php_options; + aws_crt_event_loop_group_options_set_max_threads(options, num_threads); +} + +PHP_FUNCTION(aws_crt_event_loop_group_new) { + zend_ulong php_options = 0; + + aws_php_parse_parameters("l", &php_options); + + aws_crt_event_loop_group_options *options = (void *)php_options; + aws_crt_event_loop_group *elg = aws_crt_event_loop_group_new(options); + RETURN_LONG((zend_ulong)elg); +} + +PHP_FUNCTION(aws_crt_event_loop_group_release) { + zend_ulong php_elg = 0; + + aws_php_parse_parameters("l", &php_elg); + + struct aws_event_loop_group *elg = (void *)php_elg; + aws_crt_event_loop_group_release(elg); +} diff --git a/ext/http.c b/ext/http.c new file mode 100644 index 0000000..732abf7 --- /dev/null +++ b/ext/http.c @@ -0,0 +1,38 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include "php_aws_crt.h" + +PHP_FUNCTION(aws_crt_http_message_new_from_blob) { + const char *blob = NULL; + size_t blob_len = 0; + + aws_php_parse_parameters("s", &blob, &blob_len); + + aws_crt_http_message *message = aws_crt_http_message_new_from_blob((uint8_t *)blob, blob_len); + RETURN_LONG((zend_ulong)message); +} + +PHP_FUNCTION(aws_crt_http_message_to_blob) { + zend_ulong php_msg = 0; + + aws_php_parse_parameters("l", &php_msg); + + aws_crt_http_message *message = (void *)php_msg; + uint8_t *blob = NULL; + size_t blob_len = 0; + aws_crt_http_message_to_blob(message, &blob, &blob_len); + RETURN_STRINGL((const char *)blob, blob_len); + aws_crt_mem_release(blob); +} + +PHP_FUNCTION(aws_crt_http_message_release) { + zend_ulong php_msg = 0; + + aws_php_parse_parameters("l", &php_msg); + + aws_crt_http_message *message = (void *)php_msg; + aws_crt_http_message_release(message); +} diff --git a/ext/php_aws_crt.h b/ext/php_aws_crt.h index 56eafd6..80f5119 100644 --- a/ext/php_aws_crt.h +++ b/ext/php_aws_crt.h @@ -15,11 +15,27 @@ #include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ -#if ZEND_EXTENSION_API_NO < 220131226 -# error "PHP >= 5.6 is required" +#include +#include +#include +#include + +/* ZEND_EXTENSION_API_NO from each branch of the PHP source */ +#define AWS_PHP_EXTENSION_API_5_5 220121212 +#define AWS_PHP_EXTENSION_API_5_6 220131226 +#define AWS_PHP_EXTENSION_API_7_0 320151012 +#define AWS_PHP_EXTENSION_API_7_1 320160303 +#define AWS_PHP_EXTENSION_API_7_2 320170718 +#define AWS_PHP_EXTENSION_API_7_3 320180731 +#define AWS_PHP_EXTENSION_API_7_4 320190902 +#define AWS_PHP_EXTENSION_API_8_0 420200930 + +#if ZEND_EXTENSION_API_NO < AWS_PHP_EXTENSION_API_5_5 +# error "PHP >= 5.5 is required" #endif -#define AWS_PHP_AT_LEAST_7 (ZEND_EXTENSION_API_NO >= 320151012) +#define AWS_PHP_AT_LEAST_7 (ZEND_EXTENSION_API_NO >= AWS_PHP_EXTENSION_API_7_0) +#define AWS_PHP_AT_LEAST_7_2 (ZEND_EXTENSION_API_NO >= AWS_PHP_EXTENSION_API_7_2) ZEND_BEGIN_MODULE_GLOBALS(awscrt) long log_level; @@ -29,26 +45,122 @@ ZEND_EXTERN_MODULE_GLOBALS(awscrt) #define AWSCRT_GLOBAL(v) ZEND_MODULE_GLOBALS_ACCESSOR(awscrt, v) - #if AWS_PHP_AT_LEAST_7 -/* PHP 7 removed the string duplicate parameter */ -# define AWS_RETURN_STRING(s) RETURN_STRING(s) -# define AWS_RETURN_STRINGL(s, l) RETURN_STRINGL(s, l) /* PHP 7 takes a zval*, PHP5 takes a zval** */ # define AWS_PHP_STREAM_FROM_ZVAL(s, z) php_stream_from_zval(s, z) -#else -# define AWS_RETURN_STRING(s) RETURN_STRING(s, 1) -# define AWS_RETURN_STRINGL(s, l) RETURN_STRINGL(s, l, 1) +#else /* PHP 5.5-5.6, 7.0-7.1 */ +/* PHP 7.2+ always duplicate string return values */ +# undef RETURN_STRING +# define RETURN_STRING(s) \ + { \ + RETVAL_STRING(s, 1); \ + return; \ + } +# undef RETURN_STRINGL +# define RETURN_STRINGL(s, l) \ + { \ + RETVAL_STRINGL(s, l, 1); \ + return; \ + } # define AWS_PHP_STREAM_FROM_ZVAL(s, z) php_stream_from_zval(s, &z) +#endif /* PHP 5.x */ -/* definitions for ZEND API macros taken from PHP7 and backported to 5.6 */ -# define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ - static const zend_arg_info name[] = {{NULL, 0, NULL, required_num_args, return_reference, 0, 0}, +#include "api.h" +#include "awscrt_arginfo.h" -/* PHP5 doesn't really handle type hints well, so elide them */ -# undef ZEND_ARG_TYPE_INFO -# define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) \ - {#name, sizeof(#name) - 1, NULL, 0, 0, pass_by_ref, allow_null, 0}, -#endif +/* Utility macros borrowed from common */ +#define GLUE(x, y) x y + +#define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count +#define EXPAND_ARGS(args) RETURN_ARG_COUNT args +#define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0)) + +#define OVERLOAD_MACRO2(name, count) name##count +#define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count) +#define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count) + +#define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__)) + +#define VARIABLE_LENGTH_ARRAY(type, name, length) type *name = alloca(sizeof(type) * (length)) + +/* + * PHP utility APIs for this extension + */ +/* + * Exception throwing mechanism, will never return + */ +#define aws_php_throw_exception(...) CALL_OVERLOAD(_AWS_PHP_THROW_EXCEPTION, __VA_ARGS__); +#define _AWS_PHP_THROW_EXCEPTION5(format, ...) zend_error_noreturn(E_ERROR, format, __VA_ARGS__) +#define _AWS_PHP_THROW_EXCEPTION4(format, ...) zend_error_noreturn(E_ERROR, format, __VA_ARGS__) +#define _AWS_PHP_THROW_EXCEPTION3(format, ...) zend_error_noreturn(E_ERROR, format, __VA_ARGS__) +#define _AWS_PHP_THROW_EXCEPTION2(format, ...) zend_error_noreturn(E_ERROR, format, __VA_ARGS__) +#define _AWS_PHP_THROW_EXCEPTION1(format) zend_error_noreturn(E_ERROR, format) + +/** + * throws an exception resulting from argument parsing, notes the current function name in the exception + */ +#define aws_php_argparse_fail() \ +do { \ + aws_php_throw_exception("Failed to parse arguments to %s", __func__); \ +} while (0) + +/** + * calls zend_parse_parameters() with the arguments and throws an exception if parsing fails + */ +#define aws_php_parse_parameters(type_spec, ...) \ +do { \ + if (zend_parse_parameters(ZEND_NUM_ARGS(), type_spec, __VA_ARGS__) == FAILURE) { \ + aws_php_argparse_fail(); \ + } \ +} while (0) + +/** + * calls zend_parse_parameters_none() and throws an exception if parsing fails + */ +#define aws_php_parse_parameters_none() \ +do { \ + if (zend_parse_parameters_none() == FAILURE) { \ + aws_php_argparse_fail(); \ + } \ +} while (0) + +/* Thread queue functions for managing PHP's optional threading situation */ +typedef struct _aws_php_task { + void (*callback)(void *); /* task function */ + void (*dtor)(void *); /* deletes task_data, if non-null */ + void *data; +} aws_php_task; + +/* maximum number of queued callbacks to execute at once. Since this is to support single-threaded usage, + * this can be a fairly small number, as how many callbacks could we reasonably be stacking up?! */ +#define AWS_PHP_THREAD_QUEUE_MAX_DEPTH 32 + +typedef struct _aws_php_thread_queue { + struct aws_mutex mutex; + aws_php_task queue[AWS_PHP_THREAD_QUEUE_MAX_DEPTH]; + size_t write_slot; + aws_thread_id_t thread_id; +} aws_php_thread_queue; + +extern aws_php_thread_queue s_aws_php_main_thread_queue; +bool aws_php_is_main_thread(void); + +void aws_php_thread_queue_init(aws_php_thread_queue *queue); +void aws_php_thread_queue_clean_up(aws_php_thread_queue *queue); +void aws_php_thread_queue_push(aws_php_thread_queue *queue, aws_php_task task); +bool aws_php_thread_queue_drain(aws_php_thread_queue *queue); + +/* called from worker thread to wait for the main thread to execute any queued work in PHP */ +void aws_php_thread_queue_yield(aws_php_thread_queue *queue); + +/* called from PHP thread to wait on async queued jobs, one of which MUST complete the promise */ +void aws_php_thread_queue_wait(aws_php_thread_queue *queue, struct aws_promise *promise); + +/** + * generic dispatch mechanism to call a callback provided as a zval with arguments + * that are converted to zvals based on the arg_types format string + * Uses the same format string as zend_parse_parameters + */ +zval aws_php_invoke_callback(zval *callback, const char *arg_types, ...); #endif /* PHP_AWS_CRT_H */ diff --git a/ext/signing.c b/ext/signing.c new file mode 100644 index 0000000..bb1188f --- /dev/null +++ b/ext/signing.c @@ -0,0 +1,285 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include "php_aws_crt.h" + +PHP_FUNCTION(aws_crt_signing_config_aws_new) { + if (zend_parse_parameters_none() == FAILURE) { + RETURN_NULL(); + } + + aws_crt_signing_config_aws *signing_config = aws_crt_signing_config_aws_new(); + RETURN_LONG((zend_ulong)signing_config); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_release) { + zend_ulong php_signing_config = 0; + + aws_php_parse_parameters("l", &php_signing_config); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_config_aws_release(signing_config); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_algorithm) { + zend_ulong php_signing_config = 0; + zend_ulong php_algorithm = 0; + + aws_php_parse_parameters("ll", &php_signing_config, &php_algorithm); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_algorithm algorithm = php_algorithm; + aws_crt_signing_config_aws_set_algorithm(signing_config, algorithm); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_signature_type) { + zend_ulong php_signing_config = 0; + zend_ulong php_signature_type = 0; + + aws_php_parse_parameters("ll", &php_signing_config, &php_signature_type); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signature_type signature_type = php_signature_type; + aws_crt_signing_config_aws_set_signature_type(signing_config, signature_type); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_credentials_provider) { + zend_ulong php_signing_config = 0; + zend_ulong php_credentials_provider = 0; + + aws_php_parse_parameters("ll", &php_signing_config, &php_credentials_provider); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_credentials_provider *credentials_provider = (void *)php_credentials_provider; + aws_crt_signing_config_aws_set_credentials_provider(signing_config, credentials_provider); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_region) { + zend_ulong php_signing_config = 0; + const char *region = NULL; + size_t region_len = 0; + + aws_php_parse_parameters("ls", &php_signing_config, ®ion, ®ion_len); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_config_aws_set_region(signing_config, (uint8_t *)region, region_len); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_service) { + zend_ulong php_signing_config = 0; + const char *service = NULL; + size_t service_len = 0; + + aws_php_parse_parameters("ls", &php_signing_config, &service, &service_len); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_config_aws_set_service(signing_config, (uint8_t *)service, service_len); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_use_double_uri_encode) { + zend_ulong php_signing_config = 0; + zend_bool php_use_double_uri_encode = 0; + + aws_php_parse_parameters("lb", &php_signing_config, &php_use_double_uri_encode); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_config_aws_set_use_double_uri_encode(signing_config, php_use_double_uri_encode); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_should_normalize_uri_path) { + zend_ulong php_signing_config = 0; + zend_bool php_should_normalize_uri_path = 0; + + aws_php_parse_parameters("lb", &php_signing_config, &php_should_normalize_uri_path); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_config_aws_set_should_normalize_uri_path(signing_config, php_should_normalize_uri_path); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_omit_session_token) { + zend_ulong php_signing_config = 0; + zend_bool php_omit_session_token = 0; + + aws_php_parse_parameters("lb", &php_signing_config, &php_omit_session_token); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_config_aws_set_omit_session_token(signing_config, php_omit_session_token); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_signed_body_value) { + zend_ulong php_signing_config = 0; + const char *signed_body_value = NULL; + size_t signed_body_value_len = 0; + + aws_php_parse_parameters("ls", &php_signing_config, &signed_body_value, &signed_body_value_len); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_config_aws_set_signed_body_value( + signing_config, (uint8_t *)signed_body_value, signed_body_value_len); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_signed_body_header_type) { + zend_ulong php_signing_config = 0; + zend_ulong php_signed_body_header_type = 0; + + aws_php_parse_parameters("ll", &php_signing_config, &php_signed_body_header_type); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signed_body_header_type signed_body_header_type = php_signed_body_header_type; + aws_crt_signing_config_aws_set_signed_body_header_type(signing_config, signed_body_header_type); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_expiration_in_seconds) { + zend_ulong php_signing_config = 0; + zend_ulong php_expiration_in_seconds = 0; + + aws_php_parse_parameters("ll", &php_signing_config, &php_expiration_in_seconds); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_config_aws_set_expiration_in_seconds(signing_config, php_expiration_in_seconds); +} + +PHP_FUNCTION(aws_crt_signing_config_aws_set_date) { + zend_ulong php_signing_config = 0; + zend_ulong php_timestamp = 0; + + aws_php_parse_parameters("ll", &php_signing_config, &php_timestamp); + + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + aws_crt_signing_config_aws_set_date(signing_config, php_timestamp); +} + +PHP_FUNCTION(aws_crt_signable_new_from_http_request) { + zend_ulong php_http_message = 0; + + aws_php_parse_parameters("l", &php_http_message); + + const aws_crt_http_message *http_message = (void *)php_http_message; + aws_crt_signable *signable = aws_crt_signable_new_from_http_request(http_message); + RETURN_LONG((zend_ulong)signable); +} + +PHP_FUNCTION(aws_crt_signable_new_from_chunk) { + zend_ulong php_input_stream = 0; + const char *previous_signature = NULL; + size_t previous_signature_len = 0; + + aws_php_parse_parameters("ls", &php_input_stream, &previous_signature, &previous_signature_len); + + aws_crt_input_stream *input_stream = (void *)php_input_stream; + aws_crt_signable *signable = + aws_crt_signable_new_from_chunk(input_stream, (uint8_t *)previous_signature, previous_signature_len); + RETURN_LONG((zend_ulong)signable); +} + +PHP_FUNCTION(aws_crt_signable_new_from_canonical_request) { + const char *canonical_request = NULL; + size_t canonical_request_len = 0; + + aws_crt_signable *signable = + aws_crt_signable_new_from_canonical_request((uint8_t *)canonical_request, canonical_request_len); + RETURN_LONG((zend_ulong)signable); +} + +PHP_FUNCTION(aws_crt_signable_release) { + zend_ulong php_signable = 0; + + aws_php_parse_parameters("l", &php_signable); + + aws_crt_signable *signable = (void *)php_signable; + aws_crt_signable_release(signable); +} + +PHP_FUNCTION(aws_crt_signing_result_release) { + zend_ulong php_signing_result = 0; + + aws_php_parse_parameters("l", &php_signing_result); + aws_crt_signing_result *result = (void *)php_signing_result; + aws_crt_signing_result_release(result); +} + +PHP_FUNCTION(aws_crt_signing_result_apply_to_http_request) { + zend_ulong php_signing_result = 0; + zend_ulong php_http_request = 0; + + aws_php_parse_parameters("ll", &php_signing_result, &php_http_request); + aws_crt_signing_result *result = (void *)php_signing_result; + aws_crt_http_message *request = (void *)php_http_request; + + if (aws_crt_signing_result_apply_to_http_request(result, request)) { + aws_php_throw_exception( + "Failed to apply signing result to HTTP request: %s", aws_crt_error_name(aws_crt_last_error())); + } +} + +typedef struct _signing_state { + struct aws_promise *promise; + zval *on_complete; + aws_crt_signing_result *signing_result; + int error_code; +} signing_state; + +/* called on main thread to deliver result to php */ +static void s_sign_aws_complete(void *data) { + signing_state *state = data; + zval *on_complete = state->on_complete; + aws_php_invoke_callback(on_complete, "ll", (zend_ulong)state->signing_result, (zend_ulong)state->error_code); +} + +/* called from signing process in aws_sign_request_aws */ +static void s_on_sign_request_aws_complete(aws_crt_signing_result *result, int error_code, void *user_data) { + signing_state *state = user_data; + struct aws_promise *promise = state->promise; + + state->signing_result = result; + state->error_code = error_code; + + /* + * Must execute PHP callback before this function returns, or signing_result will be killed + * so the callback is queued back to the main thread and will have run when yield returns + */ + aws_php_task complete_callback_task = { + .callback = s_sign_aws_complete, + .data = state, + }; + aws_php_thread_queue_push(&s_aws_php_main_thread_queue, complete_callback_task); + aws_php_thread_queue_yield(&s_aws_php_main_thread_queue); + + if (error_code) { + aws_promise_fail(promise, error_code); + } else { + aws_promise_complete(promise, result, NULL); + } +} + +PHP_FUNCTION(aws_crt_sign_request_aws) { + zend_ulong php_signable = 0; + zend_ulong php_signing_config = 0; + zval *php_on_complete = 0; + + aws_php_parse_parameters("llz", &php_signable, &php_signing_config, &php_on_complete); + + aws_crt_signable *signable = (void *)php_signable; + aws_crt_signing_config_aws *signing_config = (void *)php_signing_config; + + struct aws_promise *promise = aws_promise_new(aws_crt_default_allocator()); + signing_state state = { + .promise = promise, + .on_complete = php_on_complete, + }; + int ret = aws_crt_sign_request_aws(signable, signing_config, s_on_sign_request_aws_complete, &state); + if (ret != 0) { + int last_error = aws_crt_last_error(); + aws_promise_fail(promise, last_error); + aws_php_throw_exception( + "aws_crt_sign_request_aws: error starting signing process: %s", aws_crt_error_name(last_error)); + } + + aws_php_thread_queue_wait(&s_aws_php_main_thread_queue, promise); + +done: + aws_promise_release(promise); + RETURN_LONG(ret); +} diff --git a/ext/stream.c b/ext/stream.c new file mode 100644 index 0000000..9412992 --- /dev/null +++ b/ext/stream.c @@ -0,0 +1,148 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include "php_aws_crt.h" + +/* PHP streams info: + * https://git.php.net/?p=php-src.git;a=blob;f=docs/streams.md;h=0ec3846d68bf70067297d8a6c691d2591c49b48a;hb=HEAD + * https://github.com/php/php-src/blob/PHP-5.6.0/main/php_streams.h + */ + +PHP_FUNCTION(aws_crt_input_stream_options_new) { + if (zend_parse_parameters_none() == FAILURE) { + aws_php_argparse_fail(); + } + + aws_crt_input_stream_options *options = aws_crt_input_stream_options_new(); + RETURN_LONG((zend_ulong)options); +} + +PHP_FUNCTION(aws_crt_input_stream_options_release) { + zend_ulong php_options = 0; + + aws_php_parse_parameters("l", &php_options); + + aws_crt_input_stream_options *options = (void *)php_options; + aws_crt_input_stream_options_release(options); +} + +PHP_FUNCTION(aws_crt_input_stream_options_set_user_data) { + zend_ulong php_options = 0; + zval *user_data = NULL; + + aws_php_parse_parameters("lz", &php_options, &user_data); + + aws_crt_input_stream_options *options = (void *)php_options; + php_stream *stream = NULL; + AWS_PHP_STREAM_FROM_ZVAL(stream, user_data); + aws_crt_input_stream_options_set_user_data(options, stream); +} + +static int s_php_stream_seek(void *user_data, int64_t offset, aws_crt_input_stream_seek_basis basis) { + php_stream *stream = user_data; + return php_stream_seek(stream, offset, basis); +} + +static int s_php_stream_read(void *user_data, uint8_t *dest, size_t dest_length) { + php_stream *stream = user_data; + return php_stream_read(stream, (char *)dest, dest_length) != 0; +} + +static int s_php_stream_get_length(void *user_data, int64_t *out_length) { + php_stream *stream = user_data; + size_t pos = php_stream_tell(stream); + php_stream_seek(stream, 0, SEEK_END); + *out_length = php_stream_tell(stream); + php_stream_seek(stream, pos, SEEK_SET); + return 0; +} + +static int s_php_stream_get_status(void *user_data, aws_crt_input_stream_status *out_status) { + php_stream *stream = user_data; + out_status->is_valid = stream != NULL; + /* We would like to use php_stream_eof here, but certain streams (notably php://memory) + * are not actually capable of EOF, so we get to do it the hard way */ + int64_t length = 0; + int64_t pos = 0; + s_php_stream_get_length(stream, &length); + pos = php_stream_tell(stream); + out_status->is_end_of_stream = pos == length; + return 0; +} + +static void s_php_stream_destroy(void *user_data) { + (void)user_data; + /* no op, stream will be freed by PHP refcount dropping from InputStream::stream */ +} + +PHP_FUNCTION(aws_crt_input_stream_new) { + zend_ulong php_options = 0; + + aws_php_parse_parameters("l", &php_options); + + aws_crt_input_stream_options *options = (void *)php_options; + aws_crt_input_stream_options_set_seek(options, s_php_stream_seek); + aws_crt_input_stream_options_set_read(options, s_php_stream_read); + aws_crt_input_stream_options_set_get_status(options, s_php_stream_get_status); + aws_crt_input_stream_options_set_get_length(options, s_php_stream_get_length); + aws_crt_input_stream_options_set_destroy(options, s_php_stream_destroy); + aws_crt_input_stream *stream = aws_crt_input_stream_new(options); + RETURN_LONG((zend_ulong)stream); +} + +PHP_FUNCTION(aws_crt_input_stream_release) { + zend_ulong php_stream = 0; + + aws_php_parse_parameters("l", &php_stream); + + aws_crt_input_stream *stream = (void *)php_stream; + aws_crt_input_stream_release(stream); +} + +PHP_FUNCTION(aws_crt_input_stream_seek) { + zend_ulong php_stream = 0; + zend_ulong offset = 0; + zend_ulong basis = 0; + + aws_php_parse_parameters("lll", &php_stream, &offset, &basis); + + aws_crt_input_stream *stream = (void *)php_stream; + RETURN_LONG(aws_crt_input_stream_seek(stream, offset, basis)); +} + +PHP_FUNCTION(aws_crt_input_stream_read) { + zend_ulong php_stream = 0; + zend_ulong length = 0; + + aws_php_parse_parameters("ll", &php_stream, &length); + + aws_crt_input_stream *stream = (void *)php_stream; + uint8_t *buf = emalloc(length); + int ret = aws_crt_input_stream_read(stream, buf, length); + RETURN_STRINGL((const char *)buf, length); + efree(buf); +} + +PHP_FUNCTION(aws_crt_input_stream_eof) { + zend_ulong php_stream = 0; + + aws_php_parse_parameters("l", &php_stream); + + aws_crt_input_stream *stream = (void *)php_stream; + aws_crt_input_stream_status status = {0}; + aws_crt_input_stream_get_status(stream, &status); + RETURN_BOOL(status.is_end_of_stream); +} + +PHP_FUNCTION(aws_crt_input_stream_get_length) { + zend_ulong php_stream = 0; + + aws_php_parse_parameters("l", &php_stream); + + aws_crt_input_stream *stream = (void *)php_stream; + int64_t length = 0; + aws_crt_input_stream_get_length(stream, &length); + RETURN_LONG(length); +} diff --git a/gen_stub.php b/gen_stub.php new file mode 100755 index 0000000..4967032 --- /dev/null +++ b/gen_stub.php @@ -0,0 +1,1998 @@ +#!/usr/bin/env php +getPathName(); + if (preg_match('/\.stub\.php$/', $pathName)) { + $fileInfo = processStubFile($pathName, $context); + if ($fileInfo) { + $fileInfos[] = $fileInfo; + } + } + } + + return $fileInfos; +} + +function processStubFile(string $stubFile, Context $context): ?FileInfo { + try { + if (!file_exists($stubFile)) { + throw new Exception("File $stubFile does not exist"); + } + + $arginfoFile = str_replace('.stub.php', '_arginfo.h', $stubFile); + $legacyFile = str_replace('.stub.php', '_legacy_arginfo.h', $stubFile); + + $stubCode = file_get_contents($stubFile); + $stubHash = computeStubHash($stubCode); + $oldStubHash = extractStubHash($arginfoFile); + if ($stubHash === $oldStubHash && !$context->forceParse) { + /* Stub file did not change, do not regenerate. */ + return null; + } + + initPhpParser(); + $fileInfo = parseStubFile($stubCode); + $arginfoCode = generateArgInfoCode($fileInfo, $stubHash, $context->minimalArgInfo); + if (($context->forceRegeneration || $stubHash !== $oldStubHash) && file_put_contents($arginfoFile, $arginfoCode)) { + echo "Saved $arginfoFile\n"; + } + + if ($fileInfo->generateLegacyArginfo) { + foreach ($fileInfo->getAllFuncInfos() as $funcInfo) { + $funcInfo->discardInfoForOldPhpVersions(); + } + $arginfoCode = generateArgInfoCode($fileInfo, $stubHash, $context->minimalArgInfo); + if (($context->forceRegeneration || $stubHash !== $oldStubHash) && file_put_contents($legacyFile, $arginfoCode)) { + echo "Saved $legacyFile\n"; + } + } + + return $fileInfo; + } catch (Exception $e) { + echo "In $stubFile:\n{$e->getMessage()}\n"; + exit(1); + } +} + +function computeStubHash(string $stubCode): string { + return sha1(str_replace("\r\n", "\n", $stubCode)); +} + +function extractStubHash(string $arginfoFile): ?string { + if (!file_exists($arginfoFile)) { + return null; + } + + $arginfoCode = file_get_contents($arginfoFile); + if (!preg_match('/\* Stub hash: ([0-9a-f]+) \*/', $arginfoCode, $matches)) { + return null; + } + + return $matches[1]; +} + +class Context { + /** @var bool */ + public $forceParse = false; + /** @var bool */ + public $forceRegeneration = false; + /** @var bool */ + public $minimalArgInfo = false; +} + +class SimpleType { + /** @var string */ + public $name; + /** @var bool */ + public $isBuiltin; + + public function __construct(string $name, bool $isBuiltin) { + $this->name = $name; + $this->isBuiltin = $isBuiltin; + } + + public static function fromNode(Node $node): SimpleType { + if ($node instanceof Node\Name) { + if ($node->toLowerString() === 'static') { + // PHP internally considers "static" a builtin type. + return new SimpleType($node->toString(), true); + } + + assert($node->isFullyQualified()); + return new SimpleType($node->toString(), false); + } + if ($node instanceof Node\Identifier) { + return new SimpleType($node->toString(), true); + } + throw new Exception("Unexpected node type"); + } + + public static function fromPhpDoc(string $type): SimpleType + { + switch (strtolower($type)) { + case "void": + case "null": + case "false": + case "bool": + case "int": + case "float": + case "string": + case "array": + case "iterable": + case "object": + case "resource": + case "mixed": + case "self": + case "static": + return new SimpleType(strtolower($type), true); + } + + if (strpos($type, "[]") !== false) { + return new SimpleType("array", true); + } + + return new SimpleType($type, false); + } + + public static function null(): SimpleType + { + return new SimpleType("null", true); + } + + public static function void(): SimpleType + { + return new SimpleType("void", true); + } + + public function isNull(): bool { + return $this->isBuiltin && $this->name === 'null'; + } + + public function toTypeCode(): string { + assert($this->isBuiltin); + switch (strtolower($this->name)) { + case "bool": + return "_IS_BOOL"; + case "int": + return "IS_LONG"; + case "float": + return "IS_DOUBLE"; + case "string": + return "IS_STRING"; + case "array": + return "IS_ARRAY"; + case "object": + return "IS_OBJECT"; + case "void": + return "IS_VOID"; + case "callable": + return "IS_CALLABLE"; + case "iterable": + return "IS_ITERABLE"; + case "mixed": + return "IS_MIXED"; + case "static": + return "IS_STATIC"; + default: + throw new Exception("Not implemented: $this->name"); + } + } + + public function toTypeMask() { + assert($this->isBuiltin); + switch (strtolower($this->name)) { + case "null": + return "MAY_BE_NULL"; + case "false": + return "MAY_BE_FALSE"; + case "bool": + return "MAY_BE_BOOL"; + case "int": + return "MAY_BE_LONG"; + case "float": + return "MAY_BE_DOUBLE"; + case "string": + return "MAY_BE_STRING"; + case "array": + return "MAY_BE_ARRAY"; + case "object": + return "MAY_BE_OBJECT"; + case "callable": + return "MAY_BE_CALLABLE"; + case "mixed": + return "MAY_BE_ANY"; + case "static": + return "MAY_BE_STATIC"; + default: + throw new Exception("Not implemented: $this->name"); + } + } + + public function toEscapedName(): string { + return str_replace('\\', '\\\\', $this->name); + } + + public function equals(SimpleType $other) { + return $this->name === $other->name + && $this->isBuiltin === $other->isBuiltin; + } +} + +class Type { + /** @var SimpleType[] $types */ + public $types; + + public function __construct(array $types) { + $this->types = $types; + } + + public static function fromNode(Node $node): Type { + if ($node instanceof Node\UnionType) { + return new Type(array_map(['SimpleType', 'fromNode'], $node->types)); + } + if ($node instanceof Node\NullableType) { + return new Type([ + SimpleType::fromNode($node->type), + SimpleType::null(), + ]); + } + return new Type([SimpleType::fromNode($node)]); + } + + public static function fromPhpDoc(string $phpDocType) { + $types = explode("|", $phpDocType); + + $simpleTypes = []; + foreach ($types as $type) { + $simpleTypes[] = SimpleType::fromPhpDoc($type); + } + + return new Type($simpleTypes); + } + + public function isNullable(): bool { + foreach ($this->types as $type) { + if ($type->isNull()) { + return true; + } + } + return false; + } + + public function getWithoutNull(): Type { + return new Type(array_filter($this->types, function(SimpleType $type) { + return !$type->isNull(); + })); + } + + public function tryToSimpleType(): ?SimpleType { + $withoutNull = $this->getWithoutNull(); + if (count($withoutNull->types) === 1) { + return $withoutNull->types[0]; + } + return null; + } + + public function toArginfoType(): ?ArginfoType { + $classTypes = []; + $builtinTypes = []; + foreach ($this->types as $type) { + if ($type->isBuiltin) { + $builtinTypes[] = $type; + } else { + $classTypes[] = $type; + } + } + return new ArginfoType($classTypes, $builtinTypes); + } + + public static function equals(?Type $a, ?Type $b): bool { + if ($a === null || $b === null) { + return $a === $b; + } + + if (count($a->types) !== count($b->types)) { + return false; + } + + for ($i = 0; $i < count($a->types); $i++) { + if (!$a->types[$i]->equals($b->types[$i])) { + return false; + } + } + + return true; + } + + public function __toString() { + if ($this->types === null) { + return 'mixed'; + } + + return implode('|', array_map( + function ($type) { return $type->name; }, + $this->types) + ); + } +} + +class ArginfoType { + /** @var ClassType[] $classTypes */ + public $classTypes; + + /** @var SimpleType[] $builtinTypes */ + private $builtinTypes; + + public function __construct(array $classTypes, array $builtinTypes) { + $this->classTypes = $classTypes; + $this->builtinTypes = $builtinTypes; + } + + public function hasClassType(): bool { + return !empty($this->classTypes); + } + + public function toClassTypeString(): string { + return implode('|', array_map(function(SimpleType $type) { + return $type->toEscapedName(); + }, $this->classTypes)); + } + + public function toTypeMask(): string { + if (empty($this->builtinTypes)) { + return '0'; + } + return implode('|', array_map(function(SimpleType $type) { + return $type->toTypeMask(); + }, $this->builtinTypes)); + } +} + +class ArgInfo { + const SEND_BY_VAL = 0; + const SEND_BY_REF = 1; + const SEND_PREFER_REF = 2; + + /** @var string */ + public $name; + /** @var int */ + public $sendBy; + /** @var bool */ + public $isVariadic; + /** @var Type|null */ + public $type; + /** @var Type|null */ + public $phpDocType; + /** @var string|null */ + public $defaultValue; + + public function __construct(string $name, int $sendBy, bool $isVariadic, ?Type $type, ?Type $phpDocType, ?string $defaultValue) { + $this->name = $name; + $this->sendBy = $sendBy; + $this->isVariadic = $isVariadic; + $this->type = $type; + $this->phpDocType = $phpDocType; + $this->defaultValue = $defaultValue; + } + + public function equals(ArgInfo $other): bool { + return $this->name === $other->name + && $this->sendBy === $other->sendBy + && $this->isVariadic === $other->isVariadic + && Type::equals($this->type, $other->type) + && $this->defaultValue === $other->defaultValue; + } + + public function getSendByString(): string { + switch ($this->sendBy) { + case self::SEND_BY_VAL: + return "0"; + case self::SEND_BY_REF: + return "1"; + case self::SEND_PREFER_REF: + return "ZEND_SEND_PREFER_REF"; + } + throw new Exception("Invalid sendBy value"); + } + + public function getMethodSynopsisType(): Type { + if ($this->type) { + return $this->type; + } + + if ($this->phpDocType) { + return $this->phpDocType; + } + + throw new Exception("A parameter must have a type"); + } + + public function hasProperDefaultValue(): bool { + return $this->defaultValue !== null && $this->defaultValue !== "UNKNOWN"; + } + + public function getDefaultValueAsArginfoString(): string { + if ($this->hasProperDefaultValue()) { + return '"' . addslashes($this->defaultValue) . '"'; + } + + return "NULL"; + } + + public function getDefaultValueAsMethodSynopsisString(): ?string { + if ($this->defaultValue === null) { + return null; + } + + switch ($this->defaultValue) { + case 'UNKNOWN': + return null; + case 'false': + case 'true': + case 'null': + return "&{$this->defaultValue};"; + } + + return $this->defaultValue; + } +} + +interface FunctionOrMethodName { + public function getDeclaration(): string; + public function getArgInfoName(): string; + public function getMethodSynopsisFilename(): string; + public function __toString(): string; + public function isMethod(): bool; + public function isConstructor(): bool; + public function isDestructor(): bool; +} + +class FunctionName implements FunctionOrMethodName { + /** @var Name */ + private $name; + + public function __construct(Name $name) { + $this->name = $name; + } + + public function getNamespace(): ?string { + if ($this->name->isQualified()) { + return $this->name->slice(0, -1)->toString(); + } + return null; + } + + public function getNonNamespacedName(): string { + if ($this->name->isQualified()) { + throw new Exception("Namespaced name not supported here"); + } + return $this->name->toString(); + } + + public function getDeclarationName(): string { + return $this->name->getLast(); + } + + public function getDeclaration(): string { + return "ZEND_FUNCTION({$this->getDeclarationName()});\n"; + } + + public function getArgInfoName(): string { + $underscoreName = implode('_', $this->name->parts); + return "arginfo_$underscoreName"; + } + + public function getMethodSynopsisFilename(): string { + return implode('_', $this->name->parts); + } + + public function __toString(): string { + return $this->name->toString(); + } + + public function isMethod(): bool { + return false; + } + + public function isConstructor(): bool { + return false; + } + + public function isDestructor(): bool { + return false; + } +} + +class MethodName implements FunctionOrMethodName { + /** @var Name */ + private $className; + /** @var string */ + public $methodName; + + public function __construct(Name $className, string $methodName) { + $this->className = $className; + $this->methodName = $methodName; + } + + public function getDeclarationClassName(): string { + return implode('_', $this->className->parts); + } + + public function getDeclaration(): string { + return "ZEND_METHOD({$this->getDeclarationClassName()}, $this->methodName);\n"; + } + + public function getArgInfoName(): string { + return "arginfo_class_{$this->getDeclarationClassName()}_{$this->methodName}"; + } + + public function getMethodSynopsisFilename(): string { + return $this->getDeclarationClassName() . "_{$this->methodName}"; + } + + public function __toString(): string { + return "$this->className::$this->methodName"; + } + + public function isMethod(): bool { + return true; + } + + public function isConstructor(): bool { + return $this->methodName === "__construct"; + } + + public function isDestructor(): bool { + return $this->methodName === "__destruct"; + } +} + +class ReturnInfo { + /** @var bool */ + public $byRef; + /** @var Type|null */ + public $type; + /** @var Type|null */ + public $phpDocType; + + public function __construct(bool $byRef, ?Type $type, ?Type $phpDocType) { + $this->byRef = $byRef; + $this->type = $type; + $this->phpDocType = $phpDocType; + } + + public function equals(ReturnInfo $other): bool { + return $this->byRef === $other->byRef + && Type::equals($this->type, $other->type); + } + + public function getMethodSynopsisType(): ?Type { + return $this->type ?? $this->phpDocType; + } +} + +class FuncInfo { + /** @var FunctionOrMethodName */ + public $name; + /** @var int */ + public $classFlags; + /** @var int */ + public $flags; + /** @var string|null */ + public $aliasType; + /** @var FunctionName|null */ + public $alias; + /** @var bool */ + public $isDeprecated; + /** @var bool */ + public $verify; + /** @var ArgInfo[] */ + public $args; + /** @var ReturnInfo */ + public $return; + /** @var int */ + public $numRequiredArgs; + /** @var string|null */ + public $cond; + + public function __construct( + FunctionOrMethodName $name, + int $classFlags, + int $flags, + ?string $aliasType, + ?FunctionOrMethodName $alias, + bool $isDeprecated, + bool $verify, + array $args, + ReturnInfo $return, + int $numRequiredArgs, + ?string $cond + ) { + $this->name = $name; + $this->classFlags = $classFlags; + $this->flags = $flags; + $this->aliasType = $aliasType; + $this->alias = $alias; + $this->isDeprecated = $isDeprecated; + $this->verify = $verify; + $this->args = $args; + $this->return = $return; + $this->numRequiredArgs = $numRequiredArgs; + $this->cond = $cond; + } + + public function isMethod(): bool + { + return $this->name->isMethod(); + } + + public function isFinalMethod(): bool + { + return ($this->flags & Class_::MODIFIER_FINAL) || ($this->classFlags & Class_::MODIFIER_FINAL); + } + + public function isInstanceMethod(): bool + { + return !($this->flags & Class_::MODIFIER_STATIC) && $this->isMethod() && !$this->name->isConstructor(); + } + + /** @return string[] */ + public function getModifierNames(): array + { + if (!$this->isMethod()) { + return []; + } + + $result = []; + + if ($this->flags & Class_::MODIFIER_FINAL) { + $result[] = "final"; + } elseif ($this->flags & Class_::MODIFIER_ABSTRACT && $this->classFlags & ~Class_::MODIFIER_ABSTRACT) { + $result[] = "abstract"; + } + + if ($this->flags & Class_::MODIFIER_PROTECTED) { + $result[] = "protected"; + } elseif ($this->flags & Class_::MODIFIER_PRIVATE) { + $result[] = "private"; + } else { + $result[] = "public"; + } + + if ($this->flags & Class_::MODIFIER_STATIC) { + $result[] = "static"; + } + + return $result; + } + + public function hasParamWithUnknownDefaultValue(): bool + { + foreach ($this->args as $arg) { + if ($arg->defaultValue && !$arg->hasProperDefaultValue()) { + return true; + } + } + + return false; + } + + public function equalsApartFromName(FuncInfo $other): bool { + if (count($this->args) !== count($other->args)) { + return false; + } + + for ($i = 0; $i < count($this->args); $i++) { + if (!$this->args[$i]->equals($other->args[$i])) { + return false; + } + } + + return $this->return->equals($other->return) + && $this->numRequiredArgs === $other->numRequiredArgs + && $this->cond === $other->cond; + } + + public function getArgInfoName(): string { + return $this->name->getArgInfoName(); + } + + public function getDeclarationKey(): string + { + $name = $this->alias ?? $this->name; + + return "$name|$this->cond"; + } + + public function getDeclaration(): ?string + { + if ($this->flags & Class_::MODIFIER_ABSTRACT) { + return null; + } + + $name = $this->alias ?? $this->name; + + return $name->getDeclaration(); + } + + public function getFunctionEntry(): string { + if ($this->name instanceof MethodName) { + if ($this->alias) { + if ($this->alias instanceof MethodName) { + return sprintf( + "\tZEND_MALIAS(%s, %s, %s, %s, %s)\n", + $this->alias->getDeclarationClassName(), $this->name->methodName, + $this->alias->methodName, $this->getArgInfoName(), $this->getFlagsAsArginfoString() + ); + } else if ($this->alias instanceof FunctionName) { + return sprintf( + "\tZEND_ME_MAPPING(%s, %s, %s, %s)\n", + $this->name->methodName, $this->alias->getNonNamespacedName(), + $this->getArgInfoName(), $this->getFlagsAsArginfoString() + ); + } else { + throw new Error("Cannot happen"); + } + } else { + $declarationClassName = $this->name->getDeclarationClassName(); + if ($this->flags & Class_::MODIFIER_ABSTRACT) { + return sprintf( + "\tZEND_ABSTRACT_ME_WITH_FLAGS(%s, %s, %s, %s)\n", + $declarationClassName, $this->name->methodName, $this->getArgInfoName(), + $this->getFlagsAsArginfoString() + ); + } + + return sprintf( + "\tZEND_ME(%s, %s, %s, %s)\n", + $declarationClassName, $this->name->methodName, $this->getArgInfoName(), + $this->getFlagsAsArginfoString() + ); + } + } else if ($this->name instanceof FunctionName) { + $namespace = $this->name->getNamespace(); + $declarationName = $this->name->getDeclarationName(); + + if ($this->alias && $this->isDeprecated) { + return sprintf( + "\tZEND_DEP_FALIAS(%s, %s, %s)\n", + $declarationName, $this->alias->getNonNamespacedName(), $this->getArgInfoName() + ); + } + + if ($this->alias) { + return sprintf( + "\tZEND_FALIAS(%s, %s, %s)\n", + $declarationName, $this->alias->getNonNamespacedName(), $this->getArgInfoName() + ); + } + + if ($this->isDeprecated) { + return sprintf( + "\tZEND_DEP_FE(%s, %s)\n", $declarationName, $this->getArgInfoName()); + } + + if ($namespace) { + // Render A\B as "A\\B" in C strings for namespaces + return sprintf( + "\tZEND_NS_FE(\"%s\", %s, %s)\n", + addslashes($namespace), $declarationName, $this->getArgInfoName()); + } else { + return sprintf("\tZEND_FE(%s, %s)\n", $declarationName, $this->getArgInfoName()); + } + } else { + throw new Error("Cannot happen"); + } + } + + private function getFlagsAsArginfoString(): string + { + $flags = "ZEND_ACC_PUBLIC"; + if ($this->flags & Class_::MODIFIER_PROTECTED) { + $flags = "ZEND_ACC_PROTECTED"; + } elseif ($this->flags & Class_::MODIFIER_PRIVATE) { + $flags = "ZEND_ACC_PRIVATE"; + } + + if ($this->flags & Class_::MODIFIER_STATIC) { + $flags .= "|ZEND_ACC_STATIC"; + } + + if ($this->flags & Class_::MODIFIER_FINAL) { + $flags .= "|ZEND_ACC_FINAL"; + } + + if ($this->flags & Class_::MODIFIER_ABSTRACT) { + $flags .= "|ZEND_ACC_ABSTRACT"; + } + + if ($this->isDeprecated) { + $flags .= "|ZEND_ACC_DEPRECATED"; + } + + return $flags; + } + + /** + * @param FuncInfo[] $funcMap + * @param FuncInfo[] $aliasMap + * @throws Exception + */ + public function getMethodSynopsisDocument(array $funcMap, array $aliasMap): ?string { + + $doc = new DOMDocument(); + $doc->formatOutput = true; + $methodSynopsis = $this->getMethodSynopsisElement($funcMap, $aliasMap, $doc); + if (!$methodSynopsis) { + return null; + } + + $doc->appendChild($methodSynopsis); + + return $doc->saveXML(); + } + + /** + * @param FuncInfo[] $funcMap + * @param FuncInfo[] $aliasMap + * @throws Exception + */ + public function getMethodSynopsisElement(array $funcMap, array $aliasMap, DOMDocument $doc): ?DOMElement { + if ($this->hasParamWithUnknownDefaultValue()) { + return null; + } + + if ($this->name->isConstructor()) { + $synopsisType = "constructorsynopsis"; + } elseif ($this->name->isDestructor()) { + $synopsisType = "destructorsynopsis"; + } else { + $synopsisType = "methodsynopsis"; + } + + $methodSynopsis = $doc->createElement($synopsisType); + + $aliasedFunc = $this->aliasType === "alias" && isset($funcMap[$this->alias->__toString()]) ? $funcMap[$this->alias->__toString()] : null; + $aliasFunc = $aliasMap[$this->name->__toString()] ?? null; + + if (($this->aliasType === "alias" && $aliasedFunc !== null && $aliasedFunc->isMethod() !== $this->isMethod()) || + ($aliasFunc !== null && $aliasFunc->isMethod() !== $this->isMethod()) + ) { + $role = $doc->createAttribute("role"); + $role->value = $this->isMethod() ? "oop" : "procedural"; + $methodSynopsis->appendChild($role); + } + + $methodSynopsis->appendChild(new DOMText("\n ")); + + foreach ($this->getModifierNames() as $modifierString) { + $modifierElement = $doc->createElement('modifier', $modifierString); + $methodSynopsis->appendChild($modifierElement); + $methodSynopsis->appendChild(new DOMText(" ")); + } + + $returnType = $this->return->getMethodSynopsisType(); + if ($returnType) { + $this->appendMethodSynopsisTypeToElement($doc, $methodSynopsis, $returnType); + } + + $methodname = $doc->createElement('methodname', $this->name->__toString()); + $methodSynopsis->appendChild($methodname); + + if (empty($this->args)) { + $methodSynopsis->appendChild(new DOMText("\n ")); + $void = $doc->createElement('void'); + $methodSynopsis->appendChild($void); + } else { + foreach ($this->args as $arg) { + $methodSynopsis->appendChild(new DOMText("\n ")); + $methodparam = $doc->createElement('methodparam'); + if ($arg->defaultValue !== null) { + $methodparam->setAttribute("choice", "opt"); + } + if ($arg->isVariadic) { + $methodparam->setAttribute("rep", "repeat"); + } + + $methodSynopsis->appendChild($methodparam); + $this->appendMethodSynopsisTypeToElement($doc, $methodparam, $arg->getMethodSynopsisType()); + + $parameter = $doc->createElement('parameter', $arg->name); + if ($arg->sendBy !== ArgInfo::SEND_BY_VAL) { + $parameter->setAttribute("role", "reference"); + } + + $methodparam->appendChild($parameter); + $defaultValue = $arg->getDefaultValueAsMethodSynopsisString(); + if ($defaultValue !== null) { + $initializer = $doc->createElement('initializer'); + if (preg_match('/^[a-zA-Z_][a-zA-Z_0-9]*$/', $defaultValue)) { + $constant = $doc->createElement('constant', $defaultValue); + $initializer->appendChild($constant); + } else { + $initializer->nodeValue = $defaultValue; + } + $methodparam->appendChild($initializer); + } + } + } + $methodSynopsis->appendChild(new DOMText("\n ")); + + return $methodSynopsis; + } + + public function discardInfoForOldPhpVersions(): void { + $this->return->type = null; + foreach ($this->args as $arg) { + $arg->type = null; + $arg->defaultValue = null; + } + } + + private function appendMethodSynopsisTypeToElement(DOMDocument $doc, DOMElement $elementToAppend, Type $type) { + if (count($type->types) > 1) { + $typeElement = $doc->createElement('type'); + $typeElement->setAttribute("class", "union"); + + foreach ($type->types as $type) { + $unionTypeElement = $doc->createElement('type', $type->name); + $typeElement->appendChild($unionTypeElement); + } + } else { + $typeElement = $doc->createElement('type', $type->types[0]->name); + } + + $elementToAppend->appendChild($typeElement); + } +} + +class ClassInfo { + /** @var Name */ + public $name; + /** @var FuncInfo[] */ + public $funcInfos; + + public function __construct(Name $name, array $funcInfos) { + $this->name = $name; + $this->funcInfos = $funcInfos; + } +} + +class FileInfo { + /** @var FuncInfo[] */ + public $funcInfos = []; + /** @var ClassInfo[] */ + public $classInfos = []; + /** @var bool */ + public $generateFunctionEntries = false; + /** @var string */ + public $declarationPrefix = ""; + /** @var bool */ + public $generateLegacyArginfo = false; + + /** + * @return iterable + */ + public function getAllFuncInfos(): iterable { + yield from $this->funcInfos; + foreach ($this->classInfos as $classInfo) { + yield from $classInfo->funcInfos; + } + } +} + +class DocCommentTag { + /** @var string */ + public $name; + /** @var string|null */ + public $value; + + public function __construct(string $name, ?string $value) { + $this->name = $name; + $this->value = $value; + } + + public function getValue(): string { + if ($this->value === null) { + throw new Exception("@$this->name does not have a value"); + } + + return $this->value; + } + + public function getType(): string { + $value = $this->getValue(); + + $matches = []; + + if ($this->name === "param") { + preg_match('/^\s*([\w\|\\\\\[\]]+)\s*\$\w+.*$/', $value, $matches); + } elseif ($this->name === "return") { + preg_match('/^\s*([\w\|\\\\\[\]]+)\s*$/', $value, $matches); + } + + if (isset($matches[1]) === false) { + throw new Exception("@$this->name doesn't contain a type or has an invalid format \"$value\""); + } + + return $matches[1]; + } + + public function getVariableName(): string { + $value = $this->value; + if ($value === null || strlen($value) === 0) { + throw new Exception("@$this->name doesn't have any value"); + } + + $matches = []; + + if ($this->name === "param") { + preg_match('/^\s*[\w\|\\\\\[\]]+\s*\$(\w+).*$/', $value, $matches); + } elseif ($this->name === "prefer-ref") { + preg_match('/^\s*\$(\w+).*$/', $value, $matches); + } + + if (isset($matches[1]) === false) { + throw new Exception("@$this->name doesn't contain a variable name or has an invalid format \"$value\""); + } + + return $matches[1]; + } +} + +/** @return DocCommentTag[] */ +function parseDocComment(DocComment $comment): array { + $commentText = substr($comment->getText(), 2, -2); + $tags = []; + foreach (explode("\n", $commentText) as $commentLine) { + $regex = '/^\*\s*@([a-z-]+)(?:\s+(.+))?$/'; + if (preg_match($regex, trim($commentLine), $matches)) { + $tags[] = new DocCommentTag($matches[1], $matches[2] ?? null); + } + } + + return $tags; +} + +function parseFunctionLike( + PrettyPrinterAbstract $prettyPrinter, + FunctionOrMethodName $name, + int $classFlags, + int $flags, + Node\FunctionLike $func, + ?string $cond +): FuncInfo { + $comment = $func->getDocComment(); + $paramMeta = []; + $aliasType = null; + $alias = null; + $isDeprecated = false; + $verify = true; + $docReturnType = null; + $docParamTypes = []; + + if ($comment) { + $tags = parseDocComment($comment); + foreach ($tags as $tag) { + if ($tag->name === 'prefer-ref') { + $varName = $tag->getVariableName(); + if (!isset($paramMeta[$varName])) { + $paramMeta[$varName] = []; + } + $paramMeta[$varName]['preferRef'] = true; + } else if ($tag->name === 'alias' || $tag->name === 'implementation-alias') { + $aliasType = $tag->name; + $aliasParts = explode("::", $tag->getValue()); + if (count($aliasParts) === 1) { + $alias = new FunctionName(new Name($aliasParts[0])); + } else { + $alias = new MethodName(new Name($aliasParts[0]), $aliasParts[1]); + } + } else if ($tag->name === 'deprecated') { + $isDeprecated = true; + } else if ($tag->name === 'no-verify') { + $verify = false; + } else if ($tag->name === 'return') { + $docReturnType = $tag->getType(); + } else if ($tag->name === 'param') { + $docParamTypes[$tag->getVariableName()] = $tag->getType(); + } + } + } + + $varNameSet = []; + $args = []; + $numRequiredArgs = 0; + $foundVariadic = false; + foreach ($func->getParams() as $i => $param) { + $varName = $param->var->name; + $preferRef = !empty($paramMeta[$varName]['preferRef']); + unset($paramMeta[$varName]); + + if (isset($varNameSet[$varName])) { + throw new Exception("Duplicate parameter name $varName for function $name"); + } + $varNameSet[$varName] = true; + + if ($preferRef) { + $sendBy = ArgInfo::SEND_PREFER_REF; + } else if ($param->byRef) { + $sendBy = ArgInfo::SEND_BY_REF; + } else { + $sendBy = ArgInfo::SEND_BY_VAL; + } + + if ($foundVariadic) { + throw new Exception("Error in function $name: only the last parameter can be variadic"); + } + + $type = $param->type ? Type::fromNode($param->type) : null; + if ($type === null && !isset($docParamTypes[$varName])) { + throw new Exception("Missing parameter type for function $name()"); + } + + if ($param->default instanceof Expr\ConstFetch && + $param->default->name->toLowerString() === "null" && + $type && !$type->isNullable() + ) { + $simpleType = $type->tryToSimpleType(); + if ($simpleType === null) { + throw new Exception( + "Parameter $varName of function $name has null default, but is not nullable"); + } + } + + $foundVariadic = $param->variadic; + + $args[] = new ArgInfo( + $varName, + $sendBy, + $param->variadic, + $type, + isset($docParamTypes[$varName]) ? Type::fromPhpDoc($docParamTypes[$varName]) : null, + $param->default ? $prettyPrinter->prettyPrintExpr($param->default) : null + ); + if (!$param->default && !$param->variadic) { + $numRequiredArgs = $i + 1; + } + } + + foreach (array_keys($paramMeta) as $var) { + throw new Exception("Found metadata for invalid param $var of function $name"); + } + + $returnType = $func->getReturnType(); + if ($returnType === null && $docReturnType === null && !$name->isConstructor() && !$name->isDestructor()) { + throw new Exception("Missing return type for function $name()"); + } + + $return = new ReturnInfo( + $func->returnsByRef(), + $returnType ? Type::fromNode($returnType) : null, + $docReturnType ? Type::fromPhpDoc($docReturnType) : null + ); + + return new FuncInfo( + $name, + $classFlags, + $flags, + $aliasType, + $alias, + $isDeprecated, + $verify, + $args, + $return, + $numRequiredArgs, + $cond + ); +} + +function handlePreprocessorConditions(array &$conds, Stmt $stmt): ?string { + foreach ($stmt->getComments() as $comment) { + $text = trim($comment->getText()); + if (preg_match('/^#\s*if\s+(.+)$/', $text, $matches)) { + $conds[] = $matches[1]; + } else if (preg_match('/^#\s*ifdef\s+(.+)$/', $text, $matches)) { + $conds[] = "defined($matches[1])"; + } else if (preg_match('/^#\s*ifndef\s+(.+)$/', $text, $matches)) { + $conds[] = "!defined($matches[1])"; + } else if (preg_match('/^#\s*else$/', $text)) { + if (empty($conds)) { + throw new Exception("Encountered else without corresponding #if"); + } + $cond = array_pop($conds); + $conds[] = "!($cond)"; + } else if (preg_match('/^#\s*endif$/', $text)) { + if (empty($conds)) { + throw new Exception("Encountered #endif without corresponding #if"); + } + array_pop($conds); + } else if ($text[0] === '#') { + throw new Exception("Unrecognized preprocessor directive \"$text\""); + } + } + + return empty($conds) ? null : implode(' && ', $conds); +} + +function getFileDocComment(array $stmts): ?DocComment { + if (empty($stmts)) { + return null; + } + + $comments = $stmts[0]->getComments(); + if (empty($comments)) { + return null; + } + + if ($comments[0] instanceof DocComment) { + return $comments[0]; + } + + return null; +} + +function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstract $prettyPrinter) { + $conds = []; + foreach ($stmts as $stmt) { + if ($stmt instanceof Stmt\Nop) { + continue; + } + + if ($stmt instanceof Stmt\Namespace_) { + handleStatements($fileInfo, $stmt->stmts, $prettyPrinter); + continue; + } + + $cond = handlePreprocessorConditions($conds, $stmt); + if ($stmt instanceof Stmt\Function_) { + $fileInfo->funcInfos[] = parseFunctionLike( + $prettyPrinter, + new FunctionName($stmt->namespacedName), + 0, + 0, + $stmt, + $cond + ); + continue; + } + + if ($stmt instanceof Stmt\ClassLike) { + $className = $stmt->namespacedName; + $methodInfos = []; + foreach ($stmt->stmts as $classStmt) { + $cond = handlePreprocessorConditions($conds, $classStmt); + if ($classStmt instanceof Stmt\Nop) { + continue; + } + + if (!$classStmt instanceof Stmt\ClassMethod) { + throw new Exception("Not implemented {$classStmt->getType()}"); + } + + $classFlags = 0; + if ($stmt instanceof Class_) { + $classFlags = $stmt->flags; + } + + $flags = $classStmt->flags; + if ($stmt instanceof Stmt\Interface_) { + $flags |= Class_::MODIFIER_ABSTRACT; + } + + if (!($flags & Class_::VISIBILITY_MODIFIER_MASK)) { + throw new Exception("Method visibility modifier is required"); + } + + $methodInfos[] = parseFunctionLike( + $prettyPrinter, + new MethodName($className, $classStmt->name->toString()), + $classFlags, + $flags, + $classStmt, + $cond + ); + } + + $fileInfo->classInfos[] = new ClassInfo($className, $methodInfos); + continue; + } + + throw new Exception("Unexpected node {$stmt->getType()}"); + } +} + +function parseStubFile(string $code): FileInfo { + $lexer = new PhpParser\Lexer(); + $parser = new PhpParser\Parser\Php7($lexer); + $nodeTraverser = new PhpParser\NodeTraverser; + $nodeTraverser->addVisitor(new PhpParser\NodeVisitor\NameResolver); + $prettyPrinter = new class extends Standard { + protected function pName_FullyQualified(Name\FullyQualified $node) { + return implode('\\', $node->parts); + } + }; + + $stmts = $parser->parse($code); + $nodeTraverser->traverse($stmts); + + $fileInfo = new FileInfo; + $fileDocComment = getFileDocComment($stmts); + if ($fileDocComment) { + $fileTags = parseDocComment($fileDocComment); + foreach ($fileTags as $tag) { + if ($tag->name === 'generate-function-entries') { + $fileInfo->generateFunctionEntries = true; + $fileInfo->declarationPrefix = $tag->value ? $tag->value . " " : ""; + } else if ($tag->name === 'generate-legacy-arginfo') { + $fileInfo->generateLegacyArginfo = true; + } + } + } + + handleStatements($fileInfo, $stmts, $prettyPrinter); + return $fileInfo; +} + +function funcInfoToCode(FuncInfo $funcInfo, bool $minimal): string { + $code = ''; + + // Generate the minimal, most compatible arginfo across PHP versions + if ($minimal) { + $code .= sprintf("ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n", + $funcInfo->getArgInfoName(), + $funcInfo->return->byRef, + $funcInfo->numRequiredArgs); + foreach ($funcInfo->args as $argInfo) { + $code .= sprintf("\tZEND_ARG_INFO(0, %s)\n", $argInfo->name); + } + } else { + $returnType = $funcInfo->return->type; + if ($returnType !== null) { + if (null !== $simpleReturnType = $returnType->tryToSimpleType()) { + if ($simpleReturnType->isBuiltin) { + $code .= sprintf( + "AWS_PHP_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)\n", + $funcInfo->getArgInfoName(), $funcInfo->return->byRef, + $funcInfo->numRequiredArgs, + $simpleReturnType->toTypeCode(), $returnType->isNullable() + ); + } else { + $code .= sprintf( + "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(%s, %d, %d, %s, %d)\n", + $funcInfo->getArgInfoName(), $funcInfo->return->byRef, + $funcInfo->numRequiredArgs, + $simpleReturnType->toEscapedName(), $returnType->isNullable() + ); + } + } else { + $arginfoType = $returnType->toArginfoType(); + if ($arginfoType->hasClassType()) { + $code .= sprintf( + "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(%s, %d, %d, %s, %s)\n", + $funcInfo->getArgInfoName(), $funcInfo->return->byRef, + $funcInfo->numRequiredArgs, + $arginfoType->toClassTypeString(), $arginfoType->toTypeMask() + ); + } else { + $code .= sprintf( + "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(%s, %d, %d, %s)\n", + $funcInfo->getArgInfoName(), $funcInfo->return->byRef, + $funcInfo->numRequiredArgs, + $arginfoType->toTypeMask() + ); + } + } + } else { + $code .= sprintf( + "ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n", + $funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs + ); + } + + foreach ($funcInfo->args as $argInfo) { + $argKind = $argInfo->isVariadic ? "ARG_VARIADIC" : "ARG"; + $argDefaultKind = $argInfo->hasProperDefaultValue() ? "_WITH_DEFAULT_VALUE" : ""; + $argType = $argInfo->type; + if ($argType !== null) { + if (null !== $simpleArgType = $argType->tryToSimpleType()) { + if ($simpleArgType->isBuiltin) { + $code .= sprintf( + "\tZEND_%s_TYPE_INFO%s(%s, %s, %s, %d%s)\n", + $argKind, $argDefaultKind, $argInfo->getSendByString(), $argInfo->name, + $simpleArgType->toTypeCode(), $argType->isNullable(), + $argInfo->hasProperDefaultValue() ? ", " . $argInfo->getDefaultValueAsArginfoString() : "" + ); + } else { + $code .= sprintf( + "\tZEND_%s_OBJ_INFO%s(%s, %s, %s, %d%s)\n", + $argKind,$argDefaultKind, $argInfo->getSendByString(), $argInfo->name, + $simpleArgType->toEscapedName(), $argType->isNullable(), + $argInfo->hasProperDefaultValue() ? ", " . $argInfo->getDefaultValueAsArginfoString() : "" + ); + } + } else { + $arginfoType = $argType->toArginfoType(); + if ($arginfoType->hasClassType()) { + $code .= sprintf( + "\tZEND_%s_OBJ_TYPE_MASK(%s, %s, %s, %s, %s)\n", + $argKind, $argInfo->getSendByString(), $argInfo->name, + $arginfoType->toClassTypeString(), $arginfoType->toTypeMask(), + $argInfo->getDefaultValueAsArginfoString() + ); + } else { + $code .= sprintf( + "\tZEND_%s_TYPE_MASK(%s, %s, %s, %s)\n", + $argKind, $argInfo->getSendByString(), $argInfo->name, + $arginfoType->toTypeMask(), + $argInfo->getDefaultValueAsArginfoString() + ); + } + } + } else { + $code .= sprintf( + "\tZEND_%s_INFO%s(%s, %s%s)\n", + $argKind, $argDefaultKind, $argInfo->getSendByString(), $argInfo->name, + $argInfo->hasProperDefaultValue() ? ", " . $argInfo->getDefaultValueAsArginfoString() : "" + ); + } + } + } + + $code .= "ZEND_END_ARG_INFO()"; + return $code . "\n"; +} + +/** @param FuncInfo[] $generatedFuncInfos */ +function findEquivalentFuncInfo(array $generatedFuncInfos, FuncInfo $funcInfo): ?FuncInfo { + foreach ($generatedFuncInfos as $generatedFuncInfo) { + if ($generatedFuncInfo->equalsApartFromName($funcInfo)) { + return $generatedFuncInfo; + } + } + return null; +} + +/** @param iterable $funcInfos */ +function generateCodeWithConditions( + iterable $funcInfos, string $separator, Closure $codeGenerator): string { + $code = ""; + foreach ($funcInfos as $funcInfo) { + $funcCode = $codeGenerator($funcInfo); + if ($funcCode === null) { + continue; + } + + $code .= $separator; + if ($funcInfo->cond) { + $code .= "#if {$funcInfo->cond}\n"; + $code .= $funcCode; + $code .= "#endif\n"; + } else { + $code .= $funcCode; + } + } + return $code; +} + +function generateArgInfoCode(FileInfo $fileInfo, string $stubHash, bool $minimal): string { + $code = "/* This is a generated file, edit the .stub.php file instead.\n" + . " * Stub hash: $stubHash */\n"; + $generatedFuncInfos = []; + $code .= generateCodeWithConditions( + $fileInfo->getAllFuncInfos(), "\n", + function (FuncInfo $funcInfo) use(&$generatedFuncInfos, $minimal) { + /* If there already is an equivalent arginfo structure, only emit a #define */ + if ($generatedFuncInfo = findEquivalentFuncInfo($generatedFuncInfos, $funcInfo)) { + $code = sprintf( + "#define %s %s\n", + $funcInfo->getArgInfoName(), $generatedFuncInfo->getArgInfoName() + ); + } else { + $code = funcInfoToCode($funcInfo, $minimal); + } + + $generatedFuncInfos[] = $funcInfo; + return $code; + } + ); + + if ($fileInfo->generateFunctionEntries) { + $code .= "\n\n"; + + $generatedFunctionDeclarations = []; + $code .= generateCodeWithConditions( + $fileInfo->getAllFuncInfos(), "", + function (FuncInfo $funcInfo) use($fileInfo, &$generatedFunctionDeclarations) { + $key = $funcInfo->getDeclarationKey(); + if (isset($generatedFunctionDeclarations[$key])) { + return null; + } + + $generatedFunctionDeclarations[$key] = true; + return $fileInfo->declarationPrefix . $funcInfo->getDeclaration(); + } + ); + + if (!empty($fileInfo->funcInfos)) { + $code .= generateFunctionEntries(null, $fileInfo->funcInfos); + } + + foreach ($fileInfo->classInfos as $classInfo) { + $code .= generateFunctionEntries($classInfo->name, $classInfo->funcInfos); + } + } + + return $code; +} + +/** @param FuncInfo[] $funcInfos */ +function generateFunctionEntries(?Name $className, array $funcInfos): string { + $code = ""; + + $functionEntryName = "ext_functions"; + if ($className) { + $underscoreName = implode("_", $className->parts); + $functionEntryName = "class_{$underscoreName}_methods"; + } + + $code .= "\n\nstatic const zend_function_entry {$functionEntryName}[] = {\n"; + $code .= generateCodeWithConditions($funcInfos, "", function (FuncInfo $funcInfo) { + return $funcInfo->getFunctionEntry(); + }); + $code .= "\tZEND_FE_END\n"; + $code .= "};\n"; + + return $code; +} + +/** + * @param FuncInfo[] $funcMap + * @param FuncInfo[] $aliasMap + * @return array + */ +function generateMethodSynopses(array $funcMap, array $aliasMap): array { + $result = []; + + foreach ($funcMap as $funcInfo) { + $methodSynopsis = $funcInfo->getMethodSynopsisDocument($funcMap, $aliasMap); + if ($methodSynopsis !== null) { + $result[$funcInfo->name->getMethodSynopsisFilename() . ".xml"] = $methodSynopsis; + } + } + + return $result; +} + +/** + * @param FuncInfo[] $funcMap + * @param FuncInfo[] $aliasMap + * @return array + */ +function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $aliasMap): array { + $methodSynopses = []; + + $it = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($targetDirectory), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($it as $file) { + $pathName = $file->getPathName(); + if (!preg_match('/\.xml$/i', $pathName)) { + continue; + } + + $xml = file_get_contents($pathName); + if ($xml === false) { + continue; + } + + if (stripos($xml, "formatOutput = false; + $doc->preserveWhiteSpace = true; + $doc->validateOnParse = true; + $success = $doc->loadXML($replacedXml); + if (!$success) { + echo "Failed opening $pathName\n"; + continue; + } + + $docComparator = new DOMDocument(); + $docComparator->preserveWhiteSpace = false; + $docComparator->formatOutput = true; + + $methodSynopsisElements = []; + foreach ($doc->getElementsByTagName("constructorsynopsis") as $element) { + $methodSynopsisElements[] = $element; + } + foreach ($doc->getElementsByTagName("destructorsynopsis") as $element) { + $methodSynopsisElements[] = $element; + } + foreach ($doc->getElementsByTagName("methodsynopsis") as $element) { + $methodSynopsisElements[] = $element; + } + + foreach ($methodSynopsisElements as $methodSynopsis) { + if (!$methodSynopsis instanceof DOMElement) { + continue; + } + + $list = $methodSynopsis->getElementsByTagName("methodname"); + $item = $list->item(0); + if (!$item instanceof DOMElement) { + continue; + } + $funcName = $item->textContent; + if (!isset($funcMap[$funcName])) { + continue; + } + $funcInfo = $funcMap[$funcName]; + + $newMethodSynopsis = $funcInfo->getMethodSynopsisElement($funcMap, $aliasMap, $doc); + if ($newMethodSynopsis === null) { + continue; + } + + // Retrieve current signature + + $params = []; + $list = $methodSynopsis->getElementsByTagName("methodparam"); + foreach ($list as $i => $item) { + if (!$item instanceof DOMElement) { + continue; + } + + $paramList = $item->getElementsByTagName("parameter"); + if ($paramList->count() !== 1) { + continue; + } + + $paramName = $paramList->item(0)->textContent; + $paramTypes = []; + + $paramList = $item->getElementsByTagName("type"); + foreach ($paramList as $type) { + if (!$type instanceof DOMElement) { + continue; + } + + $paramTypes[] = $type->textContent; + } + + $params[$paramName] = ["index" => $i, "type" => $paramTypes]; + } + + // Check if there is any change - short circuit if there is not any. + + $xml1 = $doc->saveXML($methodSynopsis); + $xml1 = preg_replace("/&([A-Za-z0-9._{}%-]+?;)/", "REPLACED-ENTITY-$1", $xml1); + $docComparator->loadXML($xml1); + $xml1 = $docComparator->saveXML(); + + $methodSynopsis->parentNode->replaceChild($newMethodSynopsis, $methodSynopsis); + + $xml2 = $doc->saveXML($newMethodSynopsis); + $xml2 = preg_replace("/&([A-Za-z0-9._{}%-]+?;)/", "REPLACED-ENTITY-$1", $xml2); + $docComparator->loadXML($xml2); + $xml2 = $docComparator->saveXML(); + + if ($xml1 === $xml2) { + continue; + } + + // Update parameter references + + $paramList = $doc->getElementsByTagName("parameter"); + /** @var DOMElement $paramElement */ + foreach ($paramList as $paramElement) { + if ($paramElement->parentNode && $paramElement->parentNode->nodeName === "methodparam") { + continue; + } + + $name = $paramElement->textContent; + if (!isset($params[$name])) { + continue; + } + + $index = $params[$name]["index"]; + if (!isset($funcInfo->args[$index])) { + continue; + } + + $paramElement->textContent = $funcInfo->args[$index]->name; + } + + // Return the updated XML + + $replacedXml = $doc->saveXML(); + + $replacedXml = preg_replace( + [ + "/REPLACED-ENTITY-([A-Za-z0-9._{}%-]+?;)/", + "//i", + "//i", + ], + [ + "&$1", + "", + "", + ], + $replacedXml + ); + + $methodSynopses[$pathName] = $replacedXml; + } + } + + return $methodSynopses; +} + +function installPhpParser(string $version, string $phpParserDir) { + $lockFile = __DIR__ . "/PHP-Parser-install-lock"; + $lockFd = fopen($lockFile, 'w+'); + if (!flock($lockFd, LOCK_EX)) { + throw new Exception("Failed to acquire installation lock"); + } + + try { + // Check whether a parallel process has already installed PHP-Parser. + if (is_dir($phpParserDir)) { + return; + } + + $cwd = getcwd(); + chdir(__DIR__); + + $tarName = "v$version.tar.gz"; + passthru("wget https://github.com/nikic/PHP-Parser/archive/$tarName", $exit); + if ($exit !== 0) { + passthru("curl -LO https://github.com/nikic/PHP-Parser/archive/$tarName", $exit); + } + if ($exit !== 0) { + throw new Exception("Failed to download PHP-Parser tarball"); + } + if (!mkdir($phpParserDir)) { + throw new Exception("Failed to create directory $phpParserDir"); + } + passthru("tar xvzf $tarName -C PHP-Parser-$version --strip-components 1", $exit); + if ($exit !== 0) { + throw new Exception("Failed to extract PHP-Parser tarball"); + } + unlink(__DIR__ . "/$tarName"); + chdir($cwd); + } finally { + flock($lockFd, LOCK_UN); + @unlink($lockFile); + } +} + +function initPhpParser() { + static $isInitialized = false; + if ($isInitialized) { + return; + } + + if (!extension_loaded("tokenizer")) { + throw new Exception("The \"tokenizer\" extension is not available"); + } + + $isInitialized = true; + $version = "4.9.0"; + $phpParserDir = __DIR__ . "/PHP-Parser-$version"; + if (!is_dir($phpParserDir)) { + installPhpParser($version, $phpParserDir); + } + + spl_autoload_register(function(string $class) use($phpParserDir) { + if (strpos($class, "PhpParser\\") === 0) { + $fileName = $phpParserDir . "/lib/" . str_replace("\\", "/", $class) . ".php"; + require $fileName; + } + }); +} + +$optind = null; +$options = getopt("fh", [ + "force-regeneration", + "parameter-stats", + "help", + "verify", + "generate-methodsynopses", + "replace-methodsynopses", + "minimal-arginfo"], $optind); + +$context = new Context; +$printParameterStats = isset($options["parameter-stats"]); +$verify = isset($options["verify"]); +$generateMethodSynopses = isset($options["generate-methodsynopses"]); +$replaceMethodSynopses = isset($options["replace-methodsynopses"]); +$context->forceRegeneration = isset($options["f"]) || isset($options["force-regeneration"]); +$context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $generateMethodSynopses || $replaceMethodSynopses; +$context->minimalArgInfo = isset($options["minimal-arginfo"]); +$targetMethodSynopses = $argv[$optind + 1] ?? null; +if ($replaceMethodSynopses && $targetMethodSynopses === null) { + die("A target directory must be provided.\n"); +} + +if (isset($options["h"]) || isset($options["help"])) { + die("\nusage: gen-stub.php [ -f | --force-regeneration ] [ --generate-methodsynopses ] [ --replace-methodsynopses ] [ --parameter-stats ] [ --verify ] [ -h | --help ] [ name.stub.php | directory ] [ directory ]\n\n"); +} + +$fileInfos = []; +$location = $argv[$optind] ?? "."; +if (is_file($location)) { + // Generate single file. + $fileInfo = processStubFile($location, $context); + if ($fileInfo) { + $fileInfos[] = $fileInfo; + } +} else if (is_dir($location)) { + $fileInfos = processDirectory($location, $context); +} else { + echo "$location is neither a file nor a directory.\n"; + exit(1); +} + +if ($printParameterStats) { + $parameterStats = []; + + foreach ($fileInfos as $fileInfo) { + foreach ($fileInfo->getAllFuncInfos() as $funcInfo) { + foreach ($funcInfo->args as $argInfo) { + if (!isset($parameterStats[$argInfo->name])) { + $parameterStats[$argInfo->name] = 0; + } + $parameterStats[$argInfo->name]++; + } + } + } + + arsort($parameterStats); + echo json_encode($parameterStats, JSON_PRETTY_PRINT), "\n"; +} + +/** @var FuncInfo[] $funcMap */ +$funcMap = []; +/** @var FuncInfo[] $aliasMap */ +$aliasMap = []; + +foreach ($fileInfos as $fileInfo) { + foreach ($fileInfo->getAllFuncInfos() as $funcInfo) { + /** @var FuncInfo $funcInfo */ + $funcMap[$funcInfo->name->__toString()] = $funcInfo; + + if ($funcInfo->aliasType === "alias") { + $aliasMap[$funcInfo->alias->__toString()] = $funcInfo; + } + } +} + +if ($verify) { + $errors = []; + + foreach ($aliasMap as $aliasFunc) { + if (!isset($funcMap[$aliasFunc->alias->__toString()])) { + $errors[] = "Aliased function {$aliasFunc->alias}() cannot be found"; + continue; + } + + if (!$aliasFunc->verify) { + continue; + } + + $aliasedFunc = $funcMap[$aliasFunc->alias->__toString()]; + $aliasedArgs = $aliasedFunc->args; + $aliasArgs = $aliasFunc->args; + + if ($aliasFunc->isInstanceMethod() !== $aliasedFunc->isInstanceMethod()) { + if ($aliasFunc->isInstanceMethod()) { + $aliasedArgs = array_slice($aliasedArgs, 1); + } + + if ($aliasedFunc->isInstanceMethod()) { + $aliasArgs = array_slice($aliasArgs, 1); + } + } + + array_map( + function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc, &$errors) { + if ($aliasArg === null) { + assert($aliasedArg !== null); + $errors[] = "{$aliasFunc->name}(): Argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() is missing"; + return null; + } + + if ($aliasedArg === null) { + $errors[] = "{$aliasedFunc->name}(): Argument \$$aliasArg->name of alias function {$aliasFunc->name}() is missing"; + return null; + } + + if ($aliasArg->name !== $aliasedArg->name) { + $errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same name"; + return null; + } + + if ($aliasArg->type != $aliasedArg->type) { + $errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same type"; + } + + if ($aliasArg->defaultValue !== $aliasedArg->defaultValue) { + $errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same default value"; + } + }, + $aliasArgs, $aliasedArgs + ); + + if ((!$aliasedFunc->isMethod() || $aliasedFunc->isFinalMethod()) && + (!$aliasFunc->isMethod() || $aliasFunc->isFinalMethod()) && + $aliasFunc->return != $aliasedFunc->return + ) { + $errors[] = "{$aliasFunc->name}() and {$aliasedFunc->name}() must have the same return type"; + } + } + + echo implode("\n", $errors); + if (!empty($errors)) { + echo "\n"; + exit(1); + } +} + +if ($generateMethodSynopses) { + $methodSynopsesDirectory = getcwd() . "/methodsynopses"; + + $methodSynopses = generateMethodSynopses($funcMap, $aliasMap); + if (!empty($methodSynopses)) { + if (!file_exists($methodSynopsesDirectory)) { + mkdir($methodSynopsesDirectory); + } + + foreach ($methodSynopses as $filename => $content) { + if (file_put_contents("$methodSynopsesDirectory/$filename", $content)) { + echo "Saved $filename\n"; + } + } + } +} + +if ($replaceMethodSynopses) { + $methodSynopses = replaceMethodSynopses($targetMethodSynopses, $funcMap, $aliasMap); + + foreach ($methodSynopses as $filename => $content) { + if (file_put_contents($filename, $content)) { + echo "Saved $filename\n"; + } + } +} diff --git a/src/AWS/CRT/Auth/Signing.php b/src/AWS/CRT/Auth/Signing.php index b0ef2b4..ff949aa 100644 --- a/src/AWS/CRT/Auth/Signing.php +++ b/src/AWS/CRT/Auth/Signing.php @@ -9,6 +9,10 @@ use AWS\CRT\NativeResource; abstract class Signing extends NativeResource { static function signRequestAws($signable, $signing_config, $on_complete) { - return self::$crt->sign_request_aws($signable, $signing_config, $on_complete); + return self::$crt->sign_request_aws($signable->native, $signing_config->native, + function($result, $error_code) use ($on_complete) { + $signing_result = SigningResult::fromNative($result); + $on_complete($signing_result, $error_code); + }); } } \ No newline at end of file diff --git a/src/AWS/CRT/Auth/SigningConfigAWS.php b/src/AWS/CRT/Auth/SigningConfigAWS.php index aed6adf..f16ebd9 100644 --- a/src/AWS/CRT/Auth/SigningConfigAWS.php +++ b/src/AWS/CRT/Auth/SigningConfigAWS.php @@ -23,6 +23,7 @@ class SigningConfigAWS extends NativeResource { 'signed_body_value' => null, 'signed_body_header_type' => SignedBodyHeaderType::NONE, 'expiration_in_seconds' => 0, + 'date' => time() ]; } @@ -34,15 +35,28 @@ class SigningConfigAWS extends NativeResource { $sc = $this->acquire(self::$crt->signing_config_aws_new()); self::$crt->signing_config_aws_set_algorithm($sc, $options->algorithm->asInt()); self::$crt->signing_config_aws_set_signature_type($sc, $options->signature_type->asInt()); - self::$crt->signing_config_aws_set_credentials_provider($sc, $options->credentials_provider->asObject()); - self::$crt->signing_config_aws_set_region($sc, $options->region->asString()); - self::$crt->signing_config_aws_set_service($sc, $options->service->asString()); - self::$crt->signing_config_aws_set_use_double_uri_encode($sc, $options->use_double_uri_encode->asBool()); - self::$crt->signing_config_aws_set_should_normalize_uri_path($sc, $options->should_normalize_uri_path->asBool()); - self::$crt->signing_config_aws_set_omit_session_token($sc, $options->omit_session_token->asBool()); - self::$crt->signing_config_aws_set_signed_body_value($sc, $options->signed_body_value->asString()); - self::$crt->signing_config_aws_set_signed_body_header_type($sc, $options->signed_body_header_type->asInt()); - self::$crt->signing_config_aws_set_expiration_in_seconds($sc, $options->expiration_in_seconds->asInt()); + if ($credentials_provider = $options->credentials_provider->asObject()) { + self::$crt->signing_config_aws_set_credentials_provider( + $sc, + $credentials_provider->native); + } + self::$crt->signing_config_aws_set_region( + $sc, $options->region->asString()); + self::$crt->signing_config_aws_set_service( + $sc, $options->service->asString()); + self::$crt->signing_config_aws_set_use_double_uri_encode( + $sc, $options->use_double_uri_encode->asBool()); + self::$crt->signing_config_aws_set_should_normalize_uri_path( + $sc, $options->should_normalize_uri_path->asBool()); + self::$crt->signing_config_aws_set_omit_session_token( + $sc, $options->omit_session_token->asBool()); + self::$crt->signing_config_aws_set_signed_body_value( + $sc, $options->signed_body_value->asString()); + self::$crt->signing_config_aws_set_signed_body_header_type( + $sc, $options->signed_body_header_type->asInt()); + self::$crt->signing_config_aws_set_expiration_in_seconds( + $sc, $options->expiration_in_seconds->asInt()); + self::$crt->signing_config_aws_set_date($sc, $options->date->asInt()); } function __destruct() diff --git a/src/AWS/CRT/Auth/SigningResult.php b/src/AWS/CRT/Auth/SigningResult.php new file mode 100644 index 0000000..b8a4ab5 --- /dev/null +++ b/src/AWS/CRT/Auth/SigningResult.php @@ -0,0 +1,33 @@ +acquire($native); + } + + function __destruct() { + // No destruction necessary, SigningResults are transient, just release + $this->release(); + parent::__destruct(); + } + + public static function fromNative($ptr) { + return new SigningResult($ptr); + } + + public function applyToHttpRequest(&$http_request) { + self::$crt->signing_result_apply_to_http_request($this->native, $http_request->native); + // Update http_request from native + $http_request = Request::unmarshall($http_request->toBlob()); + } +} \ No newline at end of file diff --git a/src/AWS/CRT/CRT.php b/src/AWS/CRT/CRT.php index aa40a06..8531729 100644 --- a/src/AWS/CRT/CRT.php +++ b/src/AWS/CRT/CRT.php @@ -48,6 +48,25 @@ final class CRT { } } + /** + * @return bool whether or not the CRT is currently loaded with an active backend + */ + public static function isLoaded() { + return !is_null(self::$impl); + } + + /** + * @return bool whether or not the CRT is available via one of the possible backends + */ + public static function isAvailable() { + try { + new CRT(); + return true; + } catch (RuntimeException $ex) { + return false; + } + } + /** * @return integer last error code reported within the CRT */ @@ -211,6 +230,10 @@ final class CRT { return self::$impl->aws_crt_http_message_new_from_blob($blob); } + function http_message_to_blob($message) { + return self::$impl->aws_crt_http_message_to_blob($message); + } + function http_message_release($message) { self::$impl->aws_crt_http_message_release($message); } @@ -267,6 +290,10 @@ final class CRT { self::$impl->aws_crt_signing_config_aws_set_expiration_in_seconds($signing_config, $expiration_in_seconds); } + function signing_config_aws_set_date($signing_config, $timestamp) { + self::$impl->aws_crt_signing_config_aws_set_date($signing_config, $timestamp); + } + function signable_new_from_http_request($http_message) { return self::$impl->aws_crt_signable_new_from_http_request($http_message); } @@ -283,6 +310,15 @@ final class CRT { self::$impl->aws_crt_signable_release($signable); } + function signing_result_release($signing_result) { + self::$impl->aws_crt_signing_result_release($signing_result); + } + + function signing_result_apply_to_http_request($signing_result, $http_message) { + return self::$impl->aws_crt_signing_result_apply_to_http_request( + $signing_result, $http_message); + } + function sign_request_aws($signable, $signing_config, $on_complete) { return self::$impl->aws_crt_sign_request_aws($signable, $signing_config, $on_complete); } diff --git a/src/AWS/CRT/HTTP/Message.php b/src/AWS/CRT/HTTP/Message.php index a21bd85..a8c151f 100644 --- a/src/AWS/CRT/HTTP/Message.php +++ b/src/AWS/CRT/HTTP/Message.php @@ -28,6 +28,10 @@ abstract class Message extends NativeResource { parent::__destruct(); } + public function toBlob() { + return self::$crt->http_message_to_blob($this->native); + } + protected static function marshall($msg) { $buf = ""; $buf .= Encoding::encodeString($msg->method); diff --git a/tests/000_CoreTest.php b/tests/000_CoreTest.php new file mode 100644 index 0000000..e9e068a --- /dev/null +++ b/tests/000_CoreTest.php @@ -0,0 +1,22 @@ +assertTrue(CRT::isAvailable()); + } + + // We have done nothing to necessitate loading the CRT, it should not be loaded + public function testIsLoaded() { + $this->assertTrue(!CRT::isLoaded()); + } +} \ No newline at end of file diff --git a/tests/ErrorTest.php b/tests/ErrorTest.php index a9920ae..e84d1ed 100644 --- a/tests/ErrorTest.php +++ b/tests/ErrorTest.php @@ -6,14 +6,6 @@ use AWS\CRT\CRT; final class ErrorTest extends PHPUnit_Framework_TestCase { - private static $crt = null; - public static function setUpBeforeClass() { - self::$crt = new CRT(); - } - - public static function tearDownAfterClass() { - self::$crt = null; - } public function testNoInitialError() { $this->assertEquals(0, CRT::last_error()); diff --git a/tests/SigningTest.php b/tests/SigningTest.php index 4194b09..8b1ecc0 100644 --- a/tests/SigningTest.php +++ b/tests/SigningTest.php @@ -61,7 +61,6 @@ final class SigningTest extends CrtTestCase { const SIGV4TEST_SERVICE = 'service'; const SIGV4TEST_REGION = 'us-east-1'; public function testSigv4HeaderSigning() { - $this->markTestSkipped('WIP'); $date = mktime(12, 36, 0, 8, 30, 2015); $credentials_provider = new StaticCredentialsProvider([ 'access_key_id' => self::SIGV4TEST_ACCESS_KEY_ID, @@ -74,14 +73,25 @@ final class SigningTest extends CrtTestCase { 'credentials_provider' => $credentials_provider, 'region' => self::SIGV4TEST_REGION, 'service' => self::SIGV4TEST_SERVICE, + 'date' => $date, ]); $http_request = new Request('GET', '/', [], ['Host' => 'example.amazonaws.com']); $this->assertNotNull($http_request, "Unable to create HttpRequest for signing"); $signable = Signable::fromHttpRequest($http_request); $this->assertNotNull($signable, "Unable to create signable from HttpRequest"); - Signing::signRequestAws($signable, $signing_config, function() { + Signing::signRequestAws( + $signable, $signing_config, + function($signing_result, $error_code) use (&$http_request) { + $this->assertEquals(0, $error_code); + $signing_result->applyToHttpRequest($http_request); + } + ); - }); + $headers = $http_request->headers(); + $this->assertEquals( + 'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31', + $headers->get('Authorization')); + $this->assertEquals('20150830T123600Z', $headers->get('X-Amz-Date')); } } diff --git a/tests/common.inc b/tests/common.inc index 4156715..0cb443a 100644 --- a/tests/common.inc +++ b/tests/common.inc @@ -6,10 +6,24 @@ use AWS\CRT\CRT as CRT; abstract class CrtTestCase extends PHPUnit_Framework_TestCase { + private static $crt = null; + public static function setUpBeforeClass() { + self::$crt = new CRT(); + } + + public static function tearDownAfterClass() { + self::$crt = null; + } + // Ensure that after every test there are no errors in the CRT itself protected function assertPostConditions() { if (CRT::last_error()) { $this->fail("Test left an error on the stack: " . CRT::error_name(CRT::last_error())); } } + + // shim expectException -> setExpectedException for PHPUnit 4.8.x + public function expectException($exceptionName) { + $this->setExpectedException($exceptionName); + } }