PHP closure accesses private properties, resulting in IDE (PhpStorm) prompt "member has private access" error

The getInitializer function in the autoload_static.php of

Composer accesses the private properties of the ClassLoader class, causing the IDE (PhpStorm) to prompt "member has private access" error, as shown in the red box in the figure:

in fact, this is not an error, but a false alarm by IDE, but Baidu and Google have not found a way to close the error prompt for a long time. Who has encountered similar problems and how to suppress or cancel this error prompt, but can not affect other normal error prompts? Obviously, it is not an error, but PhpStorm always shows a red line indicating an error. Obsessive-compulsive disorder means that you can"t stand it.

=
20181019PM add:

at first, many respondents said that there was something wrong with the code, but in fact, there was no problem with the code. The reason for the problem lies in the understanding of closures. In view of the obscurity and vagueness of the PHP official documents, I will write my own understanding for reference below. If there are any mistakes or omissions, please correct them.

Let"s start with my understanding of closures.

the so-called closure is closed packaging, which can be understood as defining a piece of code, selectively taking a snapshot of its running context at that time, and bundling it together with closed packaging. used to make this code run in the context of the original choice at some point in the future.

This is the significance of

closures: closed packaging can carry variables (that is, external variables) in the external environment (that is, context), so that the closure can inherit variables from its parent scope, even if the external environment has been destroyed. it also does not affect the external variables in the closure that have been closed before (technically referred to as binding).

in PHP, any external variable in the parent scope used in the closure should be passed into the closure function using the use keyword (but since PHP 7.1, no such variables can be passed: superglobals, $this, or the same name as the parameter), which seems to be similar to the fact that global variables must be introduced through the global keyword.

Note: in PHP5.3+, an anonymous function is a closure function (see PHP official documentation ). When an anonymous function is defined, an object instance of the Closure closure class is implicitly created.

Let"s talk about the Closure::bind method used in the screenshot above. Since the Closure::bind method is actually a static version of the Closure::bindTo method, the Closure::bindTo method is highlighted below.

definition of Closure::bindTo method : public Closure Closure::bindTo (object $newthis [, mixed $newscope = "static"])
Closure::bind method definition: public static Closure Closure::bind (Closure $closure, object $newthis [, mixed $newscope =" static"])

Closure::bindTo is actually an instance method of the Closure class (the double colon:: is really misleading), while Closure::bind is a static method of the Closure class, which belongs to the static version of the instance method Closure::bindTo.

The

Closure::bindTo method, as an instance method of the Closure class, its function is to copy the current closure object (that is, the closure function object instance to which the instance method belongs, referred to as the closure object) as a new closure object, and can bind the specified $this object and class scope to the copied new closure object, and then return the new closure object.

the returned new closure object is the same as the function body of the current closure object, and the bound external variables (that is, the variables introduced by the use keyword) are also the same, but because different $this objects can be bound, the properties and methods of the $this object used in the function body of the current closure object can be changed, that is, the properties and methods of the new $this object are pointed to in the returned new closure object. Also because a new class scope can be bound, the scope of the external variable can be changed, which makes the scope of the external variable (which is an object) the scope of the newly bound class (that is, the class to which the external variable object belongs). That is to say, the external variables introduced in the closure are equivalent to being in the newly bound class or the object instance of the class.

therefore, if you use the private properties of the external variable object in the closure, it is in full compliance with the syntax rules, because an important feature of the closure is that the external environment in which it is located (in this case, the newly bound class scope) can be closed and packaged, so that variables in the external environment can be used directly in the closure, even if the external environment itself has been destroyed. Even if the variable is private in this external environment.

The

Closure::bind method is just a static version of the Closure::bindTo method, which is similar in function and won"t go into detail.

example:

Closure::bindTo()
    <?php
        class A {
            function __construct($val) {
                $this->val = $val;
            }
            function getClosure() {
                //returns closure bound to this object and scope
                return function() { return $this->val; };
            }
        }
        
        $ob1 = new A(1);
        $ob2 = new A(2);
        
        $cl = $ob1->getClosure();
        echo $cl(), "\n";
        $cl = $cl->bindTo($ob2);
        echo $cl(), "\n";
    
    :
    1
    2
    
Closure::bind()
    <?php
        class A {
            private static $sfoo = 1;
            private $ifoo = 2;
        }
        
        $cl1 = static function() {
            return A::$sfoo;
        };
        $cl2 = function() {
            return $this->ifoo;
        };
        
        $bcl1 = Closure::bind($cl1, null, "A");
        $bcl2 = Closure::bind($cl2, new A(), "A");
        echo $bcl1(), "\n";
        echo $bcl2(), "\n";
    
    :
    1
    2

=
20181023 add again:

reply to the supplementary answer of the respondent kumfo:

1. The parameter one of the Closure::bind function is a closure (or anonymous function, that is, the defined Closure $closure), is relative to the return value of the same closure, the parameter one passed in can be called the source closure, and the return value of the Closure::bind function can be called the target closure accordingly;

