Your project uses dangerous command injection functions
- Read doc
- Security
- Critical
More information: https://insight.symfony.com/what-we-analyse/php.use_command_injection_function
- if ($overrides !== null) {
- $environment = array_merge((array) getenv(), $overrides);
- }
- $process = proc_open(
- $command,
- [
- 0 => ['pipe', 'r'],
- 1 => ['pipe', 'w'],
- 2 => ['pipe', 'w'],
Your project uses discouraged functions to kill scripts
- Read doc
- Reliability
- Major
More information: https://insight.symfony.com/what-we-analyse/php.use_exit_function
- return true;
- }
- if (realpath($_SERVER['SCRIPT_FILENAME'] ?? '') === __FILE__) {
- exit(main());
- }
Your project should not use global variables or functions 18
- Read doc
- Reliability
- Minor
More information: https://insight.symfony.com/what-we-analyse/php.use_global_variable_or_function
- /**
- * Builds the default package name from host username and directory name.
- *
- * Falls back to the template name when no username can be determined.
- */
- function defaultPackageName(string $dirName, ?string $username): string
- {
- if ($username === null || $username === '') {
- return 'projek-xyz/template';
- }
- /**
- * Resolves the host username from the environment.
- *
- * Checks USER, LOGNAME, then USERNAME for cross-platform coverage.
- */
- function templateUsername(): ?string
- {
- foreach (['USER', 'LOGNAME', 'USERNAME'] as $variable) {
- $value = getenv($variable);
- if ($value !== false && $value !== '') {
- * Returns [package, warning]: null package means end of input (skip),
- * an invalid answer falls back to the default with a warning message.
- *
- * @return array{0: ?string, 1: ?string}
- */
- function resolvePackageInput(?string $line, string $default): array
- {
- if ($line === null) {
- return [null, null];
- }
- * Returns null on end of input, otherwise the chosen or default name.
- *
- * @param resource $in
- * @param resource $out
- */
- function promptPackageName($in, $out, string $default): ?string
- {
- fwrite($out, 'Package name [' . $default . ']: ');
- $line = fgets($in);
- [$package, $warning] = resolvePackageInput($line === false ? null : $line, $default);
- * Defaults to no on an empty answer or end of input.
- *
- * @param resource $in
- * @param resource $out
- */
- function promptGitInit($in, $out, string $package): bool
- {
- fwrite($out, 'Do you want to git init your ' . ANSI_YELLOW . $package . ANSI_RESET . ' repo? [y/N] ');
- $line = fgets($in);
- * Overrides are merged over the current environment; null inherits it.
- *
- * @param ?array<string, string> $overrides
- * @return array{0: int, 1: string}
- */
- function runCommand(string $command, ?array $overrides = null): array
- {
- $environment = null;
- if ($overrides !== null) {
- $environment = array_merge((array) getenv(), $overrides);
- /**
- * Checks whether a command can be executed.
- *
- * @param ?array<string, string> $overrides
- */
- function isCommandAvailable(string $command, ?array $overrides = null): bool
- {
- [$code] = runCommand($command . ' --version', $overrides);
- return $code === 0;
- }
- * Returns a status word: git-not-found, already-git, no-identity,
- * committed, init-failed, or commit-failed.
- *
- * @param ?array<string, string> $overrides
- */
- function initializeGitRepository(string $root, string $message, ?array $overrides = null): string
- {
- if (! isCommandAvailable('git', $overrides)) {
- return 'git-not-found';
- }
- * Interacts only when $interactive is true and github mode is off, then
- * transforms, verifies, strips the bootstrap itself, and finally
- * initializes git when requested — so the single initial commit contains
- * the bootstrapped state rather than the template state.
- */
- function bootstrapProject(string $root, $stdin, $stdout, bool $interactive, array $env): int
- {
- $target = $env['GITHUB_REPOSITORY'] ?? null;
- $githubMode = is_string($target) && $target !== '';
- $initGit = false;
- *
- * composer forks script hooks onto a real terminal only while it is
- * interactive (EventDispatcher::executeTty); with --no-interaction it
- * runs them on a pipe, so stream_isatty() mirrors composer's own mode.
- */
- function main(): int
- {
- return bootstrapProject(
- dirname(__DIR__),
- STDIN,
- STDOUT,
- * Skips dependency directories, binary files, and this script itself.
- * The visitor receives the relative path and the file content.
- *
- * @param callable(string, string): void $visitor
- */
- function walkTemplateFiles(string $root, callable $visitor): void
- {
- $skipped = ['/vendor/', '/node_modules/', '/.git/'];
- $iterator = new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
- *
- * Returns the sorted list of changed files relative to the root.
- *
- * @return string[]
- */
- function rewriteTemplateReferences(string $root, string $target): array
- {
- $patterns = ['projek-xyz/php-lib-template', 'projek-xyz/template'];
- $changed = [];
- walkTemplateFiles($root, function (
- * Resets root package versions to 0.0.0.
- *
- * Touches package.json and the two root entries of package-lock.json;
- * dependency versions and composer.lock stay untouched.
- */
- function resetPackageVersions(string $root): void
- {
- $targets = ['package.json', 'package-lock.json'];
- foreach ($targets as $file) {
- $path = $root . '/' . $file;
- }
- /**
- * Removes a file or directory tree, tolerating missing paths.
- */
- function removePath(string $path): bool
- {
- if (is_file($path)) {
- return unlink($path);
- }
- * that regenerates them is available. Returns the sorted removed paths.
- *
- * @param array{composer?: bool, npm?: bool} $capabilities
- * @return string[]
- */
- function removeTemplateOnlyFiles(string $root, bool $githubMode, array $capabilities): array
- {
- $targets = [
- '.github/README.md',
- '.github/workflows/init.yml',
- '.agents/rules',
- /**
- * Activates the commented export-ignore list in .gitattributes.
- *
- * Returns the number of entries activated.
- */
- function uncommentExportIgnoreList(string $root): int
- {
- $path = $root . '/.gitattributes';
- if (! is_file($path)) {
- return 0;
- * An empty array means the tree is clean. With no target (interactive
- * name prompt skipped) reference and name checks are omitted.
- *
- * @return string[]
- */
- function verifyTransformation(string $root, ?string $target, bool $githubMode): array
- {
- $failures = [];
- $mustBeGone = [
- '.github/README.md',
- /**
- * Removes the post-create-project-cmd hook from the project composer.json.
- *
- * Returns true when the hook was present and has been stripped.
- */
- function removeSelfFromComposerJson(string $root): bool
- {
- $path = $root . '/composer.json';
- if (! is_file($path)) {
- return false;
Your project should use dedicated PHP string functions 6
- Read doc
- Productivity
- Info
More information: https://insight.symfony.com/what-we-analyse/php.use_string_function
- $path = $file->getPathname();
- $relative = str_replace('\\', '/', ltrim(substr($path, strlen($root)), '/'));
- $skip = $path === __FILE__;
- foreach ($skipped as $directory) {
- if (strpos('/' . $relative . '/', $directory) !== false) {
- $skip = true;
- break;
- }
- }
- continue;
- }
- $content = file_get_contents($path);
- if ($content === false || strpos(substr($content, 0, 8192), "\0") !== false) {
- continue;
- }
- $visitor($relative, $content);
- }
- $root,
- $target,
- $patterns,
- &$changed
- ): void {
- if (strpos($content, 'projek-xyz/') === false) {
- return;
- }
- $replaced = str_replace($patterns, $target, $content);
- }
- }
- $packageJson = file_get_contents($root . '/package.json');
- if ($packageJson === false || strpos($packageJson, '"version": "0.0.0"') === false) {
- $failures[] = 'package.json version is not 0.0.0';
- }
- $attributes = file_get_contents($root . '/.gitattributes');
- $totalEntries = $attributes === false ? 0 : preg_match_all('/^.*export-ignore$/m', $attributes);
- string $content
- ) use (
- &$failures,
- $patterns
- ): void {
- if (strpos($content, $patterns[0]) !== false || strpos($content, $patterns[1]) !== false) {
- $failures[] = 'residual template reference in ' . $relative;
- }
- });
- }
- string $content
- ) use (
- &$failures,
- $patterns
- ): void {
- if (strpos($content, $patterns[0]) !== false || strpos($content, $patterns[1]) !== false) {
- $failures[] = 'residual template reference in ' . $relative;
- }
- });
- }