Download the PHP package precision-soft/symfony-console without Composer

On this page you can find all versions of the php package precision-soft/symfony-console. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package symfony-console

Symfony Console

ci PHP >= 8.2 PHPStan Level 8 Code Style PER-CS2.0

A Symfony bundle for generating configuration files for cron jobs and workers. It supports multiple output templates including crontab, Supervisor, and Kubernetes (CronJob and Worker) formats.

You may fork and modify it as you wish.

Any suggestions are welcomed.

Features

Requirements

Installation

Commands

Command Description
precision-soft:symfony:console:cronjob-create Generates cron job configuration files based on the bundle config
precision-soft:symfony:console:worker-create Generates worker configuration files based on the bundle config
precision-soft:symfony:console:logs-dir-create Creates the configured logs directories, idempotently

Both generation commands accept three read-only modes, none of which writes anything:

Option Behaviour
--dry-run Lists the destination paths that would be added, changed or removed, with their status
--diff The same list, each pending path followed by a unified diff of its content
--check The same list, exiting with failure when anything is pending — a CI drift check

A path is added when it does not exist yet, changed when its content differs, and removed when it sits in conf_files_dir and no command declares it any more. Paths whose content already matches are not reported. --check and --diff combine, so a failing drift check can show what drifted. A removed path's diff lists the lines being deleted under +++ /dev/null, and a file whose bytes differ only in their line endings (CRLF on disk, LF generated) is reported as changed with a diff that reads \ line endings differ.

A configuration that declares no file at all reports nothing: generation returns early on an empty set and never clears a destination directory, so reporting its content as removed would be drift no run could ever fix.

Configuration

Logs directories

cronjob-create and worker-create create their own logs directory, but only as a side effect of generating conf files, so a deployment that never runs them is left without the directories. logs-dir-create creates them on its own, and can be run at any point of a deployment.

The directories it creates are derived from precision_soft_symfony_console.cronjob.config.logs_dir and precision_soft_symfony_console.worker.config.logs_dir, so overriding either of those also moves the directory that gets created. Use the root-level logs_dirs node to add directories that no cron job or worker owns:

Both cronjob.config.logs_dir and worker.config.logs_dir have defaults, so %kernel.logs_dir%/cron and %kernel.logs_dir%/worker are created even by an application that declares no cron job and no worker.

The resulting list is deduplicated and exposed as the precision_soft_symfony_console.logs_dirs container parameter. Deduplication compares the configured values before the container expands them, so logs_dirs: ['var/log/cron'] is kept alongside %kernel.logs_dir%/cron even when both resolve to the same path — harmless, since the directories are created idempotently.

Entries must be strings, and this is enforced at container build time: a bare logs_dirs: [123] is rejected with the node named, rather than reaching the filesystem as a directory name.

logs_dirs does not deep-merge. When the node is declared in more than one configuration file — a bundle default and an environment override, say — the last declaration replaces the list instead of appending to it, so an environment can drop directories as well as add them. The two derived directories (cronjob.config.logs_dir and worker.config.logs_dir) are unaffected and always present.

Cron job configuration

precision_soft_symfony_console.yaml

If precision_soft_symfony_console.cronjob.config.settings.heartbeat is set to true, a heartbeat command will automatically be added to each generated crontab file. The auto-generated heartbeat command runs /bin/touch <logs_dir>/heartbeat.<destination_file> every minute, where <destination_file> is the destination path with its / separators replaced by ., so two files with the same base name in different sub directories keep distinct heartbeat files. You may override the heartbeat by defining a command named heartbeat in the commands list.

An overridden heartbeat is emitted into every generated crontab file, and the same command string reaches all of them. Where the heartbeat has to know which file it is proving alive — a heartbeat that records the file in a shared database rather than touching a local one — put the {destination_file} placeholder (CrontabTemplate::DESTINATION_FILE_PLACEHOLDER) in the command or in log_file_name; it is replaced with the path of the file being generated relative to conf_files_dir, flattened by replacing / with . (machine-a/crontab becomes machine-a.crontab), so the value is unique per file and safe to use as a log file name or as an argument. The placeholder is substituted in the heartbeat command only, and an override that does not contain it is emitted byte for byte as before:

