{"record":{"id":"e98d8aa05be480d6","repo":"PHPOffice/PhpSpreadsheet","slug":"invalid-protocol-for-linked-drawing","errorCode":null,"errorMessage":"Invalid protocol for linked drawing","messagePattern":"Invalid protocol for linked drawing","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Worksheet/Drawing.php","lineNumber":126,"sourceCode":"\n        $this->path = '';\n        if ($zip instanceof ZipArchive) {\n            $zipPath = explode('#', $path)[1];\n            $locate = @$zip->locateName($zipPath);\n            if ($locate !== false) {\n                if ($this->isImage($path)) {\n                    $this->path = $path;\n                    $this->setSizesAndType($path);\n                }\n            }\n        // Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979\n        } elseif (\n            filter_var($path, FILTER_VALIDATE_URL)\n            || Preg::isMatch('~^phar://~i', $path)\n            || (Preg::isMatch('/^([\\w.\\s\\x00-\\x1f]+):/', $path) && !Preg::isMatch('/^([\\w.]+):/', $path))\n        ) {\n            if (!Preg::isMatch('/^(http|https|file|ftp|s3):/', $path)) {\n                throw new PhpSpreadsheetException('Invalid protocol for linked drawing');\n            }\n            if (!$allowExternal) {\n                return $this;\n            }\n            if ($isWhitelisted !== null && !$isWhitelisted($path)) {\n                return $this;\n            }\n            // Implicit that it is a URL, rather store info than running check above on value in other places.\n            $this->isUrl = true;\n            $ctx = null;\n            // https://github.com/php/php-src/issues/16023\n            // https://github.com/php/php-src/issues/17121\n            if (str_starts_with($path, 'https:') || str_starts_with($path, 'http:')) {\n                $ctxArray = [\n                    'http' => [\n                        'user_agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',\n                        'header' => [\n                            //'Connection: keep-alive', // unacceptable performance","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Worksheet/Drawing.php#L108-L144","documentation":"Drawing::setPath() classifies its argument as zip entry, URL, or local file. If the value looks like a URL (passes FILTER_VALIDATE_URL, starts with phar://, or has a scheme-like prefix) the scheme must be one of http, https, file, ftp or s3 — anything else throws 'Invalid protocol for linked drawing'. Properly formed data:image/...;base64 URIs are accepted earlier in the method, so this fires for other schemes only.","triggerScenarios":"$drawing->setPath('phar://app.phar/assets/logo.png') (matched by the phar check, rejected by the whitelist); 'php://temp'; 'gopher://' or custom stream wrappers; 'data:image/svg+xml;base64,...' (the '+' fails the early data-image regex); uppercase schemes like 'HTTP://...'.","commonSituations":"Code packaged or run inside phar archives (boxed CLI apps, PHPUnit fixtures); passing PHP stream wrappers; HTML-editor payloads with svg+xml data URIs; note the $allowExternal flag and optional isWhitelisted callback only control fetching, not scheme validity.","solutions":["Use one of the allowed schemes, lowercase: http, https, file, ftp, s3.","For phar:// or php:// streams, copy the bytes to a temp file first and setPath() the temp path.","For non-simple data URIs (e.g. svg+xml), decode the payload and use MemoryDrawing::fromString() instead."],"exampleFix":"// before\n$drawing->setPath('phar://app.phar/assets/logo.png'); // Invalid protocol for linked drawing\n\n// after\n$tmp = tempnam(sys_get_temp_dir(), 'img');\nfile_put_contents($tmp, file_get_contents('phar://app.phar/assets/logo.png'));\n$drawing->setPath($tmp);","handlingStrategy":"validation","validationCode":"$scheme = strtolower((string) parse_url($path, PHP_URL_SCHEME));\nif ($scheme !== '' && !in_array($scheme, ['http', 'https', 'file', 'ftp', 's3'], true)) {\n    $tmp = tempnam(sys_get_temp_dir(), 'img');\n    file_put_contents($tmp, file_get_contents($path)); // phar://, php:// etc.\n    $path = $tmp;\n}\n$drawing->setPath($path);","typeGuard":"function isAllowedDrawingScheme(string $path): bool\n{\n    $scheme = strtolower((string) parse_url($path, PHP_URL_SCHEME));\n    return $scheme === '' || in_array($scheme, ['http', 'https', 'file', 'ftp', 's3'], true);\n}","tryCatchPattern":"try {\n    $drawing->setPath($path);\n} catch (PhpSpreadsheetException $e) {\n    if (str_contains($e->getMessage(), 'Invalid protocol')) {\n        $drawing = MemoryDrawing::fromString(file_get_contents($path));\n    }\n}","preventionTips":["Only http, https, file, ftp and s3 schemes (lowercase) are accepted for linked drawings","Materialize phar:// and php:// streams into temp files before setPath()","Simple data:image/...;base64 URIs are accepted, but svg+xml and other variants are not — decode those to MemoryDrawing"],"tags":["php","phpspreadsheet","url-scheme","phar","stream-wrapper","drawing"],"backgroundTag":"unsupported-url-scheme","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}