Copy this file for your language port and check items off as you go.
The purpose of tests, examples, and docs is to prove complete implementation. If a feature has no test and no example, it's not proven to work. Every feature must have at least one test exercising it.
Target Language: PHP Start Date: 2026-03-28 Python SDK Reference: ~/src/signalwire-python (the source of truth)
- Module/package initialized with git repo (main branch)
- Directory structure (see PORTING_GUIDE.md Module Layout)
- .gitignore
- README.md with quickstart example
- CLAUDE.md with development guidance
- Dependency file (composer.json)
- Logging system (levels: debug/info/warn/error, env: SIGNALWIRE_LOG_LEVEL, suppression: SIGNALWIRE_LOG_MODE=off)
- Tests: logger creation, level filtering, suppression, env var config
- Commit to git
- SWML Document model (version, sections, verbs, JSON rendering)
- Schema loaded from schema.json (embedded in package)
- 38 verb methods auto-vivified from schema (see PORTING_GUIDE.md for mapping)
- Sleep verb: takes integer, not map
- AI verb: present but overridden by AgentBase
- SWMLService HTTP server
- Basic auth (auto-generated or SWML_BASIC_AUTH_USER/PASSWORD)
- Security headers (X-Content-Type-Options, X-Frame-Options, Cache-Control)
- /health, /ready endpoints (no auth)
- Routing callbacks
- SIP username extraction from request body
- SWML_PROXY_URL_BASE support
- Tests: document CRUD, schema loads 38 verbs, all verb methods callable, service auth, HTTP endpoints, security headers
- Commit to git
- Constructor(response, post_process)
- SetResponse, SetPostProcess, AddAction, AddActions, ToMap/ToDict
- Serialization rules: response always, action only if non-empty, post_process only if true
- All 40+ action methods (see SWAIG_FUNCTION_RESULT_REFERENCE.md for exact JSON)
- Payment helpers: CreatePaymentPrompt, CreatePaymentAction, CreatePaymentParameter
- Method chaining on all methods
- Tests: construction, serialization, each action category (connect, hangup, say, update_global_data, record_call, toggle_functions, execute_rpc, send_sms, payment helpers), method chaining
- HMAC-SHA256 token creation (functionName:callID:expiry, signed, base64)
- Token validation (timing-safe comparison, expiry check)
- Random 32-byte secret per manager — fail hard if entropy unavailable
- Default expiry: 3600 seconds
- Tests: token round-trip, wrong function/callID rejected, expired rejected, tampered rejected
- Fluent builder: New, Purpose/Description, Parameter (with enum), Expression
- Webhook, WebhookExpressions, Body, Params, Foreach
- Output, FallbackOutput, ErrorKeys, GlobalErrorKeys
- ToSwaigFunction serialization
- CreateSimpleApiTool helper
- CreateExpressionTool helper
- Tests: fluent chain, parameters, webhook config, expressions, serialization, helpers
- ContextBuilder: AddContext, GetContext, Validate, ToMap
- Context: AddStep, GetStep, RemoveStep, MoveStep, all setters, ToMap
- Step: all setters (text, sections, criteria, functions, navigation, gather, reset), ToMap
- GatherInfo and GatherQuestion
- CreateSimpleContext helper
- Validation: single context must be "default"
- Tests: step config, context with steps, gather info, serialization, validation rules, fillers, MoveStep
- Constructor with functional options / builder pattern
- Prompt: SetPromptText, SetPostPrompt, POM (AddSection, AddSubsection, AddToSection, HasSection)
- Tools: DefineTool (with handler), RegisterSwaigFunction (DataMap), DefineTools, OnFunctionCall
- AI Config: hints, pattern hints, languages, pronunciations, params, global data, native functions, fillers, debug events, function includes, LLM params
- Verbs: AddPreAnswerVerb, AddAnswerVerb, AddPostAnswerVerb, AddPostAiVerb, Clear methods
- Contexts: DefineContexts returns ContextBuilder
- Skills: AddSkill one-liner, RemoveSkill, ListSkills, HasSkill
- Web: DynamicConfigCallback, proxy URL, webhook URL, post-prompt URL, query params
- SIP: EnableSipRouting, RegisterSipUsername, extractSipUsername utility
- Lifecycle: OnSummary, OnDebugEvent
- SWML Rendering: 5-phase pipeline (pre-answer, answer, post-answer, AI, post-AI)
- HTTP endpoints: / (SWML), /swaig (tool dispatch), /post_prompt (summary), /health, /ready
- Dynamic config: clone agent, apply callback, render from clone, original not mutated
- Webhook URL construction with auth and query params
- Run/Serve/AsRouter (or framework-specific mount)
- Request body size limit (1MB) on all POST handlers
- Tests: construction, prompt modes, tool registration+dispatch, AI config, verbs, contexts, skills integration, render_swml structure, dynamic config isolation, HTTP endpoints (auth, SWML, swaig dispatch, post_prompt, health), method chaining
- Commit to git
- SkillBase interface (see SKILLS_MANIFEST.md for full contract)
- BaseSkill with default implementations
- SkillManager: LoadSkill, UnloadSkill, ListLoadedSkills, HasSkill, GetSkill
- SkillRegistry: RegisterSkill, GetSkillFactory, ListSkills
- All 18 built-in skills (see SKILLS_MANIFEST.md for exact specifications):
- datetime (get_current_time, get_current_date)
- math (calculate — safe evaluator, no eval)
- joke (tell_joke)
- weather_api (get_weather — HTTP to WeatherAPI.com)
- web_search (web_search — HTTP to Google CSE)
- wikipedia_search (search_wiki — HTTP to Wikipedia API)
- google_maps (lookup_address, compute_route)
- spider (scrape_url — HTTP fetch + HTML strip)
- datasphere (search_datasphere — HTTP to SignalWire DataSphere)
- datasphere_serverless (DataMap-based DataSphere)
- swml_transfer (transfer_call — pattern matching)
- play_background_file (play/stop background audio)
- api_ninjas_trivia (get_trivia)
- native_vector_search (search_knowledge — network mode only)
- mcp_gateway (MCP client — connects to an MCP gateway)
- info_gatherer (start_questions + submit_answer — stateful)
- claude_skills (SKILL.md file loading)
- custom_skills (user-defined tools from config)
- Tests: registry lists 18, each instantiable, skills without env vars setup OK, datetime+math handlers execute, SkillManager load/unload
- Commit to git
- InfoGathererAgent (questions → start_questions + submit_answer tools)
- SurveyAgent (typed questions → validate_response + log_response tools)
- ReceptionistAgent (departments → collect_caller_info + transfer_call tools)
- FAQBotAgent (FAQs → search_faqs tool with keyword scoring)
- ConciergeAgent (venue → check_availability + get_directions tools)
- Tests: each constructible, each has expected tools, tool handlers execute
- Commit to git
- Register/Unregister agents by route
- GetAgents/GetAgent
- SIP routing (SetupSipRouting, RegisterSipUsername)
- Static file serving (with path traversal protection, security headers, MIME types)
- Health/ready endpoints
- Root index listing agents
- Run with HTTP server
- Tests: register/unregister, get agents, health endpoint, route dispatch, SIP routing, static file serving
- Commit to git
- WebSocket connection to wss://{space}
- JSON-RPC 2.0 framing
- Authentication (project/token and JWT)
- Authorization state for fast reconnect
- Auto-reconnect with exponential backoff (1s → 30s)
- 4 correlation mechanisms (JSON-RPC id, call_id, control_id, tag)
- Event ACK (immediate response to signalwire.event)
- Ping handling (respond to signalwire.ping)
- Server disconnect handling (restart flag)
- Context subscription/unsubscription
- Call object with 30+ methods (see RELAY_IMPLEMENTATION_GUIDE.md)
- 11 action types with Wait/Stop/IsDone/OnCompleted
- PlayAction: Pause, Resume, Volume
- play_and_collect gotcha handled (filter by event_type)
- detect gotcha handled (resolve on first meaningful result)
- dial tag correlation (call_id nested in params.call.call_id)
- call-gone (404/410) handled gracefully
- 22+ typed event types
- SMS/MMS messaging (SendMessage, OnMessage, delivery tracking)
- Tests: constants, event parsing (all types), action wait/resolve/callback, call creation, message state, client construction, correlation mechanism verification
- Commit to git
- HTTP client with Basic Auth
- CrudResource (List, Create, Get, Update, Delete)
- Pagination support
- SignalWireRestError
- All namespaces (see rest-apis/ OpenAPI specs):
- Fabric (13 sub-resources)
- Calling (37 commands)
- PhoneNumbers
- Datasphere
- Video
- Addresses, Queues, Recordings
- NumberGroups, VerifiedCallers, SipProfile
- Lookup, ShortCodes, ImportedNumbers
- MFA, Registry, Logs
- Project, PubSub, Chat
- Tests: client creation, all namespaces initialized (non-nil), CRUD path construction, error formatting, sub-resource verification
- Commit to git
- AWS Lambda adapter
- Google Cloud Functions adapter
- Azure Functions adapter
- CGI adapter
- Auto-detection
- swaig-test: --url, --dump-swml, --list-tools, --exec, --param, --raw, --verbose
- URL auth extraction (http://user:pass@host:port/path)
- Tests: URL parsing, param parsing, integration with live agent
- Commit to git
Documentation and examples prove the implementation is complete and usable. The requirement is 100% of the Python SDK's docs and examples (except search-related). If Python has it, the port has it.
- architecture.md, agent_guide.md, api_reference.md
- swaig_reference.md, datamap_guide.md, contexts_guide.md
- skills_system.md, skills_parameter_schema.md, third_party_skills.md
- security.md, configuration.md, llm_parameters.md, sdk_features.md
- cli_guide.md, swml_service_guide.md
- relay/README.md (API overview, quick start, code examples in target language)
- relay/RELAY_IMPLEMENTATION_GUIDE.md (copy from porting-sdk)
- relay/docs/ (getting-started, call-methods, events, messaging, client-reference)
- relay/examples/relay_answer_and_welcome.php
- relay/examples/relay_dial_and_play.php
- relay/examples/relay_ivr_connect.php
- rest/README.md (API overview, namespace examples in target language)
- rest/docs/ (getting-started, namespaces, calling, fabric, client-reference)
- rest/examples/rest_10dlc_registration.php
- rest/examples/rest_calling_ivr_and_ai.php
- rest/examples/rest_calling_play_and_record.php
- rest/examples/rest_datasphere_search.php
- rest/examples/rest_fabric_conferences_and_routing.php
- rest/examples/rest_fabric_subscribers_and_sip.php
- rest/examples/rest_fabric_swml_and_callflows.php
- rest/examples/rest_manage_resources.php
- rest/examples/rest_phone_number_management.php
- rest/examples/rest_queues_mfa_and_recordings.php
- rest/examples/rest_video_rooms.php
- examples/README.md (index with descriptions)
- simple_agent.php
- simple_dynamic_agent.php
- multi_agent_server.php
- contexts_demo.php
- datamap_demo.php
- skills_demo.php
- session_state.php
- call_flow.php
- relay_demo.php
- rest_demo.php
- prefab_info_gatherer.php
- prefab_survey.php
Tests are proof of implementation. The port must test everything the Python SDK tests. Read the Python test files in tests/unit/ and ensure equivalent coverage exists in your port for every tested behavior.
- Every public method has at least one test exercising it
- Every test the Python SDK has (except search-related) has an equivalent in the port
- All tests pass with zero failures, no tests skipped (634 tests, 1898 assertions)
- Test coverage matches Python SDK organization:
- Core: agent_base, swml_service, swml_builder, swml_renderer, swml_handler
- SWAIG: swaig_function, function_result (all 40+ actions)
- Security: session_manager, auth_handler
- DataMap: data_map (all builder methods, serialization)
- Contexts: contexts (steps, navigation, validation, gather_info)
- Mixins/Config: prompt, tool, web, auth, serverless, state, ai_config, skill
- Skills: registry, manager, each of the 18 built-in skills individually
- Prefabs: each of the 5 prefab agents
- AgentServer: registration, routing, SIP, static files
- RELAY: client, call, action types, events, messages
- REST: client, base resource, each major namespace, pagination
- CLI: argument parsing, tool listing, execution
- Utilities: schema_utils, logging, pom_builder, type_inference
- Every AgentBase public method from Python SDK has an equivalent
- All 40+ SwaigFunctionResult action methods present (including payment helpers)
- All 38 SWML verb methods present and schema-validated
- RELAY client: 4 correlation mechanisms implemented
- REST client: all 22 namespaces initialized with correct paths
- Skills registry: all 18 built-in skills registered
- agent.AddSkill() one-liner integration works (not just manual SkillManager)
- SIP username extraction utility exists
- Static file serving in AgentServer with path traversal protection
- No TODO/FIXME/HACK/PLACEHOLDER comments remain
- Every example compiles/runs without syntax errors (27 examples)
- Top-level relay/ and rest/ directories have README, docs, examples
- Basic auth uses timing-safe comparison (hash_equals, NOT
==) - Passwords never appear in log output ([REDACTED])
- No weak fallback passwords — fail to start if crypto/rand fails (RuntimeException)
- All POST handlers enforce request body size limits (1MB)
- SIP username extraction validates input format ([a-zA-Z0-9._-], max 64)
- JSON parse errors are checked, not silently ignored
- PHP is single-threaded — no mutex needed (N/A)
- HMAC token validation uses timing-safe comparison (hash_equals)
- Security headers set on all authenticated endpoints (nosniff, DENY, no-store)
- Third-party dependencies: PHPUnit only (dev), no runtime deps with known vulns
- General review: no injection, XSS, SSRF vulnerabilities found