destination_files lists crontab files that must be generated even when no command targets them. A file only comes into existence because some command names it in destination_file, so a machine that runs nothing but the heartbeat has no crontab at all — this node is how it gets one. Declared files are added to the ones the commands already produce, never instead of them, and the default destination_file is still materialised for a configuration whose only command is the heartbeat. They are generated unconditionally, so with heartbeat off a declared file that no command targets is written with no cron rows in it.

A command named heartbeat is always taken as the heartbeat override rather than as an ordinary cron row. When settings.heartbeat is false there is no heartbeat to override, so that command is not emitted into any file — turning the heartbeat off removes the override along with it.

The user setting at config level prepends the user to each crontab command line. It can be overridden per command via the command-level user option. Each command also supports log_file_name (custom log file name, defaults to <command-name>.log) and destination_file (override the config-level destination file to generate separate crontab files per command).

Two crontab syntax rules are handled for you. A % in a command part, in logs_dir or in log_file_name is emitted as \%, because cron cuts the command field at an unescaped % and feeds what follows to the command's standard input — --date=%Y-%m-%d ran as --date= with Y- on stdin. The log redirection path is single-quoted with POSIX quoting rather than escapeshellarg(), which follows LC_CTYPE and, under the C locale php runs with by default on glibc, drops every byte above 0x7f (/srv/données became /srv/donnes). The command parts themselves are shell text joined by a space: the row is run by cron's shell, so a >>, a $(…) or a quoted argument you write is passed through as written. A value carrying a newline or another control character — a command part, user, logs_dir or log_file_name — is rejected at generation time with InvalidConfigurationException, since it would end the row and start another one.

Worker configuration (Supervisor)

Each command generates a separate .conf file for Supervisor. The prefix, user, auto_start, auto_restart, log_file, and number_of_processes are available settings with defaults (can be set at the config level and overridden per command). If log_file is not specified, it defaults to <logs_dir>/<command-name>.log.

A literal % in a command part or in log_file is emitted as %%, because Supervisor %-interpolates command and the log paths (%(program_name)s, %(ENV_HOME)s) and a bare % is a fatal format error when the file is loaded. The placeholders of the program template are substituted in one pass, so a command part that happens to read %user% is data, not a second template. A value carrying a newline or another control character — a command part, prefix, user or log_file — is rejected at generation time with InvalidConfigurationException.

Splitting worker files into sub directories

By default every command lands directly in conf_files_dir as <command-name>.conf. Use destination_sub_dir and destination_suffix to spread the generated files across sub directories or to disambiguate their file names — useful when a single application config describes workers for several machines, and each machine's Supervisor only includes its own sub directory:

Generates:

Both options are resolved per command first and fall back to the config-level settings value. Setting either to an empty string at command level opts that command out of the config-level value. destination_sub_dir is always relative to conf_files_dir and is collapsed to its meaningful segments, so leading, trailing and repeated / as well as . segments are dropped; destination_suffix is inserted before the .conf extension with surrounding dots stripped. Values containing .. or a backslash are rejected at container build time, and destination_suffix additionally rejects /.

That validation runs on the configuration as written, so a value supplied through a container parameter (destination_sub_dir: '%app.machine%') is still a literal placeholder at that point and cannot be checked. ConfFileWriter remains the backstop — it rejects any generated path that escapes conf_files_dir, whatever produced it.

Both options are honoured only by SupervisorTemplate. KubernetesWorkerTemplate writes a single manifest whose name comes from destination_file, and ignores them.

Worker configuration (systemd)

Generates one concrete unit per instance, so nothing has to be templated at install time:

A number_of_processes of 1 drops the numeric suffix and writes app-name-messenger_consume.service. The - of the command key became _: Symfony normalizes the keys of a configuration array, so a command declared as messenger-consume reaches every template as messenger_consume.

Settings, each resolved per command first and falling back to the config level:

