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
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
slug: /developers/architecture/browser-service-workers
---

# サービスワーカー

<!--
# Service Workers
-->

[サービスワーカー](https://developer.mozilla.org/ja/docs/Web/API/Service_Worker_API/Using_Service_Workers) は、ブラウザー内の [`PHPRequestHandler`](/developers/architecture/browser-concepts) を使用して HTTP トラフィックを処理するために使用されます。

<!--
[A Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers) is used to handle the HTTP traffic using the in-browser [`PHPRequestHandler`](/developers/architecture/browser-concepts).
-->

PHP スクリプトが次のページを [iframe ビューポート](/developers/architecture/browser-iframe-rendering) にレンダリングするとします。

<!--
Imagine your PHP script renders the following page [in the iframe viewport](/developers/architecture/browser-iframe-rendering):
-->

```html
<html>
<head>
<title>ジョンのウェブサイト</title>
</head>
<body>
<a href="/">ホーム</a>
<a href="/blog">ブログ</a>
<a href="/contact">お問い合わせ</a>
</body>
</html>
```

<!--
```html
<html>
<head>
<title>John's Website</title>
</head>
<body>
<a href="/">Homepage</a>
<a href="/blog">Blog</a>
<a href="/contact">Contact</a>
</body>
</html>
```
-->

ユーザーが例えば「Blog」リンクをクリックすると、ブラウザは通常、リモートサーバーに HTTP リクエストを送信し、「/blog」ページを取得して、現在の iframe コンテンツの代わりに表示します。しかし、私たちのアプリはリモートサーバー上で実行されていません。ブラウザは 404 ページを表示するだけです。

<!--
When the user clicks, say the `Blog` link, the browser would normally send a HTTP request to the remote server to fetch the `/blog` page and then display it instead of the current iframe contents. However, our app isn't running on the remote server. The browser would just display a 404 page.
-->

[サービスワーカー](https://developer.mozilla.org/ja/docs/Web/API/Service_Worker_API/Using_Service_Workers) を使用します。これは、HTTP リクエストをインターセプトしてブラウザ内で処理するツールです。

<!--
Enter [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers) – a tool to intercept the HTTP requests and handle them inside the browser:
-->

![Service worker data flow](@site/static/img/workers-diagram.webp)

### サービスワーカーのセットアップ

<!--
### Service Worker setup
-->

`/index.html` にあるメインアプリケーションは、サービスワーカーの登録を担当します。

<!--
The main application living in `/index.html` is responsible for registering the service worker.
-->

最小限の設定は次のとおりです。

<!--
Here's the minimal setup:
-->

**/app.js:**

```js
import { registerServiceWorker } from '@php-wasm/web';

function main() {
await registerServiceWorker(
phpClient,
"default", // PHPインスタンススコープ
"/sw.js", // 有効な Service Worker 実装を指定する必要があります。
"1" // スクリプトの再読み込みに使用されるService Workerのバージョン。
Comment on lines +91 to +93
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc references two different service worker script paths: "/sw.js" in the registration example vs /service-worker.js in the text and filename label. This is confusing for readers and makes the setup steps ambiguous. Use a single consistent filename/path across the snippet and the surrounding explanation (either update the registration example to "/service-worker.js" or change the later references to /sw.js).

Copilot uses AI. Check for mistakes.
);

}
```

<!--
```js
import { registerServiceWorker } from '@php-wasm/web';

function main() {
await registerServiceWorker(
phpClient,
"default", // PHP instance scope
"/sw.js", // Must point to a valid Service Worker implementation.
"1" // Service worker version, used for reloading the script.
);

}
```
-->

また、HTTP リクエストを実際にインターセプトしてルーティングする `/service-worker.js` ファイルも別途必要です。最小限の実装は次のようになります。

<!--
You will also need a separate `/service-worker.js` file that actually intercepts and routes the HTTP requests. Here's what a minimal implementation looks like:
-->

**/service-worker.js**:

```js
import { initializeServiceWorker } from '@php-wasm/web';

// 現在のドメイン上のすべての HTTP トラフィックをインターセプトし、
// ワーカー スレッドに渡します。
initializeServiceWorker();
```

<!--
```js
import { initializeServiceWorker } from '@php-wasm/web';

// Intercepts all HTTP traffic on the current domain and
// passes it to the Worker Thread.
initializeServiceWorker();
```
-->
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# スコープ

<!--
# Scopes
-->

スコープを使用すると、2 つの異なるブラウザ タブでアプリを開いたときにアプリが動作し続けます。

<!--
Scopes keep your app working when you open it in two different browser tabs.
-->

サービスワーカーは、インターセプトした HTTP リクエストをレンダリングのために PHPRequestHandler に渡します。技術的には、[`BroadcastChannel`](https://developer.mozilla.org/ja/docs/Web/API/BroadcastChannel) を介してメッセージを送信し、アプリケーションが開いているすべてのブラウザタブに配信されます。これは望ましくない動作であり、処理速度が遅く、予期しない動作につながります。

<!--
The Service Worker passes the intercepted HTTP requests to the PHPRequestHandler for rendering. Technically, it sends a message through a [`BroadcastChannel`](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel) which then gets delivered to every browser tab where the application is open. This is undesirable, slow, and leads to unexpected behaviors.
-->

残念ながら、サービスワーカーは関連するワーカースレッドと直接通信できません。詳細については、[PR #31](https://github.com/WordPress/wordpress-playground/pull/31) および [issue #9](https://github.com/WordPress/wordpress-playground/issues/9) を参照してください。

<!--
Unfortunately, the Service Worker cannot directly communicate with the relevant Worker Thread – see [PR #31](https://github.com/WordPress/wordpress-playground/pull/31) and [issue #9](https://github.com/WordPress/wordpress-playground/issues/9) for more details.
-->

スコープにより、各ブラウザ タブで次の操作が可能になります。

<!--
Scopes enable each browser tab to:
-->

- 送信 HTTP リクエストに固有のタブ ID を付与する
- 異なる ID を持つ「 BroadcastChannel 」メッセージを無視する

<!--
- Brand the outgoing HTTP requests with a unique tab id
- Ignore any `BroadcastChannel` messages with a different id
-->

技術的には、スコープとは `PHPRequestHandler.absoluteUrl` に含まれる文字列です。例えば、次のようになります。

<!--
Technically, a scope is a string included in the `PHPRequestHandler.absoluteUrl`. For example:
-->

- **スコープなしアプリ**では、`/index.php` は `http://localhost:8778/wp-login.php` で利用できます。
- **スコープ付きアプリ**では、`/index.php` は `http://localhost:8778/scope:96253/wp-login.php` で利用できます。

<!--
- In an **unscoped app**, `/index.php` would be available at `http://localhost:8778/wp-login.php`
- In an **scoped app**, `/index.php` would be available at `http://localhost:8778/scope:96253/wp-login.php`
-->

サービス ワーカーはこの概念を認識しており、リクエスト URL にある `/scope:` を関連する `BroadcastChannel` 通信に添付します。

<!--
The service worker is aware of this concept and will attach the `/scope:` found in the request URL to the related `BroadcastChannel` communication.
-->

スコープ付きの `absoluteUrl` で開始されたワーカー スレッドは、**スコープ付き** であると言われます。

<!--
A worker thread initiated with a scoped `absoluteUrl` is said to be **scoped**:
-->

```js
import {
PHP,
setURLScope,
exposeAPI,
parseWorkerStartupOptions,
} from '@php-wasm/web';

// absoluteURL を直接使用しないでください:
const absoluteURL = 'http://127.0.0.1'

// 代わりに、最初にスコープを設定します。
const scope = Math.random().toFixed(16)
const scopedURL = setURLScope(absoluteURL, scope).toString()

const { phpVersion } = parseWorkerStartupOptions<{ phpVersion?: string }>();
const php = await PHP.load('8.0', {
requestHandler: {
documentRoot: '/',
absoluteUrl: scopedSiteUrl
}
});

// API を app.ts に公開します。
const [setApiReady, ] = exposeAPI( php );
setApiReady();
```

<!--
```js
import {
PHP,
setURLScope,
exposeAPI,
parseWorkerStartupOptions,
} from '@php-wasm/web';

// Don't use the absoluteURL directly:
const absoluteURL = 'http://127.0.0.1'

// Instead, set the scope first:
const scope = Math.random().toFixed(16)
const scopedURL = setURLScope(absoluteURL, scope).toString()

const { phpVersion } = parseWorkerStartupOptions<{ phpVersion?: string }>();
const php = await PHP.load('8.0', {
requestHandler: {
documentRoot: '/',
absoluteUrl: scopedSiteUrl
}
});

// Expose the API to app.ts:
const [setApiReady, ] = exposeAPI( php );
setApiReady();
```
-->
Loading