<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: wadakatu</title>
    <description>The latest articles on DEV Community by wadakatu (@wadakatu).</description>
    <link>https://dev.to/wadakatu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F805171%2F0d3c6ecc-b57f-4cd8-a0ee-59151e4439d3.jpeg</url>
      <title>DEV Community: wadakatu</title>
      <link>https://dev.to/wadakatu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wadakatu"/>
    <language>en</language>
    <item>
      <title>Spec-driven vs consumer-driven contract testing in PHP: when to use Gesso vs Pact</title>
      <dc:creator>wadakatu</dc:creator>
      <pubDate>Tue, 28 Jul 2026 14:00:35 +0000</pubDate>
      <link>https://dev.to/wadakatu/spec-driven-vs-consumer-driven-contract-testing-in-php-when-to-use-gesso-vs-pact-b6a</link>
      <guid>https://dev.to/wadakatu/spec-driven-vs-consumer-driven-contract-testing-in-php-when-to-use-gesso-vs-pact-b6a</guid>
      <description>&lt;p&gt;PHP contract testing splits into two shapes: &lt;strong&gt;spec-driven&lt;/strong&gt;, where an OpenAPI document is the source of truth and every request and response is validated against it, and &lt;strong&gt;consumer-driven&lt;/strong&gt;, where each consumer records its expectations and the provider replays them. Pact PHP is the reference tool for the second shape. Gesso is a reference tool for the first. This guide is a decision framework for picking between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is spec-driven contract testing?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Spec-driven contract testing verifies that the live implementation still matches an OpenAPI document you publish.&lt;/strong&gt; The spec is the source of truth. The tests confirm that every request the provider accepts and every response it returns conforms to that spec — nothing more, nothing less.&lt;/p&gt;

&lt;p&gt;In PHP, this is what Gesso (&lt;code&gt;studio-design/gesso&lt;/code&gt;) does. You point it at an OpenAPI 3.0/3.1/3.2 file, add one trait to your PHPUnit or Pest test case, and each assertion validates the traffic against the spec while accumulating endpoint coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is consumer-driven contract testing?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Consumer-driven contract testing verifies that a provider still satisfies the expectations recorded by each of its consumers.&lt;/strong&gt; The consumers own the contracts. Each consumer runs a mock server, records the requests it sends and the responses it expects, and publishes the resulting pact file. The provider then verifies those pacts against a live implementation.&lt;/p&gt;

