input('php_extensions'); if (is_string($extensions)) { $this->merge([ 'php_extensions' => collect(preg_split('/[,,\s]+/u', $extensions)) ->map(fn ($item) => trim((string) $item)) ->filter() ->values() ->all(), ]); } } public function rules(): array { return [ 'package_file' => ['required', 'file', 'mimes:zip', 'max:51200'], 'version' => ['nullable', 'string', 'max:32', 'regex:/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/'], 'typecho_min' => ['nullable', 'string', 'max:16'], 'typecho_max' => ['nullable', 'string', 'max:16'], 'php_min' => ['nullable', 'string', 'max:16'], 'php_max' => ['nullable', 'string', 'max:16'], 'php_extensions' => ['nullable', 'array'], 'php_extensions.*' => ['string', 'max:32'], 'is_stable' => ['nullable', 'boolean'], 'mark_as_latest' => ['nullable', 'boolean'], 'changelog' => ['nullable', 'string'], 'published_at' => ['nullable', 'date'], ]; } public function withValidator(Validator $validator): void { $validator->after(function (Validator $validator) { $file = $this->file('package_file'); if (!$file || !$file->isValid() || $this->zipHasManifestAtRoot($file->getRealPath())) { return; } if (blank((string) $this->input('version', ''))) { $validator->errors()->add('version', 'zip 根目录没有 manifest.json 时,必须手动填写版本号。'); } }); } private function zipHasManifestAtRoot(string $path): bool { $zip = new ZipArchive(); if ($zip->open($path) !== true) { return false; } try { for ($i = 0; $i < $zip->numFiles; $i++) { $stat = $zip->statIndex($i); $name = trim((string) ($stat['name'] ?? ''), '/'); if ($name === 'manifest.json') { return true; } } return false; } finally { $zip->close(); } } }