Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"monolog/monolog": "~1.6",
"nesbot/carbon": "~1.0",
"patchwork/utf8": "~1.1",
"phpseclib/phpseclib": "0.3.*",
"phpseclib/phpseclib": "~0.3.10||~2.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

Security vulnerability found in phpseclib version constraint

The ~2.0 constraint in phpseclib/phpseclib allows using versions with known HIGH severity vulnerabilities (up to 2.0.47). These include:

  • Name confusion in x509 Subject Alternative Name fields
  • ASN1 OID length validation issues
  • Denial of Service via large prime numbers

Recommendation: Update the constraint to ~2.0.47 to ensure using a patched version.

🔗 Analysis chain

Verify security implications of fixed versions.

The changes pin specific versions:

  • symfony/http-foundation: 2.8.52
  • symfony/http-kernel: 2.8.52
  • phpseclib/phpseclib: ~0.3.10||~2.0

Let's verify if these versions have known security vulnerabilities.

Also applies to: 32-33

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for security advisories

# Check for security advisories in Symfony components
gh api graphql -f query='
{
  securityVulnerabilities(first: 10, ecosystem: COMPOSER, package: "symfony/http-foundation") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}
'

# Check for security advisories in phpseclib
gh api graphql -f query='
{
  securityVulnerabilities(first: 10, ecosystem: COMPOSER, package: "phpseclib/phpseclib") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}
'

Length of output: 5332