&lt;p&gt;In PHP, this is what Pact PHP (&lt;code&gt;pact-foundation/pact-php&lt;/code&gt;) does. Since v10 it binds to the shared Pact core used by every Pact language implementation, so matching rules stay consistent across stacks (this requires ext-ffi with &lt;code&gt;ffi.enable=true&lt;/code&gt;). It gives you a mock server for consumer tests, and a verifier that replays pacts against your provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Spec-driven (Gesso)&lt;/th&gt;
&lt;th&gt;Consumer-driven (Pact PHP)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Source of truth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenAPI document&lt;/td&gt;
&lt;td&gt;Consumer-recorded pacts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who writes the contract&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The provider team (API author)&lt;/td&gt;
&lt;td&gt;Each consumer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What is verified&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Live request/response conform to the spec&lt;/td&gt;
&lt;td&gt;Live provider still satisfies each consumer's expectations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runs where&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Provider's PHPUnit / Pest suite&lt;/td&gt;
&lt;td&gt;Consumer's test suite (records the pact) + the provider's verification run — also PHPUnit-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shared infrastructure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None — the spec file lives in the repository&lt;/td&gt;
&lt;td&gt;None required — pact files can move as CI artifacts; a Pact Broker / PactFlow is what makes cross-team sharing and &lt;code&gt;can-i-deploy&lt;/code&gt; practical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Framework surface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Framework-independent core + Laravel / Symfony / Pest / PSR-7 adapters&lt;/td&gt;
&lt;td&gt;Framework-agnostic: provider verification targets any HTTP-reachable app, so no per-framework adapter is needed (v10 runs on the shared Pact core via ext-ffi)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What the tooling reports&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Which spec operations, statuses, and content types are exercised&lt;/td&gt;
&lt;td&gt;Verification status for each interaction, per consumer version and environment (via the broker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consumers you don't control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Covered as far as the spec describes them — anything your suite exercises is checked against the same document they code against&lt;/td&gt;
&lt;td&gt;Out of scope by design — a contract exists only for consumers that publish one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expectations the spec doesn't encode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Out of scope by design — Gesso enforces exactly what the document states&lt;/td&gt;
&lt;td&gt;This is the point — each consumer pins what it actually depends on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Detects drift from published docs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not its concern — pacts describe consumer expectations, not the published document (PactFlow's bi-directional contract testing adds this comparison separately)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When spec-driven (Gesso) fits better
&lt;/h2&gt;

&lt;p&gt;Reach for spec-driven testing when the spec is the contract in practice.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You publish OpenAPI as your API's public documentation.&lt;/strong&gt; Every consumer — internal, external, unknown — is expected to code against the spec. Drift between docs and implementation is the failure mode you care about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You own the API and don't control every consumer.&lt;/strong&gt; Public APIs, partner APIs, and internal APIs consumed by teams you don't sit with all share this shape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You already generate SDKs, mocks, or docs from OpenAPI.&lt;/strong&gt; The spec is load-bearing; contract testing should reinforce it, not compete with it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want per-endpoint coverage as a first-class output.&lt;/strong&gt; Gesso reports the share of operations and &lt;code&gt;(method, path, status, content-type)&lt;/code&gt; rows exercised by your suite, so a merged PR that quietly stops testing an endpoint shows up in the coverage delta.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want contract enforcement to be a plain assertion in the suite you already run.&lt;/strong&gt; No artifact to publish, nothing to coordinate between repositories — the check runs on the same PR that changes the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimal shape (Symfony; the Laravel, Pest, and plain-PSR-7 variants read almost identically):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Studio\Gesso\Attribute\OpenApiSpec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Studio\Gesso\Symfony\OpenApiAssertions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Bundle\FrameworkBundle\Test\WebTestCase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="na"&gt;#[OpenApiSpec('petstore')]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PetsApiTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WebTestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;OpenApiAssertions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_get_pets_matches_spec&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/pets'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertResponseIsSuccessful&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertClientMatchesOpenApiSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every passing assertion also records coverage against the pinned spec.&lt;/p&gt;

&lt;h2&gt;
  
  
  When consumer-driven (Pact PHP) fits better
&lt;/h2&gt;

&lt;p&gt;Reach for consumer-driven testing when consumers have expectations the spec can't or shouldn't capture.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You own several services that call each other.&lt;/strong&gt; You control both sides. Consumers can publish pacts, providers can verify them, and the broker becomes the integration ledger for the whole platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consumer expectations are narrower than the spec.&lt;/strong&gt; A consumer may only care about three fields of a response even though the spec defines twenty. Pact makes that narrow expectation explicit and lets the provider evolve the rest safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want a cross-service break to block the deploy of the service that caused it.&lt;/strong&gt; Provider verification runs against a candidate build, and a failed verification makes &lt;code&gt;can-i-deploy&lt;/code&gt; refuse that service's release — the failure lands on the provider's pipeline instead of surfacing in each consumer later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want to answer "can I deploy this?" for a specific environment.&lt;/strong&gt; The Pact Broker's &lt;code&gt;can-i-deploy&lt;/code&gt; check is the workflow output; there is no direct equivalent in spec-driven testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimal shape on the consumer side (Pact PHP v10; requires &lt;code&gt;ffi.enable=true&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PhpPact\Consumer\InteractionBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PhpPact\Consumer\Model\ConsumerRequest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PhpPact\Consumer\Model\ProviderResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PhpPact\Standalone\MockService\MockServerConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockServerConfig&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PetsWebApp'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PetsApi'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setPort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConsumerRequest&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/pets'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProviderResponse&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setBody&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Fido'&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;

&lt;span class="nv"&gt;$builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InteractionBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$builder&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;uponReceiving&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a request for pets'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;willRespondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// must be last — registers the interaction&lt;/span&gt;

&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PetsClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBaseUri&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Fido'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$builder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provider later verifies this pact against a real handler wired into its test harness.&lt;/p&gt;

&lt;h2&gt;
  
  
  A short decision framework
&lt;/h2&gt;

&lt;p&gt;Pick spec-driven (Gesso) when at least two of these are true:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OpenAPI is already the artifact you publish for consumers to code against.&lt;/li&gt;
&lt;li&gt;You don't fully control every consumer of the API.&lt;/li&gt;
&lt;li&gt;You want contract enforcement to live inside PHPUnit or Pest with no extra infrastructure.&lt;/li&gt;
&lt;li&gt;Drift between docs and implementation is the specific failure you keep hitting.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pick consumer-driven (Pact PHP) when at least two of these are true:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You own the consumers and the provider, and they ship independently.&lt;/li&gt;
&lt;li&gt;Consumer expectations are narrower or more specific than the full spec.&lt;/li&gt;
&lt;li&gt;You want &lt;code&gt;can-i-deploy&lt;/code&gt; gating on a broker as part of your release flow.&lt;/li&gt;
&lt;li&gt;Multiple consumer teams need to encode expectations the provider team doesn't get to write for them.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When &lt;em&gt;not&lt;/em&gt; to use Gesso
&lt;/h2&gt;

&lt;p&gt;Spec-driven testing isn't a universal answer. Gesso is not the right pick when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You don't publish an OpenAPI document.&lt;/strong&gt; Gesso needs a spec on disk, and writing one only to enable testing is the wrong order. Consumer-driven contracts don't need a spec at all — if you want contract testing now, Pact is the shorter path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The contracts you care about are cross-service, consumer-owned expectations.&lt;/strong&gt; Two internal services with a small, stable integration surface get more from a pact than from a full OpenAPI validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need &lt;code&gt;can-i-deploy&lt;/code&gt; gating tied to a broker.&lt;/strong&gt; Gesso doesn't provide it; the Pact ecosystem does — the check itself ships with the Pact Broker / PactFlow, not with pact-php.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You test against non-HTTP transports&lt;/strong&gt; — asynchronous messages (Pact's message pact), or gRPC via the Pact Protobuf plugin. Gesso validates HTTP requests and responses against OpenAPI; it has no message-pact equivalent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your team is drowning in flaky end-to-end tests between two services you both own.&lt;/strong&gt; In that shape, consumer-driven contracts replace those end-to-end tests more directly, because the pact is the thing the two teams argue about.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Can you use both?
&lt;/h2&gt;

&lt;p&gt;Yes, and larger PHP shops often do. A common pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gesso enforces that the provider's implementation still matches the OpenAPI document on every PR. Public / partner consumers rely on the spec being trustworthy.&lt;/li&gt;
&lt;li&gt;Pact covers the handful of internal services with narrow, cross-team expectations that don't belong in the public spec — think an internal billing pipeline consuming three fields from an account service that publishes twenty.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two tools don't fight; they answer different questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is Gesso a Pact alternative?&lt;/strong&gt;&lt;br&gt;
Only for teams whose question is "does my implementation still match my OpenAPI spec?" If the question is "does my provider still satisfy every consumer's recorded expectations?", Pact PHP is the right tool. The two solve overlapping but distinct problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need a broker to use Gesso?&lt;/strong&gt;&lt;br&gt;
No. The OpenAPI file lives in the repository and Gesso runs inside PHPUnit or Pest, so there is no additional service in the loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which OpenAPI versions does Gesso support?&lt;/strong&gt;&lt;br&gt;
3.0.x, 3.1.x, and 3.2.x. &lt;code&gt;oneOf&lt;/code&gt;/&lt;code&gt;anyOf&lt;/code&gt;/&lt;code&gt;allOf&lt;/code&gt;/&lt;code&gt;not&lt;/code&gt; are validated in every supported dialect, and &lt;code&gt;discriminator&lt;/code&gt; mappings are enforced rather than treated as a hint. 3.1 and 3.2 run against native JSON Schema 2020-12 semantics where the underlying validator supports them; the handful of 2019-09/2020-12 keywords the validator doesn't implement emit a one-shot warning rather than being silently ignored. 3.0 runs through a Draft 07 compatibility pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can Gesso test consumer code, the way Pact does on the consumer side?&lt;/strong&gt;&lt;br&gt;
Not in the pact sense. Gesso validates HTTP traffic against a spec on either side of the wire, so you can point it at a consumer's outgoing requests to check they match the spec. It won't, however, record a pact that a provider then verifies — that workflow belongs to Pact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does spec-driven testing catch the same regressions as consumer-driven testing?&lt;/strong&gt;&lt;br&gt;
It catches every drift between the implementation and the published spec. It does &lt;em&gt;not&lt;/em&gt; catch consumer expectations that the spec never encoded — for example, a consumer that depends on an optional field always being present in practice, or on one particular enum value it branches on. The spec permits both variations; the pact records that this consumer needs one of them. If those expectations matter, Pact is complementary, not redundant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does Dredd fit in?&lt;/strong&gt;&lt;br&gt;
Dredd is closer to spec-driven than consumer-driven — it runs the examples in an API description document as tests. In PHP projects that already publish OpenAPI, Gesso runs that same style of check from inside the existing suite, with coverage as an output. Dredd still fits when the checks need to run outside a PHP test runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;If your API is spec-first and you want to try spec-driven contract testing today, start from the &lt;a href="https://studio-design.github.io/gesso/quickstarts/core" rel="noopener noreferrer"&gt;Core quickstart&lt;/a&gt;, or jump straight to the framework you use: &lt;a href="https://studio-design.github.io/gesso/quickstarts/laravel" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt;, &lt;a href="https://studio-design.github.io/gesso/quickstarts/symfony" rel="noopener noreferrer"&gt;Symfony&lt;/a&gt;, or &lt;a href="https://studio-design.github.io/gesso/quickstarts/pest" rel="noopener noreferrer"&gt;Pest&lt;/a&gt;. See also &lt;a href="https://studio-design.github.io/gesso/contract-testing-symfony" rel="noopener noreferrer"&gt;Contract testing a Symfony API with OpenAPI&lt;/a&gt; for a full end-to-end walkthrough of the spec-driven path.&lt;/p&gt;

&lt;p&gt;If your integration surface is consumer-owned and you'd get more from pacts, the &lt;a href="https://docs.pact.io/implementation_guides/php/readme" rel="noopener noreferrer"&gt;Pact PHP documentation&lt;/a&gt; is the right starting point — the choice is a fit question, not a loyalty one.&lt;/p&gt;

&lt;p&gt;Source and issue tracker: &lt;a href="https://github.com/studio-design/gesso" rel="noopener noreferrer"&gt;github.com/studio-design/gesso&lt;/a&gt;. MIT licensed.&lt;/p&gt;

</description>
      <category>php</category>
      <category>testing</category>
      <category>openapi</category>
      <category>pact</category>
    </item>
    <item>
      <title>Contract testing a Symfony API with OpenAPI</title>
      <dc:creator>wadakatu</dc:creator>
      <pubDate>Mon, 27 Jul 2026 13:42:42 +0000</pubDate>
      <link>https://dev.to/wadakatu/contract-testing-a-symfony-api-with-openapi-40mj</link>
      <guid>https://dev.to/wadakatu/contract-testing-a-symfony-api-with-openapi-40mj</guid>
      <description>&lt;p&gt;Contract testing a Symfony API with OpenAPI takes four moving parts: the OpenAPI spec, a &lt;code&gt;WebTestCase&lt;/code&gt; (or a plain &lt;code&gt;Request&lt;/code&gt;/&lt;code&gt;Response&lt;/code&gt; pair), one trait, and its assertions. This guide covers all four, then shows the full runnable example.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OpenAPI contract testing?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI contract testing verifies that your live HTTP requests and responses conform to the OpenAPI schema you publish.&lt;/strong&gt; The spec is the source of truth; the tests confirm the implementation still matches it — nothing more, nothing less.&lt;/p&gt;

&lt;p&gt;This is the &lt;em&gt;provider-side, spec-driven&lt;/em&gt; flavour of contract testing. It is different from consumer-driven contract testing (Pact and friends), which starts from what each consumer expects. Both are valid. Provider-side spec testing shines when you already publish OpenAPI and want a fast, per-endpoint check that the implementation hasn't drifted.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;PHP 8.3+ and Symfony 6.4, 7.x, or 8.x&lt;/li&gt;
&lt;li&gt;A Symfony application with &lt;code&gt;symfony/http-foundation&lt;/code&gt;, &lt;code&gt;symfony/http-kernel&lt;/code&gt;, and — for the &lt;code&gt;WebTestCase&lt;/code&gt; / &lt;code&gt;KernelBrowser&lt;/code&gt; flow — &lt;code&gt;symfony/framework-bundle&lt;/code&gt; installed&lt;/li&gt;
&lt;li&gt;An OpenAPI 3.0, 3.1, or 3.2 document you want to enforce&lt;/li&gt;
&lt;li&gt;Gesso (&lt;code&gt;studio-design/gesso&lt;/code&gt;), the framework-independent core plus its Symfony adapter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; &lt;span class="s2"&gt;"studio-design/gesso:^2.0"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  symfony/http-foundation symfony/http-kernel symfony/framework-bundle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Symfony packages are suggestions, not hard requirements of Gesso — the core stays framework-independent, so you pull them in yourself to use the Symfony adapter. Skip &lt;code&gt;symfony/framework-bundle&lt;/code&gt; if you only want the plain &lt;code&gt;Request&lt;/code&gt;/&lt;code&gt;Response&lt;/code&gt; form (shown further down); include it for the &lt;code&gt;WebTestCase&lt;/code&gt; flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four moving parts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The spec
&lt;/h3&gt;

&lt;p&gt;Gesso finds spec files through the PHPUnit extension, not a Symfony bundle or a &lt;code&gt;config/packages/*.yaml&lt;/code&gt; file. Register the extension in &lt;code&gt;phpunit.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;extensions&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;bootstrap&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"Studio\Gesso\PHPUnit\OpenApiCoverageExtension"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"spec_base_path"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"openapi"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;parameter&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"specs"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"petstore"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/bootstrap&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/extensions&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;spec_base_path&lt;/code&gt; is the directory holding your documents; &lt;code&gt;specs&lt;/code&gt; lists the ones to report coverage for. YAML or JSON, 3.0.x, 3.1.x, or 3.2.x — the same loader accepts all of them (YAML needs &lt;code&gt;symfony/yaml&lt;/code&gt;). Which spec a given test uses is pinned by a &lt;code&gt;#[OpenApiSpec]&lt;/code&gt; attribute or by overriding &lt;code&gt;openApiSpec()&lt;/code&gt;; there is no Laravel-style &lt;code&gt;default_spec&lt;/code&gt; lookup in the Symfony adapter.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A WebTestCase
&lt;/h3&gt;

&lt;p&gt;Extend Symfony's &lt;code&gt;WebTestCase&lt;/code&gt; (or a plain PHPUnit class if you're constructing requests by hand):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Bundle\FrameworkBundle\Test\WebTestCase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PetsApiTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WebTestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The trait
&lt;/h3&gt;

&lt;p&gt;Mix in the Gesso Symfony assertion trait and name the spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Studio\Gesso\Attribute\OpenApiSpec&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Studio\Gesso\Symfony\OpenApiAssertions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="na"&gt;#[OpenApiSpec('petstore')]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PetsApiTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WebTestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;OpenApiAssertions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trait adds three assertion methods: &lt;code&gt;assertResponseMatchesOpenApiSchema($request, $response)&lt;/code&gt; for responses, &lt;code&gt;assertRequestMatchesOpenApiSchema($request)&lt;/code&gt; for requests, and &lt;code&gt;assertClientMatchesOpenApiSchema($client)&lt;/code&gt;, which runs both against the last call made by a test client.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The assertion
&lt;/h3&gt;

&lt;p&gt;Either shape works. When you already have a &lt;code&gt;KernelBrowser&lt;/code&gt; from &lt;code&gt;static::createClient()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_get_pets_matches_spec&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/pets'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertResponseIsSuccessful&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertClientMatchesOpenApiSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you're constructing &lt;code&gt;Request&lt;/code&gt; and &lt;code&gt;Response&lt;/code&gt; objects directly — useful for unit-style tests that don't boot the kernel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\HttpFoundation\Request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\HttpFoundation\Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/pets'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'[{"id":1,"name":"Fido"}]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertRequestMatchesOpenApiSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertResponseMatchesOpenApiSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both forms record spec coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full example
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;examples/symfony&lt;/code&gt; fixture in the Gesso repository installs and runs in CI on every push. To reproduce locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/studio-design/gesso.git
&lt;span class="nb"&gt;cd &lt;/span&gt;gesso/examples/symfony
composer &lt;span class="nb"&gt;install
&lt;/span&gt;composer &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The PHPUnit output ends with an &lt;code&gt;OpenAPI Contract Test Coverage&lt;/code&gt; report: per spec, the share of endpoints fully covered and of &lt;code&gt;(method, path, status, content-type)&lt;/code&gt; response rows validated, then a per-endpoint breakdown marking each row validated, skipped, uncovered, or request-only. That report is the point of running the tests: passing tests prove &lt;em&gt;these&lt;/em&gt; operations conform; the coverage report shows which ones you haven't covered yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check next
&lt;/h2&gt;

&lt;p&gt;Contract enforcement is the entry point. Three follow-ups matter for a real Symfony service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Coverage in CI.&lt;/strong&gt; Set &lt;code&gt;min_endpoint_coverage&lt;/code&gt; / &lt;code&gt;min_response_coverage&lt;/code&gt; on the PHPUnit extension so a PR that quietly stops testing an operation fails the build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preflight your spec.&lt;/strong&gt; Run &lt;code&gt;vendor/bin/gesso doctor --spec=openapi/petstore.json --strip-prefix=/api&lt;/code&gt; in CI. It reports whether Gesso can load and enforce the document — version and dialect, references, warned-about keywords, structurally valid operations — and exits non-zero when it can't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema-driven exploration.&lt;/strong&gt; Once the happy paths are green, generate request cases from the spec's schemas with &lt;code&gt;OpenApiSpecExplorer::explore()&lt;/code&gt; and assert each dispatched response with the same validators the trait uses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does this replace consumer-driven contract testing?&lt;/strong&gt;&lt;br&gt;
No. Provider-side spec testing verifies that your implementation matches the spec you publish. Consumer-driven testing (Pact) verifies that specific consumers' expectations are still met. If you publish OpenAPI and every consumer generates from it, provider-side testing covers the contract. If you have consumers with expectations the spec doesn't capture, keep Pact for those.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it work with a generated spec, like API Platform's?&lt;/strong&gt;&lt;br&gt;
Yes. Gesso loads a spec file; it does not care how the file was produced. OpenAPI documents generated by other tools — including API Platform — are treated the same as hand-authored ones, so long as the output is a valid OpenAPI 3.0/3.1/3.2 document on disk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about performance in a large test suite?&lt;/strong&gt;&lt;br&gt;
The parsed spec is cached and reused across assertions in the same test run. Validation cost per assertion scales with response body size and schema depth, not the total number of tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it validate requests too, or only responses?&lt;/strong&gt;&lt;br&gt;
Both, but with separate calls. &lt;code&gt;assertClientMatchesOpenApiSchema($client)&lt;/code&gt; validates the outbound request and then the response of the last client call. &lt;code&gt;assertResponseMatchesOpenApiSchema($request, $response)&lt;/code&gt; checks only the response — the &lt;code&gt;Request&lt;/code&gt; is there to supply the method and path. Use &lt;code&gt;assertRequestMatchesOpenApiSchema($request)&lt;/code&gt; for the request side of hand-built pairs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which OpenAPI versions are supported?&lt;/strong&gt;&lt;br&gt;
3.0.x, 3.1.x, and 3.2.x. &lt;code&gt;oneOf&lt;/code&gt;/&lt;code&gt;anyOf&lt;/code&gt;/&lt;code&gt;allOf&lt;/code&gt;/&lt;code&gt;not&lt;/code&gt; are validated in every supported dialect, and &lt;code&gt;discriminator&lt;/code&gt; mappings are enforced by default rather than treated as a hint. OpenAPI 3.1/3.2 keep native JSON Schema 2020-12 semantics — &lt;code&gt;const&lt;/code&gt;, &lt;code&gt;prefixItems&lt;/code&gt;, &lt;code&gt;unevaluatedProperties&lt;/code&gt;/&lt;code&gt;unevaluatedItems&lt;/code&gt;, &lt;code&gt;dependentSchemas&lt;/code&gt;/&lt;code&gt;dependentRequired&lt;/code&gt;, &lt;code&gt;$dynamicRef&lt;/code&gt;/&lt;code&gt;$dynamicAnchor&lt;/code&gt;. OpenAPI 3.0 runs through a Draft 07 compatibility pipeline, so those 2020-12-only keywords warn instead of being enforced there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;The Symfony adapter is one of four; the same core powers Laravel, Pest, and PSR-7 setups. Start from the &lt;a href="https://studio-design.github.io/gesso/quickstarts/symfony" rel="noopener noreferrer"&gt;Symfony quickstart&lt;/a&gt;, or jump to the &lt;a href="https://studio-design.github.io/gesso/fuzzing" rel="noopener noreferrer"&gt;Schema-driven fuzzing guide&lt;/a&gt; once your happy paths are green.&lt;/p&gt;

&lt;p&gt;Source and issue tracker: &lt;a href="https://github.com/studio-design/gesso" rel="noopener noreferrer"&gt;github.com/studio-design/gesso&lt;/a&gt;. MIT licensed.&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>testing</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Lumen (Laravel) v10 to v11: Contextual Binding Changes and Solutions.</title>
      <dc:creator>wadakatu</dc:creator>
      <pubDate>Mon, 14 Apr 2025 02:06:10 +0000</pubDate>
      <link>https://dev.to/wadakatu/lumen-laravel-v10-to-v11-contextual-binding-changes-and-solutions-4o7j</link>
      <guid>https://dev.to/wadakatu/lumen-laravel-v10-to-v11-contextual-binding-changes-and-solutions-4o7j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When upgrading from Lumen v10 to v11, have you ever been confused by unexpected behavior?&lt;br&gt;&lt;br&gt;
I encountered this issue myself when the changes to Contextual Binding caused previously functional code to stop working.&lt;/p&gt;

&lt;p&gt;In this article, I will explain the changes to Contextual Binding with detailed sample code. We’ll look at how it worked in v10, the issues that arise in v11, and how to fix them step by step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Reference:&lt;/strong&gt; If you’d like to learn more about the basics of Contextual Binding, please refer to the &lt;a href="https://laravel.com/docs/10.x/container#contextual-binding" rel="noopener noreferrer"&gt;official Laravel documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Background of the Changes
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;【Key Changes】

Lumen v10: &lt;span class="sb"&gt;`when`&lt;/span&gt; method-based Contextual Binding applied across the entire DI chain (including indirect dependencies).

Lumen v11: &lt;span class="sb"&gt;`when`&lt;/span&gt; method-based Contextual Binding applies only when directly injected into the specified class.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In Lumen v10, the &lt;code&gt;when&lt;/code&gt; method allowed Contextual Binding to propagate across the entire dependency injection (DI) chain.&lt;br&gt;&lt;br&gt;
In other words, a concrete class specified for one class's dependency was also reflected in subsequent layers of the DI chain.&lt;/p&gt;

&lt;p&gt;However, as of v11, this behavior has changed. The &lt;code&gt;when&lt;/code&gt; method now applies only to cases where the specified class is directly injected.&lt;br&gt;&lt;br&gt;
As a result, code that previously worked might stop functioning as intended.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dependency Diagram
&lt;/h3&gt;

&lt;p&gt;Here’s a diagram showing the dependencies discussed in this article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Behavior in v10:
CreditController ──depends→ MoneyService ──depends→ PaymentInterface
     │                                                 ↑
     └────────────when().needs()───────────────────────┘
     (The PaymentInterface binding is correctly applied from CreditController)

Behavior in v11:
CreditController ──depends→ MoneyService ──depends→ PaymentInterface
     │                       │                         ↑
     └─when().needs()─╳      └─when().needs()──────────┘
     (The binding only applies to direct dependencies)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s explore specific cases where this issue arises, along with code examples.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example Code in v10
&lt;/h2&gt;

&lt;p&gt;First, let’s look at example code that worked correctly in Lumen v10.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration in AppServiceProvider
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'default'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CreditController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;needs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;give&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'credit card'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;PaymentInterface&lt;/code&gt; is configured to switch its concrete class based on &lt;code&gt;CreditController&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CreditController
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreditController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;MoneyService&lt;/span&gt; &lt;span class="nv"&gt;$moneyService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;moneyService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CreditController&lt;/code&gt; injects &lt;code&gt;MoneyService&lt;/code&gt;, which internally calls the &lt;code&gt;paymentMethod&lt;/code&gt; of &lt;code&gt;PaymentInterface&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  MoneyService
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MoneyService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;PaymentInterface&lt;/span&gt; &lt;span class="nv"&gt;$paymentInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;paymentInterface&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MoneyService&lt;/code&gt; is a class that depends on &lt;code&gt;PaymentInterface&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution Result
&lt;/h3&gt;

&lt;p&gt;When this code is executed in Lumen v10, calling the &lt;code&gt;index&lt;/code&gt; method of &lt;code&gt;CreditController&lt;/code&gt; correctly returns &lt;code&gt;'credit card'&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Issues in v11
&lt;/h2&gt;

&lt;p&gt;What happens if we run the same code in Lumen v11?&lt;/p&gt;

&lt;h3&gt;
  
  
  Cause of the Issue
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CreditController&lt;/code&gt; injects &lt;code&gt;MoneyService&lt;/code&gt;, which in turn injects &lt;code&gt;PaymentInterface&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
However, in v11, the &lt;code&gt;when&lt;/code&gt; method applies only when directly injecting into the specified class. Therefore, even though &lt;code&gt;CreditController&lt;/code&gt; is the basis of the configuration, the &lt;code&gt;PaymentInterface&lt;/code&gt; passed to &lt;code&gt;MoneyService&lt;/code&gt; remains the default, and &lt;code&gt;'default'&lt;/code&gt; is returned.&lt;/p&gt;
&lt;h3&gt;
  
  
  Execution Result
&lt;/h3&gt;

&lt;p&gt;Calling &lt;code&gt;CreditController&lt;/code&gt; returns &lt;code&gt;'default'&lt;/code&gt; instead of the expected &lt;code&gt;'credit card'&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example of a Failing Test
&lt;/h3&gt;

&lt;p&gt;No explicit error message appears, but the return value differs from expectations. For example, the following test will fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Test code&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testCreditControllerReturnsCorrectPaymentMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/credit'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'credit card'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="c1"&gt;// Fails in v11 - 'default' is returned&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In real-world environments, this discrepancy can cause features to break, requiring time-consuming debugging.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fixed Code Example (v11-Compatible)
&lt;/h2&gt;

&lt;p&gt;How can we make this code work correctly in Lumen v11?&lt;br&gt;&lt;br&gt;
The fix is simple: change the basis of the &lt;code&gt;when&lt;/code&gt; method to &lt;code&gt;MoneyService&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration in AppServiceProvider
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'default'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CreditController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;needs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MoneyService&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;give&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$creditCardInterface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'credit card'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MoneyService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$creditCardInterface&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Key Fix

&lt;span class="sb"&gt;`needs(PaymentInterface::class)`&lt;/span&gt; → &lt;span class="sb"&gt;`needs(MoneyService::class)`&lt;/span&gt;

Consider the DI chain, and specify the class that directly requires the interface.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key here is changing the basis of the &lt;code&gt;needs&lt;/code&gt; method from &lt;code&gt;PaymentInterface&lt;/code&gt; to &lt;code&gt;MoneyService&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
This ensures that the correctly configured &lt;code&gt;PaymentInterface&lt;/code&gt; is injected into &lt;code&gt;MoneyService&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CreditController
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreditController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;MoneyService&lt;/span&gt; &lt;span class="nv"&gt;$moneyService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;moneyService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  MoneyService
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MoneyService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;PaymentInterface&lt;/span&gt; &lt;span class="nv"&gt;$paymentInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;paymentInterface&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Execution Result
&lt;/h3&gt;

&lt;p&gt;With this fixed code, calling the &lt;code&gt;index&lt;/code&gt; method of &lt;code&gt;CreditController&lt;/code&gt; correctly returns &lt;code&gt;'credit card'&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In summary, Lumen v11 enforces stricter scoping for Contextual Binding.&lt;br&gt;&lt;br&gt;
As a result, when using the &lt;code&gt;when&lt;/code&gt; method, you need to carefully specify the correct class within the DI chain.&lt;/p&gt;

&lt;p&gt;If you’ve encountered this behavior during an upgrade, please share your experience in the comments! If you know of alternative solutions or related documentation, feel free to share those as well :)&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://laravel.com/docs/10.x/container#contextual-binding" rel="noopener noreferrer"&gt;Laravel v10 Contextual Binding Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://laravel.com/docs/11.x/container#contextual-binding" rel="noopener noreferrer"&gt;Laravel v11 Contextual Binding Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>lumen</category>
      <category>servicecontainer</category>
    </item>
    <item>
      <title>How to Handle Excessive Warning Messages When Running `pecl install grpc`</title>
      <dc:creator>wadakatu</dc:creator>
      <pubDate>Wed, 25 Dec 2024 17:42:14 +0000</pubDate>
      <link>https://dev.to/wadakatu/how-to-handle-excessive-warning-messages-when-running-pecl-install-grpc-44jb</link>
      <guid>https://dev.to/wadakatu/how-to-handle-excessive-warning-messages-when-running-pecl-install-grpc-44jb</guid>
      <description>&lt;h2&gt;
  
  
  Overwhelming Warning Messages Can Be a Nuisance
&lt;/h2&gt;

&lt;p&gt;When running pecl install grpc, you might encounter an avalanche of warning messages like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#7 67.72 /tmp/pear/temp/grpc/src/core/lib/promise/detail/promise_factory.h:174:5: warning: 'always_inline' function might not be inlinable [-Wattributes]&lt;/span&gt;

&lt;span class="c"&gt;#7 352.5 /tmp/pear/temp/grpc/src/core/lib/event_engine/forkable.h:61:34: warning: 'unused' attribute ignored [-Wattributes]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There can be hundreds of these warnings, flooding your logs. This has been especially problematic during deployment, where CI/CD pipelines would exceed log limits, causing errors and halting the process.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Root Cause
&lt;/h2&gt;

&lt;p&gt;Searching for the warning messages online points to GCC, the GNU Compiler Collection.&lt;/p&gt;

&lt;p&gt;It turns out these warnings are generated by the compiler when building gRPC's source code. Since the warnings originate from gRPC’s source code, it’s not feasible for users to fix the source code directly to suppress the warnings.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Fortunately, GCC provides several options to suppress warning messages, which you can pass to the compiler during the build process:&lt;/p&gt;

&lt;p&gt;Warning Options in GCC Documentation&lt;/p&gt;

&lt;p&gt;However, there’s no straightforward way to pass these options directly when running pecl install grpc. If you know of one, please let me know—I’ll celebrate with tears of joy! 🥹&lt;/p&gt;

&lt;p&gt;So, what now?&lt;/p&gt;

&lt;p&gt;The answer came to me from a StackOverflow thread: Instead of letting pecl install handle everything, you can break the process into smaller steps and pass the options during the compilation phase.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://stackoverflow.com/a/47120441" rel="noopener noreferrer"&gt;StackOverflow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since gRPC is written in C++, we can use environment variables like CFLAGS and CXXFLAGS to specify options that suppress the warnings during compilation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wiki.gentoo.org/wiki/GCC_optimization/ja" rel="noopener noreferrer"&gt;Gentoo Wiki on GCC Optimization&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Let’s assume gRPC is being installed in a Dockerfile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;pecl &lt;span class="nb"&gt;install &lt;/span&gt;grpc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;pecl download grpc &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar &lt;/span&gt;xzf &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;grpc-&lt;span class="k"&gt;*&lt;/span&gt;.tgz | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; grpc-&lt;span class="k"&gt;*&lt;/span&gt;/ | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; phpize &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./configure &lt;span class="nt"&gt;--with-php-config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin/php-config &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-Wno-attributes -Wno-unused-parameter -Wno-deprecated-declarations -Wno-return-type"&lt;/span&gt; &lt;span class="nv"&gt;CXXFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-Wno-attributes -Wno-unused-parameter -Wno-deprecated-declarations -Wno-return-type"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The after commands mimic what pecl install does internally, but with more control. Breaking down the process revealed just how much pecl install handles behind the scenes—impressive!&lt;/p&gt;

&lt;p&gt;The warnings appeared during the make phase, so I passed the suppression options via the CFLAGS and CXXFLAGS environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-Wno-attributes -Wno-unused-parameter -Wno-deprecated-declarations -Wno-return-type"&lt;/span&gt; &lt;span class="nv"&gt;CXXFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-Wno-attributes -Wno-unused-parameter -Wno-deprecated-declarations -Wno-return-type"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How to Identify Suppression Options
&lt;/h4&gt;

&lt;p&gt;To determine which suppression options to use, look at the end of the warning messages. You’ll often see hints like [-Wattributes] or [-Wunused-parameter]. Add &lt;code&gt;no&lt;/code&gt; to the beginning of these, e.g., -Wno-attributes or -Wno-unused-parameter, to suppress them.&lt;/p&gt;

&lt;p&gt;For more details, refer to the official documentation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html" rel="noopener noreferrer"&gt;Warning Options in GCC Documentation&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With this approach, I successfully escaped the swamp of gRPC warning messages. However, this didn’t fully resolve the CI/CD log limit issue, which I’ll address in a future post.&lt;/p&gt;

&lt;p&gt;If this article helps even one person escape the gRPC warning message swamp, I’ll be delighted! 🙌&lt;/p&gt;

</description>
      <category>grpc</category>
      <category>php</category>
      <category>docker</category>
      <category>gcc</category>
    </item>
    <item>
      <title>Laravel 9, NO LTS Support!?</title>
      <dc:creator>wadakatu</dc:creator>
      <pubDate>Tue, 22 Feb 2022 15:36:00 +0000</pubDate>
      <link>https://dev.to/wadakatu/laravel-9-no-lts-support-1fll</link>
      <guid>https://dev.to/wadakatu/laravel-9-no-lts-support-1fll</guid>
      <description>&lt;h2&gt;
  
  
  Latest LTS finally!!
&lt;/h2&gt;

&lt;p&gt;Laravel 9 was finally released on February 22nd, 2022.&lt;/p&gt;

&lt;p&gt;At first, we are thrilled with &lt;strong&gt;LTS (Long Term Support) of Laravel 9&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, Mr.Otwell (The author of Laravel) &lt;strong&gt;decided to postpone the LTS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgh77yqoslpe6bzb6dcw3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgh77yqoslpe6bzb6dcw3.png" alt="RELEASE DIFF" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Postponed?
&lt;/h2&gt;

&lt;p&gt;You can find out the reason why the LTS is postponed on Mr.Otwell Tweet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbg8slxfn2l07e69vfjj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbg8slxfn2l07e69vfjj.png" alt="Twitter" width="800" height="1289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short, &lt;strong&gt;a &lt;a href="https://github.com/symfony/symfony/pull/45377" rel="noopener noreferrer"&gt;pull request&lt;/a&gt; on Symfony Repository&lt;/strong&gt; causes the postponement of the LTS.&lt;/p&gt;

&lt;h2&gt;
  
  
  LTS Again?
&lt;/h2&gt;

&lt;p&gt;The answer is "Maybe or Maybe not".&lt;/p&gt;

&lt;p&gt;In my opinion, if the &lt;a href="https://github.com/symfony/symfony/pull/45377" rel="noopener noreferrer"&gt;PR&lt;/a&gt; were rejected, There would be high probability that the LTS comes back.&lt;/p&gt;

&lt;p&gt;What do you think about it?&lt;br&gt;
Feel free to leave your thought on discussion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update! PHP 8.1 needed from Symfony 6.1
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/symfony/symfony/pull/45377" rel="noopener noreferrer"&gt;PR&lt;/a&gt; was merged yesterday on Symfony.&lt;/p&gt;

&lt;p&gt;Therefore, PHP 8.1 will be the minimum requirement for Symfony 6.1.&lt;/p&gt;

&lt;p&gt;Read this article for more information.&lt;br&gt;
&lt;a href="https://symfony.com/blog/symfony-6-1-will-require-php-8-1" rel="noopener noreferrer"&gt;https://symfony.com/blog/symfony-6-1-will-require-php-8-1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Laravel, they have already merged a &lt;a href="https://github.com/laravel/framework/pull/41250" rel="noopener noreferrer"&gt;PR&lt;/a&gt; which changes the minimum PHP Version From 8.0 to 8.1 for Laravel 10.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>symfony</category>
      <category>laravel9</category>
    </item>
    <item>
      <title>[Laravel 8.x] Refactor Factory Call with One Command</title>
      <dc:creator>wadakatu</dc:creator>
      <pubDate>Thu, 03 Feb 2022 11:01:03 +0000</pubDate>
      <link>https://dev.to/wadakatu/laravel-8x-refactor-factory-call-with-one-command-4p8h</link>
      <guid>https://dev.to/wadakatu/laravel-8x-refactor-factory-call-with-one-command-4p8h</guid>
      <description>&lt;h2&gt;
  
  
  Factory call changed
&lt;/h2&gt;

&lt;p&gt;In Laravel 8.x, the style of factory call was changed from helper to static method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Laravel 7.x or earlier&lt;/span&gt;
&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;//Laravel 8.x&lt;/span&gt;
&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes plenty of times to fetch this change into your application if you do by yourself.&lt;/p&gt;

&lt;p&gt;Don't worry. I made a package to refactor all of them with &lt;strong&gt;Just One Command.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Laravel-Factory-Refactor&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://packagist.org/packages/wadakatu/laravel-factory-refactor" rel="noopener noreferrer"&gt;https://packagist.org/packages/wadakatu/laravel-factory-refactor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This package will help you to refactor the style of factory call from helper to static method for Laravel 8.x.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Installation
&lt;/h3&gt;

&lt;p&gt;You can install the package via composer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require wadakatu/laravel-factory-refactor &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Run Artisan Command
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan refactor:factory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Voila, Refactor Completed !
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Before&lt;/span&gt;
&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;//After&lt;/span&gt;
&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  In conclusion
&lt;/h2&gt;

&lt;p&gt;'Laravel-Factory-Refactor' is my first package ever in my life.&lt;/p&gt;

&lt;p&gt;From now on, I am going to do my best to update this package to make it better.&lt;br&gt;
I hope you are going to like it.&lt;/p&gt;

&lt;p&gt;Moreover, I am looking forward to getting stars in Github⭐️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wadakatu/laravel-factory-refactor" rel="noopener noreferrer"&gt;https://github.com/wadakatu/laravel-factory-refactor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>opensource</category>
      <category>firstpost</category>
    </item>
  </channel>
</rss>
