Skip to content

Latest commit

 

History

History
74 lines (49 loc) · 4.24 KB

development.md

File metadata and controls

74 lines (49 loc) · 4.24 KB

Developing y-php

This document is for working on the port itself. For using the library, start with the README.

Setup

composer install
composer test       # full suite: unit + conformance
composer lint       # PHPCS (WordPress Coding Standards, naming sniffs excluded)
composer lint:fix   # PHPCBF auto-fixes

Run a single test class or filter by name:

vendor/bin/phpunit tests/Unit/YTextTest.php
vendor/bin/phpunit --filter testBasicInsertAndDelete

Prime directive

The wire format is the contract. PHP output must be byte-identical to JS Yjs v13.6.31 for the same logical operations. When in doubt, mirror the JS source (in node_modules/yjs/src/ after npm install, or the yjs repository at tag v13.6.31) line for line rather than improving it. Refactors that change encoded bytes, iteration order, or the struct class hierarchy are bugs, even if all unit tests still pass.

The full rules live in plan/CONTEXT.md, and cross-cutting choices made during the port are logged in plan/DECISIONS.md. Read both before changing library code; append to DECISIONS.md when you make a new cross-cutting decision.

Test architecture

The suite has two complementary halves:

1. Translated unit tests (tests/Unit/)

Each upstream yjs/tests/*.tests.js file is translated to a PHPUnit class (y-array.tests.js becomes YArrayTest.php, and so on). These include the fuzz tests (applyRandomTests), which are driven by an exact port of lib0's Xoroshiro128plus PRNG, so a given seed produces the identical operation sequence in PHP and in JS. The supporting machinery lives in tests/Support/:

  • TestConnector / TestYInstance: the simulated network from upstream testHelper.js.
  • compare(): asserts that all simulated users converge to identical JSON, state vectors, delete sets, and struct stores.

Because translations can drift from the JS source, unit tests alone are not trusted for interop. That is the job of:

2. Conformance tests (tests/Conformance/)

These replay fixtures captured from the real JavaScript Yjs and assert byte-for-byte equality in both directions (decode JS bytes, re-encode to identical bytes, and produce identical bytes from the same logical operations). The fixtures live in tests/fixtures/ and cover encoding primitives, update codecs (V1 and V2), delete sets, update utilities, multi-user convergence logs, and updates captured from real browser clients.

Regenerating fixtures

Fixtures are generated by a Node script that drives the actual JS implementation:

npm install
npm run gen-fixtures

The JS source this port mirrors is pinned in package.json (yjs 13.6.31 and lib0 0.2.99) and resolved from node_modules. The one upstream file the published yjs package does not ship, tests/compatibility.tests.js, is vendored verbatim at tools/compatibility.tests.js. Regenerate fixtures only when intentionally changing what the fixtures cover; the point of the suite is that the PHP side moves to match JS, never the reverse.

Repository layout

src/
├── Lib0/           # ported lib0 primitives (Encoding, Decoding, Buffer, Prng, ...)
├── Structs/        # Item, GC, Skip, Content* (the CRDT structs)
├── Types/          # AbstractType, YArray, YMap, YText, YXml*
├── Utils/          # Doc, Transaction, StructStore, DeleteSet, codecs, ...
├── Functions/      # namespace functions (applyUpdate, snapshot, ...)
├── Protocols/      # Sync (y-protocols/sync subset)
├── functions.php   # function loader (Composer "files" autoload)
├── aliases.php     # short Yjs\* class aliases
└── Yjs.php         # static facade over the namespace functions

The class layout intentionally matches yjs/src/ one-to-one so that diffs against upstream stay mechanical.

Coding standard

WordPress Coding Standards via PHPCS (see phpcs.xml.dist), with the naming and file-name sniffs disabled so the camelCase Yjs API and PSR-4 file names are preserved. PHP floor is 7.4 (WordPress compatibility): typed properties and arrow functions are fine; enums, readonly, and union type declarations are not. Use PHPDoc for types the language level cannot express.