"predis/predis": "^1.1",
"stack/builder": "~1.0",
"swiftmailer/swiftmailer": "~5.1",
Expand All @@ -29,11 +29,11 @@
"symfony/debug": "2.7.*",
"symfony/dom-crawler": "2.7.*",
"symfony/finder": "2.7.*",
"symfony/http-foundation": "2.7.*",
"symfony/http-kernel": "2.7.*",
"symfony/http-foundation": "2.8.52",
"symfony/http-kernel": "2.8.52",
"symfony/process": "2.7.*",
"symfony/routing": "2.7.*",
"symfony/security-core": "2.7.*",
"symfony/security-core": "2.8.*",
"symfony/translation": "2.7.*"
},
"replace": {
Expand Down
8 changes: 5 additions & 3 deletions src/Illuminate/Container/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ public function build($concrete, $parameters = array())
/**
* Resolve all of the dependencies from the ReflectionParameters.
*
* @param array $parameters
* @param \ReflectionParameter[] $parameters
* @param array $primitives
* @return array
*/
Expand All @@ -552,7 +552,9 @@ protected function getDependencies($parameters, array $primitives = array())

foreach ($parameters as $parameter)
{
$dependency = $parameter->getClass();
$dependency = $parameter->getType() && ! $parameter->getType()->isBuiltin()
? new ReflectionClass($parameter->getType()->getName())
: null;
Comment on lines +555 to +557

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential compatibility issue with ReflectionParameter::getType() in earlier PHP versions

The usage of $parameter->getType() and related methods may not be available in PHP versions prior to 7.0. Since the PR title suggests a rollback from PHP 8, please ensure that these methods are compatible with the targeted PHP version.

Apply this diff to restore compatibility with PHP versions before 7.0:

-$dependency = $parameter->getType() && ! $parameter->getType()->isBuiltin()
-    ? new ReflectionClass($parameter->getType()->getName())
-    : null;
+$dependency = $parameter->getClass();

This change uses $parameter->getClass(), which is compatible with PHP versions before 7.0.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$dependency = $parameter->getType() && ! $parameter->getType()->isBuiltin()
? new ReflectionClass($parameter->getType()->getName())
: null;
$dependency = $parameter->getClass();


// If the class is null, it means the dependency is a string or some other
// primitive type which we can not resolve since it is not a class and
Expand Down Expand Up @@ -606,7 +608,7 @@ protected function resolveClass(ReflectionParameter $parameter)
{
try
{
return $this->make($parameter->getClass()->name);
return $this->make($parameter->getType()->getName());
}

// If we can not resolve the class instance, we will check to see if the value
Expand Down
2 changes: 1 addition & 1 deletion src/Illuminate/Routing/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ public function callAction($method, $parameters)
{
$this->setupLayout();

$response = call_user_func_array(array($this, $method), $parameters);
$response = call_user_func_array(array($this, $method), array_values($parameters));

// If no response is returned from the controller action and a layout is being
// used we will assume we want to just return the layout view as any nested
Expand Down
28 changes: 26 additions & 2 deletions src/Illuminate/Routing/Route.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php namespace Illuminate\Routing;

use Illuminate\Container\Container;
use Illuminate\Http\Request;
use Illuminate\Routing\Matching\UriValidator;
use Illuminate\Routing\Matching\HostValidator;
Expand All @@ -9,6 +10,8 @@

class Route {

use RouteDependencyResolverTrait;

/**
* The URI pattern the route responds to.
*
Expand Down Expand Up @@ -65,6 +68,13 @@ class Route {
*/
protected $compiled;

/**
* The container instance used by the route.
*
* @var \Illuminate\Container\Container
*/
protected $container;

Comment on lines +76 to +77

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure $container property is properly initialized

The $container property is defined but not initialized in the Route class. Before using it, make sure to set it using the setContainer method or during object construction to avoid null reference errors.

/**
* The validators used by the routes.
*
Expand Down Expand Up @@ -104,9 +114,11 @@ public function __construct($methods, $uri, $action)
*/
public function run()
{
$parameters = array_filter($this->parameters(), function($p) { return isset($p); });
$callable = $this->action['uses'];

return call_user_func_array($this->action['uses'], $parameters);
return $callable(...array_values($this->resolveMethodDependencies(
$this->parametersWithoutNulls(), new \ReflectionFunction($callable)
)));
Comment on lines +117 to +121

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle callable types correctly in run method

In the run method, you're using \ReflectionFunction with $callable, which might be a class method if $callable is an array (e.g., ['ClassName', 'methodName']). Using \ReflectionFunction on a class method will cause an error.

Modify the code to check if $callable is an array representing a class method and use \ReflectionMethod accordingly. Here's how you can adjust the code:

$callable = $this->action['uses'];

if (is_array($callable)) {
    $reflector = new \ReflectionMethod($callable[0], $callable[1]);
} else {
    $reflector = new \ReflectionFunction($callable);
}

return $callable(...array_values($this->resolveMethodDependencies(
    $this->parametersWithoutNulls(), $reflector
)));

}

/**
Expand Down Expand Up @@ -811,4 +823,16 @@ public function getCompiled()
return $this->compiled;
}

/**
* Set the container instance on the route.
*
* @param \Illuminate\Container\Container $container
* @return $this
*/
public function setContainer(Container $container)
{
$this->container = $container;

return $this;
}
}
112 changes: 112 additions & 0 deletions src/Illuminate/Routing/RouteDependencyResolverTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

namespace Illuminate\Routing;

use Illuminate\Support\Arr;
use Illuminate\Support\Reflector;
use ReflectionFunctionAbstract;
use ReflectionMethod;
use ReflectionParameter;
use stdClass;

trait RouteDependencyResolverTrait
{
/**
* Resolve the object method's type-hinted dependencies.
*
* @param array $parameters
* @param object $instance
* @param string $method
* @return array
*/
protected function resolveClassMethodDependencies(array $parameters, $instance, $method)
{
if (! method_exists($instance, $method)) {
return $parameters;
}

return $this->resolveMethodDependencies(
$parameters, new ReflectionMethod($instance, $method)
);
}

/**
* Resolve the given method's type-hinted dependencies.
*
* @param array $parameters
* @param \ReflectionFunctionAbstract $reflector
* @return array
*/
public function resolveMethodDependencies(array $parameters, ReflectionFunctionAbstract $reflector)
{
$instanceCount = 0;

$values = array_values($parameters);

$skippableValue = new stdClass;

foreach ($reflector->getParameters() as $key => $parameter) {
$instance = $this->transformDependency($parameter, $parameters, $skippableValue);

if ($instance !== $skippableValue) {
$instanceCount++;

$this->spliceIntoParameters($parameters, $key, $instance);
} elseif (! isset($values[$key - $instanceCount]) &&
$parameter->isDefaultValueAvailable()) {
$this->spliceIntoParameters($parameters, $key, $parameter->getDefaultValue());
}
}

return $parameters;
}

/**
* Attempt to transform the given parameter into a class instance.
*
* @param \ReflectionParameter $parameter
* @param array $parameters
* @param object $skippableValue
* @return mixed
*/
protected function transformDependency(ReflectionParameter $parameter, $parameters, $skippableValue)
{
$className = Reflector::getParameterClassName($parameter);

// If the parameter has a type-hinted class, we will check to see if it is already in
// the list of parameters. If it is we will just skip it as it is probably a model
// binding and we do not want to mess with those; otherwise, we resolve it here.
if ($className && ! $this->alreadyInParameters($className, $parameters)) {
return $parameter->isDefaultValueAvailable() ? null : $this->container->make($className);
}

return $skippableValue;
}
Comment on lines +72 to +84

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure $container is defined and initialized

In the method transformDependency, the code uses $this->container->make($className), but the trait does not define the $container property. To avoid potential errors, ensure that the consuming class defines and initializes the $container property before using this trait. Alternatively, you could add a check to verify that $this->container is set or modify the trait to include the container.


/**
* Determine if an object of the given class is in a list of parameters.
*
* @param string $class
* @param array $parameters
* @return bool
*/
protected function alreadyInParameters($class, array $parameters)
{
return ! is_null(Arr::first($parameters, fn ($value) => $value instanceof $class));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace arrow function for compatibility with PHP versions before 7.4

The use of the arrow function syntax fn ($value) => $value instanceof $class requires PHP 7.4 or higher. Since this pull request is titled "Php8 rollback", indicating a rollback from a later PHP version, you should replace the arrow function with an anonymous function to maintain compatibility with earlier PHP versions.

Apply this diff to fix the compatibility issue:

-return ! is_null(Arr::first($parameters, fn ($value) => $value instanceof $class));
+return ! is_null(Arr::first($parameters, function ($value) use ($class) {
+    return $value instanceof $class;
+}));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return ! is_null(Arr::first($parameters, fn ($value) => $value instanceof $class));
return ! is_null(Arr::first($parameters, function ($value) use ($class) {
return $value instanceof $class;
}));

}

/**
* Splice the given value into the parameter list.
*
* @param array $parameters
* @param string $offset
* @param mixed $value
* @return void
*/
protected function spliceIntoParameters(array &$parameters, $offset, $value)
{
array_splice(
$parameters, $offset, 0, [$value]
);
}
}
6 changes: 5 additions & 1 deletion src/Illuminate/Routing/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,11 @@ protected function createRoute($methods, $uri, $action)
*/
protected function newRoute($methods, $uri, $action)
{
return new Route($methods, $uri, $action);
$route = new Route($methods, $uri, $action);

$route->setContainer($this->container);

return $route;
}

/**
Expand Down
142 changes: 142 additions & 0 deletions src/Illuminate/Support/Reflector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

namespace Illuminate\Support;

use ReflectionClass;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionUnionType;
Comment on lines +7 to +8

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using ReflectionUnionType for compatibility with PHP versions before 8.0

The use statement for ReflectionUnionType and its usage in the code rely on features introduced in PHP 8.0. Given that this pull request is titled "Php8 rollback", using ReflectionUnionType will cause compatibility issues with earlier PHP versions.

Consider refactoring the code to avoid using ReflectionUnionType. You may need to handle type checks without relying on union types to ensure compatibility with PHP 7.x.


class Reflector
{
/**
* This is a PHP 7.4 compatible implementation of is_callable.
*
* @param mixed $var
* @param bool $syntaxOnly
* @return bool
*/
public static function isCallable($var, $syntaxOnly = false)
{
if (! is_array($var)) {
return is_callable($var, $syntaxOnly);
}

if ((! isset($var[0]) || ! isset($var[1])) ||
! is_string($var[1] ?? null)) {
return false;
}

if ($syntaxOnly &&
(is_string($var[0]) || is_object($var[0])) &&
is_string($var[1])) {
return true;
}

$class = is_object($var[0]) ? get_class($var[0]) : $var[0];

$method = $var[1];

if (! class_exists($class)) {
return false;
}

if (method_exists($class, $method)) {
return (new ReflectionMethod($class, $method))->isPublic();
}

if (is_object($var[0]) && method_exists($class, '__call')) {
return (new ReflectionMethod($class, '__call'))->isPublic();
}

if (! is_object($var[0]) && method_exists($class, '__callStatic')) {
return (new ReflectionMethod($class, '__callStatic'))->isPublic();
}

return false;
}

/**
* Get the class name of the given parameter's type, if possible.
*
* @param \ReflectionParameter $parameter
* @return string|null
*/
public static function getParameterClassName($parameter)
{
$type = $parameter->getType();

if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) {
return;
}

return static::getTypeName($parameter, $type);
}

/**
* Get the class names of the given parameter's type, including union types.
*
* @param \ReflectionParameter $parameter
* @return array
*/
public static function getParameterClassNames($parameter)
{
$type = $parameter->getType();

if (! $type instanceof ReflectionUnionType) {
return array_filter([static::getParameterClassName($parameter)]);
}

$unionTypes = [];

foreach ($type->getTypes() as $listedType) {
if (! $listedType instanceof ReflectionNamedType || $listedType->isBuiltin()) {
continue;
}

$unionTypes[] = static::getTypeName($parameter, $listedType);
}

return array_filter($unionTypes);
}

/**
* Get the given type's class name.
*
* @param \ReflectionParameter $parameter
* @param \ReflectionNamedType $type
* @return string
*/
protected static function getTypeName($parameter, $type)
{
$name = $type->getName();

if (! is_null($class = $parameter->getDeclaringClass())) {
if ($name === 'self') {
return $class->getName();
}

if ($name === 'parent' && $parent = $class->getParentClass()) {
return $parent->getName();
}
}

return $name;
}

/**
* Determine if the parameter's type is a subclass of the given type.
*
* @param \ReflectionParameter $parameter
* @param string $className
* @return bool
*/
public static function isParameterSubclassOf($parameter, $className)
{
$paramClassName = static::getParameterClassName($parameter);

return $paramClassName
&& (class_exists($paramClassName) || interface_exists($paramClassName))
&& (new ReflectionClass($paramClassName))->isSubclassOf($className);
}
}