2. The second parameter of the Closure::bind function is an object (that is, the defined object $newthis),. This parameter is meaningful only if the $this object is used in the first parameter (that is, the source closure), and it must be passed, otherwise, an error such as "Fatal error: Uncaught Error: Using $this when not in object context in XXX.php" may be reported in the run of the null, closure. This parameter, relative to the $this object in the source closure, is called the "new" $this object (that is, object $newthis in the definition). Obviously, parameter 2 is only used to bind a specific object to the $this used in the source closure, and there seems to be no other function. As for the statement in your supplementary answer that "Closure::bind () will automatically bind this object to the scope of the closure, so accessing the private property in the closure directly in the form of $this- > is not accurate, because whether the private property of the $this object can be accessed in the closure depends on the third parameter (yes, you read it correctly, the $this object in the closure and the object introduced by the use keyword." Access to their private properties depends on the third parameter.

3. Parameter 3 of the Closure::bind function is a parameter with the default value "static". According to the default value, parameter 3 can accept data of a string type (that is, mixed $newscope =" static" in the definition), but according to the definition, it seems that other types can also be accepted. To simplify the explanation, take the string type as an example.

according to the official document, this parameter is used to bind a class scope to the source closure (represented by the fully qualified name of the class, that is,\ Namespace\ ClassName::class). Compared with the "old" scope of the default value "static"", this parameter is equivalent to binding a "new" class scope to the source closure (in your supplementary answer, "the subject later explains that it is a new class or something". You didn"t take a closer look at the supplement in my question. I didn"t say "new class" from beginning to end, only "new class scope").

then the class scope bound by this parameter (note, it is not "class scope", but "class scope". "class scope" is often easily understood as the external environment of the class. "class scope" refers to the internal scope built by the class itself, which is the scope or external environment of the internal properties and methods of the class. Is the scope of the object introduced by the second parameter $newthis and use keywords.

if the parameter does not pass an argument (that is, using its default value of "static"), or if a class scope other than the class to which the $this object and the use keyword is introduced belongs (for example,\ OtherNamespace\ OtherClassName::class), Equal to the scope of the $this object in the source closure (if the $this object is used in the source closure) and the object introduced by the use keyword (if the source closure introduces the object using the use keyword) (that is, the external environment of both) is not the class scope of the class to which they belong, but in the class scope or top-level scope of the other class (which is equivalent to general usage). It is obvious that their private properties cannot be accessed; If you pass in the class scope of the class to which both belong, it is equivalent to the object introduced by the $this object and the use keyword in the source closure (in which case the object introduced by use is the same class as the $this object, but not the same object, but different instances of the same class) are in the class to which they belong, so it is natural for both to be able to access their own private properties.

this is the particularity of closures-on the surface of the code, the objects in the closures (such as the $this object) seem to be no longer in the class to which they belong, but whether they are actually in the class to which they belong depends on the external environment (that is, class scope) in which the closure is "closed packaging". Generally speaking, the external environment of the closure house "closed packaging" (technically called "binding") is determined when the closure is defined (or static binding, in which case the code looks relatively easy to understand). The Closure::bind function simply provides a means or tool to dynamically "seal off" the external environment in which the closure is located (it is this dynamic binding that makes the code seem confusing at first glance).

as a programmer, one of my deep experiences is that you have to dig out the details, a slight difference, and if you don"t notice it, the result of understanding may be quite different, or even the opposite of the actual situation. In your first answer and supplementary answer, you did not pay attention to some of the details in my statement, so your two answers made mistakes and omissions in the understanding of my question, resulting in the wrong conclusion.

anyway, thank you again for your answer.

the $this object in the ps: source closure and the objects introduced by the use keyword can also be heterogeneous objects that do not belong to the same class, but because the third parameter can only pass in the class scope of the class to which one of the objects belongs, this will cause one of the bound target closures to report an error at run time because of accessing its private properties.


One of the

code is missing.
Closure::bind ()

16

PHP

php -l

cli

,

php
__get __set ,

Test2 error

__set
__set

**doc
phpdoc (PHP Documentor)(PS:)google

PHPDocPEARjavadoc,API

TestClass


ClassLoad XDebug issue PR

()2

PHP50% qs

SF


< H2 > explanation of Closure::bind < / H2 >

Closure::bind () has three parameters, which are described in the official documentation:

  1. closures or directly called anonymous functions;
  2. The scope of the anonymous function passed in by the
  3. parameter, that is, the object that parameter one can affect.
  4. the class scope that you want to bind to the closure, or 'static' means no change. If an object is passed in, the type name of the object is used. The class scope is used to determine the private and protected method visibility of the $this object in the closure. (official)

here, pay attention to the description of the third point. static means that the data will not be changed, that is, if the third parameter is passed 'static' , it means that the operations in the closure function operate on the data according to normal syntax logic, such as private , proctected cannot be accessed directly, and if the third parameter passes in the class name, Then the properties or methods of private and protected types in the instance object of this class are allowed in the closure, which can be said to ignore the object-oriented syntax rules of PHP itself, which is equivalent to Closure::bind () this method changes the properties of the object.

Let me take a look at the following two pieces of code:


class Loader {
    private $prefixLengthPsr4 = "fuck";
    public function getPreFixLengthPsr4() {
        return $this->prefixLengthPsr4;
    }
};
$loader = new Loader();

$closure = Closure::bind(function() {
    $this->prefixLengthPsr4 = "what the fuck";
    return $this;
},$loader,'Loader');
Menu