SudoCommand.php 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <?php
  2. /*
  3. * This file is part of Psy Shell.
  4. *
  5. * (c) 2012-2023 Justin Hileman
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Psy\Command;
  11. use PhpParser\NodeTraverser;
  12. use PhpParser\PrettyPrinter\Standard as Printer;
  13. use Psy\Input\CodeArgument;
  14. use Psy\Readline\Readline;
  15. use Psy\Sudo\SudoVisitor;
  16. use Symfony\Component\Console\Input\InputInterface;
  17. use Symfony\Component\Console\Output\OutputInterface;
  18. /**
  19. * Evaluate PHP code, bypassing visibility restrictions.
  20. */
  21. class SudoCommand extends Command
  22. {
  23. private $readline;
  24. private $parser;
  25. private $traverser;
  26. private $printer;
  27. /**
  28. * {@inheritdoc}
  29. */
  30. public function __construct($name = null)
  31. {
  32. $this->parser = new CodeArgumentParser();
  33. $this->traverser = new NodeTraverser();
  34. $this->traverser->addVisitor(new SudoVisitor());
  35. $this->printer = new Printer();
  36. parent::__construct($name);
  37. }
  38. /**
  39. * Set the Shell's Readline service.
  40. *
  41. * @param Readline $readline
  42. */
  43. public function setReadline(Readline $readline)
  44. {
  45. $this->readline = $readline;
  46. }
  47. /**
  48. * {@inheritdoc}
  49. */
  50. protected function configure()
  51. {
  52. $this
  53. ->setName('sudo')
  54. ->setDefinition([
  55. new CodeArgument('code', CodeArgument::REQUIRED, 'Code to execute.'),
  56. ])
  57. ->setDescription('Evaluate PHP code, bypassing visibility restrictions.')
  58. ->setHelp(
  59. <<<'HELP'
  60. Evaluate PHP code, bypassing visibility restrictions.
  61. e.g.
  62. <return>>>> $sekret->whisper("hi")</return>
  63. <return>PHP error: Call to private method Sekret::whisper() from context '' on line 1</return>
  64. <return>>>> sudo $sekret->whisper("hi")</return>
  65. <return>=> "hi"</return>
  66. <return>>>> $sekret->word</return>
  67. <return>PHP error: Cannot access private property Sekret::$word on line 1</return>
  68. <return>>>> sudo $sekret->word</return>
  69. <return>=> "hi"</return>
  70. <return>>>> $sekret->word = "please"</return>
  71. <return>PHP error: Cannot access private property Sekret::$word on line 1</return>
  72. <return>>>> sudo $sekret->word = "please"</return>
  73. <return>=> "please"</return>
  74. HELP
  75. );
  76. }
  77. /**
  78. * {@inheritdoc}
  79. *
  80. * @return int 0 if everything went fine, or an exit code
  81. */
  82. protected function execute(InputInterface $input, OutputInterface $output): int
  83. {
  84. $code = $input->getArgument('code');
  85. // special case for !!
  86. if ($code === '!!') {
  87. $history = $this->readline->listHistory();
  88. if (\count($history) < 2) {
  89. throw new \InvalidArgumentException('No previous command to replay');
  90. }
  91. $code = $history[\count($history) - 2];
  92. }
  93. $nodes = $this->traverser->traverse($this->parser->parse($code));
  94. $sudoCode = $this->printer->prettyPrint($nodes);
  95. $shell = $this->getApplication();
  96. $shell->addCode($sudoCode, !$shell->hasCode());
  97. return 0;
  98. }
  99. }