Setting Default Emitted as
user none, mandatory User=
working_directory %kernel.project_dir% WorkingDirectory=
restart_policy always Restart=
environment_file none, line omitted when unset EnvironmentFile=
standard_output append:<log_file> StandardOutput=
standard_error append:<log_file> StandardError=
log_file <logs_dir>/<command-name>.log the append: target of both streams

restart_policy accepts the systemd values no, on-success, on-failure, on-abnormal, on-watchdog, on-abort and always, validated at container build time. prefix is optional here, unlike for Supervisor, and the unit name is <prefix>-<command-name> sanitized down to what systemd accepts: everything outside A-Za-z0-9_.- collapses to -, .. collapses to ., and surrounding - and . are stripped. @ is deliberately not preserved, because a unit named [email protected] is a template systemd refuses to start without an instance name. A name that sanitizes down to nothing is rejected, and so are two commands whose names sanitize to the same unit.

command must start with an absolute executable path — systemd rejects a unit whose ExecStart is not absolute, so generation fails loudly instead of writing a unit that cannot start. Use /usr/bin/php rather than php.

Four systemd syntax rules are handled for you, because each fails silently or fatally at systemctl start rather than at generation time:

A value carrying a newline or another control character — user, working_directory, environment_file, standard_output, standard_error, log_file or a command part — is rejected at generation time with InvalidConfigurationException: systemd would read the text after the newline as a second directive, and systemd-analyze verify accepts such a unit without a word.

standard_output and standard_error are emitted verbatim, so they must be a form systemd accepts: inherit, null, tty, journal, kmsg, socket, or a file:, append:, truncate: or fd: prefix. They are not validated at container build time, since the accepted set grows with systemd versions.

destination_sub_dir and destination_suffix work exactly as they do for Supervisor, the suffix landing before the .service extension.

Do not point conf_files_dir at /etc/systemd/system. Generation is atomic at directory level: the whole destination directory is replaced, so every unrelated unit in it would be deleted. Generate into a directory this bundle owns, then copy or symlink the units from there and run systemctl daemon-reload.

Kubernetes CronJob template

Kubernetes Worker template

The destination_file setting is mandatory for both Kubernetes templates. The Kubernetes Worker template has no default. The Kubernetes CronJob template defaults to crontab from the cronjob config settings if not overridden per command.

Both templates write a values file whose collection is a YAML sequence:

Up to and including v4.4.0 the collection was emitted as a mapping keyed 0:, 1: instead. Both forms parse into the same structure in most consumers, but they do not merge the same way — Helm merges two values files key by key for a mapping and replaces the whole value for a sequence. A chart that indexes an entry directly (.Values.CronJobs.jobs.0) must be updated to use index or a range.

Available templates

Template class Output format
CrontabTemplate Standard crontab file
SupervisorTemplate Supervisor .conf files (one per command)
SystemdServiceTemplate systemd .service units (one per instance)
KubernetesCronjobTemplate Kubernetes CronJob manifest
KubernetesWorkerTemplate Kubernetes Worker manifest

Command traits

The bundle provides traits for long-running Symfony commands.

MemoryLimitTrait

Adds a --memory-limit option and monitors memory usage during execution.

TimeLimitTrait

Adds a --time-limit option (seconds) to stop after a given runtime.

MemoryAndTimeLimitsTrait

Combines both limits into one trait. Calls stopScriptIfLimitsReached() which throws LimitExceededException when either limit is exceeded — catch it to perform cleanup before exiting.

InstancesTrait

Adds --max-instances and --instance-index options for parallel execution of the same command.

Contracts

The bundle defines the following interfaces in the PrecisionSoft\Symfony\Console\Contract namespace:

Interface Purpose
TemplateInterface Implemented by all templates — generate(ConfigInterface, array): ConfFilesDto
ConfigInterface Provides template class, logs dir, conf files dir, and settings
SettingsInterface Provides access to the settings object via getSettings(): SettingInterface
SettingInterface Retrieves a single setting value via getSetting(string): ?string — boolean values are returned as the literal strings 'true' / 'false', null stays null, other scalars are cast to string
ExceptionInterface Implemented by every exception in the bundle — getContext(): array and setContext(?array): static

