About scope and OOP in PHP

2019-06-28 00:13发布

问题:

I am having trouble understanding how to work with objects.

The specific code:

class first{
    class second{
        public function widgets(){
            $a_variable = $a_value;
        }
        #1
    }
    $second = new second;
    #2
}
#3
$first = new first;
  1. If I initialize $a_variable as $a_variable it is only available inside the function, correct?
  2. If I initialize $a_varialbe as $this->a_variable it is only available inside class second, correct?
  3. Can I initialize $a_variable as $first->second->a_variable? If so, How would I call it at #1, #2, and #3?
  4. Can I initialize $a_varialbe as $this->second->a_variable? If so, How would I call it at #1, #2, and #3?

As you can see I am simply confused as to how OOP works.


First of all, I want to express how much I appreciate all of the help. I have already learned more than enough to consider this question a smashing success.

That said, even if it is poorly formulated, psuedo-code and invalid syntax, this code DOES run.

class class_1{

    public function function_1(){

        require('class_2.php');

        public function function_2_callback(){

            //%%%%%%  How do I set a variable here and get the DATA...

        }

        $this->class_2 = new class_2("function_2_callback");
    }

}

$class_1 = new class_1;

//&&&&&&&&&& Get the DATA here?



/* CONTENTS OF class_2.php */

class class_2($callback){

    call_user_function($callback);

}

Even if we have to look at this as an exercise. Can someone tell me how I would first set (@ %%%%%%%)and then call a variable (@ &&&&&&&&) as shown?

回答1:

First off: What you have there doesn't work, you cannot declare a class inside a class the way you are doing (notwithstanding conditionally declaring a class inside a function, which you should not do).

Scope in PHP (including OOP) is very simple:

  1. variables have function scope
  2. object properties are accessible if you have a reference to the object
  3. the visibility of object properties can be restricted

The only real scope you have is function scope for variables:

$a = 'foo';

function bar() {
    $a = 'bar';
}

The two $as are entirely unrelated, in different scopes. As simple as that.

class Foo {

    public $a = 'foo';

    public function bar() {
        $this->a;  // foo

        $a = 'bar';
    }

}

$foo = new Foo;
$foo->a;  // foo

An object property has no scope, it has visibility. You can access it if you have the object in scope. Above, $foo is the object. It's in scope, its property a is public, therefore it can be accessed using $foo->a. Inside the class, the property is accessible via $this->a.

The $a = 'bar' is a local variable in the function and has nothing to do with $this->a. It is not accessible anywhere except inside the function. Refer to rule #1, function scope.

class Bar {

    protected $b = 'bar';

    public function baz() {
        $this->b;  // bar
    }

}

$bar = new Bar;
$bar->b;  // doesn't work

If the visibility is not public, the property (here b) is not accessible from outside the class itself. Inside the class you can access it using $this->b, but not from outside using $bar->b. It's not about scope, but visibility.

And that's pretty much the scope rules in PHP.



回答2:

First of all your example is invalid code PHP does not support nested classes, meaning a class within a class.

if you define a class, a variable initialized within a method is local to that method, while you can "initialize" an attribute with $this->newattribute, you should have declared it and its visibility before (before you write a method public / private / protected $varname = "initial value";).

Your questions 3 and 4 would make sense in another context, this is when an object is passed as member of another object, see example below.

If you build it like

<?php

class First {  
  public $second;
}

class Second {
  public $a_variable;
}

$foo = new First();
$foo->second = new Second();

you can access it:

$foo->second->a_variable = "foo";

or within a method in second with

$this->a_variable;

or within a method in first with

$this->second->a_variable;


回答3:

You should NOT be nesting classes like that. That shouldn't even run. I would suggest running the code first. There are some tools online for testing small snippets of PHP code, such as this.

For the code to run as you might expected it to, it should look like this:

class second{
    public function widgets(){
        $a_variable = $a_value;
    }
}

class first{
    public $second;

    public function __construct() {
        $this->second = new second;
    }
}

$first = new first;

A variable that begins with $[a-z]+ is local to the function. A property beginning with $this->[a-z]+ (where [a-z] is 1 or more letters) is part of the object.

There's some documentation on php.net that goes over the specifics of objects in php here.

If I initialize $a_variable as $a_variable it is only available inside the function, correct?

Yes, correct. It begins with $[a-z]. Not quite true if you use the global keyword, but that's discouraged.

If I initialize $a_varialbe as $this->a_variable it is only available inside class second, correct?

Yes, but you should declare it first. You can do this with public $variable, protected $variable or private $variable. public means a property can be accesses from the outside, whereas private means only the class itself can access it. protected is private to the outside, but public to classes that extend from your class.

(public/private/public became available in PHP 5. In PHP 4 you would use var $variable, which defaults to public in PHP 5)

Can I initialize $a_variable as $first->second->a_variable?

You can arbitrarily initialize class variables without declaring them, but that's not something you should be doing.

If so, How would I call it at #1, #2, and #3?

You can't call code there (in your example). Code must be inside a function or in the global context (outside of the class definition).



回答4:

A Brief Explanation of Classes

class foo{

    // This can be accessed anywhere
    public $i_am_public;

    // This can be accessed within this class and any sub classes
    protected $i_am_protected;

    // Thi can only be accessed within this class
    private $i_am_private;

    // This function can be accessed anywhere
    public function foo_function(){

        // This variable is only available in this function
        // it cannot be accessed anywhere else
        $variable = 'Hello World';

        // However, you can access any of the other variables listed above
        // like so
        $this->i_am_public = 'public';
        $this->i_am_protected = 'protected';
        $this->i_am_private = 'private';

        // Write these variables
        echo $this->i_am_public;
        echo $this->i_am_protected;
        echo $this->i_am_private;
    }
}

$foo = new foo;

$foo->foo_function();

// You can change any publicly defined variables outside
// of the class instance like so
$foo->i_am_public = 'testing';

Specific Answers to Questions

Before I go any further, I would hugely urge you not to define a class within a class! Instead, use class extensions which I will explain later. In fact, I am surprised your code even works!

If I initialize $a_variable as $a_variable it is only available inside the function, correct?

Yes, this will only be available inside the function. If you want to access it outside of the function then you need to define it outside the function using one of the scope definitions public, protected, private.

If I initialize $a_varialbe as $this->a_variable it is only available inside class second, correct?

This depends on what scope you give it, but you shouldn't be defining a class within a class anyway.

Can I initialize $a_variable as $first->second->a_variable? If so, How would I call it at #1, #2, and #3?

I cannot answer this as I have never nested a class within a class, once again I would urge you to change this structure.

Can I initialize $a_varialbe as $this->second->a_variable? If so, How would I call it at #1, #2, and #3?

Please see above answer :-)

Nesting Classes

As mentioned, I have never seen this before, and I am surprised it even works. You should definitely change this structure.

One suggestions would be to use extensions like so:

class foo{

    // This can be accessed anywhere
    public $i_am_public;

    // This can be accessed within this class and any sub classes
    protected $i_am_protected;

    // Thi can only be accessed within this class
    private $i_am_private;

    public function foo_function(){

        echo 'Hello World';
    }
}

class bar extends foo {

    // This is a public function    
    public function baz(){

        // These will work
        $this->i_am_public = 'public';
        $this->i_am_protected = 'protected';

        // This will not work as it is only available to
        // the parent class
        $this->i_am_private = 'private';
    }
}

// This will create a new instance of bar which is an 
// extension of foo. All public variables and functions
// in both classes will work
$bar = new bar;

// This will work because it is public and it is inherited
// from the parent class
$bar->foo_function();

// This will work because it is public
$bar->baz();