';
32 |
--------------------------------------------------------------------------------
/lib/transloadit/CurlResponse.php:
--------------------------------------------------------------------------------
1 | $val) {
15 | $this->{$key} = $val;
16 | }
17 | }
18 |
19 | public function parseJson() {
20 | $decoded = json_decode($this->data, true);
21 | if (!is_array($decoded)) {
22 | return false;
23 | }
24 |
25 | $this->data = $decoded;
26 | return true;
27 | }
28 |
29 | public function error() {
30 | if (!$this->curlErrorNumber) {
31 | return false;
32 | }
33 |
34 | return sprintf(
35 | 'curl: %d: %s',
36 | $this->curlErrorNumber,
37 | $this->curlErrorMessage
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/examples/7-disable-ssl-verification.php:
--------------------------------------------------------------------------------
1 | getenv('MY_TRANSLOADIT_KEY'),
13 | 'secret' => getenv('MY_TRANSLOADIT_SECRET'),
14 | ]);
15 |
16 | $response = $transloadit->createAssembly([
17 | 'files' => [dirname(__FILE__) . '/fixture/straw-apple.jpg'],
18 | 'curlOptions' => [
19 | CURLOPT_SSL_VERIFYHOST => 0,
20 | CURLOPT_SSL_VERIFYPEER => 0,
21 | ],
22 | 'waitForCompletion' => true,
23 | 'params' => [
24 | 'steps' => [
25 | 'resize' => [
26 | 'robot' => '/image/resize',
27 | 'width' => 200,
28 | 'height' => 100,
29 | ],
30 | ],
31 | ],
32 | ]);
33 |
34 | // Show the results of the assembly we spawned
35 | echo '';
36 | print_r($response);
37 | echo '';
38 |
--------------------------------------------------------------------------------
/lib/transloadit/TransloaditResponse.php:
--------------------------------------------------------------------------------
1 | data)) {
17 | return sprintf('transloadit: bad response, no json: %s', $this->data);
18 | }
19 |
20 | if (array_key_exists('error', $this->data)) {
21 | $error = sprintf('transloadit: %s', $this->data['error']);
22 |
23 | if (array_key_exists('message', $this->data)) {
24 | $error .= sprintf(': %s', $this->data['message']);
25 | }
26 |
27 | if (array_key_exists('reason', $this->data)) {
28 | $error .= sprintf(': %s', $this->data['reason']);
29 | }
30 |
31 | return $error;
32 | }
33 |
34 | if (!array_key_exists('ok', $this->data)) {
35 | return 'transloadit: bad response data, no ok / error key included.';
36 | }
37 |
38 | return false;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | test
20 |
21 |
22 |
23 |
25 |
26 | lib
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2010 Transloadit
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/tool/generate-example-docs.php:
--------------------------------------------------------------------------------
1 | Readme.html
45 |
--------------------------------------------------------------------------------
/examples/1-assembly-request.php:
--------------------------------------------------------------------------------
1 | Assembly
8 | on your server.
9 |
10 | It takes a sample image file, uploads it to Transloadit, and starts a
11 | resizing job on it.
12 | */
13 |
14 | use transloadit\Transloadit;
15 |
16 | $transloadit = new Transloadit([
17 | 'key' => 'MY_TRANSLOADIT_KEY',
18 | 'secret' => 'MY_TRANSLOADIT_SECRET',
19 | ]);
20 |
21 | $response = $transloadit->createAssembly([
22 | // Use dirname(__FILE__) to get the current directory, then append the relative path to the image
23 | // You can replace this with an absolute path to any file on your server that PHP can access
24 | 'files' => [dirname(__FILE__) . '/fixture/straw-apple.jpg'],
25 | 'params' => [
26 | 'steps' => [
27 | 'resize' => [
28 | 'robot' => '/image/resize',
29 | 'width' => 200,
30 | 'height' => 100,
31 | ],
32 | ],
33 | ],
34 | ]);
35 |
36 | // Show the results of the assembly we spawned
37 | echo '
';
38 | print_r($response);
39 | echo '
';
40 |
--------------------------------------------------------------------------------
/test/system/Transloadit/TransloaditAssemblyCreateTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped(
13 | 'TRANSLOADIT_KEY and TRANSLOADIT_SECRET environment variables are required.'
14 | );
15 | return;
16 | }
17 |
18 | $this->transloadit = new Transloadit([
19 | 'key' => getenv('TRANSLOADIT_KEY'),
20 | 'secret' => getenv('TRANSLOADIT_SECRET'),
21 | ]);
22 | }
23 |
24 | public function testRoot() {
25 | $response = $this->transloadit->createAssembly([
26 | 'files' => [TEST_FIXTURE_DIR . '/image-resize-robot.jpg'],
27 | 'params' => [
28 | 'steps' => [
29 | 'resize' => [
30 | 'robot' => '/image/resize',
31 | 'width' => 100,
32 | 'height' => 100,
33 | 'result' => true,
34 | ],
35 | ],
36 | ]
37 | ]);
38 | $this->assertEquals('ASSEMBLY_EXECUTING', $response->data['ok']);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/examples/6-assembly-with-timeout.php:
--------------------------------------------------------------------------------
1 | getenv('MY_TRANSLOADIT_KEY'),
9 | 'secret' => getenv('MY_TRANSLOADIT_SECRET'),
10 | ]);
11 |
12 | $response = $transloadit->createAssembly([
13 | 'files' => [dirname(__FILE__) . '/fixture/straw-apple.jpg'],
14 | 'curlOptions' => [
15 | CURLOPT_TIMEOUT_MS => 1,
16 | // We can't finish in the specified: '1ms' so we expect this example
17 | // to fail with: $response->curlErrorNumber === 28
18 | //
19 | // You can pass any curl option here that your PHP/curl version supports:
20 | // https://www.php.net/manual/en/function.curl-setopt.php
21 | // Note that if you are interested in timeouts, perhaps also consider
22 | // that you can set waitForCompletion to false and use the
23 | // notify_url feature to get a webhook pingback when the Assembly is done.
24 | ],
25 | 'params' => [
26 | 'steps' => [
27 | 'resize' => [
28 | 'robot' => '/image/resize',
29 | 'width' => 200,
30 | 'height' => 100,
31 | ],
32 | ],
33 | ],
34 | ]);
35 |
36 | // Show the results of the assembly we spawned
37 | echo '';
38 | print_r([
39 | 'errcode' => $response->curlErrorNumber,
40 | 'errmsg' => $response->curlErrorMessage,
41 | ]);
42 | echo '';
43 |
--------------------------------------------------------------------------------
/test/system/Transloadit/TransloaditCreateAssemblyWaitForCompletionTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped(
13 | 'Have a look at test/config.php to get this test to run.'
14 | );
15 | return;
16 | }
17 |
18 | // @todo Load config from git excluded config file
19 | $this->transloadit = new Transloadit([
20 | 'key' => TRANSLOADIT_KEY,
21 | 'secret' => TRANSLOADIT_SECRET,
22 | ]);
23 | }
24 | public function testRoot() {
25 | $response = $this->transloadit->createAssembly([
26 | 'files' => [TEST_FIXTURE_DIR . '/image-resize-robot.jpg'],
27 | 'params' => [
28 | 'steps' => [
29 | 'resize' => [
30 | 'robot' => '/image/resize',
31 | 'width' => 100,
32 | 'height' => 100,
33 | 'result' => true,
34 | ],
35 | ],
36 | ],
37 | 'waitForCompletion' => true
38 | ]);
39 | $this->assertEquals('ASSEMBLY_COMPLETED', $response->data['ok']);
40 |
41 | $getResp = $this->transloadit->getAssembly($response->data['assembly_id']);
42 | $this->assertEquals('ASSEMBLY_COMPLETED', $getResp->data['ok']);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/test/bootstrap.php:
--------------------------------------------------------------------------------
1 | markTestSkipped(
19 | 'Have a look at test/config.php to get this test to run.'
20 | );
21 | return;
22 | }
23 |
24 | $this->request = new \transloadit\TransloaditRequest([
25 | 'key' => TRANSLOADIT_KEY,
26 | 'secret' => TRANSLOADIT_SECRET,
27 | ]);
28 |
29 | try {
30 | $nonce = bin2hex(random_bytes(16));
31 | } catch (\Exception $e) {
32 | $nonce = uniqid('php-sdk-', true);
33 | }
34 | $this->request->params['auth']['nonce'] = $nonce;
35 | }
36 | }
37 |
38 | class TransloaditRequestTestCase extends \PHPUnit\Framework\TestCase {
39 | protected \transloadit\Transloadit $transloadit;
40 |
41 | public function setUp(): void {
42 | if (!defined('TRANSLOADIT_KEY') || !defined('TRANSLOADIT_SECRET')) {
43 | $this->markTestSkipped(
44 | 'Have a look at test/config.php to get this test to run.'
45 | );
46 | return;
47 | }
48 |
49 | $this->transloadit = new \transloadit\Transloadit([
50 | 'key' => TRANSLOADIT_KEY,
51 | 'secret' => TRANSLOADIT_SECRET,
52 | ]);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/test/simple/CurlResponseTest.php:
--------------------------------------------------------------------------------
1 | response = new CurlResponse();
12 | }
13 |
14 | public function testAttributes() {
15 | $this->assertEquals(null, $this->response->data);
16 | $this->assertEquals(null, $this->response->curlErrorNumber);
17 | $this->assertEquals(null, $this->response->curlErrorMessage);
18 | $this->assertEquals(null, $this->response->curlInfo);
19 | }
20 |
21 | public function testConstructor() {
22 | $transloadit = new CurlResponse(['data' => 'foobar']);
23 | $this->assertEquals('foobar', $transloadit->data);
24 | }
25 |
26 | public function testParseJson() {
27 | $data = ['foo' => 'bar'];
28 |
29 | $this->response->data = json_encode($data);
30 | $r = $this->response->parseJson();
31 |
32 | $this->assertEquals(true, $r);
33 | $this->assertEquals($data, $this->response->data);
34 |
35 | $data = $this->response->data = 'no json';
36 | $r = $this->response->parseJson();
37 |
38 | $this->assertEquals(false, $r);
39 | $this->assertEquals($data, $this->response->data);
40 | }
41 |
42 | public function testError() {
43 | $error = $this->response->error();
44 | $this->assertEquals(false, $error);
45 |
46 | $number = $this->response->curlErrorNumber = 27;
47 | $message = $this->response->curlErrorMessage = 'Something went wrong';
48 | $error = $this->response->error();
49 | $this->assertEquals(sprintf('curl: %d: %s', $number, $message), $error);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/examples/2-assembly-form.php:
--------------------------------------------------------------------------------
1 | Assembly is shown using `Transloadit::response()`.
12 |
13 |
14 | There is no guarantee that the Assembly has already finished
15 | executing by the time the `$response` is fetched. You should use
16 | the `notify_url` parameter for this.
17 |
18 | */
19 |
20 | use transloadit\Transloadit;
21 |
22 | $transloadit = new Transloadit([
23 | 'key' => 'MY_TRANSLOADIT_KEY',
24 | 'secret' => 'MY_TRANSLOADIT_SECRET',
25 | ]);
26 |
27 | // Check if this request is a Transloadit redirect_url notification.
28 | // If so fetch the response and output the current assembly status:
29 | $response = Transloadit::response();
30 | if ($response) {
31 | echo '
Assembly Status:
';
32 | echo '
';
33 | print_r($response);
34 | echo '
';
35 | exit;
36 | }
37 |
38 | // This should work on most environments, but you might have to modify
39 | // this for your particular setup.
40 | $redirectUrl = sprintf('http://%s%s', $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI']);
41 |
42 | // Setup a simple file upload form that resizes an image to 200x100px
43 | echo $transloadit->createAssemblyForm([
44 | 'params' => [
45 | 'steps' => [
46 | 'resize' => [
47 | 'robot' => '/image/resize',
48 | 'width' => 200,
49 | 'height' => 100,
50 | ],
51 | ],
52 | 'redirect_url' => $redirectUrl,
53 | ],
54 | ]);
55 | ?>
56 |
61 |
65 |
--------------------------------------------------------------------------------
/scripts/test-in-docker.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | IMAGE_NAME=${IMAGE_NAME:-transloadit-php-sdk-dev}
5 | CACHE_DIR=.docker-cache
6 |
7 | ensure_docker() {
8 | if ! command -v docker >/dev/null 2>&1; then
9 | echo "Docker is required to run this script." >&2
10 | exit 1
11 | fi
12 |
13 | if ! docker info >/dev/null 2>&1; then
14 | if [[ -z "${DOCKER_HOST:-}" && -S "$HOME/.colima/default/docker.sock" ]]; then
15 | export DOCKER_HOST="unix://$HOME/.colima/default/docker.sock"
16 | fi
17 | fi
18 |
19 | if ! docker info >/dev/null 2>&1; then
20 | echo "Docker daemon is not reachable. Start Docker (or Colima) and retry." >&2
21 | exit 1
22 | fi
23 | }
24 |
25 | configure_platform() {
26 | if [[ -z "${DOCKER_PLATFORM:-}" ]]; then
27 | local arch
28 | arch=$(uname -m)
29 | if [[ "$arch" == "arm64" || "$arch" == "aarch64" ]]; then
30 | DOCKER_PLATFORM=linux/amd64
31 | fi
32 | fi
33 | }
34 |
35 | ensure_docker
36 | configure_platform
37 |
38 | if [[ $# -eq 0 ]]; then
39 | RUN_CMD='set -e; composer install --no-interaction --prefer-dist; make test-all'
40 | else
41 | printf -v USER_CMD '%q ' "$@"
42 | RUN_CMD="set -e; composer install --no-interaction --prefer-dist; ${USER_CMD}"
43 | fi
44 |
45 | mkdir -p "$CACHE_DIR/composer-cache" "$CACHE_DIR/npm-cache" "$CACHE_DIR/composer-home"
46 |
47 | BUILD_ARGS=()
48 | if [[ -n "${DOCKER_PLATFORM:-}" ]]; then
49 | BUILD_ARGS+=(--platform "$DOCKER_PLATFORM")
50 | fi
51 | BUILD_ARGS+=(-t "$IMAGE_NAME" -f Dockerfile .)
52 |
53 | docker build "${BUILD_ARGS[@]}"
54 |
55 | DOCKER_ARGS=(
56 | --rm
57 | --user "$(id -u):$(id -g)"
58 | -e HOME=/workspace
59 | -e COMPOSER_HOME=/workspace/$CACHE_DIR/composer-home
60 | -e COMPOSER_CACHE_DIR=/workspace/$CACHE_DIR/composer-cache
61 | -e npm_config_cache=/workspace/$CACHE_DIR/npm-cache
62 | -e TEST_NODE_PARITY="${TEST_NODE_PARITY:-0}"
63 | -v "$PWD":/workspace
64 | -w /workspace
65 | )
66 |
67 | if [[ -n "${DOCKER_PLATFORM:-}" ]]; then
68 | DOCKER_ARGS+=(--platform "$DOCKER_PLATFORM")
69 | fi
70 |
71 | if [[ -f .env ]]; then
72 | DOCKER_ARGS+=(--env-file "$PWD/.env")
73 | fi
74 |
75 | exec docker run "${DOCKER_ARGS[@]}" "$IMAGE_NAME" bash -lc "$RUN_CMD"
76 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | types:
8 | - opened
9 | - synchronize
10 | jobs:
11 | ci:
12 | if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
13 | runs-on: ubuntu-latest
14 | strategy:
15 | fail-fast: true
16 | max-parallel: 1
17 | matrix:
18 | php:
19 | - 8.1
20 | - 8.2
21 | dependencies:
22 | - locked
23 | - lowest
24 | - highest
25 | name: PHP ${{ matrix.php }} - ${{ matrix.dependencies }}
26 | steps:
27 | - uses: actions/checkout@v4
28 | with:
29 | fetch-depth: 1
30 | - uses: actions/setup-node@v4
31 | with:
32 | node-version: '20'
33 | - name: Install tsx
34 | run: npm install -g tsx
35 | - uses: shivammathur/setup-php@v2
36 | with:
37 | php-version: ${{ matrix.php }}
38 | tools: php-cs-fixer, phpunit
39 | coverage: ${{ matrix.php == '8.1' && matrix.dependencies == 'locked' && 'xdebug' || 'none' }}
40 | - uses: ramsey/composer-install@v3
41 | with:
42 | dependency-versions: ${{ matrix.dependencies }}
43 | composer-options: '--ignore-platform-reqs'
44 | - name: Test with Coverage
45 | if: matrix.php == '8.1' && matrix.dependencies == 'locked'
46 | run: |
47 | make test-all-coverage
48 | env:
49 | TRANSLOADIT_KEY: ${{secrets.TEST_ACCOUNT_KEY}}
50 | TRANSLOADIT_SECRET: ${{secrets.TEST_ACCOUNT_SECRET}}
51 | TEST_NODE_PARITY: 1
52 | - name: Test without Coverage
53 | if: matrix.php != '8.1' || matrix.dependencies != 'locked'
54 | run: |
55 | make test-all
56 | env:
57 | TRANSLOADIT_KEY: ${{secrets.TEST_ACCOUNT_KEY}}
58 | TRANSLOADIT_SECRET: ${{secrets.TEST_ACCOUNT_SECRET}}
59 | TEST_NODE_PARITY: 1
60 | - name: Publish Coverage Report
61 | if: github.event_name == 'pull_request' && matrix.php == '8.1' && matrix.dependencies == 'locked'
62 | uses: lucassabreu/comment-coverage-clover@v0.13.0
63 | with:
64 | file: ./build/logs/clover.xml
65 | with-table: true
66 | with-chart: false
67 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Feel free to fork this project. We will happily merge bug fixes or other small
4 | improvements. For bigger changes you should probably get in touch with us
5 | before you start to avoid not seeing them merged.
6 |
7 | ## Testing
8 |
9 | ### Basic Tests
10 |
11 | ```bash
12 | make test
13 | ```
14 |
15 | ### System Tests
16 |
17 | System tests require:
18 |
19 | 1. Valid Transloadit credentials in environment:
20 |
21 | ```bash
22 | export TRANSLOADIT_KEY='your-auth-key'
23 | export TRANSLOADIT_SECRET='your-auth-secret'
24 | ```
25 |
26 | Then run:
27 |
28 | ```bash
29 | make test-all
30 | ```
31 |
32 | ### Node.js Reference Implementation Parity Assertions
33 |
34 | The SDK includes assertions that compare Smart CDN URL signatures and regular request signatures with our reference Node.js implementation. To run these tests:
35 |
36 | 1. Requirements:
37 |
38 | - Node.js 20+ with npm
39 | - Ability to execute `npx transloadit smart_sig` (the CLI is downloaded on demand)
40 | - Ability to execute `npx transloadit sig` (the CLI is downloaded on demand)
41 |
42 | 2. Run the tests:
43 |
44 | ```bash
45 | export TRANSLOADIT_KEY='your-auth-key'
46 | export TRANSLOADIT_SECRET='your-auth-secret'
47 | TEST_NODE_PARITY=1 make test-all
48 | ```
49 |
50 | If you want to warm the CLI cache ahead of time you can run:
51 |
52 | ```bash
53 | npx --yes transloadit smart_sig --help
54 | ```
55 |
56 | For regular request signatures, you can also prime the CLI by running:
57 |
58 | ```bash
59 | TRANSLOADIT_KEY=... TRANSLOADIT_SECRET=... \
60 | npx --yes transloadit sig --algorithm sha1 --help
61 | ```
62 |
63 | CI opts into `TEST_NODE_PARITY=1`, and you can optionally do this locally as well.
64 |
65 | ### Run Tests in Docker
66 |
67 | Use `scripts/test-in-docker.sh` for a reproducible environment:
68 |
69 | ```bash
70 | ./scripts/test-in-docker.sh
71 | ```
72 |
73 | This builds the local image, runs `composer install`, and executes `make test-all` (unit + integration tests). Pass a custom command to run something else (composer install still runs first):
74 |
75 | ```bash
76 | ./scripts/test-in-docker.sh vendor/bin/phpunit --filter signedSmartCDNUrl
77 | ```
78 |
79 | Environment variables such as `TEST_NODE_PARITY` or the credentials in `.env` are forwarded, so you can combine parity checks and integration tests with Docker:
80 |
81 | ```bash
82 | TEST_NODE_PARITY=1 ./scripts/test-in-docker.sh
83 | ```
84 |
85 | ## Releasing a new version
86 |
87 | To release, say `3.3.0` [Packagist](https://packagist.org/packages/transloadit/php-sdk), follow these steps:
88 |
89 | 1. Make sure `PACKAGIST_TOKEN` is set in your `.env` file
90 | 1. Make sure you are in main: `git checkout main`
91 | 1. Make sure `CHANGELOG.md` and `composer.json` have been updated
92 | 1. Commit: `git add CHANGELOG.md composer.json && git commit -m "Release v3.3.0"`
93 | 1. Tag: `git tag v3.3.0`
94 | 1. Push: `git push --tags`
95 | 1. Notify Packagist (runs via Docker): `VERSION=3.3.0 ./scripts/notify-registry.sh`
96 | 1. Publish a GitHub release (include the changelog). This triggers the release workflow. (via the GitHub UI, `gh release creates v3.3.0 --title "v3.3.0" --notes-file <(cat CHANGELOG.md section)`)
97 |
98 | The notify script reuses the same Docker image as `./scripts/test-in-docker.sh`, so Docker is the only requirement on your workstation.
99 |
100 | This project implements the [Semantic Versioning](http://semver.org/) guidelines.
101 |
--------------------------------------------------------------------------------
/lib/transloadit/TransloaditRequest.php:
--------------------------------------------------------------------------------
1 | method = $method;
25 | $this->path = $path;
26 | }
27 |
28 | public function getParamsString() {
29 | $params = $this->params;
30 | if (!isset($params['auth'])) {
31 | $params['auth'] = [];
32 | }
33 |
34 | if (!ini_get('date.timezone')) {
35 | date_default_timezone_set('Etc/UTC');
36 | }
37 |
38 | $params['auth'] = $params['auth'] + [
39 | 'key' => $this->key,
40 | 'expires' => gmdate('Y/m/d H:i:s+00:00', strtotime($this->expires)),
41 | ];
42 | return json_encode($params);
43 | }
44 |
45 | public function signString($string) {
46 | if (empty($this->secret)) {
47 | return null;
48 | }
49 |
50 | return hash_hmac('sha1', $string, $this->secret);
51 | }
52 |
53 | public function prepare() {
54 | $params = $this->getParamsString();
55 | $this->fields['params'] = $params;
56 |
57 | $signature = $this->signString($params);
58 | if ($signature) {
59 | $this->fields['signature'] = $signature;
60 | }
61 |
62 | $this->configureUrl();
63 | }
64 |
65 | public function configureUrl() {
66 | if (!empty($this->url)) {
67 | return;
68 | }
69 |
70 | $this->url = sprintf(
71 | '%s%s',
72 | $this->endpoint,
73 | $this->path
74 | );
75 | }
76 |
77 | public function execute($response = null) {
78 | // note: $response is not used here, only needed to keep PHP strict mode
79 | // happy.
80 |
81 | $this->prepare();
82 | $response = parent::execute(new TransloaditResponse());
83 | $response->parseJson();
84 |
85 | if ($this->path === '/assemblies' && $this->waitForCompletion) {
86 | return $this->_waitForCompletion($response);
87 | }
88 | return $response;
89 | }
90 |
91 | private function _waitForCompletion($response) {
92 | // Try assembly_ssl_url first, fall back to assembly_url
93 | $assemblyUrl = $response->data['assembly_ssl_url'] ?? $response->data['assembly_url'] ?? null;
94 | if (!$assemblyUrl) {
95 | throw new \RuntimeException('No assembly URL found in response. Response data: ' . json_encode($response->data));
96 | }
97 |
98 | $parts = parse_url($assemblyUrl);
99 |
100 | while (true) {
101 | $req = new TransloaditRequest();
102 | $req->endpoint = 'https://' . $parts['host'];
103 | $req->path = $parts['path'];
104 | $req->curlOptions = $this->curlOptions;
105 | $response = $req->execute();
106 |
107 | if (isset($response->data['ok'])) {
108 | if ($response->data['ok'] === 'ASSEMBLY_UPLOADING' || $response->data['ok'] === 'ASSEMBLY_EXECUTING') {
109 | sleep(1);
110 | continue;
111 | }
112 | }
113 |
114 | // If this is an unknown, erroneous or completed Assembly completion state, return right away.
115 | return $response;
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/scripts/notify-registry.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | set -o errtrace
4 | # set -o xtrace
5 |
6 | IMAGE_NAME=${IMAGE_NAME:-transloadit-php-sdk-dev}
7 | CACHE_DIR=.docker-cache
8 |
9 | ensure_docker() {
10 | if ! command -v docker >/dev/null 2>&1; then
11 | echo "Docker is required to run this script." >&2
12 | exit 1
13 | fi
14 |
15 | if ! docker info >/dev/null 2>&1; then
16 | if [[ -z "${DOCKER_HOST:-}" && -S "$HOME/.colima/default/docker.sock" ]]; then
17 | export DOCKER_HOST="unix://$HOME/.colima/default/docker.sock"
18 | fi
19 | fi
20 |
21 | if ! docker info >/dev/null 2>&1; then
22 | echo "Docker daemon is not reachable. Start Docker (or Colima) and retry." >&2
23 | exit 1
24 | fi
25 | }
26 |
27 | configure_platform() {
28 | if [[ -z "${DOCKER_PLATFORM:-}" ]]; then
29 | local arch
30 | arch=$(uname -m)
31 | if [[ "$arch" == "arm64" || "$arch" == "aarch64" ]]; then
32 | DOCKER_PLATFORM=linux/amd64
33 | fi
34 | fi
35 | }
36 |
37 | if [[ "${1:-}" != "--inside-container" ]]; then
38 | ensure_docker
39 | configure_platform
40 |
41 | mkdir -p "$CACHE_DIR/composer-cache" "$CACHE_DIR/npm-cache" "$CACHE_DIR/composer-home"
42 |
43 | BUILD_ARGS=()
44 | if [[ -n "${DOCKER_PLATFORM:-}" ]]; then
45 | BUILD_ARGS+=(--platform "$DOCKER_PLATFORM")
46 | fi
47 | BUILD_ARGS+=(-t "$IMAGE_NAME" -f Dockerfile .)
48 |
49 | docker build "${BUILD_ARGS[@]}"
50 |
51 | DOCKER_ARGS=(
52 | --rm
53 | --user "$(id -u):$(id -g)"
54 | -e HOME=/workspace
55 | -e COMPOSER_HOME=/workspace/$CACHE_DIR/composer-home
56 | -e COMPOSER_CACHE_DIR=/workspace/$CACHE_DIR/composer-cache
57 | -e npm_config_cache=/workspace/$CACHE_DIR/npm-cache
58 | -v "$PWD":/workspace
59 | -w /workspace
60 | )
61 |
62 | if [[ -n "${DOCKER_PLATFORM:-}" ]]; then
63 | DOCKER_ARGS+=(--platform "$DOCKER_PLATFORM")
64 | fi
65 |
66 | if [[ -f .env ]]; then
67 | DOCKER_ARGS+=(--env-file "$PWD/.env")
68 | fi
69 |
70 | if [[ -n "${PACKAGIST_TOKEN:-}" ]]; then
71 | DOCKER_ARGS+=(-e "PACKAGIST_TOKEN=${PACKAGIST_TOKEN}")
72 | fi
73 |
74 | if [[ -n "${VERSION:-}" ]]; then
75 | DOCKER_ARGS+=(-e "VERSION=${VERSION}")
76 | fi
77 |
78 | exec docker run "${DOCKER_ARGS[@]}" "$IMAGE_NAME" bash -lc "./scripts/notify-registry.sh --inside-container"
79 | fi
80 |
81 | shift
82 |
83 | if [[ -z "${PACKAGIST_TOKEN:-}" ]]; then
84 | if [[ -f .env ]]; then
85 | # shellcheck disable=SC1091
86 | source .env || {
87 | echo "Failed to source .env"
88 | exit 1
89 | }
90 | fi
91 | if [[ -z "${PACKAGIST_TOKEN:-}" ]]; then
92 | echo "PACKAGIST_TOKEN is not set"
93 | exit 1
94 | fi
95 | fi
96 |
97 | if [[ -z "${VERSION:-}" ]]; then
98 | echo "VERSION is not set"
99 | exit 1
100 | fi
101 |
102 | if ! grep "${VERSION}" composer.json > /dev/null 2>&1; then
103 | echo "First add '${VERSION}' to composer.json please"
104 | exit 1
105 | fi
106 | if ! grep "${VERSION}" CHANGELOG.md > /dev/null 2>&1; then
107 | echo "First add '${VERSION}' to CHANGELOG.md please"
108 | exit 1
109 | fi
110 | if [[ -n "$(git status --porcelain)" ]]; then
111 | echo "Git working tree not clean. First commit all your work please."
112 | exit 1
113 | fi
114 |
115 | curl \
116 | -X POST \
117 | -H 'Content-Type: application/json' \
118 | -d '{"repository":{"url":"https://github.com/transloadit/php-sdk"}}' \
119 | "https://packagist.org/api/update-package?username=kvz&apiToken=${PACKAGIST_TOKEN}"
120 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | plugins/
5 | vendor/
6 | vendors/
7 | webroot/
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
19 | 0
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | */config/*
79 | */tests/*
80 | */src/Command/*
81 |
82 |
83 |
84 | 0
85 |
86 |
87 |
--------------------------------------------------------------------------------
/lib/transloadit/CurlRequest.php:
--------------------------------------------------------------------------------
1 | $val) {
17 | $this->{$key} = $val;
18 | }
19 | }
20 |
21 | public function getCurlOptions() {
22 | $url = $this->url;
23 |
24 | $hasBody = ($this->method === 'PUT' || $this->method === 'POST');
25 | if (!$hasBody) {
26 | $url .= '?' . http_build_query($this->fields);
27 | }
28 |
29 | if (!is_array($this->curlOptions)) {
30 | $this->curlOptions = [$this->curlOptions];
31 | }
32 |
33 | // Obtain SDK version
34 | if (empty($this->version)) {
35 | $pathToComposerJson = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..';
36 | $pathToComposerJson .= DIRECTORY_SEPARATOR . 'composer.json';
37 | $composerData = json_decode(file_get_contents($pathToComposerJson));
38 | $this->version = $composerData->version;
39 | }
40 |
41 | if (!empty($this->headers)) {
42 | foreach ($this->headers as $key => $value) {
43 | if (strpos($value, 'Transloadit-Client') === 0) {
44 | $this->headers[$key] = sprintf($value, $this->version);
45 | }
46 | }
47 | }
48 |
49 | $options = $this->curlOptions + [
50 | CURLOPT_RETURNTRANSFER => true,
51 | CURLOPT_CUSTOMREQUEST => $this->method,
52 | CURLOPT_URL => $url,
53 | CURLOPT_HTTPHEADER => $this->headers,
54 | ];
55 |
56 | if ($hasBody) {
57 | $fields = $this->fields;
58 | foreach ($this->files as $field => $file) {
59 | if (!file_exists($file)) {
60 | trigger_error('File ' . $file . ' does not exist', E_USER_ERROR);
61 | return false;
62 | }
63 | if (is_int($field)) {
64 | $field = 'file_' . ($field + 1);
65 | }
66 |
67 | // -- Start edit --
68 | // Edit by Aart Berkhout involving issue #8: CURL depricated functions (PHP 5.5)
69 | // https://github.com/transloadit/php-sdk/issues/8
70 | if (function_exists('curl_file_create')) {
71 | // For >= PHP 5.5 use curl_file_create
72 | $fields[$field] = curl_file_create($file);
73 | } else {
74 | // For < PHP 5.5 use @filename API
75 | $fields[$field] = '@' . $file;
76 | }
77 | // -- End edit --
78 | }
79 | $options[CURLOPT_POSTFIELDS] = $fields;
80 | }
81 |
82 | return $options;
83 | }
84 |
85 | public function execute($response = null) {
86 | $curl = curl_init();
87 |
88 | // -- Start edit --
89 | // For PHP 5.6 Safe Upload is required to upload files using curl in PHP 5.5, add the CURLOPT_SAFE_UPLOAD = true option
90 | if (defined('CURLOPT_SAFE_UPLOAD')) {
91 | curl_setopt($curl, CURLOPT_SAFE_UPLOAD, function_exists('curl_file_create') ? true : false);
92 | }
93 | // -- End edit --
94 |
95 | curl_setopt_array($curl, $this->getCurlOptions());
96 |
97 | if (!$response) {
98 | $response = new CurlResponse();
99 | }
100 | $response->data = curl_exec($curl);
101 | $response->curlInfo = curl_getinfo($curl);
102 | $response->curlErrorNumber = curl_errno($curl);
103 | $response->curlErrorMessage = curl_error($curl);
104 |
105 | curl_close($curl);
106 |
107 | return $response;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/test/simple/CurlRequestTest.php:
--------------------------------------------------------------------------------
1 | request = new CurlRequest();
12 | }
13 |
14 | public function testAttributes() {
15 | $this->assertEquals('GET', $this->request->method);
16 | $this->assertEquals(null, $this->request->url);
17 | $this->assertEquals([], $this->request->headers);
18 | $this->assertEquals([], $this->request->fields);
19 | $this->assertEquals([], $this->request->files);
20 | }
21 |
22 | public function testConstructor() {
23 | $request = new CurlRequest(['url' => 'foobar']);
24 | $this->assertEquals('foobar', $request->url);
25 | }
26 |
27 | public function testGetCurlOptions() {
28 | // test return transfer
29 | $options = $this->request->getCurlOptions();
30 | $this->assertEquals(true, $options[CURLOPT_RETURNTRANSFER]);
31 |
32 | // test method
33 | $this->request->method = 'PUT';
34 | $options = $this->request->getCurlOptions();
35 | $this->assertEquals($this->request->method, $options[CURLOPT_CUSTOMREQUEST]);
36 |
37 | // test url
38 | $this->request->url = 'http://foo.com/bar';
39 | $options = $this->request->getCurlOptions();
40 | $this->assertEquals($this->request->url, $options[CURLOPT_URL]);
41 |
42 | // test headers
43 | $this->request->headers = ['Foo: bar'];
44 | $options = $this->request->getCurlOptions();
45 | $this->assertEquals($this->request->headers, $options[CURLOPT_HTTPHEADER]);
46 |
47 | // test put fields
48 | $this->request->fields = ['hello' => 'world'];
49 | $options = $this->request->getCurlOptions();
50 | $this->assertEquals($this->request->fields, $options[CURLOPT_POSTFIELDS]);
51 |
52 | // test post fields
53 | $this->request->method = 'POST';
54 | $options = $this->request->getCurlOptions();
55 | $this->assertEquals($this->request->fields, $options[CURLOPT_POSTFIELDS]);
56 | $this->assertEquals($this->request->url, $options[CURLOPT_URL]);
57 |
58 | // test get query
59 | $this->request->method = 'GET';
60 | $options = $this->request->getCurlOptions();
61 | $this->assertEquals(
62 | $this->request->url . '?' . http_build_query($this->request->fields),
63 | $options[CURLOPT_URL]
64 | );
65 | $this->assertArrayNotHasKey(CURLOPT_POSTFIELDS, $options);
66 |
67 | $fixture = dirname(dirname(__FILE__)) . '/fixture/image-resize-robot.jpg';
68 |
69 | // test post files
70 | $this->request->method = 'POST';
71 | $this->request->fields = ['super' => 'cool'];
72 | $this->request->files = ['foo' => $fixture];
73 | $options = $this->request->getCurlOptions();
74 |
75 | // -- Start edit --
76 | // Edit by Aart Berkhout involving issue #8: CURL depricated functions (PHP 5.5)
77 | // https://github.com/transloadit/php-sdk/issues/8
78 | $filesOptions = function_exists('curl_file_create') ?
79 | ['foo' => curl_file_create($this->request->files['foo'])] :
80 | ['foo' => '@' . $this->request->files['foo']];
81 |
82 | $this->assertEquals(
83 | array_merge(
84 | $this->request->fields,
85 | $filesOptions
86 | ),
87 | $options[CURLOPT_POSTFIELDS]
88 | );
89 |
90 | // test file numbering
91 | $this->request->files = [$fixture];
92 | $options = $this->request->getCurlOptions();
93 |
94 | $filesOptions = function_exists('curl_file_create') ?
95 | ['file_1' => curl_file_create($this->request->files[0])] :
96 | ['file_1' => '@' . $this->request->files[0]];
97 |
98 | $this->assertEquals(
99 | array_merge(
100 | $this->request->fields,
101 | $filesOptions
102 | ),
103 | $options[CURLOPT_POSTFIELDS]
104 | );
105 | // -- End edit --
106 | }
107 |
108 | public function testExecute() {
109 | // Can't test this method because PHP doesn't allow stubbing the calls
110 | // to curl easily. However, the method hardly contains any logic as all
111 | // of that is located in other methods.
112 | $this->assertTrue(true);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [main](https://github.com/transloadit/php-sdk/tree/main)
4 |
5 | diff: https://github.com/transloadit/php-sdk/compare/3.3.0...main
6 |
7 | ## [3.3.0](https://github.com/transloadit/php-sdk/tree/3.3.0)
8 |
9 | - Replace the custom Node parity helper with the official `transloadit` CLI for Smart CDN signatures
10 | - Add a Docker-based test harness and document the parity workflow
11 | - Randomize system-test request signatures and document optional `auth.nonce` usage to avoid replay protection failures
12 |
13 | diff: https://github.com/transloadit/php-sdk/compare/3.2.0...3.3.0
14 |
15 | ## [3.2.0](https://github.com/transloadit/php-sdk/tree/3.2.0)
16 |
17 | - Implement `signedSmartCDNUrl`
18 |
19 | diff: https://github.com/transloadit/php-sdk/compare/3.1.0...3.2.0
20 |
21 | ## [3.1.0](https://github.com/transloadit/php-sdk/tree/3.1.0)
22 |
23 | - Pass down `curlOptions` when `TransloaditRequest` reinstantiates itself for `waitForCompletion`
24 |
25 | diff: https://github.com/transloadit/php-sdk/compare/3.0.4-dev...3.1.0
26 |
27 | ## [3.0.4-dev](https://github.com/transloadit/php-sdk/tree/3.0.4-dev)
28 |
29 | - Pass down `curlOptions` when `TransloaditRequest` reinstantiates itself for `waitForCompletion`
30 |
31 | diff: https://github.com/transloadit/php-sdk/compare/3.0.4...3.0.4-dev
32 |
33 | ## [3.0.4](https://github.com/transloadit/php-sdk/tree/3.0.4)
34 |
35 | - Ditch `v` prefix in versions as that's more idiomatic
36 | - Bring back the getAssembly() function
37 | - Implement Transloadit client header. Closes #25. (#28)
38 | - Fix waitForCompletion
39 | - Travis php & ubuntu version changes
40 | - fix: remove deprecation warning
41 | - Rename tl->deleteAssembly to cancelAssembly and add it to the Readme
42 |
43 | diff: https://github.com/transloadit/php-sdk/compare/v2.0.0...3.0.4
44 |
45 | ## [v2.1.0](https://github.com/transloadit/php-sdk/tree/v2.1.0)
46 |
47 | - Fix for CURL deprecated functions (thanks @ABerkhout)
48 | - CI improvements (phpunit, travis, composer)
49 | - Add example for fetching the assembly status
50 | - Add ability to set additional curl_setopt (thanks @michaelkasper)
51 |
52 | diff: https://github.com/transloadit/php-sdk/compare/v2.0.0...v2.1.0
53 |
54 | ## [v2.0.0](https://github.com/transloadit/php-sdk/tree/v2.0.0)
55 |
56 | - Retire host + protocol in favor of one endpoint property,
57 | allow passing that on to the Request object.
58 | - Improve readme (getting started)
59 | - Don't rely on globally installed phpunit when we can ship it via Composer
60 |
61 | diff: https://github.com/transloadit/php-sdk/compare/v1.0.1...v2.0.0
62 |
63 | ## [v1.0.1](https://github.com/transloadit/php-sdk/tree/v1.0.1)
64 |
65 | - Fix broken examples
66 | - Improve documentation (version changelogs)
67 |
68 | diff: https://github.com/transloadit/php-sdk/compare/v1.0.0...v1.0.1
69 |
70 | ## [v1.0.0](https://github.com/transloadit/php-sdk/tree/v1.0.0)
71 |
72 | A big thanks to [@nervetattoo](https://github.com/nervetattoo) for making this version happen!
73 |
74 | - Add support for Composer
75 | - Make phpunit run through Composer
76 | - Change to namespaced PHP
77 |
78 | diff: https://github.com/transloadit/php-sdk/compare/v0.10.0...v1.0.0
79 |
80 | ## [v0.10.0](https://github.com/transloadit/php-sdk/tree/v0.10.0)
81 |
82 | - Add support for Strict mode
83 | - Add support for more auth params
84 | - Improve documentation
85 | - Bug fixes
86 | - Refactoring
87 |
88 | diff: https://github.com/transloadit/php-sdk/compare/v0.9.1...v0.10.0
89 |
90 | ## [v0.9.1](https://github.com/transloadit/php-sdk/tree/v0.9.1)
91 |
92 | - Improve documentation
93 | - Better handling of errors & non-json responses
94 | - Change directory layout
95 |
96 | diff: https://github.com/transloadit/php-sdk/compare/v0.9...v0.9.1
97 |
98 | ## [v0.9](https://github.com/transloadit/php-sdk/tree/v0.9)
99 |
100 | - Use markdown for docs
101 | - Add support for signed GET requests
102 | - Add support for HTTPS
103 | - Simplified API
104 | - Improve handling of magic quotes
105 |
106 | diff: https://github.com/transloadit/php-sdk/compare/v0.2...v0.9
107 |
108 | ## [v0.2](https://github.com/transloadit/php-sdk/tree/v0.2)
109 |
110 | - Add error handling
111 |
112 | diff: https://github.com/transloadit/php-sdk/compare/v0.1...v0.2
113 |
114 | ## [v0.1](https://github.com/transloadit/php-sdk/tree/v0.1)
115 |
116 | The very first version
117 |
--------------------------------------------------------------------------------
/lib/transloadit/Transloadit.php:
--------------------------------------------------------------------------------
1 | $val) {
12 | $this->{$key} = $val;
13 | }
14 | }
15 |
16 | public function request($options = [], $execute = true) {
17 | $options = $options + [
18 | 'key' => $this->key,
19 | 'secret' => $this->secret,
20 | 'endpoint' => $this->endpoint,
21 | 'waitForCompletion' => false,
22 | ];
23 | $request = new TransloaditRequest($options);
24 | return ($execute)
25 | ? $request->execute()
26 | : $request;
27 | }
28 |
29 | public static function response() {
30 | if (!empty($_POST['transloadit'])) {
31 | $json = $_POST['transloadit'];
32 | if (ini_get('magic_quotes_gpc') === '1') {
33 | $json = stripslashes($json);
34 | }
35 |
36 | $response = new TransloaditResponse();
37 | $response->data = json_decode($json, true);
38 | return $response;
39 | }
40 |
41 | if (!empty($_GET['assembly_url'])) {
42 | $request = new TransloaditRequest([
43 | 'url' => $_GET['assembly_url'],
44 | ]);
45 | return $request->execute();
46 | }
47 | return false;
48 | }
49 |
50 | public function createAssemblyForm($options = []) {
51 | $out = [];
52 |
53 | $customFormAttributes = [];
54 | if (array_key_exists('attributes', $options)) {
55 | $customFormAttributes = $options['attributes'];
56 | unset($options['attributes']);
57 | }
58 |
59 | $assembly = $this->request($options + [
60 | 'method' => 'POST',
61 | 'path' => '/assemblies',
62 | ], false);
63 | $assembly->prepare();
64 |
65 | $formAttributes = [
66 | 'action' => $assembly->url,
67 | 'method' => $assembly->method,
68 | 'enctype' => 'multipart/form-data',
69 | ] + $customFormAttributes;
70 |
71 | $formAttributeList = [];
72 | foreach ($formAttributes as $key => $val) {
73 | $formAttributeList[] = sprintf('%s="%s"', $key, htmlentities($val));
74 | }
75 |
76 | $out[] = '
130 |
131 | ```
132 |
133 | ### 3. Use Uppy for file uploads
134 |
135 | We recommend using [Uppy](https://transloadit.com/docs/sdks/uppy/), our next-gen file uploader for the web, instead of the jQuery SDK. Uppy provides a more modern, flexible, and feature-rich solution for handling file uploads with Transloadit.
136 |
137 | To integrate Uppy with your PHP backend:
138 |
139 | 1. Include Uppy in your HTML:
140 |
141 | ```html
142 |
146 |
147 | ```
148 |
149 | 2. Initialize Uppy with Transloadit plugin:
150 |
151 | ```html
152 |
153 |
154 |
172 | ```
173 |
174 | 3. Handle the assembly status on your PHP backend:
175 |
176 | Create a new file named `transloadit_notify.php` in your project:
177 |
178 | ```php
179 | 'MY_TRANSLOADIT_KEY',
186 | 'secret' => 'MY_TRANSLOADIT_SECRET',
187 | ]);
188 |
189 | $response = Transloadit::response();
190 | if ($response) {
191 | // Process the assembly result
192 | $assemblyId = $response->data['assembly_id'];
193 | $assemblyStatus = $response->data['ok'];
194 |
195 | // You can store the assembly information in your database
196 | // or perform any other necessary actions here
197 |
198 | // Log the response for debugging
199 | error_log('Transloadit Assembly Completed: ' . $assemblyId);
200 | error_log('Assembly Status: ' . ($assemblyStatus ? 'Success' : 'Failed'));
201 |
202 | // Optionally, you can write the response to a file
203 | file_put_contents('transloadit_response_' . $assemblyId . '.json', json_encode($response->data));
204 |
205 | // Send a 200 OK response to Transloadit
206 | http_response_code(200);
207 | echo 'OK';
208 | } else {
209 | // If it's not a Transloadit notification, return a 400 Bad Request
210 | http_response_code(400);
211 | echo 'Bad Request';
212 | }
213 | ?>
214 | ```
215 |
216 | Make sure to replace `'https://your-site.com/transloadit_notify.php'` with the actual URL where you'll host this PHP script.
217 |
218 | For more detailed information on using Uppy with Transloadit, please refer to our [Uppy documentation](https://transloadit.com/docs/sdks/uppy/).
219 |
220 | ### 4. Fetch the Assembly Status JSON
221 |
222 | You can use the `getAssembly` method to get the Assembly Status.
223 |
224 | ```php
225 | 'MY_TRANSLOADIT_KEY',
231 | 'secret' => 'MY_TRANSLOADIT_SECRET',
232 | ]);
233 |
234 | $response = $transloadit->getAssembly($assemblyId);
235 |
236 | echo '
';
237 | print_r($response);
238 | echo '
';
239 |
240 | ```
241 |
242 | ### 5. Create an Assembly with a Template.
243 |
244 | This example demonstrates how you can use the SDK to create an Assembly
245 | with Templates.
246 |
247 | You are expected to create a Template on your Transloadit account dashboard
248 | and add the Template ID here.
249 |
250 | ```php
251 | 'MY_TRANSLOADIT_KEY',
258 | 'secret' => 'MY_TRANSLOADIT_SECRET',
259 | ]);
260 |
261 | $response = $transloadit->createAssembly([
262 | 'files' => ['/PATH/TO/FILE.jpg'],
263 | 'params' => [
264 | 'template_id' => 'MY_TEMPLATE_ID',
265 | ],
266 | ]);
267 |
268 | // Show the results of the assembly we spawned
269 | echo '
';
270 | print_r($response);
271 | echo '
';
272 |
273 | ```
274 |
275 | ### Signature Auth (Assemblies)
276 |
277 | Signature Authentication is done by the PHP SDK by default internally so you do not need to worry about this :)
278 |
279 | If you script the same request payload multiple times in quick succession (for example inside a health check or tight integration test loop), add a random nonce to keep each signature unique:
280 |
281 | ```php
282 | $params = [
283 | 'auth' => [
284 | 'key' => 'MY_TRANSLOADIT_KEY',
285 | 'expires' => gmdate('Y/m/d H:i:s+00:00', strtotime('+2 hours')),
286 | 'nonce' => bin2hex(random_bytes(16)),
287 | ],
288 | 'steps' => [
289 | // …
290 | ],
291 | ];
292 | ```
293 |
294 | The nonce is optional for regular usage, but including it in heavily scripted flows prevents Transloadit from rejecting repeated identical signatures.
295 |
296 | ### Signature Auth (Smart CDN)
297 |
298 | You can use the `signedSmartCDNUrl` method to generate signed URLs for Transloadit's [Smart CDN](https://transloadit.com/services/content-delivery/):
299 |
300 | ```php
301 | 'MY_TRANSLOADIT_KEY',
308 | 'secret' => 'MY_TRANSLOADIT_SECRET',
309 | ]);
310 |
311 | // Basic usage
312 | $url = $transloadit->signedSmartCDNUrl(
313 | 'your-workspace-slug',
314 | 'your-template-slug',
315 | 'avatars/jane.jpg'
316 | );
317 |
318 | // Advanced usage with custom parameters and expiry
319 | $url = $transloadit->signedSmartCDNUrl(
320 | 'your-workspace-slug',
321 | 'your-template-slug',
322 | 'avatars/jane.jpg',
323 | ['width' => 100, 'height' => 100], // Additional parameters
324 | 1732550672867, // Expiry date in milliseconds since epoch
325 | );
326 |
327 | echo $url;
328 | ```
329 |
330 | The generated URL will be in the format:
331 |
332 | ```
333 | https://{workspace-slug}.tlcdn.com/{template-slug}/{input-field}?{query-params}&sig=sha256:{signature}
334 | ```
335 |
336 | Note that:
337 |
338 | - The URL will expire after the specified time (default: 1 hour)
339 | - All parameters are properly encoded
340 | - The signature is generated using HMAC SHA-256
341 | - Query parameters are sorted alphabetically before signing
342 |
343 | ## Example
344 |
345 | For fully working examples take a look at [`examples/`](https://github.com/transloadit/php-sdk/tree/HEAD/examples).
346 |
347 | ## API
348 |
349 | ### $Transloadit = new Transloadit($properties = []);
350 |
351 | Creates a new Transloadit instance and applies the given $properties.
352 |
353 | #### $Transloadit->key = null;
354 |
355 | The auth key of your Transloadit account.
356 |
357 | #### $Transloadit->secret = null;
358 |
359 | The auth secret of your Transloadit account.
360 |
361 | #### $Transloadit->request($options = [], $execute = true);
362 |
363 | Creates a new `TransloaditRequest` using the `$Transloadit->key` and
364 | `$Transloadit->secret` properties.
365 |
366 | If `$execute` is set to `true`, `$TransloaditRequest->execute()` will be
367 | called and used as the return value.
368 |
369 | Otherwise the new `TransloaditRequest` instance is being returned.
370 |
371 | #### $Transloadit->createAssemblyForm($options = []);
372 |
373 | Creates a new Transloadit assembly form including the hidden 'params' and
374 | 'signature' fields. A closing form tag is not included.
375 |
376 | `$options` is an array of `TransloaditRequest` properties to be used.
377 | For example: `"params"`, `"expires"`, `"endpoint"`, etc..
378 |
379 | In addition to that, you can also pass an `"attributes"` key, which allows
380 | you to set custom form attributes. For example:
381 |
382 | ```php
383 | $Transloadit->createAssemblyForm(array(
384 | 'attributes' => array(
385 | 'id' => 'my_great_upload_form',
386 | 'class' => 'transloadit_form',
387 | ),
388 | ));
389 | ```
390 |
391 | #### $Transloadit->createAssembly($options);
392 |
393 | Sends a new assembly request to Transloadit. This is the preferred way of
394 | uploading files from your server.
395 |
396 | `$options` is an array of `TransloaditRequest` properties to be used with the exception that you can
397 | also use the `waitForCompletion` option here:
398 |
399 | `waitForCompletion` is a boolean (default is false) to indicate whether you want to wait for the
400 | Assembly to finish with all encoding results present before the callback is called. If
401 | waitForCompletion is true, this SDK will poll for status updates and return when all encoding work
402 | is done.
403 |
404 | Check example #1 above for more information.
405 |
406 | #### $Transloadit->getAssembly($assemblyId);
407 |
408 | Retrieves the Assembly status json for a given Assembly ID.
409 |
410 | #### $Transloadit->cancelAssembly($assemblyId);
411 |
412 | Cancels an assembly that is currently executing and prevents any further encodings costing money.
413 |
414 | This will result in `ASSEMBLY_NOT_FOUND` errors if invoked on assemblies that are not currently
415 | executing (anymore).
416 |
417 | #### Transloadit::response()
418 |
419 | This static method is used to parse the notifications Transloadit sends to
420 | your server.
421 |
422 | There are two kinds of notifications this method handles:
423 |
424 | - When using the `redirect_url` parameter, and Transloadit redirects
425 | back to your site, a `$_GET['assembly_url']` query parameter gets added.
426 | This method detects the presence of this parameter and fetches the current
427 | assembly status from that url and returns it as a `TransloaditResponse`.
428 | - When using the `notify_url` parameter, Transloadit sends a
429 | `$_POST['transloadit']` parameter. This method detects this, and parses
430 | the notification JSON into a `TransloaditResponse` object for you.
431 |
432 | If the current request does not seem to be invoked by Transloadit, this
433 | method returns `false`.
434 |
435 | ### $TransloaditRequest = new TransloaditRequest($properties = []);
436 |
437 | Creates a new TransloaditRequest instance and applies the given $properties.
438 |
439 | #### $TransloaditRequest->key = null;
440 |
441 | The auth key of your Transloadit account.
442 |
443 | #### $TransloaditRequest->secret = null;
444 |
445 | The auth secret of your Transloadit account.
446 |
447 | #### $TransloaditRequest->method = 'GET';
448 |
449 | Inherited from `CurlRequest`. Can be used to set the type of request to be
450 | made.
451 |
452 | #### $TransloaditRequest->curlOptions = [];
453 |
454 | Inherited from `CurlRequest`. Can be used to tweak cURL behavior using [any cURL option that your PHP/cURL version supports](https://www.php.net/manual/en/function.curl-setopt.php).
455 |
456 | Here is an [example](examples/6-assembly-with-timeout.php) that illustrates
457 | using this option to change the timeout of a request (drastically, to `1ms`, just to prove you can make the SDK abort after a time of your choosing).
458 |
459 | The default timeouts and options depend on the cURL version on your system and can be verified by checking `phpinfo()` and the [curl_setopt](https://www.php.net/manual/en/function.curl-setopt.php) documentation.
460 |
461 | #### $TransloaditRequest->endpoint = 'https://api2.transloadit.com';
462 |
463 | The endpoint to send this request to.
464 |
465 | #### $TransloaditRequest->path = null;
466 |
467 | The url path to request.
468 |
469 | #### $TransloaditRequest->url = null;
470 |
471 | Inherited from `CurlRequest`. Lets you overwrite the above endpoint / path
472 | properties with a fully custom url alltogether.
473 |
474 | #### $TransloaditRequest->fields = [];
475 |
476 | A list of additional fields to send along with your request. Transloadit
477 | will include those in all assembly related notifications.
478 |
479 | #### $TransloaditRequest->files = [];
480 |
481 | An array of paths to local files you would like to upload. For example:
482 |
483 | ```php
484 | $TransloaditRequest->files = array('/my/file.jpg');
485 | ```
486 |
487 | or
488 |
489 | ```php
490 | $TransloaditRequest->files = array('my_upload' => '/my/file.jpg');
491 | ```
492 |
493 | The first example would automatically give your file a field name of
494 | `'file_1'` when executing the request.
495 |
496 | #### $TransloaditRequest->params = [];
497 |
498 | An array representing the JSON params to be send to Transloadit. You
499 | do not have to include an `'auth'` key here, as this class handles that
500 | for you as part of `$TransloaditRequest->prepare()`.
501 |
502 | #### $TransloaditRequest->expires = '+2 hours';
503 |
504 | If you have configured a '`$TransloaditRequest->secret`', this class will
505 | automatically sign your request. The expires property lets you configure
506 | the duration for which the signature is valid.
507 |
508 | #### $TransloaditRequest->headers = [];
509 |
510 | Lets you send additional headers along with your request. You should not
511 | have to change this property.
512 |
513 | #### $TransloaditRequest->execute()
514 |
515 | Sends this request to Transloadit and returns a `TransloaditResponse`
516 | instance.
517 |
518 | ### $TransloaditResponse = new TransloaditResponse($properties = []);
519 |
520 | Creates a new TransloaditResponse instance and applies the given $properties.
521 |
522 | #### $TransloaditResponse->data = null;
523 |
524 | Inherited from `CurlResponse`. Contains an array of the parsed JSON
525 | response from Transloadit.
526 |
527 | You should generally only access this property after having checked for
528 | errors using `$TransloaditResponse->error()`.
529 |
530 | #### $TransloaditResponse->error();
531 |
532 | Returns `false` or a string containing an explanation of what went wrong.
533 |
534 | All of the following will cause an error string to be returned:
535 |
536 | - Network issues of any kind
537 | - The Transloadit response JSON contains an `{"error": "..."}` key
538 | - A malformed response was received
539 |
540 | **_Note_**: You will need to set waitForCompletion = True in the $Transloadit->createAssembly($options) function call.
541 |
542 | ## License
543 |
544 | [MIT Licensed](LICENSE)
545 |
546 | [test_badge]: https://github.com/transloadit/php-sdk/actions/workflows/ci.yml/badge.svg
547 | [test_link]: https://github.com/transloadit/php-sdk/actions/workflows/ci.yml
548 | [codecov_badge]: https://codecov.io/gh/transloadit/php-sdk/branch/main/graph/badge.svg
549 | [codecov_link]: https://codecov.io/gh/transloadit/php-sdk
550 | [php_verison_badge]: https://img.shields.io/packagist/php-v/transloadit/php-sdk
551 | [licence_badge]: https://img.shields.io/badge/License-MIT-green.svg
552 | [licence_link]: https://github.com/transloadit/php-sdk/blob/main/LICENSE
553 |
--------------------------------------------------------------------------------