From d4df2c19ca549e6461ca310a42e69bb2072c0883 Mon Sep 17 00:00:00 2001 From: ckoegel Date: Mon, 26 Oct 2026 18:06:04 -0400 Subject: [PATCH] DX-3359 Update Sample --- .github/CODEOWNERS | 2 +- .github/dependabot.yml | 19 + .github/workflows/test.yml | 15 + .gitignore | 36 +- LICENSE | 2 +- README.md | 51 ++- composer.json | 18 +- composer.lock | 888 ++++++++++++++++++++++++------------- icon-voice.svg | 11 + public/index.php | 148 ++++--- start.php | 19 + 11 files changed, 787 insertions(+), 422 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/test.yml create mode 100644 icon-voice.svg create mode 100644 start.php diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index beb9074..12776e1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # Global rule: -* @Bandwidth-Samples/dx +* @Bandwidth-Samples/band-swi @Bandwidth-Samples/band-devx diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ffb69cb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 + +updates: + - package-ecosystem: composer + directory: / + schedule: + interval: monthly + open-pull-requests-limit: 5 + ignore: + - dependency-name: "bandwidth/sdk" + update-types: + - version-update:semver-minor + - version-update:semver-patch + groups: + dependencies: + patterns: + - "*" + exclude-patterns: + - "bandwidth/sdk" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..cf48e39 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,15 @@ +name: Test + +on: + pull_request: + branches: + - main + schedule: + - cron: "0 9 * * 1" + +jobs: + contract-test: + name: Contract Test + uses: Bandwidth-Samples/sample-app-tooling/.github/workflows/php.yml@main + with: + sample: voice-bridge diff --git a/.gitignore b/.gitignore index f390797..eda4744 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,8 @@ -# Compiled class file -*.class -/target -/out -/dist -/bin -/obj +# macOS System file +.DS_Store + +# Environment file +.env # IntelliJ .idea/ @@ -13,30 +11,8 @@ # Vs Code .vscode/ -# Node -/node_modules - # PHP -**/vendor/* -*.phar +/vendor/ # Log file *.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* diff --git a/LICENSE b/LICENSE index df9461d..ddc77f4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Bandwidth Samples +Copyright (c) 2026 Bandwidth Samples Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index dfc0a51..14de3c2 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,32 @@ -# Voice-Bridge-PHP - - +# Voice Bridge + + + Voice Quick Start Guide + # Table of Contents -- [Voice-Bridge-PHP](#Voice-Bridge-PHP) - [Description](#description) - [Bandwidth](#bandwidth) - [Environmental Variables](#environmental-variables) +- [Running the Application](#running-the-application) - [Callback URLs](#callback-urls) - [Ngrok](#ngrok) # Description -A simple PHP sample application demonstrating how to bridge two live calls using the Bandwidth V2 Voice API. +This sample app masks a phone call through a Bandwidth phone number using Bandwidth's Bridge BXML verb. Once configured, anyone who calls your Bandwidth phone number will be bridged to the masked number without either party members knowing the other's phone number. + +In the Bandwidth Dashboard, set the Application's `Call initiated callback URL` to `/callbacks/inbound/voice`. This can also be done via the Dashboard API by setting `CallInitiatedCallbackUrl`. Once configured, any calls made to a number associated with that application will cause this app to create a new call to the `USER_NUMBER`. This call will come from your `BW_NUMBER` and the original call will be bridged to this one once accepted. + +Once the callee at `USER_NUMBER` confirms the call, the call flow will appear as such: + +`Random Number <-> BW_NUMBER <-> USER_NUMBER` - if the `BW_NUMBER` environment variable is the number called. + +`Random Number <-> Other BW_NUMBER <-> BW_NUMBER <-> USER_NUMBER` - if the `BW_NUMBER` environment variable is different from the number called. # Bandwidth @@ -30,12 +40,27 @@ For more information about API credentials see [here](https://dev.bandwidth.com/ The sample app uses the below environmental variables. ```sh BW_ACCOUNT_ID # Your Bandwidth Account Id -BW_USERNAME # Your Bandwidth API Username -BW_PASSWORD # Your Bandwidth API Password -BW_VOICE_APPLICATION_ID # Your Voice Application Id created in the dashboard -BW_MESSAGING_APPLICATION_ID # Your Messaging Application Id created in the dashboard -BASE_CALLBACK_URL # Your public base url +BW_CLIENT_ID # Your Bandwidth API OAuth Client Id +BW_CLIENT_SECRET # Your Bandwidth API OAuth Client Secret +BW_NUMBER # The Bandwidth phone number involved with this application USER_NUMBER # The number dialed out to when an inbound call is received on your Bandwidth number +BW_VOICE_APPLICATION_ID # Your Voice Application Id created in the dashboard +BASE_CALLBACK_URL # Your public base url to receive Bandwidth Webhooks. No trailing '/' +LOCAL_PORT # The port number you wish to run the sample on +``` + +# Running the Application + +To install the required packages for this app, run the command: + +```sh +composer install +``` + +Use the following command to run the application: + +```sh +composer start ``` # Callback URLs @@ -43,8 +68,8 @@ USER_NUMBER # The number dialed out to when an inbound call is For a detailed introduction to Bandwidth Callbacks see https://dev.bandwidth.com/guides/callbacks/callbacks.html Below are the callback paths: -* `/callbacks/inboundCall` -* `/callbacks/outboundCall` +* `/callbacks/outbound/voice` +* `/callbacks/inbound/voice` ## Ngrok @@ -53,4 +78,4 @@ After you have downloaded and installed `ngrok` run the following command to ope ```cmd ngrok http $LOCAL_PORT ``` -You can view your public URL at `http://127.0.0.1:{LOCAL_PORT}` after ngrok is running. You can also view the status of the tunnel and requests/responses here. +You can view your public URL at `http://127.0.0.1:4040` after ngrok is running. You can also view the status of the tunnel and requests/responses here. diff --git a/composer.json b/composer.json index 1440625..5adb900 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,18 @@ { + "name": "bandwidth-samples/voice-bridge-php", + "description": "Bandwidth Sample App Demonstrating the Bridge BXML Verb", + "type": "project", + "license": "MIT", "require": { - "bandwidth/sdk": "^8.1.2", - "slim/slim": "^4.7", - "slim/http": "^1.2", - "slim/psr7": "^1.3" + "php": "^8.1", + "bandwidth/sdk": "14.0.1", + "slim/slim": "4.15.3", + "slim/psr7": "1.8.0" + }, + "scripts": { + "start": [ + "Composer\\Config::disableProcessTimeout", + "@php start.php" + ] } } diff --git a/composer.lock b/composer.lock index e7d2807..44ba47e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,88 +4,40 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a670e1a604d552597f473767f54bbfb7", + "content-hash": "585172dee4437f842fea8ad030c8036e", "packages": [ - { - "name": "apimatic/jsonmapper", - "version": "v1.3.1", - "source": { - "type": "git", - "url": "https://github.com/apimatic/jsonmapper.git", - "reference": "0883d385653ebd2a194ec79e66facb7d69441404" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/apimatic/jsonmapper/zipball/0883d385653ebd2a194ec79e66facb7d69441404", - "reference": "0883d385653ebd2a194ec79e66facb7d69441404", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "^5 || ^6 || ^7", - "squizlabs/php_codesniffer": "^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "apimatic\\jsonmapper\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Christian Weiske", - "email": "christian.weiske@netresearch.de", - "homepage": "http://www.netresearch.de/", - "role": "Developer" - }, - { - "name": "Mehdi Jaffery", - "email": "mehdi.jaffery@apimatic.io", - "homepage": "http://apimatic.io/", - "role": "Developer" - } - ], - "description": "Map nested JSON structures onto PHP classes", - "support": { - "email": "mehdi.jaffery@apimatic.io", - "issues": "https://github.com/apimatic/jsonmapper/issues", - "source": "https://github.com/apimatic/jsonmapper/tree/v1.3.1" - }, - "time": "2020-04-06T20:47:48+00:00" - }, { "name": "bandwidth/sdk", - "version": "v8.1.2", + "version": "v14.0.1", "source": { "type": "git", "url": "https://github.com/Bandwidth/php-sdk.git", - "reference": "f5647a1a8542e08e2dcd86fb029e7089ff82e6bd" + "reference": "2638389c4fef620d2507f2cb6d3f9741f9884f33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Bandwidth/php-sdk/zipball/f5647a1a8542e08e2dcd86fb029e7089ff82e6bd", - "reference": "f5647a1a8542e08e2dcd86fb029e7089ff82e6bd", + "url": "https://api.github.com/repos/Bandwidth/php-sdk/zipball/2638389c4fef620d2507f2cb6d3f9741f9884f33", + "reference": "2638389c4fef620d2507f2cb6d3f9741f9884f33", "shasum": "" }, "require": { - "apimatic/jsonmapper": "~1.3.0", "ext-curl": "*", + "ext-dom": "*", "ext-json": "*", "ext-mbstring": "*", - "ext-simplexml": "*", - "mashape/unirest-php": "~3.0.1", - "php": ">=5.4.0" + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.0", + "php": "^8.1" }, "require-dev": { - "phpunit/phpunit": "^9" + "friendsofphp/php-cs-fixer": "^3.5", + "overtrue/phplint": "^9.0", + "phpunit/phpunit": "^9.0" }, "type": "library", "autoload": { "psr-4": { - "BandwidthLib\\": "src/" + "Bandwidth\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -94,24 +46,26 @@ ], "authors": [ { - "name": "Bandwidth Inc.", - "email": "support@bandwidth.com", - "homepage": "https://www.bandwidth.com/", - "role": "Owner" + "name": "Bandwidth", + "homepage": "https://www.bandwidth.com" } ], - "description": "Bandwidth's set of APIs", - "homepage": "https://www.bandwidth.com/", + "description": "Bandwidth's Communication APIs", + "homepage": "https://github.com/Bandwidth/php-sdk", "keywords": [ "api", - "bandwidth", + "openapi", + "openapi-generator", + "openapitools", + "php", + "rest", "sdk" ], "support": { "issues": "https://github.com/Bandwidth/php-sdk/issues", - "source": "https://github.com/Bandwidth/php-sdk/tree/v8.1.2" + "source": "https://github.com/Bandwidth/php-sdk/tree/v14.0.1" }, - "time": "2021-12-16T21:44:16+00:00" + "time": "2026-10-13T20:38:29+00:00" }, { "name": "fig/http-message-util", @@ -170,68 +124,348 @@ "time": "2020-11-24T22:02:12+00:00" }, { - "name": "mashape/unirest-php", - "version": "v3.0.4", + "name": "guzzlehttp/guzzle", + "version": "7.16.1", "source": { "type": "git", - "url": "https://github.com/Mashape/unirest-php.git", - "reference": "842c0f242dfaaf85f16b72e217bf7f7c19ab12cb" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "81755d989b3007ca7f88a069857553cd08e7dc2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Mashape/unirest-php/zipball/842c0f242dfaaf85f16b72e217bf7f7c19ab12cb", - "reference": "842c0f242dfaaf85f16b72e217bf7f7c19ab12cb", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/81755d989b3007ca7f88a069857553cd08e7dc2e", + "reference": "81755d989b3007ca7f88a069857553cd08e7dc2e", "shasum": "" }, "require": { - "ext-curl": "*", - "php": ">=5.4.0" + "ext-json": "*", + "guzzlehttp/promises": "^2.6", + "guzzlehttp/psr7": "^2.13.2", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" + }, + "provide": { + "psr/http-client-implementation": "1.0" }, "require-dev": { - "codeclimate/php-test-reporter": "0.1.*", - "phpunit/phpunit": "~4.4" + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.3", + "guzzlehttp/test-server": "^0.7", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { - "ext-json": "Allows using JSON Bodies for sending and parsing requests" + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, "autoload": { - "psr-0": { - "Unirest\\": "src/" + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Unirest PHP", - "homepage": "https://github.com/Mashape/unirest-php", + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", "keywords": [ "client", "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.16.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2026-10-25T21:35:21+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "3d88c829bd5f13d13847b39067dbf09a60af2154" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/3d88c829bd5f13d13847b39067dbf09a60af2154", + "reference": "3d88c829bd5f13d13847b39067dbf09a60af2154", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.6.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2026-10-18T12:46:59+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.13.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "adee536f95101929f262b8c59e8a61472f347d58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/adee536f95101929f262b8c59e8a61472f347d58", + "reference": "adee536f95101929f262b8c59e8a61472f347d58", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ "http", - "https", - "rest" + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" ], "support": { - "email": "opensource@mashape.com", - "issues": "https://github.com/Mashape/unirest-php/issues", - "source": "https://github.com/Mashape/unirest-php/tree/master" + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.13.2" }, - "time": "2016-08-11T17:49:21+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2026-10-17T23:49:46+00:00" }, { "name": "nikic/fast-route", - "version": "v1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/nikic/FastRoute.git", - "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + "reference": "7476684f39bb9124b3be69ed3d84b66723920298" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", - "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/7476684f39bb9124b3be69ed3d84b66723920298", + "reference": "7476684f39bb9124b3be69ed3d84b66723920298", "shasum": "" }, "require": { @@ -242,12 +476,12 @@ }, "type": "library", "autoload": { - "psr-4": { - "FastRoute\\": "src/" - }, "files": [ "src/functions.php" - ] + ], + "psr-4": { + "FastRoute\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -266,31 +500,31 @@ ], "support": { "issues": "https://github.com/nikic/FastRoute/issues", - "source": "https://github.com/nikic/FastRoute/tree/master" + "source": "https://github.com/nikic/FastRoute/tree/1.3.1" }, - "time": "2018-02-13T20:26:39+00:00" + "time": "2026-07-09T19:38:47+00:00" }, { "name": "psr/container", - "version": "1.0.0", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -305,7 +539,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -319,27 +553,79 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/master" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2017-02-14T16:28:37+00:00" + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -359,10 +645,10 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -374,31 +660,31 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2019-04-30T12:38:16+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { "name": "psr/http-message", - "version": "1.0.1", + "version": "2.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -413,7 +699,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -427,27 +713,27 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { "name": "psr/http-server-handler", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/http-server-handler.git", - "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7" + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/aff2f80e33b7f026ec96bb42f63242dc50ffcae7", - "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", "shasum": "" }, "require": { "php": ">=7.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -467,7 +753,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP server-side request handler", @@ -483,28 +769,27 @@ "server" ], "support": { - "issues": "https://github.com/php-fig/http-server-handler/issues", - "source": "https://github.com/php-fig/http-server-handler/tree/master" + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" }, - "time": "2018-10-30T16:46:14+00:00" + "time": "2023-04-10T20:06:20+00:00" }, { "name": "psr/http-server-middleware", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/http-server-middleware.git", - "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5" + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/2296f45510945530b9dceb8bcedb5cb84d40c5f5", - "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", "shasum": "" }, "require": { "php": ">=7.0", - "psr/http-message": "^1.0", + "psr/http-message": "^1.0 || ^2.0", "psr/http-server-handler": "^1.0" }, "type": "library", @@ -525,7 +810,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP server-side middleware", @@ -541,36 +826,36 @@ ], "support": { "issues": "https://github.com/php-fig/http-server-middleware/issues", - "source": "https://github.com/php-fig/http-server-middleware/tree/master" + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" }, - "time": "2018-10-30T17:12:04+00:00" + "time": "2023-04-11T06:14:47+00:00" }, { "name": "psr/log", - "version": "1.1.3", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -580,7 +865,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", @@ -591,9 +876,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.3" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2020-03-23T09:12:05+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "ralouphie/getallheaders", @@ -639,118 +924,39 @@ }, "time": "2019-03-08T08:55:37+00:00" }, - { - "name": "slim/http", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/slimphp/Slim-Http.git", - "reference": "3bc9d61b5243cab0d75c89d778bd69464de07354" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim-Http/zipball/3bc9d61b5243cab0d75c89d778bd69464de07354", - "reference": "3bc9d61b5243cab0d75c89d778bd69464de07354", - "shasum": "" - }, - "require": { - "ext-fileinfo": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-simplexml": "*", - "php": "^7.2 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0" - }, - "require-dev": { - "adriansuter/php-autoload-override": "^1.2", - "laminas/laminas-diactoros": "^2.4", - "nyholm/psr7": "^1.3", - "php-http/psr7-integration-tests": "dev-master", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8.5 || ^9.3", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Slim\\Http\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Josh Lockhart", - "email": "hello@joshlockhart.com", - "homepage": "http://joshlockhart.com" - }, - { - "name": "Andrew Smith", - "email": "a.smith@silentworks.co.uk", - "homepage": "http://silentworks.co.uk" - }, - { - "name": "Rob Allen", - "email": "rob@akrabat.com", - "homepage": "http://akrabat.com" - }, - { - "name": "Pierre Berube", - "email": "pierre@lgse.com", - "homepage": "http://www.lgse.com" - } - ], - "description": "Slim PSR-7 Object Decorators", - "homepage": "http://slimframework.com", - "keywords": [ - "http", - "psr-7", - "psr7" - ], - "support": { - "issues": "https://github.com/slimphp/Slim-Http/issues", - "source": "https://github.com/slimphp/Slim-Http/tree/1.2.0" - }, - "time": "2020-11-20T06:43:10+00:00" - }, { "name": "slim/psr7", - "version": "1.3.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim-Psr7.git", - "reference": "235d2e5a5ee1ad4b97b96870f37f3091b22fffd7" + "reference": "76e7e3b1cdfd583e9035c4c966c08e01e45ce959" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/235d2e5a5ee1ad4b97b96870f37f3091b22fffd7", - "reference": "235d2e5a5ee1ad4b97b96870f37f3091b22fffd7", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/76e7e3b1cdfd583e9035c4c966c08e01e45ce959", + "reference": "76e7e3b1cdfd583e9035c4c966c08e01e45ce959", "shasum": "" }, "require": { - "fig/http-message-util": "^1.1.4", - "php": "^7.2 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "ralouphie/getallheaders": "^3", - "symfony/polyfill-php80": "^1.18" + "fig/http-message-util": "^1.1.5", + "php": "^8.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.0 || ^2.0", + "ralouphie/getallheaders": "^3.0" }, "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" + "psr/http-factory-implementation": "^1.0", + "psr/http-message-implementation": "^1.0 || ^2.0" }, "require-dev": { - "adriansuter/php-autoload-override": "^1.2", + "adriansuter/php-autoload-override": "^1.5|| ^2.0", "ext-json": "*", - "http-interop/http-factory-tests": "^0.7.0", - "php-http/psr7-integration-tests": "dev-master", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^8.5 || ^9.3", - "squizlabs/php_codesniffer": "^3.5", - "weirdan/prophecy-shim": "^1.0 || ^2.0.2" + "http-interop/http-factory-tests": "^1.0 || ^2.0", + "php-http/psr7-integration-tests": "^1.5", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^9.6 || ^10", + "squizlabs/php_codesniffer": "^3.13" }, "type": "library", "autoload": { @@ -766,22 +972,22 @@ { "name": "Josh Lockhart", "email": "hello@joshlockhart.com", - "homepage": "http://joshlockhart.com" + "homepage": "https://joshlockhart.com" }, { "name": "Andrew Smith", "email": "a.smith@silentworks.co.uk", - "homepage": "http://silentworks.co.uk" + "homepage": "https://silentworks.co.uk" }, { "name": "Rob Allen", "email": "rob@akrabat.com", - "homepage": "http://akrabat.com" + "homepage": "https://akrabat.com" }, { "name": "Pierre Berube", "email": "pierre@lgse.com", - "homepage": "http://www.lgse.com" + "homepage": "https://www.lgse.com" } ], "description": "Strict PSR-7 implementation", @@ -793,50 +999,52 @@ ], "support": { "issues": "https://github.com/slimphp/Slim-Psr7/issues", - "source": "https://github.com/slimphp/Slim-Psr7/tree/1.3.0" + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.8.0" }, - "time": "2020-11-28T06:28:46+00:00" + "time": "2025-11-02T17:51:19+00:00" }, { "name": "slim/slim", - "version": "4.7.1", + "version": "4.15.3", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "0905e0775f8c1cfb3bbcfabeb6588dcfd8b82d3f" + "reference": "3675bf6baac66b07032575b7bef4200b60b7974b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/0905e0775f8c1cfb3bbcfabeb6588dcfd8b82d3f", - "reference": "0905e0775f8c1cfb3bbcfabeb6588dcfd8b82d3f", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/3675bf6baac66b07032575b7bef4200b60b7974b", + "reference": "3675bf6baac66b07032575b7bef4200b60b7974b", "shasum": "" }, "require": { "ext-json": "*", "nikic/fast-route": "^1.3", - "php": "^7.2 || ^8.0", - "psr/container": "^1.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.1 || ^2.0", "psr/http-server-handler": "^1.0", "psr/http-server-middleware": "^1.0", - "psr/log": "^1.1" + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "require-dev": { - "adriansuter/php-autoload-override": "^1.2", + "adriansuter/php-autoload-override": "^1.4 || ^2", "ext-simplexml": "*", - "guzzlehttp/psr7": "^1.7", - "http-interop/http-factory-guzzle": "^1.0", - "laminas/laminas-diactoros": "^2.4", - "nyholm/psr7": "^1.3", - "nyholm/psr7-server": "^1.0.1", - "phpspec/prophecy": "^1.12", - "phpstan/phpstan": "^0.12.58", - "phpunit/phpunit": "^8.5.13", - "slim/http": "^1.2", - "slim/psr7": "^1.3", - "squizlabs/php_codesniffer": "^3.5", - "weirdan/prophecy-shim": "^1.0 || ^2.0.2" + "guzzlehttp/psr7": "^2.6", + "httpsoft/http-message": "^1.1", + "httpsoft/http-server-request": "^1.1", + "laminas/laminas-diactoros": "^2.17 || ^3", + "nyholm/psr7": "^1.8", + "nyholm/psr7-server": "^1.1", + "phpspec/prophecy": "^1.19", + "phpspec/prophecy-phpunit": "^2.1", + "phpstan/phpstan": "^1 || ^2", + "phpunit/phpunit": "^9.6 || ^10 || ^11 || ^12", + "slim/http": "^1.3", + "slim/psr7": "^1.6", + "squizlabs/php_codesniffer": "^3.10", + "vimeo/psalm": "^5 || ^6" }, "suggest": { "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", @@ -863,17 +1071,17 @@ { "name": "Andrew Smith", "email": "a.smith@silentworks.co.uk", - "homepage": "http://silentworks.co.uk" + "homepage": "https://silentworks.co.uk" }, { "name": "Rob Allen", "email": "rob@akrabat.com", - "homepage": "http://akrabat.com" + "homepage": "https://akrabat.com" }, { "name": "Pierre Berube", "email": "pierre@lgse.com", - "homepage": "http://www.lgse.com" + "homepage": "https://www.lgse.com" }, { "name": "Gabriel Manricks", @@ -909,42 +1117,110 @@ "type": "tidelift" } ], - "time": "2020-12-01T19:41:22+00:00" + "time": "2026-09-01T17:11:05+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.22.0", + "name": "symfony/deprecation-contracts", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", - "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "3.7-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-05T06:23:12+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74", + "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -976,7 +1252,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.43.0" }, "funding": [ { @@ -987,21 +1263,27 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2026-09-25T15:50:05+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.0.0" + "platform": { + "php": "^8.1" + }, + "platform-dev": {}, + "plugin-api-version": "2.9.0" } diff --git a/icon-voice.svg b/icon-voice.svg new file mode 100644 index 0000000..281f4a1 --- /dev/null +++ b/icon-voice.svg @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.php b/public/index.php index 8a76cde..b4782fa 100644 --- a/public/index.php +++ b/public/index.php @@ -1,95 +1,103 @@ $BW_USERNAME, - "voiceBasicAuthPassword" => $BW_PASSWORD - ) -); +$config = Configuration::getDefaultConfiguration() + ->setClientId($BW_CLIENT_ID) + ->setClientSecret($BW_CLIENT_SECRET); +$callsApi = new CallsApi(new Client(), $config); -// Instantiate Bandwidth Client -$client = new BandwidthLib\BandwidthClient($config); - -// Instantiate App $app = AppFactory::create(); - -// Add error middleware $app->addErrorMiddleware(true, true, true); -$voice_client = $client->getVoice()->getClient(); - -error_reporting(E_ALL ^ E_DEPRECATED); # JSON Serializer functions deprecated in PHP 8 - -$app->get('/', function(Request $request, Response $response) { - return $response->withStatus(204); -}); - -$app->post('/callbacks/inboundCall', function (Request $request, Response $response) { - global $BW_ACCOUNT_ID, $BW_VOICE_APPLICATION_ID, $BASE_CALLBACK_URL, $USER_NUMBER, $voice_client; - $data = $request->getParsedBody(); - - $body = new BandwidthLib\Voice\Models\CreateCallRequest(); - $body->from = $data['from']; - $body->to = $USER_NUMBER; - $body->answerUrl = $BASE_CALLBACK_URL . "/callbacks/outboundCall"; - $body->applicationId = $BW_VOICE_APPLICATION_ID; - $body->tag = $data['callId']; - - try { - $apiResponse = $voice_client->createCall($BW_ACCOUNT_ID, $body); - } catch (BandwidthLib\APIException $e) { - $response->getBody()->write($e); +function parseBody(Request $request): ?array +{ + $data = json_decode($request->getBody()->getContents(), true); + return is_array($data) ? $data : null; +} + +function xmlResponse(Response $response, BxmlResponse $bxml): Response +{ + $response->getBody()->write($bxml->toBxml()); + return $response->withHeader('Content-Type', 'application/xml'); +} + +$app->post('/callbacks/inbound/voice', function (Request $request, Response $response) use ($callsApi, $BW_ACCOUNT_ID, $BW_VOICE_APPLICATION_ID, $BW_NUMBER, $USER_NUMBER, $BASE_CALLBACK_URL) { + $data = parseBody($request); + if (($data['eventType'] ?? null) !== 'initiate') { return $response->withStatus(400); } - $bxmlResponse = new BandwidthLib\Voice\Bxml\Response(); - - $speakSentence = new BandwidthLib\Voice\Bxml\SpeakSentence("Hold while we connect you"); - $speakSentence->voice("kate"); - - $ring = new BandwidthLib\Voice\Bxml\Ring(); - $ring->duration(30); - $ring->answerCall(false); + $createCall = new CreateCall([ + 'application_id' => $BW_VOICE_APPLICATION_ID, + 'to' => $USER_NUMBER, + 'from' => $BW_NUMBER, + 'answer_url' => "{$BASE_CALLBACK_URL}/callbacks/outbound/voice", + 'tag' => $data['callId'] ?? null, + ]); - $bxmlResponse->addVerb($speakSentence); - $bxmlResponse->addVerb($ring); - - $response = $response->withStatus(200)->withHeader('Content-Type', 'application/xml'); - $response->getBody()->write($bxmlResponse->toBxml()); - return $response; + try { + $callsApi->createCall($BW_ACCOUNT_ID, $createCall); + $bxml = new BxmlResponse([ + new SpeakSentence('Hold while we connect you.'), + new Ring(duration: 30), + ]); + } catch (Throwable $e) { + error_log('Failed to create call: ' . $e->getMessage()); + $bxml = new BxmlResponse([ + new SpeakSentence('An error occurred. Ending call.'), + new Hangup(), + ]); + } + return xmlResponse($response, $bxml); }); -$app->post('/callbacks/outboundCall', function (Request $request, Response $response) { - global $BW_ACCOUNT_ID, $BW_VOICE_APPLICATION_ID, $BASE_CALLBACK_URL, $voice_client; - $data = $request->getParsedBody(); - - $bxmlResponse = new BandwidthLib\Voice\Bxml\Response(); - - $speakSentence = new BandwidthLib\Voice\Bxml\SpeakSentence("Hold while we connect you. We will begin the bridge now."); - $speakSentence->voice("kate"); - - $bridge = new BandwidthLib\Voice\Bxml\Bridge($data['tag']); +$app->post('/callbacks/outbound/voice', function (Request $request, Response $response) { + $data = parseBody($request); + if ($data === null) { + return $response->withStatus(400); + } - $bxmlResponse->addVerb($speakSentence); - $bxmlResponse->addVerb($bridge); + $bxml = new BxmlResponse(); + + switch ($data['eventType'] ?? null) { + case 'answer': + $bxml = new BxmlResponse([ + new SpeakSentence('Hold while we connect you. We will begin to bridge you now.'), + new Bridge($data['tag'] ?? ''), + ]); + break; + case 'disconnect': + error_log("Call {$data['callId']} has disconnected"); + break; + default: + error_log('Unexpected event type ' . ($data['eventType'] ?? 'null') . ' received'); + break; + } - $response = $response->withStatus(200)->withHeader('Content-Type', 'application/xml'); - $response->getBody()->write($bxmlResponse->toBxml()); - return $response; + return xmlResponse($response, $bxml); }); $app->run(); diff --git a/start.php b/start.php new file mode 100644 index 0000000..0431216 --- /dev/null +++ b/start.php @@ -0,0 +1,19 @@ + getenv($name) === false +); + +if ($missing || !ctype_digit($port)) { + fwrite(STDERR, "Please set the environmental variables defined in the README\n"); + exit(1); +} + +$server = proc_open( + [PHP_BINARY, '-S', "127.0.0.1:{$port}", '-t', __DIR__ . '/public', __DIR__ . '/public/index.php'], + [STDIN, STDOUT, STDERR], + $pipes +); +exit(proc_close($server));