cosmira / yii1-strangler-proxy
A Yii 1.1 filter for incremental JSON API migration with contract adaptation and legacy access checks.
Requires
- php: ^8.2
- ext-json: *
- guzzlehttp/guzzle: ^7.7
- yiisoft/yii: ^1.1.31
Requires (Dev)
- cosmira/soda: ^1.2.1
- infection/infection: ^0.32.4
- laravel/pint: ^1.27
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^11.5
- rector/rector: ^2.6.7
Suggests
- ext-curl: Use the default cURL handler for large multipart uploads without buffering the entire request body.
Provides
None
Conflicts
None
Replaces
None
- dev-main
- 5.3.0
- 5.2.0
- 5.1.0
- 5.0.0
- 4.1.0
- 4.0.0
- 3.0.0
- 2.1.0
- 2.0.6
- 2.0.5
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 1.5.0
- 1.4.0
- 1.3.2
- 1.3.1
- 1.3.0
- 1.2.0
- 1.1.0
- 1.0.0
- v0.1.0
- dev-codex/database-field-contract
- dev-codex/declarative-route-bodies
- dev-codex/native-write-relations
- dev-codex/model-contracts
- dev-codex/controller-conventions
- dev-codex/resource-owned-contracts
- dev-codex/controller-contracts
- dev-codex/large-file-streaming
- dev-codex/release-2.0.6
- dev-codex/release-2.0.5
- dev-codex/release-2.0.4
- dev-codex/release-2.0.3
- dev-codex/release-2.0.2
- dev-codex/release-2.0.1
- dev-codex/release-2.0
This package is auto-updated.
Last update: 2026-11-09 16:09:56 UTC
README
Documentation for 5.3.0.
Move Yii 1.1 API actions to a new backend without changing the existing client. Keep authentication, sessions, and access checks in Yii. One fluent filter forwards enabled actions and translates differences between the two API contracts. Unmapped actions and disabled features continue to run in Yii.
Requires PHP 8.2+ and Yii 1.1.31+. Supports JSON APIs, form-to-JSON mapping, and selected multipart uploads.
- Installation
- Forward your first action
- Describe contract differences
- Connect existing REST controllers
- Run the complete example
- Documentation
Installation
Install the package in your Yii application:
composer require cosmira/yii1-strangler-proxy:5.3.0
Load Composer's vendor/autoload.php before the Yii bootstrap. Commit your
application's composer.lock and use composer install for deployments.
Forward your first action
In your application's Yii configuration array, add params.strangler.
This example uses an existing backend endpoint returning {"ID":42,"NAME":"Desk"}:
'params' => [ 'strangler' => [ 'base_uri' => 'https://backend.example', 'features' => ['catalog' => false], ], ],
HTTPS certificates are verified by default. Protect the backend with a trusted channel before enabling the feature; the integration guide includes a server-owned token example.
Import the builder at the top of your existing controller file:
use Cosmira\Strangler\Strangler;
Append this entry to the array returned by filters(), after the existing
security filters. Keep those entries and actionView() in place.
Strangler::proxy('catalog') ->get('view', '/api/items/{id}') ->build(),
Routes set outgoing HTTP methods. Authentication, incoming-method checks, CSRF, and resource access must run in preceding Yii filters.
Forwarding skips the action and its lifecycle hooks. See access checks and cleanup.
First verify that direct backend calls without the trusted token fail, and that
all Yii access checks run before Strangler. Then set params.strangler.features.catalog to the
boolean true, reload your application's configuration and, with a permitted
legacy session, request the existing API:
curl -i --cookie yii-session.txt 'https://legacy.example/index.php?r=catalog/view&id=42'
Expect the backend body {"ID":42,"NAME":"Desk"}, its status, and X-Strangler: 1.
Set params.strangler.features.catalog to false and repeat: Yii should run actionView() instead.
Use an HTTP(S) origin for base_uri, without a path, query, or credentials.
Put the backend path in each route.
When the APIs differ
Without a controller contract, uppercase() enables naming, query translation,
and the Yii response envelope. Its argument names the native primary key:
use Cosmira\Strangler\Strangler; Strangler::proxy('catalog') ->uppercase('ITEM_ID') ->response(fields: ['updated_at' => 'CHANGE_DATE']) ->get('get', '/api/items') ->get('view', '/api/items/{id}') ->post('create', '/api/items') ->put('edit', '/api/items/{id}') ->delete('delete', '/api/items/{id}') ->build();
This convention expects backend records under data. For the view action,
{"data":{"id":42,"updated_at":"2026-11-08"}} becomes
{"state":"data","data":{"ITEM_ID":42,"CHANGE_DATE":"2026-11-08"}}.
Lists also require meta.total. Ordinary response attributes become uppercase;
input, filter, and sort names become lowercase. Describe only naming or structural
differences. The proxy does not restrict names to the backend's supported fields.
Response aliases do not change input names. Use request(fields: [...]) for input
differences. A controller contract supplies the native error envelope; otherwise
configure error mapping.
See API adaptation for body-to-route bindings, relations, request guards, and custom envelopes.
Connect existing REST controllers
If your controllers share a model accessor and a parsed request envelope, connect
those existing methods once. Put this class where Composer or Yii can autoload it.
This example assumes the controllers expose getModel() and data():
use Cosmira\Strangler\ControllerContract; class ApiContract extends ControllerContract { public function model(CController $controller): CActiveRecord { return $controller->getModel(); } public function payload(CController $controller): array { return $controller->data(); } }
Keep one fluent filter in each controller. Declare routes and resource-specific differences:
use Cosmira\Strangler\Strangler; Strangler::proxy('catalog') ->using(new ApiContract()) ->get('view', '/api/items/{id}') ->build();
using() enables naming, query, and response conventions. The package reads the
native model's primary key when handling a selected request, after preceding Yii
filters. Building the filter does not read the model. Conventional
get, view, create, edit, and delete actions also check the incoming method
and request shape. Authentication, permissions, CSRF, and
application-specific rules remain in your existing Yii filters.
Shared names and value conversions belong in the same controller contract.
Reuse a related resource
Declare a resource's differences in its existing controller filter. Related records can reuse that declaration instead of repeating field maps or branching on record IDs:
return Strangler::proxy('catalog') ->using(new ApiContract()) ->response(relations: [ 'items' => ItemController::class, 'owner' => ['controller' => PersonController::class, 'many' => false], ]) ->get('view', '/api/catalogs/{id}') ->build();
Related controllers supply their own Strangler record declarations. Resolution is
lazy and does not run actions or init(). See
requirements and overrides.
Run the complete example
The catalog example runs Yii and a backend against
one SQLite dataset. It requires pdo_sqlite:
git clone --branch 5.3.0 --depth 1 https://github.com/cosmira/yii1-strangler-proxy.git
cd yii1-strangler-proxy
composer install
composer example:verify
The verifier starts local servers, checks legacy and proxied contracts, and removes
its temporary data. Success prints All catalog contracts passed. and a check count.
Before enabling a feature
Only boolean true enables a feature. Missing flags and unmapped actions stay in
Yii. Disabling a flag changes routing; it does not undo backend writes.
The proxy sends X-User-Id and X-Strangler-Locale over your
trusted channel. These headers do not grant
resource permissions. Client Cookie and Authorization are not forwarded by default.
Backend Set-Cookie headers are removed.
There are no retries or fallback to Yii after forwarding. A transport failure
returns 503, even if the backend committed a write. Follow the
operations guide for uncertain outcomes.
For files, use mapFiles(). Large uploads
require cURL for bounded PHP memory; binary downloads must bypass JSON mapping.
See file performance for buffering limits and measurements.
Documentation
| Guide | What it covers |
|---|---|
| API adaptation | Naming conventions, query and body differences, response graphs, and uploads. |
| Compatibility checklist | Request guards, callback scope, related resources, and checks before enabling a feature. |
| Integration | Security filters, trusted headers, configuration, callbacks, and development checks. |
| File performance | Upload transport requirements, buffering, and reproducible measurements. |
| Operations | Deployment, feature flags, diagnostics, rollback, and uncertain writes. |
| Runnable catalog | A complete local migration with native and proxied actions. |
For package development, follow the local checks. Report bugs or propose changes in GitHub issues.