# give/4.18.0/src/Framework/Routes/ScriptResponse.php

GiveWP – Donation Plugin and Fundraising Platform, version 4.18.0. 155 lines.

- Page: https://pluginprobe.com/plugins/give/4.18.0/code/src/Framework/Routes/ScriptResponse.php
- Raw: https://pluginprobe.com/plugins/give/4.18.0/raw/src/Framework/Routes/ScriptResponse.php
- Modified: 2026-09-23T17:56:52+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/give/4.18.0/code/src/Framework/Routes/ScriptResponse.php#L10-L20`.

```php
<?php

namespace Give\Framework\Routes;

use Give\Framework\Support\Facades\Scripts\ScriptAsset;

/**
 * Sends a built script with revalidation headers. The ETag is the build hash
 * from the script's asset file, so it changes exactly when the bundle does.
 *
 * @since 4.17.0
 */
class ScriptResponse
{
    /**
     * Seconds a browser may reuse the response before revalidating its ETag.
     *
     * @since 4.17.0
     */
    protected int $maxAge = HOUR_IN_SECONDS;

    /**
     * @since 4.17.0
     */
    protected string $file;

    /**
     * @since 4.17.0
     */
    protected string $assetFile;

    /**
     * @since 4.17.0
     */
    protected string $objectName = '';

    /**
     * @var callable|null
     *
     * @since 4.17.0
     */
    protected $data;

    /**
     * @since 4.17.0
     *
     * @param string $file Absolute path to the built script. Its version comes
     *                     from the .asset.php file @wordpress/scripts writes
     *                     next to it.
     */
    public function __construct(string $file)
    {
        $this->file = $file;
        $this->assetFile = preg_replace('/\.js$/', '.asset.php', $file);
    }

    /**
     * Expose data to the script as a global object, the way wp_localize_script()
     * does for enqueued scripts. The callable runs at request time, after
     * translations are loaded, so the data can carry the site's translated
     * strings.
     *
     * @since 4.17.0
     *
     * @param callable $data Returns the array to expose; must be JSON-encodable. Receives the
     *                       request data the router matched (query arguments and a path id).
     */
    public function localize(string $objectName, callable $data): self
    {
        $this->objectName = $objectName;
        $this->data = $data;

        return $this;
    }

    /**
     * @since 4.17.0
     *
     * @param array $request Request data handed to the localize callable.
     */
    public function send(array $request = []): void
    {
        if (!is_readable($this->file)) {
            status_header(404);
            exit;
        }

        $prologue = $this->prologue($request);
        $etag = $this->etag($prologue);

        header('Content-Type: application/javascript; charset=utf-8');
        header('X-Content-Type-Options: nosniff');
        header("Cache-Control: public, max-age={$this->maxAge}");
        header("ETag: {$etag}");

        if ($this->matchesIfNoneMatch($etag, $_SERVER['HTTP_IF_NONE_MATCH'] ?? '')) {
            status_header(304);
            exit;
        }

        echo $prologue;
        readfile($this->file);
        exit;
    }

    /**
     * The statement printed ahead of the file when data is localized.
     *
     * @since 4.17.0
     */
    public function prologue(array $request = []): string
    {
        if (!$this->data) {
            return '';
        }

        return sprintf("var %s = %s;\n", $this->objectName, wp_json_encode(($this->data)($request)));
    }

    /**
     * The build hash from the asset file, quoted as a strong validator. Localized
     * data is part of the response, so its hash is part of the validator too.
     *
     * @since 4.17.0
     */
    public function etag(string $prologue = ''): string
    {
        $version = (string) ScriptAsset::getVersion($this->assetFile);

        if ($prologue !== '') {
            $version .= '-' . substr(md5($prologue), 0, 8);
        }

        return sprintf('"%s"', $version);
    }

    /**
     * Weak validators and the "-gzip" suffix mod_deflate appends both name the same file.
     *
     * @since 4.17.0
     */
    public function matchesIfNoneMatch(string $etag, string $header): bool
    {
        if ($header === '') {
            return false;
        }

        $candidates = array_map(static function (string $value): string {
            return trim(str_replace(['W/', '-gzip"'], ['', '"'], $value));
        }, explode(',', wp_unslash($header)));

        return in_array($etag, $candidates, true) || in_array('*', $candidates, true);
    }
}

```
