8000 Add "executable" option to server:run console command by tomasfejfar · Pull Request #23721 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

Add "executable" option to server:run console command #23721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

tomasfejfar
Copy link
Contributor
Q A
Branch? 3.4
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? there are no relevant tests for WebServerBundle
Fixed tickets #22101
License MIT
Doc PR will be added when finalized

The PR adds the "executable" option to server:run command to allow running server with custom executable (as in bin\console server:run --executable="php -c /path/to/ini")

I'm having a hard time testing this. The webserver bundle does not have any tests. I tried to add the symfony repo locally to my other project to test it, but I got:

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Can only install one of: symfony/symfony[dev-custom-server-executable, v3.3.3].
    - don't install symfony/web-server-bundle v3.3.3|don't install symfony/symfony dev-dev-custom-server-executable
    - Installation request for symfony/symfony dev-dev-custom-server-executable -> satisfiable by symfony/symfony[dev-dev-custom-server-executable].
    - Installation request for symfony/web-server-bundle 3.3.3 -> satisfiable by symfony/web-server-bundle[v3.3.3], symfony/symfony[v3.3.3].

Any pointers welcome.

@@ -69,6 +70,8 @@ public function __construct($documentRoot, $env, $address = null, $router = null
if (!ctype_digit($this->port)) {
throw new \InvalidArgumentException(sprintf('Port "%s" is not valid.', $this->port));
}

$this->executable = $executable;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I welcome any suggestions on how to validate this. Using is_executable would only work for full paths. And this can be anything from xdebug (from my original issue) to /usr/bin/php7_custom_build -c /custom/php.ini.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now the command would just fail on the higher layer (in WebServer::run)

$finder = new PhpExecutableFinder();
if (false === $executable = $finder->find()) {
throw new \RuntimeException('Unable to find the PHP executable.');
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move this logic to WebServerConfig constructor now that it holds an executable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved

@chalasr chalasr added this to the 3.4 milestone Jul 31, 2017
$env,
$input->getArgument('addressport'),
$input->getOption('router'),
$input->getOption('executable')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is in your oppinion "executable" correct name for what it holds? (e.g.: php or php -c /path/to/ini or /usr/bin/custom_php). Other options I considered were "command" (although that clashes with Symfony command) or "binary" (which is usually the file itself, not the whole command).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe executable and binary in this scenario are synonyms. Bash calls it command, in my mind it makes a bit more sense (even though the command will be the whole string that the Process will run).

@tomasfejfar tomasfejfar force-pushed the custom-server-executable branch from 85061fb to cde9568 Compare July 31, 2017 20:16
@tomasfejfar
Copy link
Contributor Author

I added a test for WebServerConfig class. In first commit the test is added for the existing implementation. Then a change commit adds the "executable" parameter and tests that it works as expected.

I still couldn't properly test whether the code works as a whole (see first message) and would very much welcome any suggestions on how to do it.

@tomasfejfar tomasfejfar force-pushed the custom-server-executable branch 4 times, most recently from be96c77 to 011d1db Compare July 31, 2017 20:31
@fabpot
Copy link
Member
fabpot commented Aug 1, 2017

Instead of adding an option for an edge case, I propose to be able to configure the executable via an env var.

@fernandocarletti
Copy link

I was working on the very same issue, but I came with a different approach: 3.4...xurumelous:feature/cli-settings-server

I did not change the $binary variable , only added support for php settings definition: php bin/console server:run localhost:8001 -c 'xdebug.remote_port=9001' -c 'xdebug.idekey=SAMPLE'. It is a bit more limited, since a custom php.ini cannot be set.

@tomasfejfar
Copy link
Contributor Author

@fabpot that makes sense. I'll try to update the code asap.

@xurumelous yes, I thought about that as well. It's actually what most people use when debugging CLI apps. But it would not work for my case. If you look at #22101 - my exact use cases is xdebug wrapper for the php binary. What's great about it is that you have centralized configuration and all you need to know is that you need to call xdebug instead of php. I always had a hard time describing someone how to pass the params - which is why I created the wrapper. It's setup once and forget. Also you may have multiple PHP versions and want to run it in a specific one (i.e. test something in 7.0 even though 7.1 is your default).

@fernandocarletti
Copy link

@tomasfejfar Yes, it makes sense. Are you making the change for the env variable? If you need help you can count on me. I really need to be able to set xdebug.remote_port per project. Right now I'm basically doing it manually, getting the command the server:run would run and running it myself.

@tomasfejfar
Copy link
Contributor Author

This implementation should allow you to set the executable to php -dxdebug.remote_port=123

If you could do the change for the env variable and send a PR from your fork to this branch, that would be great. I'll probably not have time for it this week.

Did you manage to test your changes somehow? How did you approach it? I've coded the whole thing without ever running it because I couldn't get it to work (as noted in the first post) :(

@tomasfejfar
Copy link
Contributor Author
tomasfejfar commented Aug 2, 2017

@fabpot what about setting the executable as a parameter and pulling it from the DI? I would prefer changing parameters.yml to fiddling with ENV variables. Also it could have specific settings for different envs (dev/prod).

@fernandocarletti
Copy link

@tomasfejfar I believe I'll have time for this later today or tomorrow night.

I tested in the vendor folder. I ran composer with --prefer-source option, so it cloned the repository for each dependency. I worked on this in the vendor folder, when it was ready, I patched my fork with the changes I made (git diff >> /tmp/patch then patch -p1 < /tmp/patch in my fork). I'm not very used to Symfony development, but that was my workaround to test it before running.

I thought you have tested it, so I did not mention one issue before, I'll comment on the line I'm talking about.


$process = new Process(array($binary, '-S', $config->getAddress(), $config->getRouter()));
$process = new Process(array($executable, '-S', $config->getAddress(), $config->getRouter()));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should "explode" $executable string. Depending on how the Process class is escaping the array, the first one will be the command name, see the example:

$ 'echo -e test'
bash: echo -e test: command not found

If you pass a whole string as the command, it will be interpreted as its name. I did not test it, but I believe it will do the same in the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yes... definitely. Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not as simple as it seems. Consider following executable: php -dmemory_limit=16M -dzend_extension="C:\Program Files\some_extension.dll". If I were to split this with spaces, it would break around the Program Files part. And parsing CLI arguments is way over the scope of this PR IMHO. I've googled for some ready-made solutions and everything is in the ballpark of hundred lines of code.

I tried to repurpose \Symfony\Component\Console\Input\StringInput for this, but it feels very hacky and I needed to add new public method to it.

See 50fda23

Tested with php index.php s:r --docroot web\ --executable "php -dmemory_limit=512M -dinclude_path=\"c:\Program Files\""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any suggestions welcome

@tomasfejfar tomasfejfar force-pushed the custom-server-executable branch 3 times, most recently from a990d3c to 8970e9f Compare August 7, 2017 20:43
@tomasfejfar
Copy link
Contributor Author

I kept the various commits to allow easy revert if some of them are not OK.

Tested on Windows:

php index.php s:r --docroot web\
SET SYMFONY_SERVER_EXECUTABLE=xdebug&&php index.php s:r --do
6D40
croot web\
SET SYMFONY_SERVER_EXECUTABLE=php -dmemory_limit=512M -dinclude_path="c:\\Program Files"&& php index.php s:r --docroot web\

Works as expected.

All green. Ready for review.

@tomasfejfar
Copy link
Contributor Author

Can I do anything else to move this forward?

@tomasfejfar
Copy link
Contributor Author

Any update on this?

$executable = $config->getExecutable();

// we need to separate the executable from the rest of the string as StringInput won't handle it
$firstPartOfCommand = explode(' ', $executable)[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about a path with a space in it?

$executable = null;

$envExecutable = getenv('SYMFONY_SERVER_EXECUTABLE');
if ($envExecutable !== false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be false !== $envExecutable

$executable = $envExecutable;
}

if ($executable === null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yoda-style as well

F438
@@ -177,12 +177,12 @@ private function parseArgument($token)
$arg = $this->definition->getArgument($c);
$this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token;

// if last argument isArray(), append token to last argument
// if last argument isArray(), append token to last argument
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be reverted (fabbot false positive)

} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
$arg = $this->definition->getArgument($c - 1);
$this->arguments[$arg->getName()][] = $token;

// unexpected argument
// unexpected argument
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be reverted (fabbot false positive)

Copy link
Member
@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomasfejfar: the current logic is broken, because it implicitly mixes escaped and unescaped command line arguments (this previous comment hints it.) Instead, what about not allowing command line args in the new env var? That would require you to create a small shell wrapper to add them there, thus make everything else much simpler.
If you really want to push args in the env var, the logic needs to consider that:

  • the env var is already shell-safe and doesn't need further escaping
  • the added args need to be escaped
  • the concatenated string needs to be prefixed by "exec" on Linux, to not break signalling the child process.

Escaping should be done in a similar way as done here:
https://github.com/symfony/symfony/blob/3.4/src/Symfony/Component/Process/ProcessBuilder.php#L270-L273

@nicolas-grekas
Copy link
Member

what about not allowing command line args in the new env var?

Oh btw, if you follow this path, what about putting that env var in PhpExecutableFinder?

@nicolas-grekas nicolas-grekas modified the milestones: 3.4, 4.1 Oct 8, 2017
@nicolas-grekas
Copy link
Member

Moving to 4.1. Rebase on master might be needed, where PHP 7.1 features can be used btw.

@tomasfejfar tomasfejfar force-pushed the custom-server-executable branch from 8970e9f to f14fde3 Compare November 3, 2017 22:34
@tomasfejfar tomasfejfar force-pushed the custom-server-executable branch from f14fde3 to b5ff2ba Compare November 3, 2017 22:35
fabpot added a commit that referenced this pull request Dec 31, 2017
…_BINARY` env var (nicolas-grekas)

This PR was merged into the 4.1-dev branch.

Discussion
----------

[Process] Make `PhpExecutableFinder` look for the `PHP_BINARY` env var

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #22101
| License       | MIT
| Doc PR        | -

I think this is enough to fix the linked issue and thus replace #23721.
ping @tomasfejfar FYI

Commits
-------

4bd01f2 [Process] Make `PhpExecutableFinder` look for the `PHP_BINARY` env var
@fabpot fabpot closed this Dec 31, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
0