メインコンテンツへスキップ
パッケージに定期的な同期やHeartbeatを追加するには、処理の提供とスケジュールへの登録を分けます。インストールしただけで利用アプリケーションの定期処理を増やさず、利用者が有効化・実行頻度・複数サーバーでの動作を選べる設計にします。 このページではLaravel Framework v13.39.0 の公式ソースを確認し、タスクスケジューリングの基本操作をパッケージ開発に適用します。プロバイダーの読み込みはパッケージの自動検出、設定の公開はパッケージ設定のマージとキャッシュを参照してください。

Scheduleの解決後に登録する

通常のLaravelアプリケーションでは、FoundationServiceProvider が Schedule::class をsingletonとして登録します。その生成処理はConsole Kernelの resolveConsoleSchedule() を呼び、アプリケーションのタイムゾーンとスケジューラ用キャッシュストアを適用します。 パッケージはこの共有Scheduleを拡張し、自分用の new Schedule() を作って終わりにしないようにします。次のプロバイダーは、Acme\Courier\ がComposerのPSR-4で src/ に対応し、パッケージに config/courier.php がある構成です。
callAfterResolving() は、未解決なら今後の解決を待ち、すでに解決済みなら取得してその場でもコールバックを呼びます。これにより、別のプロバイダーが先にScheduleを使った場合も登録できます。詳細はサービス解決後フックを参照してください。 runningInConsole() は特定のArtisanコマンドの判定ではありません。この例ではHTTP経由でScheduleを使う用途を対象外にします。また、フックを登録するプロバイダー自体は遅延プロバイダーにしません。必要な時点までに boot() が動かなければ、フックも登録されないためです。
イベントの登録とタスクの実行は別です。このプロバイダーを読み込んでも、その場でHeartbeatを実行するわけではありません。登録だけではcronや schedule:work も起動しません。

利用者が明示的に有効化する

パッケージ側の config/courier.php は、初期状態を無効にします。
利用アプリケーションでは設定を公開・作成し、schedule.enabled を true に変更します。この例では値を登録時に読み取ります。タイムゾーンはUTCを初期値とし、別のタイムゾーンを選ぶ場合は夏時間による実行の重複・欠落も考慮します。 設定は浅くマージされるため、登録器でも各キーの既定値を指定します。部分的な上書きで schedule 配列のほかのキーがなくなっても、基本設定で登録できます。

同じScheduleへの二重登録を防ぐ

次の src/CourierSchedule.php は、ログ1件を記録する最小のHeartbeatを登録します。WeakMap により、この登録器が同じScheduleオブジェクトへ追加するのは1回だけにします。
この登録器をsingletonにしているので、プロバイダーのフックから再度取得しても登録済み判定を共有します。一方、新しいScheduleには登録できます。プロセス全体の静的な「登録した」フラグで、新しいコンテナやテストのScheduleまで除外しないようにします。 Schedule::call()・exec()・job() はイベントを配列へ追加します。同じ名前のイベントを見つけて置換したり、重複排除したりするAPIではありません。
withoutOverlapping() は登録の重複を修正しません。同じタスクが2件登録され、先の実行が終了してロックが解放されれば、後のイベントも実行され得ます。onOneServer() も二重登録を除去する機能ではありません。上のWeakMapは、この登録器の重複だけを防ぎます。利用者が routes/console.php に同じタスクを追加する場合は、パッケージの自動登録を無効にし、登録の担当を1か所にします。

イベント名はロックの互換性にも関わる

name() は description() の別名ですが、ロック名への影響はイベントの種類で異なります。以下は既定のmutex名です。 CallbackEventでは、withoutOverlapping() や onOneServer() より先に空でない名前を付けます。名前が未設定なら、これらのメソッドは例外を投げます。 job() は通常、ジョブクラス名などを初期名にします。ただし、同じジョブを異なる引数・対象で登録する場合は、対象ごとの名前を明示します。引数だけを変えても、初期名が自動的に別名になるわけではありません。
SyncCatalogJob はパッケージが提供するジョブクラスの例です。ジョブの実装方法はキューを参照してください。 名前やコマンド、cron式を変更すると、種類によってロックの識別子も変わります。旧版と新版のプロセスが混在するデプロイでは、同じ処理でも別のロックを取得する可能性があります。単なる表示文言の変更と同じ扱いにせず、タスク名を安定した公開仕様として管理します。

