PHP的Calling Scope

  • 作者: Laruence
  • 本文地址: http://www.laruence.com/2012/06/14/2628.html

问题在这里: 这是php中__call和__callStatic在被继承后会产生的bug?

这个问题乍看, 确实很容易让人迷惑, 但实际上, 造成这样的误解的根本原因在于: 在PHP中, 判断静态与否不是靠”::”(PAAMAYIM_NEKUDOTAYIM)符号, 而是靠calling scope.

那么, 什么是calling scope?

在PHP中, 调用一个方法的时候, $this指针指向的对象就是这个方法被调用时刻的calling scope. 对于下面的例子:

    <?php
    Foo::bar();
    ?>

在调用bar方法的时候, 处于一个没有calling scope域的上下文中, 所以这个是静态调用.

而对于如下的例子:

    <?php
    class A {
         public function test() {
             Foo::bar();
         }
     }
    $a = new A();
    $a->test();

在调用bar方法的时候, 处于一个$a对象的上下文中, 也就是说, 此时的calling scope是$a对象, 所以这个其实不是静态调用.

为了验证这一个结论, 请看下面的一个实际例子:

    <?php
     class Foo {
         public function bar() {
             var_dump($this);
         }
     }
     class A {
         public function test() {
             Foo::bar();
         }
     }
     $a = new A();
     $a->test();
    ?>
输出什么呢?
object(A)#1 (0) {}

在调用bar的时候, 这个看似”静态”调用的调用, $this指针却是被赋值的, 指向的是$a对象, 那么这个还算静态调用么?

我举这个例子是为了说明这个问题, 但大家在实际的应用中, 大家尽量要避免使用”::”来调用一个非静态的方法, PHP也会对于这种调用给出一个Strict 警告:

Strict Standards: Non-static method Foo::bar() should not be called statically, assuming $this from incompatible context

也许有人会说这个应该算bug吧? 其实不然, 更多的应该是错误使用造成的, 因为你在一个有calling scope的上下文中采用”静态的形式”调用了一个类的非静态方法所致.

那么PHP为什么要这么设计呢? 考虑下面的例子:

    <?php
     class A {
        public function __construct() {
        }
     }
      class B extends A {
        public function __construct() {
            parent::__construct();
       }
       }
当我们调用父类的构造函数的时候, 我们是有意的要把当前的scope传递给父类的构造函数作为calling scope的.
在现代软件开发和系统设计中,**function calling(函数调用)** 和 **tool calling(工具调用)** 是两个相关但有区别的概念。它们通常用于不同的目的,并在不同上下文中被使用。 ### 三、函数调用(Function Calling) 函数调用是编程中最基本的操作之一,指的是程序执行过程中调用一个定义好的函数以完成特定任务的行为。函数通常是代码中的模块化单元,具有明确的输入参数和返回值[^1]。 例如,在 Python 中可以这样定义并调用一个函数: ```python def add(a, b): return a + b result = add(3, 5) ``` 函数调用的优势在于: - 可重用性:同一个函数可以在多个地方被调用。 - 封装性:隐藏实现细节,只暴露接口。 - 易于维护:修改函数逻辑只需更新一次。 函数调用通常发生在程序内部,不需要依赖外部服务或运行时环境。 ### 四、工具调用(Tool Calling) 工具调用则更广泛地指调用某种“外部”功能或服务,这些功能可能是由其他系统、API 或命令行工具提供的。工具调用常用于自动化流程、集成测试、部署脚本等场景[^2]。 例如,在一个包含多个工具的字典中选择并调用某个工具函数: ```python import functools names_to_functions = { "retrieve_payment_status": functools.partial(retrieve_payment_status, df=df), "retrieve_payment_date": functools.partial(retrieve_payment_date, df=df), } function_result = names_to_functions[tool_function.name](**args) ``` 工具调用的特点包括: - 外部依赖:通常需要与外部系统交互,如数据库、HTTP API、CLI 工具等。 - 状态管理:可能涉及上下文保存、历史记录(如 `chat_history.append(tool_msg)`)。 - 动态路由:可以根据运行时条件动态决定调用哪个工具[^2]。 工具调用适用于构建可扩展的应用程序架构,尤其是在微服务、插件系统或 AI Agent 架构中非常常见。 ### 五、区别总结 | 特性 | 函数调用 | 工具调用 | |------------------|------------------------------|--------------------------------------| | 调用对象 | 内部定义的函数 | 外部系统或服务 | | 是否需外部依赖 | 否 | 是 | | 调用方式 | 直接通过函数名调用 | 通过配置、路由或插件机制调用 | | 应用场景 | 通用逻辑处理 | 集成第三方服务、动态行为控制 | | 示例 | `add(3, 5)` | `names_to_functions[tool_function.name](**args)` | ### 六、典型使用方法对比 - **函数调用示例**: ```python def calculate_discount(price, discount_rate): return price * (1 - discount_rate) final_price = calculate_discount(100, 0.1) ``` - **工具调用示例**: ```python from langchain.runnables import OpenAIFunctionsRouter router = OpenAIFunctionsRouter([func1, func2]) response = router.invoke({"query": "get user info"}) ``` 工具调用通常会结合 `Runnable` 接口来实现异步、链式调用,支持更多高级特性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值