drupflare/worker - v1.0.4
    Preparing search index...

    Variable RENDER_PAGE_PHPConst

    RENDER_PAGE_PHP: "\nuse Drupal\\Core\\Cache\\CacheableResponseInterface;\nuse Drupal\\Core\\DrupalKernel;\nuse Drupal\\Core\\Site\\Settings;\nuse Symfony\\Component\\HttpFoundation\\Request;\n\n// __CFW_FIBER_SHIM__\n// __CFW_HOST_HELPERS__\n// __CFW_PW_SERVE_INLINE__\nchdir('/drupal');\n\n$path = json_decode(__CFW_PATH__);\n$origin = json_decode(__CFW_ORIGIN__);\n\n// the host trio is derived from the ORIGIN rather than hardcoded, and cfw_serve() overwrites it\n// again from the request it builds, so the superglobals and the Request object cannot disagree\n$__host = $origin === '' ? 'localhost' : (string) parse_url($origin, PHP_URL_HOST);\n$__port =\n\t$origin === ''\n\t\t? 80\n\t\t: (int) (parse_url($origin, PHP_URL_PORT) ?:\n\t\t(strncmp($origin, 'https:', 6) === 0\n\t\t\t? 443\n\t\t\t: 80));\n$_SERVER['HTTP_HOST'] = $__port === 80 || $__port === 443 ? $__host : $__host . ':' . $__port;\n$_SERVER['SERVER_NAME'] = $__host;\n$_SERVER['SERVER_PORT'] = (string) $__port;\nif (strncmp($origin, 'https:', 6) === 0) {\n\t$_SERVER['HTTPS'] = 'on';\n} else {\n\tunset($_SERVER['HTTPS']);\n}\n$_SERVER['REQUEST_URI'] = $path;\n$_SERVER['REQUEST_METHOD'] = 'GET';\n$_SERVER['SCRIPT_NAME'] = '/index.php';\n$_SERVER['SCRIPT_FILENAME'] = '/drupal/index.php';\n$_SERVER['PHP_SELF'] = '/index.php';\n$_SERVER['DOCUMENT_ROOT'] = '/drupal';\n$_SERVER['REMOTE_ADDR'] = '127.0.0.1';\n$_SERVER['SERVER_SOFTWARE'] = 'workerd';\n$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';\n\n$out = [];\n$clock = function () {\n\treturn microtime(true) * 1000;\n};\n$t0 = $clock();\n\ntry {\n\tif (!isset($GLOBALS['__pw_autoloader']) || !is_object($GLOBALS['__pw_autoloader'])) {\n\t\t$GLOBALS['__pw_autoloader'] = require '/drupal/autoload.php';\n\t}\n\t$autoloader = $GLOBALS['__pw_autoloader'];\n\n\tif (!isset($GLOBALS['__pw_kernel'])) {\n\t\t$request = Request::create($path, 'GET');\n\t\t$kernel = new DrupalKernel('prod', $autoloader);\n\t\tDrupalKernel::bootEnvironment();\n\t\t$sitePath = DrupalKernel::findSitePath($request);\n\t\t$kernel->setSitePath($sitePath);\n\t\tSettings::initialize('/drupal', $sitePath, $autoloader);\n\t\t$kernel->boot();\n\t\t$GLOBALS['__pw_kernel'] = $kernel;\n\t\t$out['bootedKernel'] = 1;\n\t}\n\n\t// a fill must render, so clear what would otherwise answer for it\n\tforeach (json_decode(__CFW_BINS__, true) as $bin) {\n\t\ttry {\n\t\t\tDrupal::cache($bin)->deleteAll();\n\t\t} catch (Throwable $e) {\n\t\t}\n\t}\n\ttry {\n\t\t$middleware = Drupal::service('http_middleware.page_cache');\n\t\t$rp = new ReflectionProperty($middleware, 'cid');\n\t\t$rp->setValue($middleware, null);\n\t} catch (Throwable $e) {\n\t}\n\n\t$response = cfw_serve($path, __CFW_SERVE_ARGS__);\n\t$out['destructed'] = $GLOBALS['__pw_destructed'] ?? null;\n\t// sendContent() RATHER THAN getContent(), and the difference is whether forms work at all.\n\t// BigPipe replaces the CSRF token with a lazy placeholder and substitutes it during\n\t// BigPipeResponse::sendContent(); getContent() returns the pre-substitution HTML, so every form\n\t// shipped a big_pipe_nojs_placeholder_attribute_safe marker where its token belonged, and every\n\t// submission came back \"The form has become outdated\".\n\t//\n\t// Buffered rather than sent, because there is no SAPI here to send to. Safe for a plain\n\t// Response, whose sendContent() only echoes what getContent() returns; a throw falls back to it.\n\t$body = '';\n\tif (method_exists($response, 'sendContent')) {\n\t\t$depth = ob_get_level();\n\t\tob_start();\n\t\ttry {\n\t\t\t$response->sendContent();\n\t\t\t$body = (string) ob_get_clean();\n\t\t} catch (Throwable $e) {\n\t\t\twhile (ob_get_level() > $depth) {\n\t\t\t\t@ob_end_clean();\n\t\t\t}\n\t\t\t$out['sendError'] = get_class($e) . ': ' . $e->getMessage();\n\t\t\t// BigPipe::sendContent() has no try/finally around performPostSendTasks(), so a throw here\n\t\t\t// skips the session save -- and the CSRF seed is minted during placeholder replacement,\n\t\t\t// which is inside the call that just threw. Closing it is what a SAPI shutdown would do.\n\t\t\t$out['sessionClosed'] = cfw_close_session();\n\t\t\t$body = (string) $response->getContent();\n\t\t}\n\t} else {\n\t\t$body = (string) $response->getContent();\n\t}\n\t$out['status'] = $response->getStatusCode();\n\t$out['html'] = $body;\n\t$out['bytes'] = strlen($body);\n\t$out['pageCache'] = $response->headers->get('x-drupal-cache');\n\t$out['dynamicCache'] = $response->headers->get('x-drupal-dynamic-cache');\n\t// the cache tags this render bubbled, so a compiled plan can be invalidated by the same tags\n\t// Drupal would invalidate the render cache with\n\ttry {\n\t\t$out['cacheTags'] =\n\t\t\t$response instanceof CacheableResponseInterface\n\t\t\t\t? array_values($response->getCacheableMetadata()->getCacheTags())\n\t\t\t\t: [];\n\t} catch (Throwable $e) {\n\t\t$out['cacheTags'] = [];\n\t}\n\t$out['contentType'] = $response->headers->get('content-type');\n\t$out['location'] = $response->headers->get('location');\n\t// what Drupal said about storing this, which page_cache_kill_switch and any module with a\n\t// reason to opt out both express here and nowhere else\n\t$out['cacheControl'] = $response->headers->get('cache-control');\n\t// the request headers this response says it depends on; `cfw_page` is keyed by path alone\n\t$out['vary'] = implode(', ', $response->headers->all('vary'));\n\t// EVERY x-drupal-* HEADER, because dropping one silently changed what the BROWSER does.\n\t// ajax.js refuses a response whose url is not in drupalSettings.ajaxTrustedUrl unless it carries\n\t// X-Drupal-Ajax-Token, and answers \"The response failed verification so will not be processed.\"\n\t// -- so every AJAX interaction that was not a plain form submit died at the client with a valid\n\t// response in hand. Measured on Add field. A prefix rather than a list, so the next one core adds\n\t// is carried without anybody having to notice.\n\t// EVERY OTHER HEADER TOO, joined, because a module's CSP or X-Frame-Options is set on the\n\t// response and the host decides which names reach a browser (set-cookie has its own path)\n\t$passed = [];\n\tforeach ($response->headers->all() as $name => $values) {\n\t\t$values = is_array($values) ? $values : [$values];\n\t\tif (stripos((string) $name, 'x-drupal-') === 0) {\n\t\t\tif (isset($values[0])) {\n\t\t\t\t$passed[(string) $name] = (string) $values[0];\n\t\t\t}\n\t\t} elseif (strcasecmp((string) $name, 'set-cookie') !== 0 && $values !== []) {\n\t\t\t$passed[(string) $name] = implode(', ', array_map('strval', $values));\n\t\t}\n\t}\n\t$out['passHeaders'] = $passed;\n\t// BOTH SOURCES, because Drupal sets a session cookie through neither one consistently:\n\t// a logout or a Symfony-managed cookie lands on the Response, while session_start() emits its\n\t// own Set-Cookie into PHP's header list, which the Response never sees. Reading one of them\n\t// would drop the login cookie silently and leave the session unrecoverable by the browser.\n\t$cookies = [];\n\tforeach ($response->headers->all('set-cookie') as $line) {\n\t\t$cookies[] = (string) $line;\n\t}\n\tif (function_exists('headers_list')) {\n\t\tforeach (headers_list() as $line) {\n\t\t\tif (stripos($line, 'set-cookie:') === 0) {\n\t\t\t\t$cookies[] = trim(substr($line, 11));\n\t\t\t}\n\t\t}\n\t}\n\t$out['setCookie'] = array_values(array_unique($cookies));\n} catch (Throwable $e) {\n\t$out['error'] = get_class($e) . ': ' . $e->getMessage();\n\t$out['at'] = $e->getFile() . ':' . $e->getLine();\n}\n\n// what the between-request reset actually did, and who Drupal thinks is asking. Both are cheap\n// reads and both were needed to find the session leak; a render that comes back as the WRONG USER\n// is not distinguishable from a correct one by its bytes\n$out['reset'] = $GLOBALS['__pw_reset'] ?? null;\ntry {\n\t$out['uid'] = (int) Drupal::currentUser()->id();\n} catch (Throwable $e) {\n\t$out['uid'] = null;\n}\n// THE ROLE SET, sorted, because the edge plan key is derived from it. A plan keyed on the raw\n// cookie is one plan per session per path, which maximises the cold-KV case it was built to avoid;\n// keyed on roles it is one per role set. Sorted so two accounts holding the same roles in a\n// different order produce one key rather than two\ntry {\n\t$__roles = array_values(Drupal::currentUser()->getRoles());\n\tsort($__roles);\n\t$out['roles'] = $__roles;\n} catch (Throwable $e) {\n\t$out['roles'] = null;\n}\n\n$out['renderMs'] = round($clock() - $t0, 2);\necho json_encode($out);\n"