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

    Variable UPDB_UNIT_PHPConst

    UPDB_UNIT_PHP: "\nuse Drupal\\Core\\Cache\\Cache;\nuse Drupal\\Core\\Cache\\CacheTagsPurgeInterface;\n\n// __CFW_FIBER_SHIM__\nchdir('/drupal');\n\n$u = json_decode(__CFW_PAYLOAD__, true);\n$out = ['ok' => false, 'kind' => $u['kind'], 'seq' => $u['seq'], 'finished' => 0];\n\ntry {\n\t// __CFW_UPDB_PREAMBLE__\n\n\t// The sandbox is the only mid-hook state there is. Refuse a malformed one rather\n\t// than start a hook from scratch that already ran half of itself.\n\t$sandbox = [];\n\tif (is_string($u['sandbox']) && $u['sandbox'] !== '') {\n\t\t$raw = base64_decode($u['sandbox'], true);\n\t\tif ($raw === false) {\n\t\t\tthrow new RuntimeException('stored sandbox is not valid base64');\n\t\t}\n\t\t$restored = @unserialize($raw);\n\t\tif (!is_array($restored)) {\n\t\t\tthrow new RuntimeException('stored sandbox did not unserialize to an array');\n\t\t}\n\t\t$sandbox = $restored;\n\t\t$out['sandboxRestored'] = count($sandbox);\n\t}\n\n\t$results = [];\n\tif (!empty($u['abortList'])) {\n\t\t$results['#abort'] = array_values($u['abortList']);\n\t}\n\t// finished defaults to 1, exactly as _batch_process() seeds it (batch.inc:286);\n\t// without that update_do_one() never records the schema version.\n\t$context = ['sandbox' => $sandbox, 'results' => $results, 'finished' => 1, 'message' => ''];\n\n\t$kind = $u['kind'];\n\n\tif ($kind === 'maint_on' || $kind === 'maint_off') {\n\t\t// maint_off restores the PRE-RUN value rather than forcing FALSE, which is what\n\t\t// DbUpdateController::batchFinished() does via the session. Here the pre-run\n\t\t// value lives in the run row, so it survives an eviction that a session would\n\t\t// not.\n\t\t$want = $kind === 'maint_on' ? true : $u['maintTarget'] === true;\n\t\tDrupal::state()->set('system.maintenance_mode', $want);\n\t\t$out['maintenanceMode'] = Drupal::state()->get('system.maintenance_mode') ? 1 : 0;\n\t\t$out['wanted'] = $want ? 1 : 0;\n\t\t$out['finished'] = 1;\n\t\t$out['ok'] = $out['maintenanceMode'] === ($want ? 1 : 0);\n\t\tif (!$out['ok']) {\n\t\t\t$out['error'] =\n\t\t\t\t'maintenance_mode did not take: state read back as ' . $out['maintenanceMode'];\n\t\t}\n\t} elseif ($kind === 'update') {\n\t\t// indivisible unless the hook cooperates, which is core's design rather than a\n\t\t// limitation here. A batch-aware hook_update_N() takes $sandbox by reference and\n\t\t// sets $sandbox['#finished'] below 1 to ask to be re-entered, which is exactly the\n\t\t// seam this chain rides: one invocation per pass, sandbox persisted between them.\n\t\t// A hook that is NOT batch-aware runs to completion in one call and nothing can\n\t\t// interrupt it -- measured: a setTimeout(1) raced against a 119 ms synchronous\n\t\t// wasm call LOST, because the timer cannot fire until the call returns. So a\n\t\t// single non-batch-aware hook is the one place this design cannot guarantee a\n\t\t// 10 ms slice, and the run records what it actually cost instead of pretending.\n\t\t$module = (string) $u['module'];\n\t\t$number = (int) $u['number'];\n\t\t$fn = $module . '_update_' . $number;\n\t\t$reg = Drupal::service('update.update_hook_registry');\n\t\t$out['installedBefore'] = $reg->getInstalledVersion($module);\n\n\t\tif ($u['seedSchema'] !== null) {\n\t\t\t$reg->setInstalledVersion($module, (int) $u['seedSchema']);\n\t\t\t$out['seededTo'] = (int) $u['seedSchema'];\n\t\t}\n\n\t\t$equivalent = null;\n\t\ttry {\n\t\t\t$equivalent = $reg->getEquivalentUpdate($module, $number);\n\t\t} catch (Throwable $e) {\n\t\t}\n\n\t\tif (!function_exists($fn) && $equivalent === null) {\n\t\t\t// The plan named a function this tree does not define. That is a stale plan,\n\t\t\t// not a failed update, so it must not be retried and must not advance.\n\t\t\t$out['refused'] = 'missing-function';\n\t\t\t$out['error'] =\n\t\t\t\t$fn .\n\t\t\t\t' is not defined and has no equivalent-update record; the plan was built against a different code tree';\n\t\t} else {\n\t\t\t$escaped = null;\n\t\t\ttry {\n\t\t\t\tupdate_do_one($module, (string) $number, array_values($u['depMap']), $context);\n\t\t\t} catch (Throwable $e) {\n\t\t\t\t// update.inc:191 catches Exception, not Throwable, so an Error inside the\n\t\t\t\t// hook lands here with the schema version unmoved and whatever the hook\n\t\t\t\t// already wrote still written.\n\t\t\t\t$escaped = get_class($e) . ': ' . $e->getMessage();\n\t\t\t}\n\t\t\t$out['finished'] =\n\t\t\t\tisset($context['finished']) && is_numeric($context['finished'])\n\t\t\t\t\t? (float) $context['finished']\n\t\t\t\t\t: 1.0;\n\t\t\t$out['abort'] = array_values($context['results']['#abort'] ?? []);\n\t\t\t$ret = $context['results'][$module][$number] ?? [];\n\t\t\tif (isset($ret['results']['query'])) {\n\t\t\t\t$out['message'] = substr((string) $ret['results']['query'], 0, 400);\n\t\t\t}\n\t\t\tif (!empty($ret['#abort']['query'])) {\n\t\t\t\t$out['abortMessage'] = substr((string) $ret['#abort']['query'], 0, 400);\n\t\t\t}\n\t\t\t$out['success'] = !empty($ret['results']['success']);\n\t\t\t$out['installedAfter'] = $reg->getInstalledVersion($module);\n\t\t\tif ($escaped !== null) {\n\t\t\t\t$out['escaped'] = $escaped;\n\t\t\t\t$out['finished'] = 0;\n\t\t\t\tif (!in_array($fn, $out['abort'], true)) {\n\t\t\t\t\t$out['abort'][] = $fn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$serialized = null;\n\t\t\ttry {\n\t\t\t\t$serialized = serialize($context['sandbox'] ?? []);\n\t\t\t} catch (Throwable $e) {\n\t\t\t\t$out['sandboxError'] = get_class($e) . ': ' . $e->getMessage();\n\t\t\t}\n\t\t\tif ($serialized !== null) {\n\t\t\t\t$out['sandbox'] = base64_encode($serialized);\n\t\t\t}\n\t\t\t$out['ok'] = empty($out['abort']) && $escaped === null && !isset($out['sandboxError']);\n\t\t}\n\t} elseif ($kind === 'post_update') {\n\t\t$fn = (string) $u['fn'];\n\t\tif (strpos($fn, '_post_update_') === false) {\n\t\t\t$out['refused'] = 'not-a-post-update';\n\t\t\t$out['error'] = $fn . ' does not contain _post_update_';\n\t\t} else {\n\t\t\t$parts = explode('_post_update_', $fn, 2);\n\t\t\t$extension = $parts[0];\n\t\t\t$postReg = Drupal::service('update.post_update_registry');\n\t\t\t// Loads <extension>.post_update.php. update_invoke_post_update() does this\n\t\t\t// itself, but it then silently does nothing when the function is absent --\n\t\t\t// leaving the unit \"finished\" without having run or registered anything. So\n\t\t\t// the file is loaded here and function_exists() is checked first.\n\t\t\ttry {\n\t\t\t\t$postReg->getUpdateFunctions($extension);\n\t\t\t} catch (Throwable $e) {\n\t\t\t\t$out['loadError'] = get_class($e) . ': ' . $e->getMessage();\n\t\t\t}\n\t\t\tif (!function_exists($fn)) {\n\t\t\t\t$out['refused'] = 'missing-function';\n\t\t\t\t$out['error'] =\n\t\t\t\t\t$fn .\n\t\t\t\t\t' is not defined after loading ' .\n\t\t\t\t\t$extension .\n\t\t\t\t\t'.post_update.php; the plan was built against a different code tree';\n\t\t\t} else {\n\t\t\t\t$escaped = null;\n\t\t\t\ttry {\n\t\t\t\t\tupdate_invoke_post_update($fn, $context);\n\t\t\t\t} catch (Throwable $e) {\n\t\t\t\t\t$escaped = get_class($e) . ': ' . $e->getMessage();\n\t\t\t\t}\n\t\t\t\t$out['finished'] =\n\t\t\t\t\tisset($context['finished']) && is_numeric($context['finished'])\n\t\t\t\t\t\t? (float) $context['finished']\n\t\t\t\t\t\t: 1.0;\n\t\t\t\t$out['abort'] = array_values($context['results']['#abort'] ?? []);\n\t\t\t\t$name = $parts[1];\n\t\t\t\t$ret = $context['results'][$extension][$name] ?? [];\n\t\t\t\tif (isset($ret['results']['query'])) {\n\t\t\t\t\t$out['message'] = substr((string) $ret['results']['query'], 0, 400);\n\t\t\t\t}\n\t\t\t\tif (!empty($ret['#abort']['query'])) {\n\t\t\t\t\t$out['abortMessage'] = substr((string) $ret['#abort']['query'], 0, 400);\n\t\t\t\t}\n\t\t\t\t$out['success'] = !empty($ret['results']['success']);\n\t\t\t\tif ($escaped !== null) {\n\t\t\t\t\t$out['escaped'] = $escaped;\n\t\t\t\t\t$out['finished'] = 0;\n\t\t\t\t\tif (!in_array($fn, $out['abort'], true)) {\n\t\t\t\t\t\t$out['abort'][] = $fn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$serialized = null;\n\t\t\t\ttry {\n\t\t\t\t\t$serialized = serialize($context['sandbox'] ?? []);\n\t\t\t\t} catch (Throwable $e) {\n\t\t\t\t\t$out['sandboxError'] = get_class($e) . ': ' . $e->getMessage();\n\t\t\t\t}\n\t\t\t\tif ($serialized !== null) {\n\t\t\t\t\t$out['sandbox'] = base64_encode($serialized);\n\t\t\t\t}\n\t\t\t\t// update_invoke_post_update() registers the function itself once finished,\n\t\t\t\t// so the caller reads the registry rather than being told.\n\t\t\t\t$out['ok'] =\n\t\t\t\t\tempty($out['abort']) && $escaped === null && !isset($out['sandboxError']);\n\t\t\t}\n\t\t}\n\t} elseif ($kind === 'flush') {\n\t\t$step = (string) ($u['step'] ?? 'all');\n\t\t$did = [];\n\t\tif ($step === 'all') {\n\t\t\t// the one unbounded call in this file, and it is unreachable unless the caller\n\t\t\t// asked for it twice: buildPlanUnits() throws without allowUnbounded, and this\n\t\t\t// refuses without the flag on the unit itself. Measured cost: 282.9 ms in wasm\n\t\t\t// / 268.8 ms native with a 78.5 MB peak, against a 10 ms free-plan invocation\n\t\t\t// cap. It exists for paid plans, where one invocation has 30 s.\n\t\t\tif ($u['unbounded'] !== true) {\n\t\t\t\t$out['refused'] = 'unbounded-flush';\n\t\t\t\t$out['error'] =\n\t\t\t\t\t'drupal_flush_all_caches() is 282.9 ms in wasm against a 10 ms free-plan cap; run the eleven split steps instead, or set allowUnbounded';\n\t\t\t} else {\n\t\t\t\tdrupal_flush_all_caches();\n\t\t\t\t$did[] = 'all';\n\t\t\t}\n\t\t} elseif ($step === 'cache_flush') {\n\t\t\t// hook_cache_flush across installed modules. Bounded by module count, and each\n\t\t\t// implementation is a handful of deletes. NOT split further: a per-module split\n\t\t\t// would need the module list in the plan, and core makes no ordering promise\n\t\t\t// between implementations.\n\t\t\tDrupal::moduleHandler()->invokeAll('cache_flush');\n\t\t\t$did[] = $step;\n\t\t} elseif ($step === 'purge_tags') {\n\t\t\t$invalidator = Drupal::service('cache_tags.invalidator');\n\t\t\tif ($invalidator instanceof CacheTagsPurgeInterface) {\n\t\t\t\t$invalidator->purge();\n\t\t\t\t$did[] = $step;\n\t\t\t} else {\n\t\t\t\t$did[] = $step . ':not-purgeable';\n\t\t\t}\n\t\t} elseif ($step === 'bins') {\n\t\t\t// unbounded in the worst case: deleteAll() on cache_render or cache_data is one\n\t\t\t// DELETE\n\t\t\t// whose row count is the bin's size, and rows written is the binding free-plan\n\t\t\t// meter. src/cron.js caps cache_data at 5,000 rows for exactly this reason, so\n\t\t\t// on a site whose GC is running this is bounded in practice; on one whose is\n\t\t\t// not, it is not. It is ALSO not splittable per bin without the bin list in the\n\t\t\t// plan, which would then be stale after the container rebuild two steps later.\n\t\t\t// Reported per run in the unit's rows_written so the real cost arrives as a\n\t\t\t// measurement rather than an assumption.\n\t\t\t$bins = 0;\n\t\t\tforeach (Cache::getBins() as $bin) {\n\t\t\t\t$bin->deleteAll();\n\t\t\t\t$bins++;\n\t\t\t}\n\t\t\t$out['bins'] = $bins;\n\t\t\t$did[] = $step;\n\t\t} elseif ($step === 'assets') {\n\t\t\tDrupal::service('asset.css.collection_optimizer')->deleteAll();\n\t\t\tDrupal::service('asset.js.collection_optimizer')->deleteAll();\n\t\t\tDrupal::service('asset.query_string')->reset();\n\t\t\t$did[] = $step;\n\t\t} elseif ($step === 'statics') {\n\t\t\tdrupal_static_reset();\n\t\t\t$did[] = $step;\n\t\t} elseif ($step === 'twig') {\n\t\t\tDrupal::service('twig')->invalidate();\n\t\t\t$did[] = $step;\n\t\t} elseif ($step === 'extension_lists') {\n\t\t\tDrupal::service('extension.list.profile')->reset();\n\t\t\tDrupal::service('extension.list.theme_engine')->reset();\n\t\t\tDrupal::service('theme_handler')->refreshInfo();\n\t\t\tDrupal::theme()->resetActiveTheme();\n\t\t\t$did[] = $step;\n\t\t} elseif ($step === 'container') {\n\t\t\t// indivisible, and the most expensive step after the bins delete. Compiling the\n\t\t\t// container is one pass over every service definition and every compiler pass;\n\t\t\t// there is no seam inside it. The two calls stay together: between\n\t\t\t// them Drupal::service() would resolve against a container marked dead and not\n\t\t\t// yet replaced.\n\t\t\t//\n\t\t\t// No wasm measurement exists for this step alone. What IS measured is the whole\n\t\t\t// flush at 282.9 ms, and cold boot -- which includes one container build -- at\n\t\t\t// 3,754 ms of edge cpuTime, so this step is plausibly the largest single piece\n\t\t\t// of the flush. DERIVED, not measured: it is the first thing to instrument.\n\t\t\t$kernel = $GLOBALS['__pw_kernel'];\n\t\t\t$kernel->invalidateContainer();\n\t\t\t$kernel->rebuildContainer();\n\t\t\t$did[] = $step;\n\t\t} elseif ($step === 'module_data') {\n\t\t\tDrupal::service('extension.list.module')->reset();\n\t\t\tDrupal::moduleHandler()->reload();\n\t\t\t$did[] = $step;\n\t\t} elseif ($step === 'rebuild_hooks') {\n\t\t\t// hook_rebuild across installed modules; same bound and same reason as\n\t\t\t// cache_flush above.\n\t\t\tDrupal::moduleHandler()->invokeAll('rebuild');\n\t\t\t$did[] = $step;\n\t\t} elseif ($step === 'router') {\n\t\t\t// INDIVISIBLE. RouteBuilder::rebuild() collects every route from every module,\n\t\t\t// runs the alter events and writes the dumped router tables in one pass, and\n\t\t\t// core's own comment requires it to be LAST so the router reflects everything\n\t\t\t// above it. It also acquires a lock, which on a persistent interpreter is the\n\t\t\t// hazard src/site-php.js documents: nothing calls releaseAll() at process\n\t\t\t// shutdown here, which is why alarm() sweeps expired semaphore rows.\n\t\t\tDrupal::service('router.builder')->rebuild();\n\t\t\t$did[] = $step;\n\t\t} else {\n\t\t\t$out['refused'] = 'unknown-flush-step';\n\t\t\t$out['error'] = 'unknown flush step: ' . $step;\n\t\t}\n\t\tif (!isset($out['refused'])) {\n\t\t\t$out['flushed'] = $did;\n\t\t\t$out['finished'] = 1;\n\t\t\t$out['ok'] = true;\n\t\t}\n\t} else {\n\t\t$out['refused'] = 'unknown-kind';\n\t\t$out['error'] = 'unknown unit kind: ' . $kind;\n\t}\n} catch (Throwable $e) {\n\t$out['error'] = get_class($e) . ': ' . $e->getMessage();\n\t$out['trace'] = substr($e->getTraceAsString(), 0, 900);\n\t$out['ok'] = false;\n}\n\necho json_encode($out);\n"