├── .gitattributes
├── .gitignore
├── .phive
└── phars.xml
├── LICENSE
├── Makefile
├── composer.json
├── composer.lock
├── daprdocs
├── content
│ └── en
│ │ └── php-sdk-docs
│ │ ├── _index.md
│ │ ├── php-actors
│ │ ├── _index.md
│ │ └── php-actor-reference.md
│ │ ├── php-app
│ │ ├── _index.md
│ │ └── php-unit-testing.md
│ │ ├── php-pubsub
│ │ └── _index.md
│ │ ├── php-serialization.md
│ │ └── php-state
│ │ └── _index.md
└── readme.md
├── docker-compose.yml
├── examples
├── actor
│ ├── .env
│ ├── .gitignore
│ ├── Makefile
│ ├── components
│ │ └── statestore.yml
│ ├── composer.json
│ ├── composer.lock
│ ├── docker-compose.yml
│ ├── global-config.php
│ ├── images
│ │ ├── Caddyfile
│ │ ├── fpm.conf
│ │ └── opcache.ini
│ ├── index.php
│ ├── readme.md
│ ├── services
│ │ ├── actor
│ │ │ ├── Counter.php
│ │ │ ├── ICounter.php
│ │ │ ├── State.php
│ │ │ ├── config.php
│ │ │ └── index.php
│ │ └── client
│ │ │ ├── ICounter.php
│ │ │ ├── config.php
│ │ │ └── index.php
│ └── test.php
├── docker-compose.common.yml
├── images
│ ├── Caddyfile
│ ├── caddy.Dockerfile
│ ├── fpm.conf
│ ├── opcache.ini
│ ├── service.Dockerfile
│ └── xdebug.ini
├── laravel
│ ├── .editorconfig
│ ├── .env
│ ├── .env.example
│ ├── .gitattributes
│ ├── .gitignore
│ ├── .styleci.yml
│ ├── README.md
│ ├── app
│ │ ├── Console
│ │ │ └── Kernel.php
│ │ ├── Exceptions
│ │ │ └── Handler.php
│ │ ├── Http
│ │ │ ├── Controllers
│ │ │ │ └── Controller.php
│ │ │ ├── Kernel.php
│ │ │ └── Middleware
│ │ │ │ ├── Authenticate.php
│ │ │ │ ├── EncryptCookies.php
│ │ │ │ ├── PreventRequestsDuringMaintenance.php
│ │ │ │ ├── RedirectIfAuthenticated.php
│ │ │ │ ├── TrimStrings.php
│ │ │ │ ├── TrustHosts.php
│ │ │ │ ├── TrustProxies.php
│ │ │ │ └── VerifyCsrfToken.php
│ │ ├── Models
│ │ │ ├── ExampleState.php
│ │ │ └── User.php
│ │ └── Providers
│ │ │ ├── AppServiceProvider.php
│ │ │ ├── AuthServiceProvider.php
│ │ │ ├── BroadcastServiceProvider.php
│ │ │ ├── DaprServiceProvider.php
│ │ │ ├── EventServiceProvider.php
│ │ │ └── RouteServiceProvider.php
│ ├── artisan
│ ├── bootstrap
│ │ ├── app.php
│ │ └── cache
│ │ │ └── .gitignore
│ ├── components
│ │ ├── pubsub.yaml
│ │ └── statestore.yaml
│ ├── composer.json
│ ├── composer.lock
│ ├── config
│ │ ├── app.php
│ │ ├── auth.php
│ │ ├── broadcasting.php
│ │ ├── cache.php
│ │ ├── cors.php
│ │ ├── database.php
│ │ ├── filesystems.php
│ │ ├── hashing.php
│ │ ├── logging.php
│ │ ├── mail.php
│ │ ├── queue.php
│ │ ├── services.php
│ │ ├── session.php
│ │ └── view.php
│ ├── database
│ │ ├── .gitignore
│ │ ├── factories
│ │ │ └── UserFactory.php
│ │ ├── migrations
│ │ │ ├── 2014_10_12_000000_create_users_table.php
│ │ │ ├── 2014_10_12_100000_create_password_resets_table.php
│ │ │ └── 2019_08_19_000000_create_failed_jobs_table.php
│ │ └── seeders
│ │ │ └── DatabaseSeeder.php
│ ├── docker-compose.yml
│ ├── package.json
│ ├── phpunit.xml
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.php
│ │ ├── robots.txt
│ │ └── web.config
│ ├── resources
│ │ ├── css
│ │ │ └── app.css
│ │ ├── js
│ │ │ ├── app.js
│ │ │ └── bootstrap.js
│ │ ├── lang
│ │ │ └── en
│ │ │ │ ├── auth.php
│ │ │ │ ├── pagination.php
│ │ │ │ ├── passwords.php
│ │ │ │ └── validation.php
│ │ └── views
│ │ │ └── welcome.blade.php
│ ├── routes
│ │ ├── api.php
│ │ ├── channels.php
│ │ ├── console.php
│ │ └── web.php
│ ├── server.php
│ ├── start.php
│ ├── storage
│ │ ├── app
│ │ │ ├── .gitignore
│ │ │ └── public
│ │ │ │ └── .gitignore
│ │ ├── framework
│ │ │ ├── .gitignore
│ │ │ ├── cache
│ │ │ │ ├── .gitignore
│ │ │ │ └── data
│ │ │ │ │ └── .gitignore
│ │ │ ├── sessions
│ │ │ │ └── .gitignore
│ │ │ ├── testing
│ │ │ │ └── .gitignore
│ │ │ └── views
│ │ │ │ └── .gitignore
│ │ └── logs
│ │ │ └── .gitignore
│ ├── test.php
│ ├── tests
│ │ ├── CreatesApplication.php
│ │ ├── Feature
│ │ │ └── ExampleTest.php
│ │ ├── TestCase.php
│ │ └── Unit
│ │ │ └── ExampleTest.php
│ └── webpack.mix.js
└── secrets
│ ├── .dockerignore
│ ├── .env
│ ├── .gitignore
│ ├── Makefile
│ ├── components
│ ├── file-secrets.yml
│ └── secrets.json
│ ├── composer.json
│ ├── composer.lock
│ ├── deployments
│ ├── client-service.yml
│ ├── secret-service.yml
│ ├── simple-secret.yml
│ └── some-secrets.yml
│ ├── docker-compose.yml
│ ├── global-config.php
│ ├── images
│ ├── Caddyfile
│ ├── fpm.conf
│ └── opcache.ini
│ ├── index.php
│ ├── readme.md
│ ├── services
│ ├── client
│ │ ├── config.php
│ │ └── index.php
│ └── secrets
│ │ ├── config.php
│ │ └── index.php
│ └── test.php
├── phpunit.xml
├── psalm-baseline.xml
├── psalm.xml
├── psysh-bootstrap.php
├── psysh-config.php
├── readme.md
├── src
├── config.php
└── lib
│ ├── Actors
│ ├── .phpstorm.meta.php
│ ├── Actor.php
│ ├── ActorConfig.php
│ ├── ActorProxy.php
│ ├── ActorReference.php
│ ├── ActorRuntime.php
│ ├── ActorState.php
│ ├── ActorTrait.php
│ ├── Attributes
│ │ ├── DaprType.php
│ │ ├── Delete.php
│ │ ├── Get.php
│ │ ├── Post.php
│ │ └── Put.php
│ ├── Generators
│ │ ├── CachedGenerator.php
│ │ ├── DynamicGenerator.php
│ │ ├── ExistingOnly.php
│ │ ├── FileGenerator.php
│ │ ├── GenerateProxy.php
│ │ ├── IGenerateProxy.php
│ │ └── ProxyFactory.php
│ ├── HealthCheck.php
│ ├── IActor.php
│ ├── IActorReference.php
│ ├── Internal
│ │ ├── Caches
│ │ │ ├── CacheInterface.php
│ │ │ ├── FileCache.php
│ │ │ ├── KeyNotFound.php
│ │ │ └── MemoryCache.php
│ │ ├── InternalProxy.php
│ │ └── KeyResponse.php
│ ├── ReentrantConfig.php
│ ├── Reminder.php
│ └── Timer.php
│ ├── App.php
│ ├── Attributes
│ └── FromBody.php
│ ├── Client
│ ├── AppId.php
│ ├── BindingRequest.php
│ ├── BindingResponse.php
│ ├── DaprClient.php
│ ├── DaprClientBuilder.php
│ ├── DaprHttpClient.php
│ ├── DeleteTransactionRequest.php
│ ├── HttpActorTrait.php
│ ├── HttpBindingTrait.php
│ ├── HttpInvokeTrait.php
│ ├── HttpPubSubTrait.php
│ ├── HttpSecretsTrait.php
│ ├── HttpStateTrait.php
│ ├── HttpTokenTrait.php
│ ├── MetadataResponse.php
│ ├── PromiseHandlingTrait.php
│ ├── StateTransactionRequest.php
│ └── UpsertTransactionRequest.php
│ ├── DaprClient.php
│ ├── DaprResponse.php
│ ├── Deserialization
│ ├── .phpstorm.meta.php
│ ├── Attributes
│ │ ├── ArrayOf.php
│ │ └── AsClass.php
│ ├── DeserializationConfig.php
│ ├── Deserializer.php
│ ├── Deserializers
│ │ ├── DateInterval.php
│ │ ├── DateTime.php
│ │ └── IDeserialize.php
│ ├── IDeserializer.php
│ └── InvokerParameterResolver.php
│ ├── Formats.php
│ ├── Middleware
│ ├── Defaults
│ │ ├── ActorToken.php
│ │ ├── Response
│ │ │ └── ApplicationJson.php
│ │ ├── TokenAuth.php
│ │ └── Tracing.php
│ ├── IRequestMiddleware.php
│ └── IResponseMiddleware.php
│ ├── Mocks
│ └── TestClient.php
│ ├── PubSub
│ ├── CloudEvent.php
│ ├── Publish.php
│ ├── Subscription.php
│ ├── Subscriptions.php
│ └── Topic.php
│ ├── SecretManager.php
│ ├── Serialization
│ ├── Attributes
│ │ └── AlwaysObject.php
│ ├── ISerializer.php
│ ├── SerializationConfig.php
│ ├── Serializer.php
│ └── Serializers
│ │ ├── DateInterval.php
│ │ ├── DateTime.php
│ │ ├── ISerialize.php
│ │ └── StateItem.php
│ ├── State
│ ├── Attributes
│ │ └── StateStore.php
│ ├── FileWriter.php
│ ├── IManageState.php
│ ├── Internal
│ │ ├── StateHelpers.php
│ │ └── Transaction.php
│ ├── StateItem.php
│ ├── StateManager.php
│ ├── StateManagerOld.php
│ ├── TransactionalState.php
│ └── TransactionalStateOld.php
│ ├── consistency
│ ├── Consistency.php
│ ├── EventualFirstWrite.php
│ ├── EventualLastWrite.php
│ ├── StrongFirstWrite.php
│ └── StrongLastWrite.php
│ └── exceptions
│ ├── DaprException.php
│ ├── Http
│ ├── HttpException.php
│ └── NotFound.php
│ ├── SaveStateFailure.php
│ └── StateAlreadyCommitted.php
└── update-examples.php
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Don't include these files in the package
2 | /tests export-ignore
3 | /src/index.php export-ignore
4 | /.github export-ignore
5 | /components export-ignore
6 | /deploy export-ignore
7 | /images export-ignore
8 | .htaccess export-ignore
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /.lsync.pid
3 | /make/
4 | /.idea/
5 | .phpunit.result.cache
6 | coverage.xml
7 | /tools/
8 |
--------------------------------------------------------------------------------
/.phive/phars.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | export GITHUB_SHA=latest
3 | export DAPR_VERSION=1.3.0-rc.1
4 |
5 | PHIVE=$(shell which phive || echo .phive/phive)
6 |
7 | .PHONY: integration-tests
8 | integration-tests: build tools
9 | docker-compose down -v
10 | docker-compose up &
11 | sleep 10
12 | curl --silent --output /tmp/test-results.json --write-out "%{http_code}" -H 'dapr-api-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' http://localhost:9502/do_tests
13 | docker-compose down -v
14 | cat /tmp/test-results.json | jq .
15 |
16 | .PHONY: stop
17 | docker-compose down -v
18 |
19 | composer.lock: composer.json
20 | composer update
21 |
22 | vendor/autoload.php: composer.lock
23 | composer install
24 | touch vendor/autoload.php
25 |
26 | .PHONY: build
27 | build: build-caddy build-tests
28 |
29 | .PHONY: build-tests
30 | build-tests: vendor/autoload.php
31 | docker build -t tests:latest -f images/tests.Dockerfile .
32 |
33 | .PHONY: build-caddy
34 | build-caddy: vendor/autoload.php
35 | docker build -t caddy:latest -f images/caddy.Dockerfile .
36 |
37 | $(PHIVE):
38 | wget -O phive.phar "https://phar.io/releases/phive.phar"
39 | wget -O phive.phar.asc "https://phar.io/releases/phive.phar.asc"
40 | gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
41 | gpg --verify phive.phar.asc phive.phar
42 | rm phive.phar.asc
43 | chmod +x phive.phar
44 | mv phive.phar $(PHIVE)
45 |
46 | tools: .phive/phars.xml $(PHIVE)
47 | rm -rf tools && echo $(PHIVE)
48 | $(PHIVE) install --trust-gpg-keys 12CE0F1D262429A5,4AA394086372C20A
49 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dapr/php-sdk",
3 | "type": "library",
4 | "description": "Dapr Implementation in PHP",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Rob Landers",
9 | "email": "landers.robert@gmail.com"
10 | },
11 | {
12 | "name": "Hendrik Heil",
13 | "email": "hendrik@ciidyr.com"
14 | }
15 | ],
16 | "require": {
17 | "ext-curl": "*",
18 | "ext-json": "*",
19 | "ext-mbstring": "*",
20 | "guzzlehttp/guzzle": "^7.3",
21 | "laminas/laminas-httphandlerrunner": "^1.3",
22 | "monolog/monolog": "^2.2",
23 | "nette/php-generator": "^3.5",
24 | "nikic/fast-route": "^1.3",
25 | "nyholm/psr7": "^1.3",
26 | "nyholm/psr7-server": "^1.0",
27 | "php": "^8.0",
28 | "php-di/invoker": "^2.3",
29 | "php-di/php-di": "^6.3",
30 | "psr/log": "^1.1|^2.0|^3.0",
31 | "psr/http-server-middleware": ">=1.0.1",
32 | "jetbrains/phpstorm-attributes": "1.0"
33 | },
34 | "require-dev": {
35 | "ext-xdebug": "*",
36 | "phpunit/phpunit": "^9"
37 | },
38 | "autoload": {
39 | "psr-4": {
40 | "Dapr\\": "src/lib",
41 | "Dapr\\TestActors\\": "test/actors"
42 | }
43 | },
44 | "scripts": {
45 | "test": "./vendor/bin/phpunit --testdox tests --coverage-clover coverage.xml tests --whitelist src",
46 | "start": [
47 | "COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose build --pull",
48 | "docker-compose up -d"
49 | ],
50 | "dapr-init": [
51 | "dapr init --runtime-version 1.1.0"
52 | ],
53 | "clean": [
54 | "docker-compose stop",
55 | "docker-compose rm -fv"
56 | ],
57 | "integration-tests": [
58 | "curl localhost:9502/do_tests"
59 | ],
60 | "lint": "./tools/psalm"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/daprdocs/content/en/php-sdk-docs/php-pubsub/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | type: docs
3 | title: "Publish and Subscribe with PHP"
4 | linkTitle: "Publish and Subscribe"
5 | weight: 1000
6 | description: How to use
7 | no_list: true
8 | ---
9 |
10 | With Dapr, you can publish anything, including cloud events. The SDK contains a simple cloud event implementation, but
11 | you can also just pass an array that conforms to the cloud event spec or use another library.
12 |
13 | ```php
14 | post('/publish', function(\Dapr\Client\DaprClient $daprClient) {
16 | $daprClient->publishEvent(pubsubName: 'pubsub', topicName: 'my-topic', data: ['something' => 'happened']);
17 | });
18 | ```
19 |
20 | For more information about publish/subscribe, check out [the howto]({{< ref howto-publish-subscribe.md >}}).
21 |
22 | ## Data content type
23 |
24 | The PHP SDK allows setting the data content type either when constructing a custom cloud event, or when publishing raw
25 | data.
26 |
27 | {{< tabs CloudEvent "Raw" >}}
28 |
29 | {{% codetab %}}
30 |
31 | ```php
32 | data = $xml;
35 | $event->data_content_type = 'application/xml';
36 | ```
37 |
38 | {{% /codetab %}}
39 | {{% codetab %}}
40 |
41 | ```php
42 | publishEvent(pubsubName: 'pubsub', topicName: 'my-topic', data: $raw_data, contentType: 'application/octet-stream');
47 | ```
48 |
49 | {{% alert title="Binary data" color="warning" %}}
50 |
51 | Only `application/octet-steam` is supported for binary data.
52 |
53 | {{% /alert %}}
54 |
55 | {{% /codetab %}}
56 |
57 | {{< /tabs >}}
58 |
59 | ## Receiving cloud events
60 |
61 | In your subscription handler, you can have the DI Container inject either a `Dapr\PubSub\CloudEvent` or an `array` into
62 | your controller. The former does some validation to ensure you have a proper event. If you need direct access to the
63 | data, or the events do not conform to the spec, use an `array`.
64 |
--------------------------------------------------------------------------------
/daprdocs/content/en/php-sdk-docs/php-serialization.md:
--------------------------------------------------------------------------------
1 | ---
2 | type: docs
3 | title: "Custom Serialization"
4 | linkTitle: "Custom Serializers"
5 | weight: 1000
6 | description: How to configure serialization
7 | no_list: true
8 | ---
9 |
10 | Dapr uses JSON serialization and thus (complex) type information is lost when sending/receiving data.
11 |
12 | ## Serialization
13 |
14 | When returning an object from a controller, passing an object to the `DaprClient`, or storing an object in a state store,
15 | only public properties are scanned and serialized. You can customize this behavior by implementing `\Dapr\Serialization\ISerialize`.
16 | For example, if you wanted to create an ID type that serialized to a string, you may implement it like so:
17 |
18 | ```php
19 | id;
29 | }
30 | }
31 | ```
32 |
33 | This works for any type that we have full ownership over, however, it doesn't work for classes from libraries or PHP itself.
34 | For that, you need to register a custom serializer with the DI container:
35 |
36 | ```php
37 | [SomeClass::class => new SerializeSomeClass()],
50 | ];
51 | ```
52 |
53 | ## Deserialization
54 |
55 | Deserialization works exactly the same way, except the interface is `\Dapr\Deserialization\Deserializers\IDeserialize`.
56 |
--------------------------------------------------------------------------------
/daprdocs/content/en/php-sdk-docs/php-state/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | type: docs
3 | title: "State Management with PHP"
4 | linkTitle: "State management"
5 | weight: 1000
6 | description: How to use
7 | no_list: true
8 | ---
9 |
10 | Dapr offers a great modular approach to using state in your application. The best way to learn the basics is to visit
11 | [the howto]({{< ref howto-get-save-state.md >}}).
12 |
13 | ## Metadata
14 |
15 | Many state components allow you to pass metadata to the component to control specific aspects of the component's
16 | behavior. The PHP SDK allows you to pass that metadata through:
17 |
18 | ```php
19 | run(
22 | fn(\Dapr\State\StateManager $stateManager) =>
23 | $stateManager->save_state('statestore', new \Dapr\State\StateItem('key', 'value', metadata: ['port' => '112'])));
24 |
25 | // using the DaprClient
26 | $app->run(fn(\Dapr\Client\DaprClient $daprClient) => $daprClient->saveState(storeName: 'statestore', key: 'key', value: 'value', metadata: ['port' => '112']))
27 | ```
28 |
29 | This is an example of how you might pass the port metadata to [Cassandra]({{< ref setup-cassandra.md >}}).
30 |
31 | Every state operation allows passing metadata.
32 |
33 | ## Consistency/concurrency
34 |
35 | In the PHP SDK, there are four classes that represent the four different types of consistency and concurrency in Dapr:
36 |
37 | ```php
38 | }}
62 |
63 | {{% codetab %}}
64 |
65 | ```php
66 | run(function (TransactionObject $object ) {
72 | $object->begin(prefix: 'my-prefix-');
73 | $object->key = 'value';
74 | // commit to key `my-prefix-key`
75 | $object->commit();
76 | });
77 | ```
78 |
79 | {{% /codetab %}}
80 | {{% codetab %}}
81 |
82 | ```php
83 | run(function(\Dapr\State\StateManager $stateManager) {
89 | $stateManager->load_object($obj = new StateObject(), prefix: 'my-prefix-');
90 | // original value is from `my-prefix-key`
91 | $obj->key = 'value';
92 | // save to `my-prefix-key`
93 | $stateManager->save_object($obj, prefix: 'my-prefix-');
94 | });
95 | ```
96 |
97 | {{% /codetab %}}
98 |
99 | {{< /tabs >}}
100 |
--------------------------------------------------------------------------------
/daprdocs/readme.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dapr/php-sdk/f0f37c39c7b95b6402c617bdfb6743b0a3b8c8cf/daprdocs/readme.md
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | # A Docker Compose file for spinning up integration tests in CI, you can run these locally by running `make`
2 | version: "3"
3 | services:
4 | placement:
5 | image: daprio/dapr:${DAPR_VERSION}
6 | command: [ "./placement","-port","50006" ]
7 | dev:
8 | image: tests:${GITHUB_SHA}
9 | environment:
10 | DAPR_API_TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
11 | APP_API_TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
12 | depends_on:
13 | - placement
14 | ports:
15 | - 9502:80
16 | dev-caddy:
17 | image: caddy:${GITHUB_SHA}
18 | depends_on:
19 | - dev
20 | network_mode: service:dev
21 | dev-daprd:
22 | image: daprio/daprd:${DAPR_VERSION}
23 | environment:
24 | DAPR_API_TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
25 | APP_API_TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
26 | command: [ "./daprd","-app-id","dev","-app-port","80","-placement-host-address","placement:50006","-components-path","/components" ]
27 | volumes:
28 | - ./components:/components
29 | depends_on:
30 | - dev
31 | network_mode: service:dev
32 | redis:
33 | image: redis:latest
34 |
--------------------------------------------------------------------------------
/examples/actor/.env:
--------------------------------------------------------------------------------
1 | DOCKER_USER=withinboredom
2 | DAPR_VERSION=1.7.0
3 |
--------------------------------------------------------------------------------
/examples/actor/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | .idea/
3 |
--------------------------------------------------------------------------------
/examples/actor/Makefile:
--------------------------------------------------------------------------------
1 | SHELL := /bin/bash
2 |
3 | include .env
4 | export $(shell sed 's/=.*//' .env)
5 | export GIT_SHA=$(shell git rev-parse HEAD)
6 |
7 | .PHONY: deps
8 | deps: service/actor-http service/actor-service service/client-http service/client-service
9 | @echo 'all done: run `make start` to get started'
10 |
11 | .PHONY: service/actor-service
12 | service/actor-service: images/opcache.ini images/fpm.conf
13 | docker build --pull --build-arg SERVICE=actor -f ../images/service.Dockerfile --target production -t php-actor-service:${GIT_SHA} .
14 |
15 | .PHONY: service/actor-http
16 | service/actor-http: images/Caddyfile
17 | docker build --pull --build-arg SERVICE=actor -f ../images/caddy.Dockerfile --target base -t php-actor-http:${GIT_SHA} .
18 |
19 | .PHONY: service/client-service
20 | service/client-service: images/opcache.ini images/opcache.ini
21 | docker build --pull --build-arg SERVICE=client -f ../images/service.Dockerfile --target production -t php-client-service:${GIT_SHA} .
22 |
23 | .PHONY: service/client-http
24 | service/client-http: images/Caddyfile
25 | docker build --pull --build-arg SERICE=client -f ../images/caddy.Dockerfile --target base -t php-client-http:${GIT_SHA} .
26 |
27 | .PHONY: start
28 | start:
29 | DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker-compose -f docker-compose.yml -f ../docker-compose.common.yml up -d
30 |
31 | .PHONY: stop
32 | stop:
33 | docker-compose -f docker-compose.yml -f ../docker-compose.common.yml stop
34 |
35 | .PHONY: clean
36 | clean: stop
37 | docker-compose -f docker-compose.yml -f ../docker-compose.common.yml rm -f
38 |
39 | .PHONY: logs
40 | logs:
41 | docker-compose -f docker-compose.yml -f ../docker-compose.common.yml logs
42 |
43 | images/fpm.conf: ../images/fpm.conf
44 | mkdir -p images
45 | cp ../images/fpm.conf images/fpm.conf
46 | touch images/fpm.conf
47 |
48 | images/opcache.ini: ../images/opcache.ini
49 | mkdir -p images
50 | cp ../images/opcache.ini images/opcache.ini
51 | touch images/opcache.ini
52 |
53 | images/xdebug.ini: ../images/xdebug.ini
54 | mkdir -p images
55 | cp ../images/xdebug.ini images/xdebug.ini
56 | touch images/xdebug.ini
57 |
58 | images/Caddyfile: ../images/Caddyfile
59 | mkdir -p images
60 | cp ../images/Caddyfile images/Caddyfile
61 | touch images/Caddyfile
62 |
--------------------------------------------------------------------------------
/examples/actor/components/statestore.yml:
--------------------------------------------------------------------------------
1 | apiVersion: dapr.io/v1alpha1
2 | kind: Component
3 | metadata:
4 | name: statestore
5 | spec:
6 | type: state.redis
7 | metadata:
8 | - name: redisHost
9 | value: redis:6379
10 | - name: redisPassword
11 | value: ""
12 | - name: actorStateStore
13 | value: "true"
14 |
--------------------------------------------------------------------------------
/examples/actor/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dapr/actor-example",
3 | "license": "MIT",
4 | "description": "A basic example demonstrating actors",
5 | "require": {
6 | "dapr/php-sdk": "dev-main"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/actor/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | actor-service:
4 | image: php-actor-service:${GIT_SHA}
5 | actor-http:
6 | image: php-actor-http:${GIT_SHA}
7 | depends_on:
8 | - actor-service
9 | network_mode: service:actor-service
10 | actor-daprd:
11 | image: daprio/daprd:${DAPR_VERSION}
12 | command: [ "./daprd","-app-id","actor","-app-port","80","-placement-host-address","placement:50006","-components-path","/components" ]
13 | volumes:
14 | - ./components:/components
15 | depends_on:
16 | - actor-http
17 | network_mode: service:actor-service
18 | client-service:
19 | image: php-client-service:${GIT_SHA}
20 | ports:
21 | - 8080:80
22 | client-http:
23 | image: php-client-http:${GIT_SHA}
24 | depends_on:
25 | - client-service
26 | network_mode: service:client-service
27 | client-daprd:
28 | image: daprio/daprd:${DAPR_VERSION}
29 | command: [ "./daprd","-app-id","client","-app-port","80","-placement-host-address","placement:50006","-components-path","/components" ]
30 | depends_on:
31 | - client-http
32 | network_mode: service:client-service
33 | volumes:
34 | - ./components:/components
35 | redis:
36 | image: 'redis:alpine'
37 |
--------------------------------------------------------------------------------
/examples/actor/global-config.php:
--------------------------------------------------------------------------------
1 | $builder->addDefinitions(
10 | __DIR__.'/global-config.php',
11 | SERVICE_ROOT.'/config.php'
12 | )->enableCompilation(sys_get_temp_dir())->enableDefinitionCache()
13 | );
14 |
15 | include SERVICE_ROOT.'/index.php';
16 |
17 | $app->start();
18 |
--------------------------------------------------------------------------------
/examples/actor/readme.md:
--------------------------------------------------------------------------------
1 | # Secret Example
2 |
3 | In this example we illustrate a production service that returns secrets. It consists of two services:
4 |
5 | 1. [actor-service](services/actor/index.php): A simple counter actor that allows getting the current count or
6 | incrementing the count.
7 | 2. [client-service](services/client/index.php): A simple API to communicate with the actor.
8 |
9 | ## Running the example
10 |
11 | ### Docker Compose
12 |
13 | > Requirements:
14 | > - Docker Compose
15 | > - Docker
16 | > - `make`
17 | > - `jq` (optional)
18 |
19 | #### Build the images
20 |
21 | You'll need to build some custom images, do that using `make`.
22 |
23 | ```bash
24 | make
25 | ```
26 |
27 | #### Start the services
28 |
29 | You'll also use `make` to start a docker-compose file which will spin up dapr, the actor service, and the client
30 | service.
31 |
32 | ```bash
33 | make start
34 | ```
35 |
36 | #### Query an actor
37 |
38 | You can query the current state of an actor using `curl` (and `jq` to format the JSON response). You can also increment
39 | the actor. Here's an example using an actor with the id `hello_world`:
40 |
41 | ```bash
42 | curl -s http://localhost:8080/method/hello_world/get_count | jq .
43 | curl -s http://localhost:8080/method/hello_world/increment_and_get | jq .
44 | ```
45 |
46 | #### Clean up
47 |
48 | Delete the containers:
49 |
50 | ```bash
51 | make clean
52 | ```
53 |
--------------------------------------------------------------------------------
/examples/actor/services/actor/Counter.php:
--------------------------------------------------------------------------------
1 | state->count;
22 | }
23 |
24 | #[Post]
25 | function increment_and_get(): int
26 | {
27 | return $this->state->count += 1;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/actor/services/actor/ICounter.php:
--------------------------------------------------------------------------------
1 | [Counter::class],
5 | ];
6 |
--------------------------------------------------------------------------------
/examples/actor/services/actor/index.php:
--------------------------------------------------------------------------------
1 | get(
8 | '/',
9 | fn() => [
10 | 'links' => [
11 | 'Method Invoke' => 'GET /method//',
12 | 'Methods' => [
13 | 'get_count' => 'http://localhost:8080/method/hello_world/get_count',
14 | 'increment_and_get' => 'http://localhost:8080/method/hello_world/increment_and_get',
15 | ],
16 | ],
17 | ]
18 | );
19 |
20 | $app->get(
21 | '/method/{actor_id}/{method}',
22 | fn(string $actor_id, string $method, ActorProxy $actorProxy) => $actorProxy
23 | ->get(ICounter::class, $actor_id)->$method()
24 | );
25 |
--------------------------------------------------------------------------------
/examples/actor/test.php:
--------------------------------------------------------------------------------
1 | 0,
7 | "http://localhost:8080/method/$id/increment_and_get" => 1,
8 | ];
9 |
10 | $failed = false;
11 |
12 | for ($i = 0; $i < 5; $i++) {
13 | $logs = `GIT_SHA=t docker-compose logs actor-daprd`;
14 | if (str_contains($logs, 'placement tables updated, version: 1')) {
15 | echo "Running Tests!\n";
16 | break;
17 | }
18 | echo "Waiting for actors to be registered...\n";
19 | sleep(2);
20 | }
21 |
22 | foreach ($urls as $url => $expected) {
23 | echo "Calling $url: ";
24 | $result = json_decode(`curl -s $url`, true);
25 | if ($result === $expected) {
26 | echo "PASS\n";
27 | } else {
28 | echo "FAILED\n";
29 | echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
30 | $failed = true;
31 | }
32 | }
33 |
34 | exit($failed ? 1 : 0);
35 |
--------------------------------------------------------------------------------
/examples/docker-compose.common.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | placement:
4 | image: daprio/dapr:${DAPR_VERSION}
5 | command: ["./placement","-port","50006"]
6 |
--------------------------------------------------------------------------------
/examples/images/Caddyfile:
--------------------------------------------------------------------------------
1 | :80
2 |
3 | root * /app
4 | uri replace /assets/ /services/ui/src/assets/
5 | file_server * {
6 | hide .git
7 | }
8 | php_fastcgi localhost:9000
9 | encode gzip
10 |
--------------------------------------------------------------------------------
/examples/images/caddy.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM caddy AS base
2 | ARG SERVICE
3 | COPY services/$SERVICE /app/services/$SERVICE
4 | COPY index.php /app/index.php
5 | COPY global-config.php /app/global-config.php
6 | COPY images/Caddyfile /etc/caddy/Caddyfile
7 |
--------------------------------------------------------------------------------
/examples/images/service.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:8.0-fpm AS base
2 | ENV VERSION=1
3 | COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
4 | RUN apt-get update && apt-get install -y wget git unzip && apt-get clean && rm -rf /var/cache/apt/lists
5 | RUN install-php-extensions curl intl zip sodium opcache apcu @composer && mkdir -p /app
6 | WORKDIR /app
7 |
8 | FROM base AS vendor
9 | COPY composer.json composer.json
10 | COPY composer.lock composer.lock
11 | RUN composer install --no-dev -o -n
12 |
13 | FROM base AS config
14 | ARG SERVICE
15 | ENV SERVICE=$SERVICE
16 | COPY services/$SERVICE services/$SERVICE
17 | COPY --from=vendor /app/vendor vendor
18 | COPY index.php index.php
19 | COPY global-config.php global-config.php
20 |
21 | FROM config AS production
22 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
23 | ENV PHP_CLI_SERVER_WORKERS=100
24 | COPY images/opcache.ini /tmp/opcache.ini
25 | COPY images/fpm.conf /usr/local/etc/php-fpm.d/www.conf
26 | RUN cat /tmp/opcache.ini >> $PHP_INI_DIR/php.ini
27 |
28 | FROM production AS development
29 | ARG SERVICE
30 | COPY images/xdebug.ini /tmp/xdebug.ini
31 | ENV DBGP_IDEKEY=$SERVICE
32 | RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" && \
33 | cd $(php -d 'display_errors=stderr' -r 'echo ini_get("extension_dir");') && \
34 | mv /php-disabled/xdebug.so . && mv /php-disabled/docker-php-ext-xdebug.ini $PHP_INI_DIR/conf.d/docker-php-ext-xdebug.ini
35 | RUN cat /tmp/xdebug.ini >> $PHP_INI_DIR/php.ini
36 | COPY images/opcache.ini /tmp/opcache.ini
37 | RUN cat /tmp/opcache.ini >> $PHP_INI_DIR/php.ini
38 |
--------------------------------------------------------------------------------
/examples/images/xdebug.ini:
--------------------------------------------------------------------------------
1 | [xdebug]
2 | ;xdebug.discover_client_host = true
3 | xdebug.mode = develop,debug
4 | xdebug.log = /tmp/xdebug.log
5 | xdebug.log_level = 0
6 | xdebug.client_host = host.docker.internal
7 | xdebug.start_with_request = true
8 |
--------------------------------------------------------------------------------
/examples/laravel/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.{yml,yaml}]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/examples/laravel/.env:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=base64:jJfpP0ZYEiP46qjg6yZs51xj8iYqmRmf9plZ6ppmOAI=
4 | APP_DEBUG=true
5 | APP_URL=http://example-app.test
6 |
7 | LOG_CHANNEL=stack
8 | LOG_LEVEL=debug
9 |
10 | DB_CONNECTION=mysql
11 | DB_HOST=mysql
12 | DB_PORT=3306
13 | DB_DATABASE=example_app
14 | DB_USERNAME=sail
15 | DB_PASSWORD=password
16 |
17 | BROADCAST_DRIVER=log
18 | CACHE_DRIVER=file
19 | QUEUE_CONNECTION=sync
20 | SESSION_DRIVER=file
21 | SESSION_LIFETIME=120
22 |
23 | MEMCACHED_HOST=memcached
24 |
25 | REDIS_HOST=redis
26 | REDIS_PASSWORD=null
27 | REDIS_PORT=6379
28 |
29 | MAIL_MAILER=smtp
30 | MAIL_HOST=mailhog
31 | MAIL_PORT=1025
32 | MAIL_USERNAME=null
33 | MAIL_PASSWORD=null
34 | MAIL_ENCRYPTION=null
35 | MAIL_FROM_ADDRESS=null
36 | MAIL_FROM_NAME="${APP_NAME}"
37 |
38 | AWS_ACCESS_KEY_ID=
39 | AWS_SECRET_ACCESS_KEY=
40 | AWS_DEFAULT_REGION=us-east-1
41 | AWS_BUCKET=
42 |
43 | PUSHER_APP_ID=
44 | PUSHER_APP_KEY=
45 | PUSHER_APP_SECRET=
46 | PUSHER_APP_CLUSTER=mt1
47 |
48 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
49 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
50 |
51 | SCOUT_DRIVER=meilisearch
52 | MEILISEARCH_HOST=http://meilisearch:7700
53 |
--------------------------------------------------------------------------------
/examples/laravel/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_URL=http://localhost
6 |
7 | LOG_CHANNEL=stack
8 | LOG_LEVEL=debug
9 |
10 | DB_CONNECTION=mysql
11 | DB_HOST=127.0.0.1
12 | DB_PORT=3306
13 | DB_DATABASE=example_app
14 | DB_USERNAME=root
15 | DB_PASSWORD=
16 |
17 | BROADCAST_DRIVER=log
18 | CACHE_DRIVER=file
19 | QUEUE_CONNECTION=sync
20 | SESSION_DRIVER=file
21 | SESSION_LIFETIME=120
22 |
23 | MEMCACHED_HOST=127.0.0.1
24 |
25 | REDIS_HOST=127.0.0.1
26 | REDIS_PASSWORD=null
27 | REDIS_PORT=6379
28 |
29 | MAIL_MAILER=smtp
30 | MAIL_HOST=mailhog
31 | MAIL_PORT=1025
32 | MAIL_USERNAME=null
33 | MAIL_PASSWORD=null
34 | MAIL_ENCRYPTION=null
35 | MAIL_FROM_ADDRESS=null
36 | MAIL_FROM_NAME="${APP_NAME}"
37 |
38 | AWS_ACCESS_KEY_ID=
39 | AWS_SECRET_ACCESS_KEY=
40 | AWS_DEFAULT_REGION=us-east-1
41 | AWS_BUCKET=
42 |
43 | PUSHER_APP_ID=
44 | PUSHER_APP_KEY=
45 | PUSHER_APP_SECRET=
46 | PUSHER_APP_CLUSTER=mt1
47 |
48 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
49 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
50 |
--------------------------------------------------------------------------------
/examples/laravel/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.css linguist-vendored
3 | *.scss linguist-vendored
4 | *.js linguist-vendored
5 | CHANGELOG.md export-ignore
6 |
--------------------------------------------------------------------------------
/examples/laravel/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /public/hot
3 | /public/storage
4 | /storage/*.key
5 | /vendor
6 | .env.backup
7 | .phpunit.result.cache
8 | docker-compose.override.yml
9 | Homestead.json
10 | Homestead.yaml
11 | npm-debug.log
12 | yarn-error.log
13 | .idea
14 |
--------------------------------------------------------------------------------
/examples/laravel/.styleci.yml:
--------------------------------------------------------------------------------
1 | php:
2 | preset: laravel
3 | disabled:
4 | - no_unused_imports
5 | finder:
6 | not-name:
7 | - index.php
8 | - server.php
9 | js:
10 | finder:
11 | not-name:
12 | - webpack.mix.js
13 | css: true
14 |
--------------------------------------------------------------------------------
/examples/laravel/README.md:
--------------------------------------------------------------------------------
1 | # Laravel Example and Dapr
2 |
3 | This shows how to use Laravel as a client and as a service. If you take a peek into
4 | the [docker-compose](./docker-compose.yml) file, you'll see the Laravel Sail app is repeated twice. This is because
5 | Laravel Sail is a single-threaded server, so it's not possible to invoke itself. So the `laravel.test` app runs as a
6 | front-end client and the `laravel.api` runs as an API server; but they're both the same code and configuration.
7 |
8 | ## Running the example
9 |
10 | 1. Install PHP 8.0+
11 | 1. Run `composer install`
12 | 1. Run `DAPR_VERSION=1.2.0 ./vendor/bin/sail up` or whichever version of a dapr sidecar is most current
13 | 2. Go to [http://localhost](http://localhost)
14 | 3. Go to [http://localhost/welcome/name](http://localhost/welcome/name) changing the `name` part of the uri to a
15 | different string
16 | 4. Visit [http://localhost](http://localhost) to see the name
17 |
18 | # What's Included
19 |
20 | 1. A [state store component](./components/statestore.yaml) with prefixes turned off to share state between the two apps
21 | 2. A [simple dapr provider](./app/Providers/DaprServiceProvider.php) which configures the client and serializers
22 | 3. A [slightly modified view](./resources/views/welcome.blade.php) which displays the current state
23 | 4. An [api route](./routes/api.php) to show the API implementation that updates the name state
24 | 4. A [front-end route](./routes/web.php) to show how to update state and invoke other services
25 | 5. A [docker-compose.yml](./docker-compose.yml) file which shows how to configure `daprd` for testing
26 | 6. A [.env](./.env) file which works with Laravel Sail
27 |
28 | # Note
29 |
30 | It's not recommended to use Laravel as an Actor host.
31 |
--------------------------------------------------------------------------------
/examples/laravel/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 | command('inspire')->hourly();
28 | }
29 |
30 | /**
31 | * Register the commands for the application.
32 | *
33 | * @return void
34 | */
35 | protected function commands()
36 | {
37 | $this->load(__DIR__.'/Commands');
38 |
39 | require base_path('routes/console.php');
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/laravel/app/Exceptions/Handler.php:
--------------------------------------------------------------------------------
1 | reportable(function (Throwable $e) {
38 | //
39 | });
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/laravel/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | [
33 | \App\Http\Middleware\EncryptCookies::class,
34 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
35 | \Illuminate\Session\Middleware\StartSession::class,
36 | // \Illuminate\Session\Middleware\AuthenticateSession::class,
37 | \Illuminate\View\Middleware\ShareErrorsFromSession::class,
38 | \App\Http\Middleware\VerifyCsrfToken::class,
39 | \Illuminate\Routing\Middleware\SubstituteBindings::class,
40 | ],
41 |
42 | 'api' => [
43 | 'throttle:api',
44 | \Illuminate\Routing\Middleware\SubstituteBindings::class,
45 | ],
46 | ];
47 |
48 | /**
49 | * The application's route middleware.
50 | *
51 | * These middleware may be assigned to groups or used individually.
52 | *
53 | * @var array
54 | */
55 | protected $routeMiddleware = [
56 | 'auth' => \App\Http\Middleware\Authenticate::class,
57 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
58 | 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
59 | 'can' => \Illuminate\Auth\Middleware\Authorize::class,
60 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
61 | 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
62 | 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
63 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
64 | 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
65 | ];
66 | }
67 |
--------------------------------------------------------------------------------
/examples/laravel/app/Http/Middleware/Authenticate.php:
--------------------------------------------------------------------------------
1 | expectsJson()) {
18 | return route('login');
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/laravel/app/Http/Middleware/EncryptCookies.php:
--------------------------------------------------------------------------------
1 | check()) {
26 | return redirect(RouteServiceProvider::HOME);
27 | }
28 | }
29 |
30 | return $next($request);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/laravel/app/Http/Middleware/TrimStrings.php:
--------------------------------------------------------------------------------
1 | allSubdomainsOfApplicationUrl(),
18 | ];
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/laravel/app/Http/Middleware/TrustProxies.php:
--------------------------------------------------------------------------------
1 | 'datetime',
42 | ];
43 | }
44 |
--------------------------------------------------------------------------------
/examples/laravel/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | 'App\Policies\ModelPolicy',
17 | ];
18 |
19 | /**
20 | * Register any authentication / authorization services.
21 | *
22 | * @return void
23 | */
24 | public function boot()
25 | {
26 | $this->registerPolicies();
27 |
28 | //
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/laravel/app/Providers/BroadcastServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton(
17 | DaprClient::class,
18 | fn(ContainerInterface $container) => DaprClient::clientBuilder()->useHttpClient(
19 | "http://localhost:".config('dapr.port', '3500')
20 | )->withLogger($container->get(LoggerInterface::class))->withSerializationConfig(
21 | $container->get(SerializationConfig::class)
22 | )->withDeserializationConfig($container->get(DeserializationConfig::class))->build()
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/examples/laravel/app/Providers/EventServiceProvider.php:
--------------------------------------------------------------------------------
1 | [
19 | SendEmailVerificationNotification::class,
20 | ],
21 | ];
22 |
23 | /**
24 | * Register any events for your application.
25 | *
26 | * @return void
27 | */
28 | public function boot()
29 | {
30 | //
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/laravel/app/Providers/RouteServiceProvider.php:
--------------------------------------------------------------------------------
1 | configureRateLimiting();
39 |
40 | $this->routes(function () {
41 | Route::prefix('api')
42 | ->middleware('api')
43 | ->namespace($this->namespace)
44 | ->group(base_path('routes/api.php'));
45 |
46 | Route::middleware('web')
47 | ->namespace($this->namespace)
48 | ->group(base_path('routes/web.php'));
49 | });
50 | }
51 |
52 | /**
53 | * Configure the rate limiters for the application.
54 | *
55 | * @return void
56 | */
57 | protected function configureRateLimiting()
58 | {
59 | RateLimiter::for('api', function (Request $request) {
60 | return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
61 | });
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/examples/laravel/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(Illuminate\Contracts\Console\Kernel::class);
34 |
35 | $status = $kernel->handle(
36 | $input = new Symfony\Component\Console\Input\ArgvInput,
37 | new Symfony\Component\Console\Output\ConsoleOutput
38 | );
39 |
40 | /*
41 | |--------------------------------------------------------------------------
42 | | Shutdown The Application
43 | |--------------------------------------------------------------------------
44 | |
45 | | Once Artisan has finished running, we will fire off the shutdown events
46 | | so that any final work may be done by the application before we shut
47 | | down the process. This is the last thing to happen to the request.
48 | |
49 | */
50 |
51 | $kernel->terminate($input, $status);
52 |
53 | exit($status);
54 |
--------------------------------------------------------------------------------
/examples/laravel/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | singleton(
30 | Illuminate\Contracts\Http\Kernel::class,
31 | App\Http\Kernel::class
32 | );
33 |
34 | $app->singleton(
35 | Illuminate\Contracts\Console\Kernel::class,
36 | App\Console\Kernel::class
37 | );
38 |
39 | $app->singleton(
40 | Illuminate\Contracts\Debug\ExceptionHandler::class,
41 | App\Exceptions\Handler::class
42 | );
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Return The Application
47 | |--------------------------------------------------------------------------
48 | |
49 | | This script returns the application instance. The instance is given to
50 | | the calling script so we can separate the building of the instances
51 | | from the actual running of the application and sending responses.
52 | |
53 | */
54 |
55 | return $app;
56 |
--------------------------------------------------------------------------------
/examples/laravel/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/examples/laravel/components/pubsub.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: dapr.io/v1alpha1
2 | kind: Component
3 | metadata:
4 | name: pubsub
5 | spec:
6 | type: pubsub.redis
7 | metadata:
8 | - name: redisHost
9 | value: redis:6379
10 | - name: redisPassword
11 | value: ""
12 |
--------------------------------------------------------------------------------
/examples/laravel/components/statestore.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: dapr.io/v1alpha1
2 | kind: Component
3 | metadata:
4 | name: statestore
5 | spec:
6 | type: state.redis
7 | metadata:
8 | - name: redisHost
9 | value: redis:6379
10 | - name: redisPassword
11 | value: ""
12 | - name: actorStateStore
13 | value: "true"
14 | - name: keyPrefix
15 | value: "none"
16 |
--------------------------------------------------------------------------------
/examples/laravel/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laravel/laravel",
3 | "type": "project",
4 | "description": "The Laravel Framework.",
5 | "keywords": ["framework", "laravel"],
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.0",
9 | "dapr/php-sdk": "^1.0",
10 | "fideloper/proxy": "^4.4",
11 | "fruitcake/laravel-cors": "^2.0",
12 | "guzzlehttp/guzzle": "^7.0.1",
13 | "laravel/framework": "^8.12",
14 | "laravel/tinker": "^2.5"
15 | },
16 | "require-dev": {
17 | "facade/ignition": "^2.5",
18 | "fakerphp/faker": "^1.9.1",
19 | "laravel/sail": "^1.0.1",
20 | "mockery/mockery": "^1.4.2",
21 | "nunomaduro/collision": "^5.0",
22 | "phpunit/phpunit": "^9.3.3"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "App\\": "app/",
27 | "Database\\Factories\\": "database/factories/",
28 | "Database\\Seeders\\": "database/seeders/"
29 | }
30 | },
31 | "autoload-dev": {
32 | "psr-4": {
33 | "Tests\\": "tests/"
34 | }
35 | },
36 | "scripts": {
37 | "post-autoload-dump": [
38 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
39 | "@php artisan package:discover --ansi"
40 | ],
41 | "post-root-package-install": [
42 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
43 | ],
44 | "post-create-project-cmd": [
45 | "@php artisan key:generate --ansi"
46 | ]
47 | },
48 | "extra": {
49 | "laravel": {
50 | "dont-discover": []
51 | }
52 | },
53 | "config": {
54 | "optimize-autoloader": true,
55 | "preferred-install": "dist",
56 | "sort-packages": true
57 | },
58 | "minimum-stability": "dev",
59 | "prefer-stable": true
60 | }
61 |
--------------------------------------------------------------------------------
/examples/laravel/config/broadcasting.php:
--------------------------------------------------------------------------------
1 | env('BROADCAST_DRIVER', 'null'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Broadcast Connections
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may define all of the broadcast connections that will be used
26 | | to broadcast events to other systems or over websockets. Samples of
27 | | each available type of connection are provided inside this array.
28 | |
29 | */
30 |
31 | 'connections' => [
32 |
33 | 'pusher' => [
34 | 'driver' => 'pusher',
35 | 'key' => env('PUSHER_APP_KEY'),
36 | 'secret' => env('PUSHER_APP_SECRET'),
37 | 'app_id' => env('PUSHER_APP_ID'),
38 | 'options' => [
39 | 'cluster' => env('PUSHER_APP_CLUSTER'),
40 | 'useTLS' => true,
41 | ],
42 | ],
43 |
44 | 'ably' => [
45 | 'driver' => 'ably',
46 | 'key' => env('ABLY_KEY'),
47 | ],
48 |
49 | 'redis' => [
50 | 'driver' => 'redis',
51 | 'connection' => 'default',
52 | ],
53 |
54 | 'log' => [
55 | 'driver' => 'log',
56 | ],
57 |
58 | 'null' => [
59 | 'driver' => 'null',
60 | ],
61 |
62 | ],
63 |
64 | ];
65 |
--------------------------------------------------------------------------------
/examples/laravel/config/cors.php:
--------------------------------------------------------------------------------
1 | ['api/*', 'sanctum/csrf-cookie'],
19 |
20 | 'allowed_methods' => ['*'],
21 |
22 | 'allowed_origins' => ['*'],
23 |
24 | 'allowed_origins_patterns' => [],
25 |
26 | 'allowed_headers' => ['*'],
27 |
28 | 'exposed_headers' => [],
29 |
30 | 'max_age' => 0,
31 |
32 | 'supports_credentials' => false,
33 |
34 | ];
35 |
--------------------------------------------------------------------------------
/examples/laravel/config/filesystems.php:
--------------------------------------------------------------------------------
1 | env('FILESYSTEM_DRIVER', 'local'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Filesystem Disks
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may configure as many filesystem "disks" as you wish, and you
24 | | may even configure multiple disks of the same driver. Defaults have
25 | | been setup for each driver as an example of the required options.
26 | |
27 | | Supported Drivers: "local", "ftp", "sftp", "s3"
28 | |
29 | */
30 |
31 | 'disks' => [
32 |
33 | 'local' => [
34 | 'driver' => 'local',
35 | 'root' => storage_path('app'),
36 | ],
37 |
38 | 'public' => [
39 | 'driver' => 'local',
40 | 'root' => storage_path('app/public'),
41 | 'url' => env('APP_URL').'/storage',
42 | 'visibility' => 'public',
43 | ],
44 |
45 | 's3' => [
46 | 'driver' => 's3',
47 | 'key' => env('AWS_ACCESS_KEY_ID'),
48 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
49 | 'region' => env('AWS_DEFAULT_REGION'),
50 | 'bucket' => env('AWS_BUCKET'),
51 | 'url' => env('AWS_URL'),
52 | 'endpoint' => env('AWS_ENDPOINT'),
53 | ],
54 |
55 | ],
56 |
57 | /*
58 | |--------------------------------------------------------------------------
59 | | Symbolic Links
60 | |--------------------------------------------------------------------------
61 | |
62 | | Here you may configure the symbolic links that will be created when the
63 | | `storage:link` Artisan command is executed. The array keys should be
64 | | the locations of the links and the values should be their targets.
65 | |
66 | */
67 |
68 | 'links' => [
69 | public_path('storage') => storage_path('app/public'),
70 | ],
71 |
72 | ];
73 |
--------------------------------------------------------------------------------
/examples/laravel/config/hashing.php:
--------------------------------------------------------------------------------
1 | 'bcrypt',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Bcrypt Options
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may specify the configuration options that should be used when
26 | | passwords are hashed using the Bcrypt algorithm. This will allow you
27 | | to control the amount of time it takes to hash the given password.
28 | |
29 | */
30 |
31 | 'bcrypt' => [
32 | 'rounds' => env('BCRYPT_ROUNDS', 10),
33 | ],
34 |
35 | /*
36 | |--------------------------------------------------------------------------
37 | | Argon Options
38 | |--------------------------------------------------------------------------
39 | |
40 | | Here you may specify the configuration options that should be used when
41 | | passwords are hashed using the Argon algorithm. These will allow you
42 | | to control the amount of time it takes to hash the given password.
43 | |
44 | */
45 |
46 | 'argon' => [
47 | 'memory' => 1024,
48 | 'threads' => 2,
49 | 'time' => 2,
50 | ],
51 |
52 | ];
53 |
--------------------------------------------------------------------------------
/examples/laravel/config/queue.php:
--------------------------------------------------------------------------------
1 | env('QUEUE_CONNECTION', 'sync'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Queue Connections
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may configure the connection information for each server that
24 | | is used by your application. A default configuration has been added
25 | | for each back-end shipped with Laravel. You are free to add more.
26 | |
27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
28 | |
29 | */
30 |
31 | 'connections' => [
32 |
33 | 'sync' => [
34 | 'driver' => 'sync',
35 | ],
36 |
37 | 'database' => [
38 | 'driver' => 'database',
39 | 'table' => 'jobs',
40 | 'queue' => 'default',
41 | 'retry_after' => 90,
42 | 'after_commit' => false,
43 | ],
44 |
45 | 'beanstalkd' => [
46 | 'driver' => 'beanstalkd',
47 | 'host' => 'localhost',
48 | 'queue' => 'default',
49 | 'retry_after' => 90,
50 | 'block_for' => 0,
51 | 'after_commit' => false,
52 | ],
53 |
54 | 'sqs' => [
55 | 'driver' => 'sqs',
56 | 'key' => env('AWS_ACCESS_KEY_ID'),
57 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
58 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
59 | 'queue' => env('SQS_QUEUE', 'default'),
60 | 'suffix' => env('SQS_SUFFIX'),
61 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
62 | 'after_commit' => false,
63 | ],
64 |
65 | 'redis' => [
66 | 'driver' => 'redis',
67 | 'connection' => 'default',
68 | 'queue' => env('REDIS_QUEUE', 'default'),
69 | 'retry_after' => 90,
70 | 'block_for' => null,
71 | 'after_commit' => false,
72 | ],
73 |
74 | ],
75 |
76 | /*
77 | |--------------------------------------------------------------------------
78 | | Failed Queue Jobs
79 | |--------------------------------------------------------------------------
80 | |
81 | | These options configure the behavior of failed queue job logging so you
82 | | can control which database and table are used to store the jobs that
83 | | have failed. You may change them to any database / table you wish.
84 | |
85 | */
86 |
87 | 'failed' => [
88 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
89 | 'database' => env('DB_CONNECTION', 'mysql'),
90 | 'table' => 'failed_jobs',
91 | ],
92 |
93 | ];
94 |
--------------------------------------------------------------------------------
/examples/laravel/config/services.php:
--------------------------------------------------------------------------------
1 | [
18 | 'domain' => env('MAILGUN_DOMAIN'),
19 | 'secret' => env('MAILGUN_SECRET'),
20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
21 | ],
22 |
23 | 'postmark' => [
24 | 'token' => env('POSTMARK_TOKEN'),
25 | ],
26 |
27 | 'ses' => [
28 | 'key' => env('AWS_ACCESS_KEY_ID'),
29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
31 | ],
32 |
33 | ];
34 |
--------------------------------------------------------------------------------
/examples/laravel/config/view.php:
--------------------------------------------------------------------------------
1 | [
17 | resource_path('views'),
18 | ],
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Compiled View Path
23 | |--------------------------------------------------------------------------
24 | |
25 | | This option determines where all the compiled Blade templates will be
26 | | stored for your application. Typically, this is within the storage
27 | | directory. However, as usual, you are free to change this value.
28 | |
29 | */
30 |
31 | 'compiled' => env(
32 | 'VIEW_COMPILED_PATH',
33 | realpath(storage_path('framework/views'))
34 | ),
35 |
36 | ];
37 |
--------------------------------------------------------------------------------
/examples/laravel/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite
2 | *.sqlite-journal
3 |
--------------------------------------------------------------------------------
/examples/laravel/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->name,
27 | 'email' => $this->faker->unique()->safeEmail,
28 | 'email_verified_at' => now(),
29 | 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
30 | 'remember_token' => Str::random(10),
31 | ];
32 | }
33 |
34 | /**
35 | * Indicate that the model's email address should be unverified.
36 | *
37 | * @return \Illuminate\Database\Eloquent\Factories\Factory
38 | */
39 | public function unverified()
40 | {
41 | return $this->state(function (array $attributes) {
42 | return [
43 | 'email_verified_at' => null,
44 | ];
45 | });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/examples/laravel/database/migrations/2014_10_12_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->string('email')->unique();
20 | $table->timestamp('email_verified_at')->nullable();
21 | $table->string('password');
22 | $table->rememberToken();
23 | $table->timestamps();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down()
33 | {
34 | Schema::dropIfExists('users');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/laravel/database/migrations/2014_10_12_100000_create_password_resets_table.php:
--------------------------------------------------------------------------------
1 | string('email')->index();
18 | $table->string('token');
19 | $table->timestamp('created_at')->nullable();
20 | });
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | *
26 | * @return void
27 | */
28 | public function down()
29 | {
30 | Schema::dropIfExists('password_resets');
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/laravel/database/migrations/2019_08_19_000000_create_failed_jobs_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('uuid')->unique();
19 | $table->text('connection');
20 | $table->text('queue');
21 | $table->longText('payload');
22 | $table->longText('exception');
23 | $table->timestamp('failed_at')->useCurrent();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down()
33 | {
34 | Schema::dropIfExists('failed_jobs');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/laravel/database/seeders/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | create();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/laravel/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "dev": "npm run development",
5 | "development": "mix",
6 | "watch": "mix watch",
7 | "watch-poll": "mix watch -- --watch-options-poll=1000",
8 | "hot": "mix watch --hot",
9 | "prod": "npm run production",
10 | "production": "mix --production"
11 | },
12 | "devDependencies": {
13 | "axios": "^0.21",
14 | "laravel-mix": "^6.0.6",
15 | "lodash": "^4.17.19",
16 | "postcss": "^8.1.14"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/laravel/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | ./tests/Unit
10 |
11 |
12 | ./tests/Feature
13 |
14 |
15 |
16 |
17 | ./app
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/examples/laravel/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dapr/php-sdk/f0f37c39c7b95b6402c617bdfb6743b0a3b8c8cf/examples/laravel/public/favicon.ico
--------------------------------------------------------------------------------
/examples/laravel/public/index.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class);
50 |
51 | $response = tap($kernel->handle(
52 | $request = Request::capture()
53 | ))->send();
54 |
55 | $kernel->terminate($request, $response);
56 |
--------------------------------------------------------------------------------
/examples/laravel/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/examples/laravel/public/web.config:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/laravel/resources/css/app.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dapr/php-sdk/f0f37c39c7b95b6402c617bdfb6743b0a3b8c8cf/examples/laravel/resources/css/app.css
--------------------------------------------------------------------------------
/examples/laravel/resources/js/app.js:
--------------------------------------------------------------------------------
1 | require('./bootstrap');
2 |
--------------------------------------------------------------------------------
/examples/laravel/resources/js/bootstrap.js:
--------------------------------------------------------------------------------
1 | window._ = require('lodash');
2 |
3 | /**
4 | * We'll load the axios HTTP library which allows us to easily issue requests
5 | * to our Laravel back-end. This library automatically handles sending the
6 | * CSRF token as a header based on the value of the "XSRF" token cookie.
7 | */
8 |
9 | window.axios = require('axios');
10 |
11 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
12 |
13 | /**
14 | * Echo exposes an expressive API for subscribing to channels and listening
15 | * for events that are broadcast by Laravel. Echo and event broadcasting
16 | * allows your team to easily build robust real-time web applications.
17 | */
18 |
19 | // import Echo from 'laravel-echo';
20 |
21 | // window.Pusher = require('pusher-js');
22 |
23 | // window.Echo = new Echo({
24 | // broadcaster: 'pusher',
25 | // key: process.env.MIX_PUSHER_APP_KEY,
26 | // cluster: process.env.MIX_PUSHER_APP_CLUSTER,
27 | // forceTLS: true
28 | // });
29 |
--------------------------------------------------------------------------------
/examples/laravel/resources/lang/en/auth.php:
--------------------------------------------------------------------------------
1 | 'These credentials do not match our records.',
17 | 'password' => 'The provided password is incorrect.',
18 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
19 |
20 | ];
21 |
--------------------------------------------------------------------------------
/examples/laravel/resources/lang/en/pagination.php:
--------------------------------------------------------------------------------
1 | '« Previous',
17 | 'next' => 'Next »',
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/examples/laravel/resources/lang/en/passwords.php:
--------------------------------------------------------------------------------
1 | 'Your password has been reset!',
17 | 'sent' => 'We have emailed your password reset link!',
18 | 'throttled' => 'Please wait before retrying.',
19 | 'token' => 'This password reset token is invalid.',
20 | 'user' => "We can't find a user with that email address.",
21 |
22 | ];
23 |
--------------------------------------------------------------------------------
/examples/laravel/routes/api.php:
--------------------------------------------------------------------------------
1 | get('/user', function (Request $request) {
18 | return $request->user();
19 | });
20 |
21 | Route::get('/invoke-example/{name}', function(string $name, Request $request, \Dapr\State\StateManager $stateManager, \Psr\Log\LoggerInterface $logger) {
22 | $state = new \App\Models\ExampleState();
23 | $stateManager->load_object($state);
24 | $state->last_name_seen = $name;
25 | $logger->critical('Setting last seen name to {name}', ['name' => $name]);
26 | $stateManager->save_object($state);
27 | return ['setName' => $name];
28 | });
29 |
--------------------------------------------------------------------------------
/examples/laravel/routes/channels.php:
--------------------------------------------------------------------------------
1 | id === (int) $id;
18 | });
19 |
--------------------------------------------------------------------------------
/examples/laravel/routes/console.php:
--------------------------------------------------------------------------------
1 | comment(Inspiring::quote());
19 | })->purpose('Display an inspiring quote');
20 |
--------------------------------------------------------------------------------
/examples/laravel/routes/web.php:
--------------------------------------------------------------------------------
1 | load_object($state);
21 | $state->page_views += 1;
22 | $stateManager->save_object($state);
23 |
24 | return view('welcome', ['page_views' => $state->page_views, 'last_name_seen' => $state->last_name_seen]);
25 | }
26 | );
27 |
28 | Route::get(
29 | '/welcome/{name}',
30 | function (string $name, \Dapr\Client\DaprClient $client) {
31 | // todo: no security
32 | return $client->invokeMethod('GET', new \Dapr\Client\AppId('api'), "api/invoke-example/$name")
33 | ->getBody()
34 | ->getContents();
35 | }
36 | );
37 |
--------------------------------------------------------------------------------
/examples/laravel/server.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | $uri = urldecode(
11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
12 | );
13 |
14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the
15 | // built-in PHP web server. This provides a convenient way to test a Laravel
16 | // application without having installed a "real" web server software here.
17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
18 | return false;
19 | }
20 |
21 | require_once __DIR__.'/public/index.php';
22 |
--------------------------------------------------------------------------------
/examples/laravel/start.php:
--------------------------------------------------------------------------------
1 | 'http://localhost',
8 | 'page-views' => '1',
9 | 'name' => 'go to /welcome/{name} to see a name here',
10 | ],
11 | [
12 | 'url' => 'http://localhost/welcome/test',
13 | 'page-views' => null,
14 | 'name' => null,
15 | ],
16 | [
17 | 'url' => 'http://localhost',
18 | 'page-views' => '2',
19 | 'name' => 'test',
20 | ],
21 | ];
22 |
23 | $failed = false;
24 |
25 | foreach ($urls as ['url' => $url, 'page-views' => $expected_page_views, 'name' => $expected_name]) {
26 | echo "Calling $url:\n";
27 | $result = `curl -s $url`;
28 |
29 | if ( !empty($expected_page_views)) {
30 | echo "Checking page views are $expected_page_views: ";
31 | $view_pos = strpos($result, 'views: ');
32 | $views = explode(' ', substr($result, $view_pos, strlen('views: X')))[1] ?? null;
33 | if ($views === $expected_page_views) {
34 | echo "PASS\n";
35 | } else {
36 | echo "FAILED\n";
37 | echo $result;
38 | $failed = true;
39 | }
40 | }
41 |
42 | if ( !empty($expected_name)) {
43 | echo "Checking name matches '$expected_name': ";
44 | $name_pos = strpos($result, 'name: ');
45 | $name = explode(' ', substr($result, $name_pos, strlen('name: ') + strlen($expected_name)), 2)[1] ?? null;
46 | if ($name === $expected_name) {
47 | echo "PASS\n";
48 | } else {
49 | echo "FAILED\n";
50 | echo $result;
51 | $failed = true;
52 | }
53 | }
54 | }
55 |
56 | exit($failed ? 1 : 0);
57 |
--------------------------------------------------------------------------------
/examples/laravel/tests/CreatesApplication.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class)->bootstrap();
19 |
20 | return $app;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/laravel/tests/Feature/ExampleTest.php:
--------------------------------------------------------------------------------
1 | get('/');
18 |
19 | $response->assertStatus(200);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/laravel/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/laravel/webpack.mix.js:
--------------------------------------------------------------------------------
1 | const mix = require('laravel-mix');
2 |
3 | /*
4 | |--------------------------------------------------------------------------
5 | | Mix Asset Management
6 | |--------------------------------------------------------------------------
7 | |
8 | | Mix provides a clean, fluent API for defining some Webpack build steps
9 | | for your Laravel applications. By default, we are compiling the CSS
10 | | file for the application as well as bundling up all the JS files.
11 | |
12 | */
13 |
14 | mix.js('resources/js/app.js', 'public/js')
15 | .postCss('resources/css/app.css', 'public/css', [
16 | //
17 | ]);
18 |
--------------------------------------------------------------------------------
/examples/secrets/.dockerignore:
--------------------------------------------------------------------------------
1 | vendor/
2 |
--------------------------------------------------------------------------------
/examples/secrets/.env:
--------------------------------------------------------------------------------
1 | DAPR_VERSION=1.2.0
2 |
--------------------------------------------------------------------------------
/examples/secrets/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 |
--------------------------------------------------------------------------------
/examples/secrets/Makefile:
--------------------------------------------------------------------------------
1 | SHELL := /bin/bash
2 |
3 | export GIT_SHA=$(shell git rev-parse HEAD)
4 | include .env
5 | export $(shell sed 's/=.*//' .env)
6 |
7 | .PHONY: deps
8 | deps: php-secrets-service php-secrets-http docker-compose.yml php-secret-client-http php-secret-client-service
9 |
10 | .PHONY: php-secrets-service
11 | php-secrets-service: images/fpm.conf images/opcache.ini
12 | docker build -f ../images/service.Dockerfile -t php-secrets-service:$(GIT_SHA) --pull --build-arg SERVICE=secrets --target production .
13 |
14 | .PHONY: php-secrets-http
15 | php-secrets-http: images/Caddyfile
16 | docker build -f ../images/caddy.Dockerfile -t php-secrets-http:$(GIT_SHA) --pull --build-arg SERVICE=secrets --target base .
17 |
18 | .PHONY: php-secret-client-service
19 | php-secret-client-service: images/fpm.conf images/opcache.ini
20 | docker build -f ../images/service.Dockerfile -t php-secret-client-service:$(GIT_SHA) --pull --build-arg SERVICE=client --target production .
21 |
22 | .PHONY: php-secret-client-http
23 | php-secret-client-http: images/Caddyfile
24 | docker build -f ../images/caddy.Dockerfile -t php-secret-client-http:$(GIT_SHA) --pull --build-arg SERVICE=client --target base .
25 |
26 | .PHONY: start
27 | start:
28 | DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker-compose -f docker-compose.yml -f ../docker-compose.common.yml up -d
29 |
30 | .PHONY: stop
31 | stop:
32 | docker-compose -f docker-compose.yml -f ../docker-compose.common.yml stop
33 |
34 | .PHONY: logs
35 | logs:
36 | docker-compose -f docker-compose.yml -f ../docker-compose.common.yml logs
37 |
38 | .PHONY: clean
39 | clean: stop
40 | docker-compose -f docker-compose.yml -f ../docker-compose.common.yml rm -f
41 | rm -rf images
42 |
43 | .PHONY: push
44 | push: deps
45 | docker-compose push
46 |
47 | .PHONY: deploy
48 | deploy:
49 | kubectl apply -f deployments/simple-secret.yml
50 | kubectl apply -f deployments/some-secrets.yml
51 | envsubst < deployments/secret-service.yml | kubectl apply -f -
52 | envsubst < deployments/client-service.yml | kubectl apply -f -
53 |
54 | .PHONY: deploy-delete
55 | deploy-delete:
56 | kubectl delete -f deployments/simple-secret.yml
57 | kubectl delete -f deployments/some-secrets.yml
58 | envsubst < deployments/secret-service.yml | kubectl delete -f -
59 | envsubst < deployments/client-service.yml | kubectl delete -f -
60 |
61 | images/fpm.conf: ../images/fpm.conf
62 | mkdir -p images
63 | cp ../images/fpm.conf images/fpm.conf
64 | touch images/fpm.conf
65 |
66 | images/opcache.ini: ../images/opcache.ini
67 | mkdir -p images
68 | cp ../images/opcache.ini images/opcache.ini
69 | touch images/opcache.ini
70 |
71 | images/xdebug.ini: ../images/xdebug.ini
72 | mkdir -p images
73 | cp ../images/xdebug.ini images/xdebug.ini
74 | touch images/xdebug.ini
75 |
76 | images/Caddyfile: ../images/Caddyfile
77 | mkdir -p images
78 | cp ../images/Caddyfile images/Caddyfile
79 | touch images/Caddyfile
80 |
--------------------------------------------------------------------------------
/examples/secrets/components/file-secrets.yml:
--------------------------------------------------------------------------------
1 | apiVersion: dapr.io/v1alpha1
2 | kind: Component
3 | metadata:
4 | name: file-secrets
5 | namespace: secret-example
6 | spec:
7 | type: secretstores.local.file
8 | version: v1
9 | metadata:
10 | - name: secretsFile
11 | value: /components/secrets.json
12 | - name: nestedSeparator
13 | value: "."
14 |
--------------------------------------------------------------------------------
/examples/secrets/components/secrets.json:
--------------------------------------------------------------------------------
1 | {
2 | "some-secret": {
3 | "nested": "got the nested secret!",
4 | "other": "some other secret"
5 | },
6 | "simple-secret": "got the simple secret!"
7 | }
8 |
--------------------------------------------------------------------------------
/examples/secrets/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dapr/secret-example",
3 | "license": "MIT",
4 | "description": "A basic example demonstrating secret access",
5 | "require": {
6 | "dapr/php-sdk": "dev-main"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/secrets/deployments/client-service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: client-service
6 | name: client
7 | spec:
8 | replicas: 5
9 | selector:
10 | matchLabels:
11 | app.kubernetes.io/name: client-service
12 | template:
13 | metadata:
14 | labels:
15 | app.kubernetes.io/name: client-service
16 | annotations:
17 | dapr.io/enabled: "true"
18 | dapr.io/app-id: client
19 | dapr.io/app-port: "80"
20 | dapr.io/max-concurrency: "100"
21 | spec:
22 | containers:
23 | - image: ${DOCKER_USER}/php-client-service:latest
24 | name: client
25 | imagePullPolicy: Always
26 | - image: ${DOCKER_USER}/php-client-http:latest
27 | name: client-http
28 | imagePullPolicy: Always
29 |
--------------------------------------------------------------------------------
/examples/secrets/deployments/secret-service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: secrets-service
6 | name: secrets
7 | spec:
8 | replicas: 5
9 | selector:
10 | matchLabels:
11 | app.kubernetes.io/name: secrets-service
12 | template:
13 | metadata:
14 | labels:
15 | app.kubernetes.io/name: secrets-service
16 | annotations:
17 | dapr.io/enabled: "true"
18 | dapr.io/app-id: secrets
19 | dapr.io/app-port: "80"
20 | dapr.io/max-concurrency: "100"
21 | spec:
22 | containers:
23 | - image: ${DOCKER_USER}/php-secrets-service:latest
24 | name: secrets
25 | imagePullPolicy: Always
26 | - image: ${DOCKER_USER}/php-secrets-http:latest
27 | name: secrets-http
28 | imagePullPolicy: Always
29 |
--------------------------------------------------------------------------------
/examples/secrets/deployments/simple-secret.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: simple-secret
5 | type: Opaque
6 | data:
7 | data: Z290IGEgc2VjcmV0IGZyb20gazhz
8 |
--------------------------------------------------------------------------------
/examples/secrets/deployments/some-secrets.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: some-secret
5 | type: Opaque
6 | data:
7 | nested: Z290IGEgc2VjcmV0IGZyb20gazhz
8 |
--------------------------------------------------------------------------------
/examples/secrets/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | secrets-service:
4 | image: php-secrets-service:${GIT_SHA}
5 | build:
6 | args:
7 | SERVICE: secrets
8 | dockerfile: ../../examples/images/service.Dockerfile
9 | context: ../../examples/secrets
10 | target: production
11 | secrets-http:
12 | image: php-secrets-http:${GIT_SHA}
13 | build:
14 | args:
15 | SERVICE: secrets
16 | dockerfile: ../../examples/images/caddy.Dockerfile
17 | context: ../../examples/secrets
18 | depends_on:
19 | - secrets-service
20 | network_mode: service:secrets-service
21 | secrets-daprd:
22 | image: daprio/daprd:${DAPR_VERSION}
23 | command: [ "./daprd","-app-id","secrets","-app-port","80","-placement-host-address","placement:50006","-components-path","/components" ]
24 | volumes:
25 | - ./components:/components
26 | depends_on:
27 | - secrets-http
28 | network_mode: service:secrets-service
29 | client-service:
30 | image: php-secret-client-service:${GIT_SHA}
31 | build:
32 | args:
33 | SERVICE: client
34 | dockerfile: ../../examples/images/service.Dockerfile
35 | context: ../../examples/secrets
36 | target: production
37 | ports:
38 | - 8080:80
39 | client-http:
40 | image: php-secret-client-http:${GIT_SHA}
41 | build:
42 | args:
43 | SERVICE: client
44 | dockerfile: ../../examples/images/caddy.Dockerfile
45 | context: ../../examples/secrets
46 | depends_on:
47 | - client-service
48 | network_mode: service:client-service
49 | client-daprd:
50 | image: daprio/daprd:${DAPR_VERSION}
51 | command: [ "./daprd","-app-id","client","-app-port","80","-placement-host-address","placement:50006","-components-path","/components" ]
52 | depends_on:
53 | - client-http
54 | network_mode: service:client-service
55 |
--------------------------------------------------------------------------------
/examples/secrets/global-config.php:
--------------------------------------------------------------------------------
1 | $builder->addDefinitions(
10 | __DIR__.'/global-config.php',
11 | SERVICE_ROOT.'/config.php'
12 | )->enableDefinitionCache()->enableCompilation(sys_get_temp_dir())
13 | );
14 |
15 | include SERVICE_ROOT.'/index.php';
16 |
17 | $app->start();
18 |
--------------------------------------------------------------------------------
/examples/secrets/readme.md:
--------------------------------------------------------------------------------
1 | # Secret Example
2 |
3 | In this example we illustrate a production service that returns secrets. It consists of two services:
4 |
5 | 1. [secrets-service](services/secrets/index.php): this gets the secrets from the configured secrets store and returns it.
6 | 2. [client-service](services/client/index.php): Invokes the secret service and returns the secret. This is exposed locally.
7 |
8 | For development (with docker-compose), secrets are stored in [secrets.json](components/secrets.json)
9 |
10 | ## Running the example
11 |
12 | ### Docker Compose
13 |
14 | > Requirements:
15 | > - Docker Compose
16 | > - Docker
17 | > - `make`
18 | > - `jq` (optional)
19 |
20 | 1. Run `make` to build the images
21 | 2. Run `make start` to start the containers in the background
22 | 3. Run `curl -s localhost:8080/run | jq .` to call the client-service and return the secrets.
23 |
24 | ### Kubernetes
25 |
26 | > Requirements
27 | > - A configured kubernetes cluster
28 | > - Dapr installed on the cluster
29 | > - `make`
30 | > - A Docker Hub user
31 |
32 | 1. Update [the `.env` file](.env) with your Docker Hub username
33 | 2. Run `make push` to build and push the images
34 | 3. Run `make deploy` to deploy to Kubernetes
35 | 4. Wait for the pods to be running: `watch kubectl get pods`
36 | 4. In another terminal run `kubectl port-forward deployment/client 8080:80`
37 | 5. View the secrets using `curl -s localhost:8080/run | jq .` to call the client-service and return the secrets.
38 |
--------------------------------------------------------------------------------
/examples/secrets/services/client/config.php:
--------------------------------------------------------------------------------
1 | get(
6 | '/run',
7 | function (DaprClient $client) {
8 | return $client->get('/invoke/secrets/method/list-secrets')->data;
9 | }
10 | );
11 |
--------------------------------------------------------------------------------
/examples/secrets/services/secrets/config.php:
--------------------------------------------------------------------------------
1 | get(
7 | '/list-secrets',
8 | function (LoggerInterface $logger, DaprClient $client) {
9 | $logger->critical('Fetching Secrets.');
10 | try {
11 | $file_secrets = [
12 | 'simple-secret' => $client->getSecret('file-secrets', 'simple-secret')['simple-secret'],
13 | 'nested-secret' => $client->getSecret('file-secrets', 'some-secret.nested')['some-secret.nested'],
14 | ];
15 | } catch (\Dapr\exceptions\DaprException) {
16 | $k8s_secrets = [
17 | 'simple-secret' => $client->getSecret('kubernetes', 'simple-secret')['data'],
18 | 'nested-secret' => $client->getSecret('kubernetes', 'some-secret')['nested'],
19 | ];
20 | }
21 | $secrets = $file_secrets ?? $k8s_secrets ?? [];
22 |
23 | return [
24 | 'simple_secret' => $secrets['simple-secret'] ?? null,
25 | 'nested_secret' => $secrets['nested-secret'] ?? null,
26 | ];
27 | }
28 | );
29 |
--------------------------------------------------------------------------------
/examples/secrets/test.php:
--------------------------------------------------------------------------------
1 | [
7 | 'simple_secret' => 'got the simple secret!',
8 | 'nested_secret' => 'got the nested secret!',
9 | ],
10 | ];
11 |
12 | $failed = false;
13 |
14 | foreach ($urls as $url => $expected) {
15 | echo "Calling $url: ";
16 | $result = json_decode(`curl -s $url`, true);
17 | if ($result === $expected) {
18 | echo "PASS\n";
19 | } else {
20 | echo "FAILED\n";
21 | echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
22 | $failed = true;
23 | }
24 | }
25 |
26 | exit($failed ? 1 : 0);
27 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 | tests
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/psalm-baseline.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/psysh-bootstrap.php:
--------------------------------------------------------------------------------
1 | build();
4 |
--------------------------------------------------------------------------------
/psysh-config.php:
--------------------------------------------------------------------------------
1 | [__DIR__ . '/vendor/autoload.php', __DIR__ . '/psysh-bootstrap.php'],
5 | 'startupMessage' => sprintf('$client configured')
6 | ];
7 |
--------------------------------------------------------------------------------
/src/lib/Actors/.phpstorm.meta.php:
--------------------------------------------------------------------------------
1 | id;
24 | }
25 |
26 | public function remind(string $name, Reminder $data): void
27 | {
28 | }
29 |
30 | public function on_activation(): void
31 | {
32 | }
33 |
34 | public function on_deactivation(): void
35 | {
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/lib/Actors/ActorProxy.php:
--------------------------------------------------------------------------------
1 | logger?->debug('Getting actor proxy for {i}||{id}', ['i' => $interface, 'id' => $id]);
44 |
45 | $reflected_interface = new ReflectionClass($interface);
46 | $type = $override_type ?? ($reflected_interface->getAttributes(
47 | DaprType::class
48 | )[0] ?? null)?->newInstance()->type;
49 |
50 | if (empty($type)) {
51 | $this->logger?->critical('{i} is missing a DaprType attribute', ['i' => $interface]);
52 | throw new LogicException("$interface must have a DaprType attribute");
53 | }
54 |
55 | $generator = $this->proxyFactory->get_generator($interface, $type);
56 |
57 | return $generator->get_proxy($id);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/lib/Actors/ActorReference.php:
--------------------------------------------------------------------------------
1 | get_id();
30 |
31 | if ($id === null) {
32 | throw new LogicException('actor(proxy) must implement get_id()');
33 | }
34 |
35 | $detected_dapr_type = self::get_dapr_type($actor);
36 |
37 | $dapr_type = $actor->DAPR_TYPE ?? $detected_dapr_type?->type;
38 |
39 | if ($dapr_type === null) {
40 | throw new LogicException('Missing DaprType attribute on '.$actor::class);
41 | }
42 |
43 | return new self(id: $id, actor_type: $dapr_type);
44 | }
45 |
46 | private static function get_dapr_type(object|string $type): DaprType|null
47 | {
48 | $reflector = new ReflectionClass($type);
49 | /**
50 | * @var DaprType|null $type_attribute
51 | */
52 | $type_attribute = ($reflector->getAttributes(DaprType::class)[0] ?? null)?->newInstance();
53 |
54 | return $type_attribute;
55 | }
56 |
57 | public static function deserialize(mixed $value, IDeserializer $deserializer): mixed
58 | {
59 | return new ActorReference(id: $value['ActorId'], actor_type: $value['ActorType']);
60 | }
61 |
62 | /**
63 | * @inheritDoc
64 | */
65 | public function bind(string $interface, ProxyFactory $proxy_factory): mixed
66 | {
67 | return $proxy_factory->get_generator($interface, $this->actor_type)->get_proxy(
68 | $this->id
69 | );
70 | }
71 |
72 | /**
73 | * @inheritDoc
74 | */
75 | public function get_actor_id(): string
76 | {
77 | return $this->id;
78 | }
79 |
80 | /**
81 | * @inheritDoc
82 | */
83 | public function get_actor_type(): string
84 | {
85 | return $this->actor_type;
86 | }
87 |
88 | /**
89 | * @param ActorReference $value
90 | * @param ISerializer $serializer
91 | *
92 | * @return array
93 | */
94 | public function serialize(mixed $value, ISerializer $serializer): array
95 | {
96 | return ['ActorId' => $value->id, 'ActorType' => $value->actor_type];
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/lib/Actors/Attributes/DaprType.php:
--------------------------------------------------------------------------------
1 | cache_dir = sys_get_temp_dir().DIRECTORY_SEPARATOR.'dapr-proxy-cache'.DIRECTORY_SEPARATOR;
29 | }
30 |
31 | /**
32 | * Set the cache directory
33 | *
34 | * @param string $dir
35 | */
36 | public function set_cache_dir(string $dir) {
37 | $this->cache_dir = $dir;
38 | }
39 |
40 | public function get_proxy(string $id): object
41 | {
42 | if ( ! class_exists($this->get_full_class_name())) {
43 | $file_generator = new FileGenerator($this->interface, $this->dapr_type, $this->client);
44 | $file = $file_generator->generate_file();
45 | if ( ! is_dir($this->cache_dir)) {
46 | mkdir($this->cache_dir);
47 | }
48 | $filename = $this->cache_dir.$this->get_short_class_name();
49 | FileWriter::write($filename, $file);
50 | require_once $filename;
51 | }
52 |
53 | return parent::get_proxy($id);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/lib/Actors/Generators/ExistingOnly.php:
--------------------------------------------------------------------------------
1 | get_full_class_name());
40 | $proxy = $reflection->newInstance($this->client);
41 | $proxy->id = $id;
42 |
43 | return $proxy;
44 | }
45 |
46 | /**
47 | * @codeCoverageIgnore Never happens
48 | * @param Method $method
49 | */
50 | protected function generate_failure_method(Method $method): void
51 | {
52 | throw new LogicException();
53 | }
54 |
55 | /**
56 | * @codeCoverageIgnore Never happens
57 | * @param Method $method
58 | * @param string $id
59 | */
60 | protected function generate_proxy_method(Method $method, string $id): void
61 | {
62 | throw new LogicException();
63 | }
64 |
65 | /**
66 | * @codeCoverageIgnore Never happens
67 | * @param Method $method
68 | * @param string $id
69 | */
70 | protected function generate_get_id(Method $method, string $id): void
71 | {
72 | throw new LogicException();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/lib/Actors/Generators/GenerateProxy.php:
--------------------------------------------------------------------------------
1 | getName()) {
32 | 'remind', 'on_activation', 'on_deactivation' => $this->generate_failure_method($method),
33 | 'create_reminder', 'get_reminder', 'delete_reminder', 'create_timer', 'delete_timer' => null,
34 | 'get_id' => $this->generate_get_id($method, $id),
35 | default => $this->generate_proxy_method($method, $id),
36 | };
37 | }
38 |
39 | /**
40 | * Generate a method that shouldn't be called from outside the actor.
41 | *
42 | * @param Method $method The method
43 | *
44 | * @return Method
45 | */
46 | protected abstract function generate_failure_method(Method $method);
47 |
48 | /**
49 | * Write a method to get the current actor id.
50 | *
51 | * @param Method $method
52 | * @param string $id
53 | *
54 | * @return Method
55 | */
56 | protected abstract function generate_get_id(Method $method, string $id);
57 |
58 | /**
59 | * Write a method that calls the actor.
60 | *
61 | * @param Method $method
62 | * @param string $id
63 | *
64 | * @return Method
65 | */
66 | protected abstract function generate_proxy_method(Method $method, string $id);
67 |
68 | /**
69 | * @param ClassType $interface
70 | *
71 | * @return Method[] available methods for the interface
72 | */
73 | protected function get_methods(ClassType $interface): array
74 | {
75 | return array_merge($interface->getMethods(), ClassType::from(IActor::class)->getMethods());
76 | }
77 |
78 | protected function get_full_class_name(): string
79 | {
80 | return "\\".$this->get_namespace()."\\".$this->get_short_class_name();
81 | }
82 |
83 | protected function get_namespace(): string
84 | {
85 | return "Dapr\\Proxies";
86 | }
87 |
88 | protected function get_short_class_name(): string
89 | {
90 | $internal_type = preg_replace('/[^a-zA-Z0-9_]*/', '', $this->dapr_type);
91 |
92 | return 'dapr_proxy_'.$internal_type;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/lib/Actors/Generators/IGenerateProxy.php:
--------------------------------------------------------------------------------
1 | $interface, 'dapr_type' => $dapr_type];
44 |
45 | return match ($this->mode) {
46 | ProxyFactory::DYNAMIC => new DynamicGenerator($interface, $dapr_type, $this->client),
47 | ProxyFactory::GENERATED_CACHED => new CachedGenerator($interface, $dapr_type, $this->client),
48 | ProxyFactory::GENERATED => new FileGenerator($interface, $dapr_type, $this->client),
49 | ProxyFactory::ONLY_EXISTING => new ExistingOnly($interface, $dapr_type, $this->client),
50 | default => throw new InvalidArgumentException('mode must be a supported mode'),
51 | };
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/lib/Actors/HealthCheck.php:
--------------------------------------------------------------------------------
1 | reference, $this->state_name);
23 | $base_dir = self::get_base_path($this->reference->get_actor_type(), $this->reference->get_actor_id());
24 | if ( ! file_exists($base_dir)) {
25 | mkdir($base_dir, recursive: true);
26 | }
27 | $this->state_name = mb_ereg_replace('([^\w\s\d\-_~,;\[\]\(\).])', '', $this->state_name) . '';
28 | $this->state_name = mb_ereg_replace('([\.]{2,})', '', $this->state_name) . '';
29 | $this->cache_file = $base_dir.DIRECTORY_SEPARATOR.$this->state_name.'.actor';
30 | $this->unserialize_cache();
31 | }
32 |
33 | private static function get_base_path(string $dapr_type, string $actor_id): string
34 | {
35 | return sys_get_temp_dir().DIRECTORY_SEPARATOR.
36 | 'actor-cache'.DIRECTORY_SEPARATOR.$dapr_type.DIRECTORY_SEPARATOR.$actor_id;
37 | }
38 |
39 | /**
40 | * @inheritDoc
41 | */
42 | private function unserialize_cache()
43 | {
44 | if (file_exists($this->cache_file)) {
45 | $this->data = unserialize(file_get_contents($this->cache_file));
46 | }
47 | }
48 |
49 | public static function clear_actor(string $dapr_type, string $actor_id): void
50 | {
51 | $path = self::get_base_path($dapr_type, $actor_id);
52 | if ( ! file_exists($path)) {
53 | return;
54 | }
55 | foreach (scandir($path) as $file) {
56 | if ($file === '.' || $file === '..') {
57 | continue;
58 | }
59 | unlink($path.DIRECTORY_SEPARATOR.$file);
60 | }
61 | rmdir($path);
62 | }
63 |
64 | /**
65 | * @inheritDoc
66 | */
67 | public function flush_cache(): void
68 | {
69 | $this->serialize_cache();
70 | }
71 |
72 | /**
73 | * @inheritDoc
74 | */
75 | private function serialize_cache()
76 | {
77 | if ($this->data === []) {
78 | if (file_exists($this->cache_file)) {
79 | unlink($this->cache_file);
80 | }
81 |
82 | return;
83 | }
84 |
85 | FileWriter::write($this->cache_file, serialize($this->data));
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/lib/Actors/Internal/Caches/KeyNotFound.php:
--------------------------------------------------------------------------------
1 | has_key($key)) {
28 | return $this->data[$key];
29 | }
30 | throw new KeyNotFound();
31 | }
32 |
33 | /**
34 | * @inheritDoc
35 | */
36 | public function set_key(string $key, mixed $data): void
37 | {
38 | $this->data[$key] = $data;
39 | }
40 |
41 | /**
42 | * @inheritDoc
43 | */
44 | public function evict(string $key): void
45 | {
46 | unset($this->data[$key]);
47 | }
48 |
49 | /**
50 | * @inheritDoc
51 | */
52 | public function reset(): void
53 | {
54 | $this->data = [];
55 | }
56 |
57 | /**
58 | * @inheritDoc
59 | */
60 | public function flush_cache(): void
61 | {
62 | }
63 |
64 | public function has_key(string $key): bool
65 | {
66 | return array_key_exists($key, $this->data);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/lib/Actors/Internal/InternalProxy.php:
--------------------------------------------------------------------------------
1 | $name, $arguments);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/lib/Actors/Internal/KeyResponse.php:
--------------------------------------------------------------------------------
1 | "string", 'period' => "string", 'data' => "false|string"])]
72 | public function to_array(): array
73 | {
74 | return [
75 | 'dueTime' => Formats::normalize_interval($this->due_time),
76 | 'period' => Formats::normalize_interval($this->period),
77 | 'data' => json_encode($this->data),
78 | ];
79 | }
80 |
81 | public function serialize(mixed $value, ISerializer $serializer): mixed
82 | {
83 | if ($this->repetitions >= 0) {
84 | return [
85 | 'dueTime' => Formats::normalize_interval($this->due_time),
86 | 'period' => "R{$this->repetitions}/" . $serializer->as_array($this->period),
87 | 'data' => $serializer->as_json($this->data),
88 | ];
89 | }
90 |
91 | return [
92 | 'dueTime' => Formats::normalize_interval($this->due_time),
93 | 'period' => Formats::normalize_interval($this->period),
94 | 'data' => $serializer->as_json($this->data)
95 | ];
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/lib/Actors/Timer.php:
--------------------------------------------------------------------------------
1 | to_array();
32 | }
33 |
34 | #[ArrayShape(['dueTime' => "string", 'period' => "string", 'callback' => "string", 'data' => "array|null"])]
35 | public function to_array(): array
36 | {
37 | return [
38 | 'dueTime' => Formats::normalize_interval($this->due_time),
39 | 'period' => Formats::normalize_interval($this->period),
40 | 'callback' => $this->callback,
41 | 'data' => $this->data,
42 | ];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/lib/Attributes/FromBody.php:
--------------------------------------------------------------------------------
1 | namespace) ? $this->id : "{$this->id}.{$this->namespace}";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/lib/Client/BindingRequest.php:
--------------------------------------------------------------------------------
1 | $metadata
17 | */
18 | public function __construct(public BindingRequest $request, public mixed $data, public array $metadata)
19 | {
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/lib/Client/DaprClientBuilder.php:
--------------------------------------------------------------------------------
1 | deserializationConfig, $this->serializationConfig, $this->logger);
31 | }
32 |
33 | public function withSerializationConfig(SerializationConfig $serializationConfig): self
34 | {
35 | return new self($this->defaultHttpHost, $this->deserializationConfig, $serializationConfig, $this->logger);
36 | }
37 |
38 | public function withDeserializationConfig(DeserializationConfig $deserializationConfig): self
39 | {
40 | return new self($this->defaultHttpHost, $deserializationConfig, $this->serializationConfig, $this->logger);
41 | }
42 |
43 | public function withLogger(LoggerInterface $logger): self
44 | {
45 | return new self($this->defaultHttpHost, $this->deserializationConfig, $this->serializationConfig, $logger);
46 | }
47 |
48 | public function build(): DaprClient
49 | {
50 | return new DaprHttpClient(
51 | $this->defaultHttpHost,
52 | new Deserializer($this->deserializationConfig, $this->logger),
53 | new Serializer($this->serializationConfig, $this->logger),
54 | $this->logger
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/lib/Client/DaprHttpClient.php:
--------------------------------------------------------------------------------
1 | baseHttpUri, '/')) {
35 | $this->baseHttpUri = rtrim($this->baseHttpUri, '/');
36 | }
37 | $options = [
38 | 'base_uri' => $this->baseHttpUri,
39 | 'allow_redirects' => false,
40 | 'headers' => [
41 | 'User-Agent' => 'DaprPHPSDK/v1.2',
42 | 'Accept' => 'application/json',
43 | 'Content-Type' => 'application/json'
44 | ]
45 | ];
46 |
47 | if ($this->getDaprToken() !== null) {
48 | $options['headers']['dapr-api-token'] = $this->getDaprToken();
49 | }
50 |
51 | if (!empty(ActorToken::$token)) {
52 | $options['headers']['Dapr-Reentrancy-Id'] = &ActorToken::$token;
53 | }
54 |
55 | $this->httpClient = new Client($options);
56 | }
57 |
58 | public function isDaprHealthy(): bool
59 | {
60 | try {
61 | $result = $this->httpClient->get('/v1.0/healthz');
62 | if (204 === $result->getStatusCode()) {
63 | return true;
64 | }
65 | return false;
66 | } catch (\Throwable $exception) {
67 | return false;
68 | }
69 | }
70 |
71 | public function getMetadata(): MetadataResponse|null
72 | {
73 | try {
74 | $result = $this->httpClient->get('/v1.0/metadata');
75 | return $this->deserializer->from_json(MetadataResponse::class, $result->getBody()->getContents());
76 | } catch (\Throwable $exception) {
77 | return null;
78 | }
79 | }
80 |
81 | public function shutdown(bool $afterRequest = true): void
82 | {
83 | $shutdown = fn() => $this->httpClient->post('/v1.0/shutdown');
84 | if ($afterRequest) {
85 | register_shutdown_function($shutdown);
86 | return;
87 | }
88 |
89 | $shutdown();
90 | }
91 |
92 | protected function getHttpClient(): Client
93 | {
94 | return $this->httpClient;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/lib/Client/DeleteTransactionRequest.php:
--------------------------------------------------------------------------------
1 | invokeBindingAsync($bindingRequest, $dataType)->wait();
28 | }
29 |
30 | public function invokeBindingAsync(BindingRequest $bindingRequest, string $dataType = 'array'): PromiseInterface
31 | {
32 | return $this->handlePromise(
33 | $this->httpClient->putAsync(
34 | '/v1.0/bindings/' . rawurlencode($bindingRequest->bindingName),
35 | [
36 | 'body' => $this->serializer->as_json(
37 | [
38 | 'data' => $bindingRequest->data,
39 | 'metadata' => $bindingRequest->metadata,
40 | 'operation' => $bindingRequest->operation
41 | ]
42 | )
43 | ]
44 | ),
45 | fn(ResponseInterface $response) => new BindingResponse(
46 | $bindingRequest,
47 | $this->deserializer->from_json($dataType, $response->getBody()->getContents()),
48 | $response->getHeaders()
49 | )
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/lib/Client/HttpInvokeTrait.php:
--------------------------------------------------------------------------------
1 | handlePromise(
35 | $this->invokeMethodAsync($httpMethod, $appId, $methodName, $data, $metadata)
36 | )->wait();
37 | }
38 |
39 | public function invokeMethodAsync(
40 | string $httpMethod,
41 | AppId $appId,
42 | string $methodName,
43 | mixed $data = null,
44 | array $metadata = []
45 | ): PromiseInterface {
46 | $options = [];
47 | if (!empty($data)) {
48 | $options['body'] = $this->serializer->as_json($data);
49 | }
50 | $options['headers'] = $metadata;
51 | $appId = rawurlencode($appId->getAddress());
52 | $methodName = rawurlencode($methodName);
53 | $methodName = str_replace('%2F', '/', $methodName);
54 | return $this->handlePromise(
55 | $this->httpClient->requestAsync($httpMethod, "/v1.0/invoke/$appId/method/$methodName", $options)
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/lib/Client/HttpPubSubTrait.php:
--------------------------------------------------------------------------------
1 | publishEventAsync($pubsubName, $topicName, $data, $metadata, $contentType)->wait(true);
34 | }
35 |
36 | public function publishEventAsync(
37 | string $pubsubName,
38 | string $topicName,
39 | mixed $data,
40 | array $metadata = [],
41 | string $contentType = 'application/json'
42 | ): PromiseInterface {
43 | $options = [
44 | 'query' => array_merge(
45 | ...array_map(fn($key, $value) => ["metadata.$key" => $value], array_keys($metadata), $metadata)
46 | ),
47 | 'body' => $this->serializer->as_json($data),
48 | 'headers' => [
49 | 'Content-Type' => $contentType,
50 | ]
51 | ];
52 | $pubsubName = rawurlencode($pubsubName);
53 | $topicName = rawurlencode($topicName);
54 | return $this->handlePromise($this->httpClient->postAsync("/v1.0/publish/$pubsubName/$topicName", $options));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/lib/Client/HttpSecretsTrait.php:
--------------------------------------------------------------------------------
1 | getSecretAsync($storeName, $key, $metadata)->wait();
26 | }
27 |
28 | public function getSecretAsync(string $storeName, string $key, array $metadata = []): PromiseInterface
29 | {
30 | $storeName = rawurlencode($storeName);
31 | $key = rawurlencode($key);
32 | return $this->handlePromise(
33 | $this->httpClient->getAsync(
34 | "/v1.0/secrets/$storeName/$key",
35 | [
36 | 'query' => array_merge(
37 | ...array_map(
38 | fn($key, $value) => ["metadata.$key" => $value],
39 | array_keys($metadata),
40 | $metadata
41 | )
42 | )
43 | ]
44 | ),
45 | fn(ResponseInterface $response) => $this->deserializer->from_json(
46 | 'array',
47 | $response->getBody()->getContents()
48 | )
49 | );
50 | }
51 |
52 | public function getBulkSecret(string $storeName, array $metadata = []): array
53 | {
54 | return $this->getBulkSecretAsync($storeName, $metadata)->wait();
55 | }
56 |
57 | public function getBulkSecretAsync(string $storeName, array $metadata = []): PromiseInterface
58 | {
59 | $storeName = rawurlencode($storeName);
60 | return $this->handlePromise(
61 | $this->httpClient->getAsync(
62 | "/v1.0/secrets/$storeName/bulk",
63 | [
64 | 'query' => array_merge(
65 | ...array_map(
66 | fn($key, $value) => ["metadata.$key" => $value],
67 | array_keys($metadata),
68 | $metadata
69 | )
70 | )
71 | ]
72 | ),
73 | fn(ResponseInterface $response) => $this->deserializer->from_json(
74 | 'array',
75 | $response->getBody()->getContents()
76 | )
77 | );
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/lib/Client/HttpTokenTrait.php:
--------------------------------------------------------------------------------
1 | $response instanceof DaprException ? throw $response : $response;
26 | }
27 | if (empty($errorTransformer)) {
28 | $errorTransformer = fn(\Throwable $exception) => match ($exception::class) {
29 | ServerException::class, ClientException::class => throw new DaprException(
30 | $exception->hasResponse()
31 | ? $exception->getResponse()->getBody()->getContents()
32 | : $exception->getMessage(),
33 | $exception->getCode(),
34 | $exception
35 | ),
36 | default => throw $exception
37 | };
38 | }
39 | return $closure->then(
40 | $transformResult,
41 | $errorTransformer
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/lib/Client/StateTransactionRequest.php:
--------------------------------------------------------------------------------
1 | deserializers[\DateInterval::class])) {
26 | $this->add(\DateInterval::class, new DateInterval());
27 | }
28 | if (empty($this->deserializers[\DateTime::class])) {
29 | $this->add(\DateTime::class, new DateTime());
30 | }
31 | }
32 |
33 | /**
34 | * Adds a deserializer for a given type
35 | *
36 | * @param string $type
37 | * @param IDeserialize $deserializer
38 | */
39 | public function add(string $type, IDeserialize $deserializer)
40 | {
41 | $this->deserializers[$type] = $deserializer;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/lib/Deserialization/Deserializers/DateInterval.php:
--------------------------------------------------------------------------------
1 | getParameters();
30 |
31 | if ( ! empty($resolvedParameters)) {
32 | $parameters = array_diff_key($parameters, $resolvedParameters);
33 | }
34 |
35 | foreach ($parameters as $index => $parameter) {
36 | if ( ! empty($parameter->getAttributes(FromBody::class))) {
37 | $body = json_decode($this->request->getBody()->getContents(), true);
38 |
39 | $resolvedParameters[$index] = $this->deserializer->detect_from_parameter($parameter, $body);
40 | }
41 | }
42 |
43 | return $resolvedParameters;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/lib/Formats.php:
--------------------------------------------------------------------------------
1 | m || $diff->y) {
38 | throw new LogicException('Interval cannot use months or years with Dapr');
39 | }
40 |
41 | return $diff->format(self::FROM_INTERVAL);
42 | }
43 |
44 | public static function coalesce(DateInterval $interval): bool|DateInterval
45 | {
46 | $from = new DateTime();
47 | $to = clone $from;
48 | $to = $to->add($interval);
49 | $diff = $to->diff($from, true);
50 | $diff->h += $diff->d * self::DAY_IN_HOURS;
51 | $diff->d = 0;
52 |
53 | return $diff;
54 | }
55 |
56 | /**
57 | * @param string $dapr_interval
58 | *
59 | * @return DateInterval|null
60 | * @throws Exception
61 | */
62 | public static function from_dapr_interval(string $dapr_interval): ?DateInterval
63 | {
64 | if (empty($dapr_interval)) {
65 | return null;
66 | }
67 |
68 | $parts = array_combine(
69 | preg_split('/[0-9]/', $dapr_interval, 0, PREG_SPLIT_NO_EMPTY),
70 | preg_split('/[a-z]/', $dapr_interval, 0, PREG_SPLIT_NO_EMPTY)
71 | );
72 | $interval = ['PT'];
73 | $seconds = 0.0;
74 | foreach ($parts as $time => $value) {
75 | $seconds += match ($time) {
76 | 'ns' => ((float)$value) * self::NANOSECOND_TO_SECOND,
77 | 'us', 'µs' => ((float)$value) * self::MICROSECOND_TO_SECOND,
78 | 'ms' => ((float)$value) * self::MILLISECOND_TO_SECOND,
79 | 's' => (float)$value,
80 | default => 0,
81 | };
82 | $interval[] = match ($time) {
83 | 'm' => $value . 'M',
84 | 'h' => $value . 'H',
85 | default => null
86 | };
87 | }
88 | if ($seconds > 0) {
89 | $interval[] = $seconds . 'S';
90 | }
91 |
92 | return new DateInterval(implode('', $interval));
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/lib/Middleware/Defaults/ActorToken.php:
--------------------------------------------------------------------------------
1 | hasHeader('Dapr-Reentrancy-Id')) {
22 | self::$token = $request->getHeader('Dapr-Reentrancy-Id');
23 | } else {
24 | self::$token = [];
25 | }
26 |
27 | return $request;
28 | }
29 |
30 | public function response(ResponseInterface $response): ResponseInterface
31 | {
32 | if (!empty(self::$token)) {
33 | return $response->withHeader('Dapr-Reentrancy-Id', self::$token);
34 | }
35 |
36 | return $response;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/lib/Middleware/Defaults/Response/ApplicationJson.php:
--------------------------------------------------------------------------------
1 | hasHeader('Content-Type')) {
17 | return $response;
18 | }
19 |
20 | return $response->withHeader('Content-Type', 'application/json');
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/lib/Middleware/Defaults/TokenAuth.php:
--------------------------------------------------------------------------------
1 | getAppToken();
21 | if ($token === null) {
22 | return $request;
23 | }
24 |
25 | if (!$request->hasHeader('dapr-api-token')) {
26 | throw new NotFound();
27 | }
28 |
29 | if (!hash_equals($token, $request->getHeader('dapr-api-token')[0] ?? '')) {
30 | throw new NotFound();
31 | }
32 |
33 | return $request;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/lib/Middleware/Defaults/Tracing.php:
--------------------------------------------------------------------------------
1 | hasHeader('traceparent')) {
22 | $this->trace_parent = $request->getHeader('traceparent')[0];
23 | $this->trace_state = $request->hasHeader('tracestate') ? $request->getHeader('tracestate')[0] : null;
24 | }
25 |
26 | return $request;
27 | }
28 |
29 | public function response(ResponseInterface $response): ResponseInterface
30 | {
31 | if ($this->trace_parent !== null) {
32 | $response = $response->withHeader('traceparent', $this->trace_parent);
33 | if ($this->trace_state !== null) {
34 | $response = $response->withHeader('tracestate', $this->trace_state);
35 | }
36 | }
37 |
38 | return $response;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/lib/Middleware/IRequestMiddleware.php:
--------------------------------------------------------------------------------
1 | factory->make(
43 | Topic::class,
44 | [
45 | 'pubsub' => $this->pubsub,
46 | 'topic' => $topic,
47 | 'client' => $this->container->get(\Dapr\DaprClient::class)
48 | ]
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/lib/PubSub/Subscription.php:
--------------------------------------------------------------------------------
1 | as_array($this->subscriptions);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/lib/PubSub/Topic.php:
--------------------------------------------------------------------------------
1 | logger !== null) {
37 | $this->logger->debug('Sending {event} to {topic}', ['event' => $event, 'topic' => $this->topic]);
38 | } elseif ($this->client instanceof NewClient) {
39 | $this->client->logger->debug('Sending {event} to {topic}', ['event' => $event, 'topic' => $this->topic]);
40 | }
41 | if ($event instanceof CloudEvent) {
42 | $content_type = 'application/cloudevents+json';
43 | $this->client->extra_headers = [
44 | 'Content-Type: application/cloudevents+json',
45 | ];
46 |
47 | $event = $event->to_array();
48 | }
49 |
50 | if ($this->client instanceof DaprClient) {
51 | try {
52 | $this->client->post("/publish/{$this->pubsub}/{$this->topic}", $event, $metadata);
53 |
54 | $this->client->extra_headers = ['Content-Type: ' . $content_type];
55 |
56 | return true;
57 | } catch (DaprException) { // @codeCoverageIgnoreStart
58 | return false;
59 | } // @codeCoverageIgnoreEnd
60 | }
61 |
62 | if ($this->client instanceof NewClient) {
63 | try {
64 | $this->client->publishEvent($this->pubsub, $this->topic, $event, $metadata ?? [], $content_type);
65 | return true;
66 | } catch (DaprException) {
67 | return false;
68 | }
69 | }
70 |
71 | return false;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/lib/SecretManager.php:
--------------------------------------------------------------------------------
1 | logger->debug('Retrieving all secrets from {secret_store}', ['secret_store' => $secret_store]);
32 | $result = $this->client->get("/secrets/$secret_store/bulk");
33 | self::handle_response_code($result->code);
34 |
35 | return $result->data;
36 | }
37 |
38 | /**
39 | * Throw exceptions on errors
40 | *
41 | * @param int $code
42 | *
43 | * @throws DaprException
44 | */
45 | private function handle_response_code(int $code): void
46 | {
47 | switch ($code) {
48 | case 200:
49 | case 204:
50 | return;
51 | case 400:
52 | throw new DaprException('Secret store missing or not configured');
53 | case 403:
54 | throw new DaprException('Access denied');
55 | case 500:
56 | throw new DaprException('Failed to get secret or no secret store defined');
57 | }
58 | }
59 |
60 | /**
61 | * Retrieve a secret from the store.
62 | *
63 | * @param string $secret_store The store to get the secret from.
64 | * @param string $name The name of the secret to get.
65 | * @param array $parameters Optional parameters for the secret store
66 | *
67 | * @return array
68 | * @throws DaprException
69 | */
70 | public function retrieve(string $secret_store, string $name, array $parameters = []): ?array
71 | {
72 | $this->logger->debug(
73 | 'Retrieving secret {name} from {secret_store}',
74 | ['name' => $name, 'secret_store' => $secret_store]
75 | );
76 | $result = $this->client->get("/secrets/$secret_store/$name", $parameters);
77 | $this->handle_response_code($result->code);
78 |
79 | return $result->data;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/lib/Serialization/Attributes/AlwaysObject.php:
--------------------------------------------------------------------------------
1 | serializers[\DateInterval::class])) {
24 | $this->add(\DateInterval::class, new DateInterval());
25 | }
26 | if (empty($this->serializers[\DateTime::class])) {
27 | $this->add(\DateTime::class, new DateTime());
28 | }
29 | if (empty($this->serializers[StateItem::class])) {
30 | $this->add(StateItem::class, new Serializers\StateItem());
31 | }
32 | }
33 |
34 | public function add(string $type, ISerialize $serializer): void
35 | {
36 | $this->serializers[$type] = $serializer;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/lib/Serialization/Serializers/DateInterval.php:
--------------------------------------------------------------------------------
1 | format('P%yY%mM%dDT%hH%iM%sS%fF')),
21 | 'PT'
22 | ) ?: self::DEFAULT_DT;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/lib/Serialization/Serializers/DateTime.php:
--------------------------------------------------------------------------------
1 | format(DATE_W3C);
18 | }
19 | throw new InvalidArgumentException('Date time is not a date time');
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/lib/Serialization/Serializers/ISerialize.php:
--------------------------------------------------------------------------------
1 | $value->key,
18 | 'value' => $serializer->as_array($value->value),
19 | ];
20 | if (isset($value->etag)) {
21 | $item['etag'] = $value->etag;
22 | $item['options'] = [
23 | 'consistency' => $value->consistency->get_consistency(),
24 | 'concurrency' => $value->consistency->get_concurrency(),
25 | ];
26 | }
27 | if ( ! empty($value->metadata)) {
28 | $item['metadata'] = $value->metadata;
29 | }
30 |
31 | return $item;
32 | }
33 |
34 | return $value;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/lib/State/Attributes/StateStore.php:
--------------------------------------------------------------------------------
1 | getAttributes(StateStore::class) as $attribute) {
25 | return $attribute->newInstance();
26 | }
27 | throw new LogicException('Tried to load state without a Dapr\State\Attributes\StateStore attribute');
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/lib/State/Internal/Transaction.php:
--------------------------------------------------------------------------------
1 | transaction);
39 | usort($transaction, fn($a, $b) => $a['order'] <=> $b['order']);
40 |
41 | return array_map(
42 | function ($a) {
43 | unset($a['order']);
44 | if (isset($a['request']['value'])) {
45 | $a['request']['value'] = $this->serializer->as_array($a['request']['value']);
46 | }
47 |
48 | return $a;
49 | },
50 | $transaction
51 | );
52 | }
53 |
54 | public function is_empty(): bool
55 | {
56 | return empty($this->transaction);
57 | }
58 |
59 | /**
60 | * Upsert a value in a transaction
61 | *
62 | * @param string $key
63 | * @param mixed $value
64 | */
65 | public function upsert(string $key, mixed $value): void
66 | {
67 | $this->transaction[$key] = [
68 | 'order' => $this->counter++,
69 | 'operation' => 'upsert',
70 | 'request' => [
71 | 'key' => $key,
72 | 'value' => $value,
73 | ],
74 | ];
75 | }
76 |
77 | /**
78 | * Delete a value in a transaction
79 | *
80 | * @param string $key
81 | */
82 | public function delete(string $key): void
83 | {
84 | $this->transaction[$key] = [
85 | 'order' => $this->counter++,
86 | 'operation' => 'delete',
87 | 'request' => [
88 | 'key' => $key,
89 | ],
90 | ];
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/lib/State/StateItem.php:
--------------------------------------------------------------------------------
1 | consistency)) {
23 | $this->consistency = new StrongLastWrite();
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/lib/consistency/Consistency.php:
--------------------------------------------------------------------------------
1 | dapr_error_code = $array['errorCode'];
32 | break;
33 | }
34 |
35 | $original_exception->file = $array['file'] ?? $backtrace[1]['file'];
36 | $original_exception->line = $array['line'] ?? $backtrace[1]['line'];
37 |
38 | return $original_exception;
39 | }
40 |
41 | /**
42 | * @param Exception|null $exception
43 | *
44 | * @return array|null
45 | */
46 | public static function serialize_to_array(?Exception $exception): ?array
47 | {
48 | if ($exception === null || ! ($exception instanceof Exception)) {
49 | return null;
50 | }
51 |
52 | return [
53 | 'message' => $exception->getMessage(),
54 | 'errorCode' => get_class($exception),
55 | 'file' => $exception->getFile(),
56 | 'line' => $exception->getLine(),
57 | 'inner' => self::serialize_to_array($exception->getPrevious()),
58 | ];
59 | }
60 |
61 | public function get_dapr_error_code(): string
62 | {
63 | return $this->dapr_error_code;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/lib/exceptions/Http/HttpException.php:
--------------------------------------------------------------------------------
1 | require?->{'dapr/php-sdk'};
15 | if ($php_sdk === null) {
16 | continue;
17 | }
18 | if (!empty($sha)) {
19 | $sha = "#$sha";
20 | }
21 | $composer->require->{'dapr/php-sdk'} = "dev-$branch$sha";
22 | file_put_contents(
23 | "examples/$example/composer.json",
24 | json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_LINE_TERMINATORS) . "\n"
25 | );
26 | chdir("examples/$example");
27 | echo `composer update`;
28 | chdir(__DIR__);
29 | }
30 |
--------------------------------------------------------------------------------