Services

MemoryService

Static utility for memory operations (PrecisionSoft\Symfony\Console\Service\MemoryService):

Method Description
setMemoryLimitIfNotHigher(string $newLimit): void Raises memory_limit only if the new limit is higher than the current one
getMemoryUsage(): string Returns current memory usage in human-readable format
convertBytesToHumanReadable(int $bytes): string Converts bytes to human-readable string (e.g. 128 MB)
returnBytes(string $value): int Parses a memory string (512M, 1G) into bytes

AttributeService

Static utility for command metadata (PrecisionSoft\Symfony\Console\Service\AttributeService):

Method Description
getCommandName(string $commandClass): string Extracts the command name from the AsCommand attribute of a command class

Exceptions

All exceptions extend PrecisionSoft\Symfony\Console\Exception\Exception:

Exception Thrown when
ConfGenerateException Configuration file generation or write fails
InvalidConfigurationException Required configuration is missing or invalid
InvalidValueException A value (e.g. memory limit) cannot be parsed
LimitExceededException Memory or time limit is exceeded (MemoryAndTimeLimitsTrait)
SettingNotFoundException A requested setting does not exist on the DTO

Exception context

Every exception carries a structured context array next to its message, so the facts describing a failure do not have to be parsed back out of a string:

getContext() returns [] when nothing was attached. Context values are expected to be scalars — SymfonyStyleTrait::formatThrowable() renders them as JSON. The bundle attaches templateClass and confFilesDir when a template fails, logsDir when a logs directory cannot be created, and destinationDir, backupDirectory and backupRestored when an activation failure leaves a backup behind — that last one being the case where an operator most needs to know where the previous configuration went.

ConfGenerateException::from($throwable, $context) re-wraps a throwable while keeping it as the previous one. It is declared on ConfGenerateException rather than on the shared base class because SettingNotFoundException takes a different constructor signature, which the new static() inside from() could not honour.

Error and its subclasses are deliberately never wrapped: a TypeError raised inside a custom template is a bug in that template, and turning it into a ConfGenerateException would send the operator looking for a bad configuration value that does not exist. Exceptions thrown by a custom template are wrapped, so the @throws ConfGenerateException on generate() still holds for anything a caller is expected to handle.

When a command prints a throwable it prints the whole previous chain, each link as class::file::line plus its context, joined by <-:

AbstractCommand

PrecisionSoft\Symfony\Console\Command\AbstractCommand extends Symfony's Command and provides:

For custom templates

Create a template service implementing TemplateInterface (PrecisionSoft\Symfony\Console\Contract\TemplateInterface) and add to your services.yaml:

Troubleshooting

Memory limit trait reports incorrect usage

MemoryLimitTrait reads memory_limit from php.ini via \ini_get(). If your environment sets -1 (unlimited), the trait returns false for getMemoryLimitReached() — this is intentional. To enforce a limit, always pass an explicit value to configureMemoryLimit().

Generated config files have wrong permissions

ConfFileWriter creates files with the permissions of the running PHP process. If the generated crontab or Supervisor config needs specific ownership (e.g. root), adjust permissions after generation or run the command as the target user.

Kubernetes template throws InvalidConfigurationException

Both KubernetesCronjobTemplate and KubernetesWorkerTemplate require the destination_file setting. Unlike CrontabTemplate (which defaults to crontab), Kubernetes templates have no default — set it explicitly in your config.

Command traits conflict with existing setUp/tearDown

The command traits (MemoryLimitTrait, TimeLimitTrait) use initialize() hooks, not setUp()/tearDown(). They are safe to combine with any test base class. Call initializeMemoryLimit() or initializeTimeLimit() in your command's initialize() method.

Security Considerations

Heartbeat files

When heartbeat is enabled, the crontab generator adds a /bin/touch <logs_dir>/heartbeat.<destination_file> command that runs every minute. Ensure:

Path traversal protection

