在调用方法之前对参数进行修改

发布于 2025-02-12 12:05:51 字数 2644 浏览 1 评论 0原文

在该方法实际执行之前,对方法参数进行一些修改是什么最好的方法?

我已经尝试通过属性和装饰器/代理模式的使用结合来实现此目的:

#[\Attribute]
class Query
{
}

class Foo
{
   #[Query]
   public function request(array $query = [])
   {
   }

   public function foo(array $query = [])
   {
   }

   #[Query]
   public function bar(string $name, int $age, array $query = [])
   {
   }
}

class FooDecorator
{
   private Foo $target;

   public function __construct(Foo $target)
   {
      $this->target = $target;
   }

   public function __call(string $methodName, array $args)
   {
      $class = get_class($this->target);
      try {
        $reflection = new \ReflectionClass($class);
        $methods = $reflection->getMethods();
        $attributeName = __NAMESPACE__ . '\Query';
        foreach ($methods as $method) {
            if ($method->getName() !== $methodName) {
                continue;
            }
            $attributes = $method->getAttributes();
            foreach ($attributes as $attribute) {
                if ($attribute->getName() === $attributeName) {
                    $parameters = $method->getParameters();
                    foreach ($parameters as $key => $param) {
                        if ($param->getName() === 'query') {
                            // Here we filter the parameter named 'query'
                            $args[$key] = $this->validateQueryParameter($args[$key]);
                            break;
                        }
                    }
                }
            }
        }
      } catch (\Exception $e) {
      }
      if (method_exists($this->target, $methodName)) {
          return call_user_func_array([$this->target, $methodName], $args);
      }
   }

   private function validateQueryParameter(array $query): array
   {
      $allowed = [
        'foo',
        'bar',
      ];
      $query = array_filter($query = array_change_key_case($query), function ($key) use ($allowed) {
        // Filter out any non-allowed keys
        return in_array($key, $allowed);
      }, ARRAY_FILTER_USE_KEY);
      return $query;
   }
}

$foo = new FooDecorator(new Foo());
// Should remove faz & baz, but keep foo in the query
$foo->query(['faz' => 1, 'baz' => 2, 'foo' => 3]);
// Should not perform anything on the query parameter
$foo->foo(['baz' => 1]);
// Should remove faz & baz, but keep foo in the query
$foo->bar('foo', 100, ['faz' => 1, 'baz' => 2, 'foo' => 3]);

这是按预期的工作,但是由于我在这里使用了装饰器,因此现在我缺少foo 类。

我知道我可以使用界面并声明foo中包含的所有方法,但是随后我需要在装饰器中实现每个方法(实际类包含许多方法),这似乎是过分的。

What would be the best way to perform some modification on a method argument before the method is actually executed?

I have tried achieving this with a combination of attributes and use of the decorator/proxy pattern:

#[\Attribute]
class Query
{
}

class Foo
{
   #[Query]
   public function request(array $query = [])
   {
   }

   public function foo(array $query = [])
   {
   }

   #[Query]
   public function bar(string $name, int $age, array $query = [])
   {
   }
}

class FooDecorator
{
   private Foo $target;

   public function __construct(Foo $target)
   {
      $this->target = $target;
   }

   public function __call(string $methodName, array $args)
   {
      $class = get_class($this->target);
      try {
        $reflection = new \ReflectionClass($class);
        $methods = $reflection->getMethods();
        $attributeName = __NAMESPACE__ . '\Query';
        foreach ($methods as $method) {
            if ($method->getName() !== $methodName) {
                continue;
            }
            $attributes = $method->getAttributes();
            foreach ($attributes as $attribute) {
                if ($attribute->getName() === $attributeName) {
                    $parameters = $method->getParameters();
                    foreach ($parameters as $key => $param) {
                        if ($param->getName() === 'query') {
                            // Here we filter the parameter named 'query'
                            $args[$key] = $this->validateQueryParameter($args[$key]);
                            break;
                        }
                    }
                }
            }
        }
      } catch (\Exception $e) {
      }
      if (method_exists($this->target, $methodName)) {
          return call_user_func_array([$this->target, $methodName], $args);
      }
   }

   private function validateQueryParameter(array $query): array
   {
      $allowed = [
        'foo',
        'bar',
      ];
      $query = array_filter($query = array_change_key_case($query), function ($key) use ($allowed) {
        // Filter out any non-allowed keys
        return in_array($key, $allowed);
      }, ARRAY_FILTER_USE_KEY);
      return $query;
   }
}

$foo = new FooDecorator(new Foo());
// Should remove faz & baz, but keep foo in the query
$foo->query(['faz' => 1, 'baz' => 2, 'foo' => 3]);
// Should not perform anything on the query parameter
$foo->foo(['baz' => 1]);
// Should remove faz & baz, but keep foo in the query
$foo->bar('foo', 100, ['faz' => 1, 'baz' => 2, 'foo' => 3]);

This works as expected, but since I am using a decorator here I am now missing the hints for each method included in the Foo class.

I know that I could use an interface and declare all methods included in Foo, but then I would need to implement every method (the real class contains many many methods) in the decorator, which seems like overkill.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文