Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "sdkdocs/php"]
path = sdkdocs/php
url = https://github.com/dapr/php-sdk.git
[submodule "translations/docs-zh"]
path = translations/docs-zh
url = https://github.com/dapr/docs-zh.git
Expand Down
2 changes: 1 addition & 1 deletion hugo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ module:
- source: sdkdocs/python/content/en/python-sdk-contributing
target: content/contributing/sdk-contrib/
lang: en
- source: sdkdocs/php/daprdocs/content/en/php-sdk-docs
- source: sdkdocs/php/content/en/php-sdk-docs
target: content/developing-applications/sdks/php
lang: en
- source: sdkdocs/dotnet/content/en/dotnet-sdk-docs
Expand Down
1 change: 0 additions & 1 deletion sdkdocs/php
Submodule php deleted from 82c728
118 changes: 118 additions & 0 deletions sdkdocs/php/content/en/php-sdk-docs/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
type: docs
title: "Dapr PHP SDK"
linkTitle: "PHP"
weight: 1000
description: PHP SDK packages for developing Dapr applications
no_list: true
cascade:
github_repo: https://github.com/dapr/php-sdk
github_subdir: daprdocs/content/en/php-sdk-docs
path_base_for_github_subdir: content/en/developing-applications/sdks/php/
github_branch: main
---

Dapr offers an SDK to help with the development of PHP applications. Using it, you can create PHP clients, servers, and virtual actors with Dapr.

## Setting up

### Prerequisites