onOneServerとwithoutOverlappingを分けて考える

  • onOneServer() は、その実行時刻に実行を担当するサーバーを選びます。確認した CacheSchedulingMutex は、イベントのmutex名に時分を加え、1時間の有効期限で保持します。タスク終了に合わせて解放する重複防止ロックとは別です。
  • withoutOverlapping() は、前の実行が続いている間の重複を防ぎます。指定する期限の単位は分で、既定は1,440分です。短すぎる期限では、処理が終わる前にロックが失効します。
  • job() がキューへ投入する場合、スケジュールイベントの完了はジョブの処理完了ではありません。ワーカーでの重複制御は、ジョブ側のユニークジョブや WithoutOverlapping ミドルウェアなども別に設計します。
onOneServer() を有効にする利用者には、公式ドキュメントが指定する database・memcached・dynamodb・redis の共有キャッシュ構成を必要条件として案内します。ロックは業務処理のexactly-onceを保証するものではなく、再実行されても壊れない処理も必要です。

重い依存関係は実行時に解決する

上のHeartbeatの LoggerInterface は、イベント実行時に CallbackEvent がコンテナの call() を使って解決します。フックの登録時にログを出したり、クライアントを生成したりしない構成です。 コンストラクターに外部接続や業務処理を置かない設計は、Artisanコマンドの依存解決でも重要です。 また、call() と job() はCallbackEventなので、runInBackground() は使えません。時間のかかる処理はキューに委譲するか、必要に応じて command() のバックグラウンド実行を選びます。

アプリケーション全体の設定を奪わない

Schedule::useCache() はScheduleが持つmutexオブジェクトのストアを変更します。パッケージのイベントだけを対象にする設定ではなく、同じmutexを共有するイベントにも影響します。パッケージの boot() で無条件に呼ばず、利用アプリケーションが選ぶ設定として扱います。 同様に、Schedule::alwaysOnOneServer()・スケジューラの一時停止・メンテナンスモード中の実行方針を、パッケージが勝手に変更しないようにします。

設定変更後はScheduleを新しく作る

例の enabled・cron式・タイムゾーンは登録時の設定です。生成済みScheduleに対して config() の値を変えるだけでは、イベントが追加・削除されたり、設定が読み直されたりしません。WeakMapの判定も再設定を行うためのものではありません。 設定キャッシュを利用するデプロイではキャッシュを再構築し、新しい設定を読み込むプロセスで schedule:list を確認します。長時間動くスケジューラや、1分未満のタスクを実行中の schedule:run についても、旧コード・旧設定が残らない運用を確認します。サブ分タスクのデプロイ時の中断はタスクスケジューリングを参照してください。

登録を実行から分けてテストする

次は、パッケージのComposerオートロードとPHPUnitを使う tests/CourierScheduleTest.php の例です。mutexをテストダブルにし、外部キャッシュや実際のcronを使わずに登録結果を確認します。
このテストは登録器の単体テストです。プロバイダーの自動検出・設定マージ・Console Kernelへの統合は、Orchestra Testbenchや検証用アプリケーションで別に確認します。 登録テストを通すために schedule:run で利用アプリケーションの全タスクを実行しないようにします。本体のテストと、制御した環境でのスケジューラ統合テストを分けることで、無関係な定期処理の副作用を避けられます。

関連ページ

タスクスケジューリング

実行頻度、cronの導入、キューへの委譲、デプロイ時の運用を確認します。

パッケージのサービス解決後フック

未解決・解決済みサービスへの適用と、再登録時の注意点を確認します。

参照した一次情報

最終更新日 2026年11月1日