V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iyaozhen
V2EX  ›  PHP

PHP 类的一个疑问

  •  
  •   iyaozhen · 2015-06-14 11:58:07 +08:00 · 3573 次点击
    这是一个创建于 3483 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我有一个类,几个方法都需要用到其它一个加解密类。

    我是实例化一次然后用个变量(类的属性)存起来还是每次需要时再实例化好?

    其实我是不太明白

    require("class.php");   // 把可能需要的文件都在构造函数中 require 会影响性能吗?
    new class();
    

    两步发生了什么?

    16 条回复    2015-06-14 14:07:20 +08:00
    laoyuan
        1
    laoyuan  
       2015-06-14 12:06:19 +08:00
    PHP 请求之间是独立的
    shuimugan
        2
    shuimugan  
       2015-06-14 12:09:06 +08:00   ❤️ 1
    可以写把你的类写成单例模式(就是不能直接new,在类的方法里new,只new一次),或者把加解密函数写成静态函数(不需要new,以 类::函数() 的方式调用)
    require 的说明http://php.net/manual/zh/function.require.php
    raincious
        3
    raincious  
       2015-06-14 12:10:01 +08:00   ❤️ 2
    呵呵。

    你要是多次require的话,应该会提示redeclared class(之类)。最好用一个Autoloader来自动require,因为每次用一个Class的时候,PHP会检查这个Class是不是已经载入了,这样就不存在require冲突的问题了。(require_once是一个解法,但不是最优的)

    另外至于是不是要new一个加密Class:
    1、如果你的consturctor操作比较多,比较慢,或者根本没必要多次new,那么建立一个Singleton就好了。(protected/private construct,然后建立一个public static方法在内部new好,再缓存new出来的Instance)
    2、如果你的consturctor操作不多,且Class中的数据需要用不同的Instance隔离,那么就必须new。
    loveyu
        4
    loveyu  
       2015-06-14 12:10:05 +08:00   ❤️ 1
    性能基本无影响,就是这样玩的。
    zakokun
        5
    zakokun  
       2015-06-14 12:10:51 +08:00
    单例
    zakokun
        6
    zakokun  
       2015-06-14 12:11:25 +08:00   ❤️ 1
    @zakokun 单例模式或者干脆静态类都可以。
    timsims
        7
    timsims  
       2015-06-14 12:25:27 +08:00   ❤️ 1
    单例是反模式,不方便测试

    楼主的意思,我的理解是: A类内有若干个方法需要调用B类

    那么可以使用依赖注入,先把B类实例化后,作为参数传给A类的构造函数里

    Class A
    {
    protected $bClass;

    public function __construct(B $bClass)
    {
    $this->$bClass = $bClass;
    }

    public function someMethod()
    {
    $this->$bClass->method();
    }
    }

    class B
    {
    public function method(){}
    }

    $b = new B;
    $a = new A($b);
    iyaozhen
        8
    iyaozhen  
    OP
       2015-06-14 12:32:50 +08:00
    @shuimugan @zakokun 谢谢,单例模式、静态类是个好方法。

    @raincious 没多次 require,这个我有注意。谢谢指点,其实我现在多不多次 new 都行。只是一直不明白 new class() 这一步底层发生了什么(当构造函数什么都没做的情况下)?
    ab
        9
    ab  
       2015-06-14 12:34:59 +08:00
    菜鸟问一下,为什么不include
    iyaozhen
        10
    iyaozhen  
    OP
       2015-06-14 12:41:29 +08:00
    @timsims 嗯嗯,就是这个意思。其实我现在做法是这样:
    Class A
    {
    protected $bClass;

    public function someMethod1()
    {
    $this->$bClass = new B();
    $this->$bClass->method1();
    // $tmp = new B();
    // $tmp->method1();
    }

    public function someMethod2()
    {
    $this->$bClass->method2();
    // $tmp = new B();
    // $tmp->method2();
    }
    }

    class B
    {
    public function method1(){}
    public function method2(){}
    }

    因业务逻辑关系 someMethod1() 一定在 someMethod2() 前面调用。
    注释里面的是每次都 new 的情况。

    主要疑问就是 $this->$bClass 存着实例化的 class B 有什么坑吗?(class B 就是加解密,CPU 运算,没有涉及到 IO)
    iyaozhen
        11
    iyaozhen  
    OP
       2015-06-14 12:46:04 +08:00
    @ab 因为是必须要引入的库,没引入的话流程进行不下去,所以用了 require。

    require 和 include 几乎完全一样,除了处理失败的方式不同之外。require 在出错时产生 E_COMPILE_ERROR 级别的错误,换句话说将导致脚本中止。而 include 只产生警告(E_WARNING),脚本会继续运行。
    timsims
        12
    timsims  
       2015-06-14 13:04:16 +08:00   ❤️ 1
    @iyaozhen

    实例化本身不存在什么坑,但是在类中去实例化对象就存在耦合问题(你这里就是A类的方法中new B),假如有一天你想把项目里所有class B都替换成class C,那就相当麻烦,这就是为什么我推荐通过依赖注入在A的构造函数里传入实例化后的B

    不过我那个例子其实还不是最优的,最优的做法应该是注入一个接口,让B实现这个接口

    具体还是看用代码说明。。

    ```
    Interface WhatEver
    {
    public function method1();
    public function method2();
    }

    Class A
    {
    protected $bClass;

    public function __construct(WhatEver $bClass)
    {
    $this->bClass = $bClass;
    }

    }

    class B implements WhatEver
    {
    public function method1(){}
    public function method2(){}
    }

    class C implements WhatEver
    {
    public function method1(){}
    public function method2(){}
    }

    $b = new B();
    $a = new A($b);

    // 有一天你想把B无痛替换成C
    $c = new C();
    $a = new A($c); // 就这么简单
    ```
    raincious
        13
    raincious  
       2015-06-14 13:08:28 +08:00   ❤️ 1
    @iyaozhen

    PHP干了啥,这你得探索下源代码了。

    当然,语言的用户(PHP程序员)而言,如果不考虑构造函数的消耗,那么new一个对象的消耗是十分低的,你可以自己做个测试,让PHP建立上万个对象然后看看总耗时。

    除非对象太多(取决于你的可用内存)可能会出现GC效能下降的问题(参见Composer那次),但就这个问题来说应该不会可能与到。
    hitsmaxft
        14
    hitsmaxft  
       2015-06-14 13:16:38 +08:00   ❤️ 1
    由于 php 的源代码(加载后编译成 opcode)在fpm 模式下,也算是资源而已。所以每次请求都不得不重新加载 class 所在的源代码。


    在性能开销上考虑的话,可以引入 opcache 缓存扩展, 比如 zend opcache (5.5内置, 5.3和5.4 需要自行编译, apc 等其他老扩展的真心不推荐)

    另外, 用 require 和 include 差别不大, 这种关键代码无论如何都不应该加载失败的。但注意 _once 系列函数别用。 至少在 apc 下是有 bug 的,这种函数是玩玩用的。

    按需 new 或者单例,取决于这个实例的创建成本。这部分 @raincious 已经解释了

    其实你的问题关键点不在 include , 而在 new XXclass() ; 会触发 查找类和调用 autoload 等等流程。

    new XXX() -> XXX 不存在? -> 触发 autoload -> 类存在了?调用构造器 : 继续调用其他autoloader -> 还是没找到? 抛出异常
    iyaozhen
        15
    iyaozhen  
    OP
       2015-06-14 13:23:28 +08:00
    @timsims 谢谢,耦合问题没注意到。

    @raincious 谢谢。自己应该多试试。
    hahamy
        16
    hahamy  
       2015-06-14 14:07:20 +08:00   ❤️ 1
    如果class B有内在的逻辑,那么每次new B都是生成新的对象实例,相互之间不会影响,单例上一次对对象的操作,仍保留在对象内,下次调用的还是这个对象,所以看B的逻辑来做选择
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5806 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 02:33 · PVG 10:33 · LAX 18:33 · JFK 21:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.