- [Composer](https://getcomposer.org/)
- [PHP 8](https://www.php.net/)

### Optional Prerequisites

- [Docker](https://www.docker.com/)
- [xdebug](http://xdebug.org/) -- for debugging

## Initialize your project

In a directory where you want to create your service, run `composer init` and answer the questions.
Install with `composer require dapr/php-sdk` and any other dependencies you may wish to use.

## Configure your service

Create a config.php, copying the contents below:

```php
<?php

use Dapr\Actors\Generators\ProxyFactory;
use Dapr\Middleware\Defaults\{Response\ApplicationJson,Tracing};
use Psr\Log\LogLevel;
use function DI\{env,get};

return [
// set the log level
'dapr.log.level' => LogLevel::WARNING,

// Generate a new proxy on each request - recommended for development
'dapr.actors.proxy.generation' => ProxyFactory::GENERATED,

// put any subscriptions here
'dapr.subscriptions' => [],

// if this service will be hosting any actors, add them here
'dapr.actors' => [],

// if this service will be hosting any actors, configure how long until dapr should consider an actor idle
'dapr.actors.idle_timeout' => null,

// if this service will be hosting any actors, configure how often dapr will check for idle actors
'dapr.actors.scan_interval' => null,

// if this service will be hosting any actors, configure how long dapr will wait for an actor to finish during drains
'dapr.actors.drain_timeout' => null,

// if this service will be hosting any actors, configure if dapr should wait for an actor to finish
'dapr.actors.drain_enabled' => null,

// you shouldn't have to change this, but the setting is here if you need to
'dapr.port' => env('DAPR_HTTP_PORT', '3500'),

// add any custom serialization routines here
'dapr.serializers.custom' => [],

// add any custom deserialization routines here
'dapr.deserializers.custom' => [],

// the following has no effect, as it is the default middlewares and processed in order specified
'dapr.http.middleware.request' => [get(Tracing::class)],
'dapr.http.middleware.response' => [get(ApplicationJson::class), get(Tracing::class)],
];
```

## Create your service

Create `index.php` and put the following contents:

```php
<?php

require_once __DIR__.'/vendor/autoload.php';

use Dapr\App;

$app = App::create(configure: fn(\DI\ContainerBuilder $builder) => $builder->addDefinitions(__DIR__ . '/config.php'));
$app->get('/hello/{name}', function(string $name) {
return ['hello' => $name];
});
$app->start();
```

## Try it out

Initialize dapr with `dapr init` and then start the project with `dapr run -a dev -p 3000 -- php -S 0.0.0.0:3000`.

<!-- IGNORE_LINKS -->
You can now open a web browser and point it to [http://localhost:3000/hello/world](http://localhost:3000/hello/world)
replacing `world` with your name, a pet's name, or whatever you want.
<!-- END_IGNORE -->

Congratulations, you've created your first Dapr service! I'm excited to see what you'll do with it!

## More Information

- [Packagist](https://packagist.org/packages/dapr/php-sdk)
- [Dapr SDK serialization]({{% ref sdk-serialization.md %}})
164 changes: 164 additions & 0 deletions sdkdocs/php/content/en/php-sdk-docs/php-actors/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
---
type: docs
title: "Virtual Actors"
linkTitle: "Actors"
weight: 1000
description: How to build actors
no_list: true
---

If you're new to the actor pattern, the best place to learn about the actor pattern is in
the [Actor Overview.]({{% ref actors-overview.md %}})

In the PHP SDK, there are two sides to an actor, the Client, and the Actor (aka, the Runtime). As a client of an actor,
you'll interact with a remote actor via the `ActorProxy` class. This class generates a proxy class on-the-fly using one
of several configured strategies.

When writing an actor, state can be managed for you. You can hook into the actor lifecycle, and define reminders and
timers. This gives you considerable power for handling all types of problems that the actor pattern is suited for.

## The Actor Proxy

Whenever you want to communicate with an actor, you'll need to get a proxy object to do so. The proxy is responsible for
serializing your request, deserializing the response, and returning it to you, all while obeying the contract defined by
the specified interface.

In order to create the proxy, you'll first need an interface to define how and what you send and receive from an actor.
For example, if you want to communicate with a counting actor that solely keeps track of counts, you might define the
interface as follows:

```php
<?php
#[\Dapr\Actors\Attributes\DaprType('Counter')]
interface ICount {
function increment(int $amount = 1): void;
function get_count(): int;
}
```

It's a good idea to put this interface in a shared library that the actor and clients can both access (if both are written in PHP). The `DaprType`
attribute tells the DaprClient the name of the actor to send to. It should match the implementation's `DaprType`, though
you can override the type if needed.

```php
<?php
$app->run(function(\Dapr\Actors\ActorProxy $actorProxy) {
$actor = $actorProxy->get(ICount::class, 'actor-id');
$actor->increment(10);
});
```

## Writing Actors

To create an actor, you need to implement the interface you defined earlier and also add the `DaprType` attribute. All
actors *must* implement `IActor`, however there's an `Actor` base class that implements the boilerplate making your
implementation much simpler.

Here's the counter actor:

```php
<?php
#[\Dapr\Actors\Attributes\DaprType('Count')]
class Counter extends \Dapr\Actors\Actor implements ICount {
function __construct(string $id, private CountState $state) {
parent::__construct($id);
}

function increment(int $amount = 1): void {
$this->state->count += $amount;
}

function get_count(): int {
return $this->state->count;
}
}
```

The most important bit is the constructor. It takes at least one argument with the name of `id` which is the id of the
actor. Any additional arguments are injected by the DI container, including any `ActorState` you want to use.

### Actor Lifecycle

An actor is instantiated via the constructor on every request targeting that actor type. You can use it to calculate
ephemeral state or handle any kind of request-specific startup you require, such as setting up other clients or
connections.

After the actor is instantiated, the `on_activation()` method may be called. The `on_activation()` method is called any
time the actor "wakes up" or when it is created for the first time. It is not called on every request.

Next, the actor method is called. This may be from a timer, reminder, or from a client. You may perform any work that
needs to be done and/or throw an exception.

Finally, the result of the work is returned to the caller. After some time (depending on how you've configured the
service), the actor will be deactivated and `on_deactivation()` will be called. This may not be called if the host dies,
daprd crashes, or some other error occurs which prevents it from being called successfully.

## Actor State

Actor state is a "Plain Old PHP Object" (POPO) that extends `ActorState`. The `ActorState` base class provides a couple
of useful methods. Here's an example implementation:

```php
<?php
class CountState extends \Dapr\Actors\ActorState {
public int $count = 0;
}
```

## Registering an Actor

Dapr expects to know what actors a service may host at startup. You need to add it to the configuration:

{{< tabpane text=true >}}

{{% tab header="Production" %}}

If you want to take advantage of pre-compiled dependency injection, you need to use a factory:

```php
<?php
// in config.php

return [
'dapr.actors' => fn() => [Counter::class],
];
```

All that is required to start the app:

```php
<?php

require_once __DIR__ . '/vendor/autoload.php';

$app = \Dapr\App::create(
configure: fn(\DI\ContainerBuilder $builder) => $builder->addDefinitions('config.php')->enableCompilation(__DIR__)
);
$app->start();
```

{{% /tab %}}
{{% tab header="Development" %}}

```php
<?php
// in config.php

return [
'dapr.actors' => [Counter::class]
];
```

All that is required to start the app:

```php
<?php

require_once __DIR__ . '/vendor/autoload.php';

$app = \Dapr\App::create(configure: fn(\DI\ContainerBuilder $builder) => $builder->addDefinitions('config.php'));
$app->start();
```

{{% /tab %}}
{{< /tabpane >}}
Loading
Loading