destination_file, destination_files, destination_sub_dir and destination_suffix are the four configuration values appended to conf_files_dir to form a generated path, and all four reject .. and backslashes at container build time, naming the offending node. destination_file and destination_files additionally reject a value built only from . and / segments, which would otherwise normalize away to nothing and resolve to conf_files_dir itself. That check runs on the configuration as written, so a value supplied through a container parameter is still a literal placeholder at that point and cannot be checked — which is why the writer remains the backstop.

conf_files_dir itself must be a real directory. Activation renames the destination path, so a symlink there would be replaced by a real directory rather than written through — silently destroying the link. Both generation and the read-only preview modes refuse it with ConfGenerateException, so --check cannot pass what a write would break.

ConfFileWriter validates that all generated file paths stay within the configured conf_files_dir. Paths containing .. or resolving outside the destination directory are rejected with ConfGenerateException. Each written file is additionally canonicalized via realpath and re-checked against the (also canonicalized) temporary directory, blocking symlink-based escapes that pass textual checks; the temp directory itself is verified to be a real directory (not a pre-existing symlink) to close a TOCTOU window after mkdir. Do not bypass these checks by symlinking the destination to a sensitive location.

Configuration values in generated files

Command parts (the command array) are rendered into generated config files with the escaping each consumer needs and nothing more: systemd's ExecStart quoting plus $$, \; and %%; cron's \% and a POSIX-quoted log path; Supervisor's %%. Crontab and Supervisor command parts remain shell text, so the shell metacharacters you write are run by the shell — do not pass untrusted user input as command strings or settings. Every value written into a crontab row, a Supervisor program or a systemd unit is rejected with InvalidConfigurationException when it carries a control character, because a newline would end the directive and start another one. Kubernetes manifests are written by Symfony\Component\Yaml\Yaml::dump(), which quotes whatever needs quoting.

A ; in a Supervisor command is a known limitation. SupervisorTemplate writes command = <parts> verbatim, and ; opens an inline comment in the INI dialect — so a command such as sh -c 'a; b' produces a file whose command directive reads back as sh -c a, silently dropping the rest. The bundle neither quotes nor rejects it; keep ; out of the command parts, or wrap the composite command in a script and point the worker at that instead.

Example application

A runnable slice of a product catalogue's operations lives under .example/: two commands built on AbstractCommand and its traits (a sharded, limit-aware price-list import worker and a time-limited exchange-rate refresh cron job), one bundle configuration, and a test suite that generates and asserts the crontab rows, the Supervisor programs, the systemd units (verified with systemd-analyze wherever it is available) and the Kubernetes values files, plus the three preview modes. It installs the package from the working tree through a path repository, so it always tests the code as it stands; run it with .dev/validate/all.sh --example or cd .example && composer install && composer check. The directory is export-ignored and never reaches a consumer's vendor/.

Dev

The development environment uses Docker. The ./dc script is a Docker Compose wrapper located in .dev/.

Run the full gate the way the pre-commit hook runs it - the CI workflow in .github/workflows/ci.yml calls the same composer scripts, so the two cannot drift:

Mutation testing is opt-in for the same reason, plus cost - it runs the suite once per mutant:

Infection is a pinned phar in the image, not a composer dependency, and infection.json5 carries a minMsi floor equal to the last measured score, so the section fails when a change makes the suite weaker rather than only reporting a number. Raise the floor when the score improves.

Build against another PHP version with the PHP_VERSION build argument - each version is tagged as its own image, so switching back and forth costs nothing:

Coverage is available through pcov, which is installed but disabled by default:

After editing a file, ./dc restart dev (a few seconds) is enough to be sure the container is not serving a stale copy - the bind mount can keep the old inode after an atomic rewrite.


All versions of symfony-console with dependencies

PHP Build Version
Package Version
Requires php Version >=8.2
symfony/config Version ^7.0 || ^8.0
symfony/console Version ^7.0 || ^8.0
symfony/dependency-injection Version ^7.0 || ^8.0
symfony/filesystem Version ^7.0 || ^8.0
symfony/http-kernel Version ^7.0 || ^8.0
symfony/yaml Version ^7.0 || ^8.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package precision-soft/symfony-console contains the following files

Loading the files please wait ...