NewProcOpenCmdArraySniff.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. <?php
  2. /**
  3. * PHPCompatibility, an external standard for PHP_CodeSniffer.
  4. *
  5. * @package PHPCompatibility
  6. * @copyright 2012-2019 PHPCompatibility Contributors
  7. * @license https://opensource.org/licenses/LGPL-3.0 LGPL3
  8. * @link https://github.com/PHPCompatibility/PHPCompatibility
  9. */
  10. namespace PHPCompatibility\Sniffs\ParameterValues;
  11. use PHPCompatibility\AbstractFunctionCallParameterSniff;
  12. use PHP_CodeSniffer_File as File;
  13. use PHP_CodeSniffer_Tokens as Tokens;
  14. /**
  15. * As of PHP 7.4, `proc_open()` now also accepts an array instead of a string for the command.
  16. *
  17. * In that case, the process will be opened directly (without going through a shell) and
  18. * PHP will take care of any necessary argument escaping.
  19. *
  20. * PHP version 7.4
  21. *
  22. * @link https://www.php.net/manual/en/migration74.new-features.php#migration74.new-features.standard.proc-open
  23. * @link https://www.php.net/manual/en/function.proc-open.php
  24. *
  25. * @since 9.3.0
  26. */
  27. class NewProcOpenCmdArraySniff extends AbstractFunctionCallParameterSniff
  28. {
  29. /**
  30. * Functions to check for.
  31. *
  32. * @since 9.3.0
  33. *
  34. * @var array
  35. */
  36. protected $targetFunctions = array(
  37. 'proc_open' => true,
  38. );
  39. /**
  40. * Do a version check to determine if this sniff needs to run at all.
  41. *
  42. * @since 9.3.0
  43. *
  44. * @return bool
  45. */
  46. protected function bowOutEarly()
  47. {
  48. return false;
  49. }
  50. /**
  51. * Process the parameters of a matched function.
  52. *
  53. * @since 9.3.0
  54. *
  55. * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
  56. * @param int $stackPtr The position of the current token in the stack.
  57. * @param string $functionName The token content (function name) which was matched.
  58. * @param array $parameters Array with information about the parameters.
  59. *
  60. * @return int|void Integer stack pointer to skip forward or void to continue
  61. * normal file processing.
  62. */
  63. public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
  64. {
  65. if (isset($parameters[1]) === false) {
  66. return;
  67. }
  68. $tokens = $phpcsFile->getTokens();
  69. $targetParam = $parameters[1];
  70. $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $targetParam['start'], $targetParam['end'], true);
  71. if ($nextNonEmpty === false) {
  72. // Shouldn't be possible.
  73. return;
  74. }
  75. if ($tokens[$nextNonEmpty]['code'] !== \T_ARRAY
  76. && $tokens[$nextNonEmpty]['code'] !== \T_OPEN_SHORT_ARRAY
  77. ) {
  78. // Not passed as an array.
  79. return;
  80. }
  81. if ($this->supportsBelow('7.3') === true) {
  82. $phpcsFile->addError(
  83. 'The proc_open() function did not accept $cmd to be passed in array format in PHP 7.3 and earlier.',
  84. $nextNonEmpty,
  85. 'Found'
  86. );
  87. }
  88. if ($this->supportsAbove('7.4') === true) {
  89. if (strpos($targetParam['raw'], 'escapeshellarg(') === false) {
  90. // Efficiency: prevent needlessly walking the array.
  91. return;
  92. }
  93. $items = $this->getFunctionCallParameters($phpcsFile, $nextNonEmpty);
  94. if (empty($items)) {
  95. return;
  96. }
  97. foreach ($items as $item) {
  98. for ($i = $item['start']; $i <= $item['end']; $i++) {
  99. if ($tokens[$i]['code'] !== \T_STRING
  100. || $tokens[$i]['content'] !== 'escapeshellarg'
  101. ) {
  102. continue;
  103. }
  104. // @todo Potential future enhancement: check if it's a call to the PHP native function.
  105. $phpcsFile->addWarning(
  106. 'When passing proc_open() the $cmd parameter as an array, PHP will take care of any necessary argument escaping. Found: %s',
  107. $i,
  108. 'Invalid',
  109. array($item['raw'])
  110. );
  111. // Only throw one error per array item.
  112. break;
  113. }
  114. }
  115. }
  116. }
  117. }