SitePoint Sponsor

User Tag List

Results 1 to 20 of 20
  1. #1
    Community Advisor silver trophybronze trophy
    dresden_phoenix's Avatar
    Join Date
    Jun 2008
    Location
    Madison, WI
    Posts
    2,812
    Mentioned
    34 Post(s)
    Tagged
    2 Thread(s)

    Class inheritance/visibility.

    the PHP manual says
    . Members declared protected can be accessed only within the class itself and by inherited and parent classes.Members declared as private may only be accessed by the class that defines the member.
    This actually works. That is if I declare something protected or private, it can not be accessed from an instance (ala $a->prop; ). And when I declare something as private it can only be withing the class itself and , theoretically and functionally , is not passed on to any extensions of the root class. In practice this seems to work: if I declare $prop as private $this->prop will not work in a child class ( unless I declare another private $prop within that child class).

    Ok by now you are saying well if you are going to do that then why not make $prop protected instead of private? Actually i usually do, although sometimes it seems if I have many nested classes and I just want a couple of them tho have access to $prop that it is logical to make $prop private so that it is not inherited by child classes. again this works, functionaly.

    My question is about something I noticed via var_dumps of objects.
    PHP Code:
            class Test1{
            public         
    $tesVar    =    'Veronica';
            public         
    $hold    =    array();
            protected     
    $name    =    '';
            public         
    $another=    'other';
            private     
    $births    =    'Test2';
            private     
    $OnlyHere    =    'only once';

            function 
    Test1($name){
                
    $this->name=$name;
            }
            function 
    output (){
                    foreach (
    $this as $k=>$that){
                        echo 
    $k,'=',$that,'<br>';
                    }
                    if (isset(
    $this->OnlyHere)) {echo $this->OnlyHere.'<br>';}
                    else {echo 
    "just chickens!";}
                    echo    
    '<b>I produce ', (($this->births) ?$this->births:'nothing'),'</b><br>' ;
                    echo 
    '-----<br>';
            
            }
            function 
    makeChild ($name="anonn"){
                if(
    $this->births)  { 
                    
    $this->hold[$name]=new $this->births($name);
                    return 
    $this->hold[$name];
                } 
                return 
    false;
            }
        }
        class 
    Test2 extends Test1{
            private     
    $births        =    'Test3';
        }
        class 
    Test3 extends Test2{
            private     
    $births        =    false;// neuters class
        
    }
        
        
    $Test1=new Test1('Start');

        
    $Test1->tesVar="And Ray";
        
    $Test1->makeChild("first");
        
    $Test1->hold['first']->makeChild('second');
        echo
    '<pre>';
        
    $Test1->hold['first']->output();
        
    var_dump($Test1); 
    When property is PRIVATE it still appears in the var_dump of descendant objects, even if it was NOT declared in their classes ( I would expect this for PROTECTED, but not for PRIVATE). This is .. the property isnt accessible.. but it APPEARS to be set anyway?!?

    Even weirder, if you re declare a private property so that it has a different value.. then you have MULTIPLE intances of the same property ( again only the one recently declared is actually accessible form the class.. but its still weird)

    Code:
    object(Test1)#1 (5) {
      ["tesVar"]=>
      string(7) "And Ray"
      ["hold"]=>
      array(1) {
        ["first"]=>
        object(Test2)#2 (6) {
          ["births:private"]=>
          string(5) "Test3"
          ["tesVar"]=>
          string(8) "Veronica"
          ["hold"]=>
          array(1) {
            ["second"]=>
            object(Test2)#3 (6) {
              ["births:private"]=>
              string(5) "Test3"
              ["tesVar"]=>
              string(8) "Veronica"
              ["hold"]=>
              array(1) {
                ["anonn"]=>
                object(Test2)#4 (6) {
                  ["births:private"]=>
                  string(5) "Test3"
                  ["tesVar"]=>
                  string(8) "Veronica"
                  ["hold"]=>
                  array(0) {
                  }
                  ["name:protected"]=>
                  string(5) "anonn"
                  ["another"]=>
                  string(5) "other"
                  ["births:private"]=>
                  string(5) "Test2"
                }
              }
              ["name:protected"]=>
              string(6) "second"
              ["another"]=>
              string(5) "other"
              ["births:private"]=>
              string(5) "Test2"
            }
          }
          ["name:protected"]=>
          string(5) "first"
          ["another"]=>
          string(5) "other"
          ["births:private"]=>
          string(5) "Test2"
        }
      }
      ["name:protected"]=>
      string(5) "Start"
      ["another"]=>
      string(5) "other"
      ["births:private"]=>
      string(5) "Test2"
    }

    This 'semi-duplication' doesn't occur if the property is set to PROTECTED instead of PRIVATE, still it doesn't seem to be logical either way.


    ALSO
    In my test 'only once' should never be echoed since the property that contains it is PRIVATE and not inherited by child classes.
    I would have expected, that in any object , BUT one of class TEST1, that isset( $this->OnlyHere) would be false.

    Actually, it's even more convoluted than that. What seems to be happening is the INHERITED FUNCTIONS ( output()), are executed in the root class and not at the instance class, and as such they also have access to all the PRIVATE root class properties. The solution would be to rewrite the EXACT SAME function within the first child class. But doesn't that negate the point of OOP?

    Could some one shed some ( simplified) light on this subject?

  2. #2
    SitePoint Enthusiast
    Join Date
    Mar 2011
    Posts
    70
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    It's working exactly the way it should be. Setting a property to private will does not stop it from being inherited. It is still instantiated and assigned a value by the root class. The only difference between private and protected is whether or not a child class has direct access to it's value. In your example, the output() function is echoing all variable within its scope.

    For instance, think about the way get and set functions work. They allow access to a private member variable by a child without allowing direct access to the value itself. If a parent function does not have access to the values within its own scope then there would be no reason for it to exist.

    For example:
    PHP Code:
    <?php

    class Test1 {
        
        private 
    $name;
        
        public function 
    __construct(){}
        
        public function 
    set$name )
        {
                
    $this->name $name;
        } 
        
        public function 
    getName()
        {
            return 
    $this->name;
        }
        
        public function 
    parentOutput()
        {
            echo 
    $this->name '<br />';
        }
        
    }

    class 
    Test2 extends Test1 {
        
        public function 
    __construct$name 
        {
            
    $this->set$name ); // works because it is a function defined in the parent and retains access to parent variable. 
           
        
    }
        
        public function 
    childOutput()
        {
            echo 
    $this->name '<br />'// will no work because the value exists but is invisible in the current scope.
        
    }
        
        
    }

    $test = new Test2'John Doe' );
    $test->parentOutput(); // will display the name 
    $test->childOutput(); // will cause an error
    Calling the output method will display the name because the function was declared in the parent class and retains access to all parent member variables.

    Calling the childOutput method will throw an error because it is declared in the child class. Even though it inherits the name variable it cannot see it or access it without the use of the getName function.

    However, if we re-write the childOutput to the following it will work, because the getName function retains access to the name variable.

    PHP Code:
        public function childOutput()
        {
            echo 
    $this->getName() . '<br />'// value access through parent function
        

    Does this help at all?

  3. #3
    Community Advisor silver trophybronze trophy
    dresden_phoenix's Avatar
    Join Date
    Jun 2008
    Location
    Madison, WI
    Posts
    2,812
    Mentioned
    34 Post(s)
    Tagged
    2 Thread(s)
    It's working exactly the way it should be. Setting a property to private will does not stop it from being inherited.The only difference between private and protected is whether or not a child class has direct access to it's value.
    That's what I was trying to confirm. It made sense to me from the onset, but it also seemed 'wasteful' as inaccessible properties still get stored in child classes ( if that's makes sense)

    I do have ONE QUESTION remaining, and that is inherited method seem to act as if they were in the parent class ( and as such they have access to properties or property values set at the parent scope

    Perhaps this is not what inheritance was meant for and am wrong for thinking this way.. which was that an inherited method had teh current scope, not the scope of the parent. The reason I find it cumbersome otherwise is because( as you essentially utilized this in your example) the magic variable $this->... will always point to the class from which the method was inherited and NOT the current class... unless the method is copied in the new class ( which really doesnt do much for cutting down code or redundancy.

    This is what I mean:

    PHP Code:

    class Test1 {
        
        private 
    $name;
        private 
    $value10;
        public function 
    output()
        {
            echo 
    $this->value '<br />'//
        
    }
        
    }
    class 
    Test2 extends Test1 {
       private  
    $value=5// for the sake  of example, lets say $value must be '5' anytime an intance of Test2 is dealt with. 
    }

    $test = new Test2();
    $test->output(); // will display the 'value' property of the class ; one would expect '10' if Test1, '5' if Test2. 
    This works, however if we rewrite the function again in the child class ( of course doing so defeats any attempt at code frugality and makes the code redundant)

    PHP Code:
     class Test1 {
        
        private 
    $name;
        private 
    $value10;
        public function 
    output()
        {
            echo 
    $this->value '<br />'//
        
    }
        
    }
    class 
    Test2 extends Test1 {
       private  
    $value=5;   
        public function 
    output()
        {
            echo 
    $this->value '<br />'//
        
    }
       
    }

    $test = new Test2();
    $test->output(); // will display the 'value' property of the class ; one would expect '10' if Test1, '5' if Test2. 
    It seems a simple enough fix to just make $value protected , instead of public, but what if $value is not supposed to be present at all in TEST 2?

    PHP Code:

    class Test1 {
        
        private 
    $name;
        private 
    $value10;
        public function 
    output()
        {
            echo 
    $this->value '<br />'//
        
    }
        
    }
    class 
    Test2 extends Test1 {}
    $test = new Test2();
    $test->output(); 
    I still outputs 1) and unsetting $value in Test2 wouldn't help, as the function is actually running in Test1 and acting on the properties of Test1, even tho its an instance of Test2. I hope my question makes sense...

  4. #4
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,214
    Mentioned
    153 Post(s)
    Tagged
    0 Thread(s)
    If Test2 should not have $value, then you shouldn't be inherting from a class that contains $value (ie: Test1). Inheriting means you take on all methods and variables from the class you are extending. You must take on private variable as the methods in the inherited class may require the use of those private variables.

    Now when you re-defined output(), you actually overwrote the base method for output in Test1, so when you had an instance of Test2()->output() you ran the output defined within Test2() instead of Test1() because you overwrote it.

    Extending is best summed up by the following example (at least it has always helped me:
    PHP Code:
    class Animal
    {
      private 
    $fed false;
      protected 
    $type "Unknown";
      public function 
    MakeNoise()
      {
        echo 
    "No Noise Defined";
      }

      public function 
    GetType()
      {
        echo 
    "Type of " $this->type;
      }

      public function 
    FeedMe()
      {
        
    $this->fed true;
      }

      public function 
    HasBeenFed()
      {
        return 
    $this->fed;
      }
    }

    class 
    Dog extends Animal
    {
      public function 
    __construct()
      {
        
    $this->type "Dog";
      }

      public function 
    MakeNoise()
      {
        echo 
    "Woof Woof";
      }
    }

    class 
    Cat extends Animal
    {
      public function 
    __construct()
      {
        
    $this->type "Cat";
      }

      public function 
    MakeNoise()
      {
        echo 
    "Meow Meow";
      }
    }

    $dog = new Dog();
    $dog->GetType(); // Type of Dog
    $dog->MakeNoise(); // Woof Woof
    $dog->HasBeenFed(); // false;
    $dog->FeedMe();
    $dog->HasBeenFed(); // true;
    $cat = new Cat();
    $cat->GetType(); // Type of Cat
    $cat->MakeNoise(); // Meow Meow
    $cat->HasBeenFed(); // false;
    $cat->FeedMe();
    $cat->HasBeenFed(); // true;
    $animal = new Animal();
    $animal->GetType(); // Type of Unknown
    $animal->MakeNoise(); // No Noise Defined
    $animal->HasBeenFed(); // false;
    $animal->FeedMe();
    $animal->HasBeenFed(); // true; 
    Both Dog and Cat, can't access $this->fed, but calling HasBeenFed and FeedMe both need and have access to $this->fed.

  5. #5
    SitePoint Enthusiast
    Join Date
    Mar 2011
    Posts
    70
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    I never really thought about it that way. The thing with PHP is that it allows us to get away with a lot that we would not be able to with a strictly typed language.

    When inheriting a class the parents constructor should always be called explicitly. The child the does not magically gain access to the methods in the parent class. It must first create an accessible instance that it can use. So really, even though the parents methods are accessible by the child, they must still operate within their own scope. The parent has no idea that it has been inherited or extended and it acts as such.

    Another thing to keep in mind is that you can always access a method that has been overridden using the parent:: operator.

    PHP Code:
    <?php

     
    class Test1 
         
        private 
    $value 10
        
        public function 
    __construct$value )
        {
            
    $this->value $value;
        }
        public function 
    output() 
        { 
            echo 
    'Test1\'s value is ' $this->value '<br />'// 
        



    class 
    Test2 extends Test1 
        private  
    $value 5;    

        public function 
    __construct$value )
        {
           
    parent::__construct$value );
           
    $this->value $value 2;
        }
        public function 
    output() 
        { 
            
    parent::output(); // outputs 5
            
    echo 'Test2\'s value is ' $this->value '<br />'// outputs 10
        
    }   
    }

    $test = new Test2); 
    $test->output();
    Also as cpradio said, if you are extending a class that has values you don't need then the object architecture you are using should be re-evaluated. The beauty of programming is that you can make it work in whatever way will work best for you.

  6. #6
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,214
    Mentioned
    153 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by JeremyC View Post
    When inheriting a class the parents constructor should always be called explicitly.
    True words. I messed up in my example, my construct for Dog and Cat should have parent::__construct(); in them. Good catch JeremyC

  7. #7
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,311
    Mentioned
    19 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by dresden_phoenix View Post
    I do have ONE QUESTION remaining, and that is inherited method seem to act as if they were in the parent class ( and as such they have access to properties or property values set at the parent scope
    This isn't really a question, but the description is correct. A method has access to only those private fields that were declared within that method's class block.

    The reason I find it cumbersome otherwise is because ... the magic variable $this will always point to the class from which the method was inherited and NOT the current class
    Only when it comes to private fields. Private fields are kind of like local variables. They live in their own little bubble, and nothing outside can touch them. On the other hand, fields that are protected or public can be redefined by subclasses.

  8. #8
    Community Advisor silver trophybronze trophy
    dresden_phoenix's Avatar
    Join Date
    Jun 2008
    Location
    Madison, WI
    Posts
    2,812
    Mentioned
    34 Post(s)
    Tagged
    2 Thread(s)
    OK.. that works..

    But you must admit the syntax parent:: output(); // outputs 5 seems backwards. I mean I would have never thought of having child output call parent output using "parent::" as I woudl have assumed "parent::" INVOKED the parent scoped ( not to mention it an extra 3 lines) I would have figured the 'default' behaviour would be the child fuction had the child scope and IF you needed the parent scope you would have used :: parent.

    As arbitrary as it would seem.. I think i do get how it works now.. thank you.

  9. #9
    SitePoint Enthusiast
    Join Date
    Mar 2011
    Posts
    70
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dresden_phoenix View Post
    OK.. that works..

    But you must admit the syntax parent:: output(); // outputs 5 seems backwards. I mean I would have never thought of having child output call parent output using "parent::" as I woudl have assumed "parent::" INVOKED the parent scoped ( not to mention it an extra 3 lines) I would have figured the 'default' behaviour would be the child fuction had the child scope and IF you needed the parent scope you would have used :: parent.

    As arbitrary as it would seem.. I think i do get how it works now.. thank you.
    You are correct, parent:: should only be used if you need to access the parents functionality of an overridden method. The only reason that I put it in the childs output() method was to try and demonstrate how it worked, it was not necessary by any means. I'm sorry if that seemed confusing.

  10. #10
    . shoooo... silver trophy logic_earth's Avatar
    Join Date
    Oct 2005
    Location
    CA
    Posts
    9,013
    Mentioned
    8 Post(s)
    Tagged
    0 Thread(s)
    Some advice I took eons ago when designing classes/objects. Never use public or private on properties, always protected. By not using public you keep the properties encapsulated from the outside world, allowing only the object to change them. By not using private you don't hinder inheritance. Then you don't have this complex mess like we are discussing now. (Side note, public and protected is all I use on methods. I never use private for anything.)
    Logic without the fatal effects.
    All code snippets are licensed under WTFPL.


  11. #11
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    6 Thread(s)
    Quote Originally Posted by logic_earth View Post
    Some advice I took eons ago when designing classes/objects. Never use public or private on properties, always protected. By not using public you keep the properties encapsulated from the outside world, allowing only the object to change them. By not using private you don't hinder inheritance. Then you don't have this complex mess like we are discussing now. (Side note, public and protected is all I use on methods. I never use private for anything.)
    Can you describe a situation that is part of your 'complex mess'; what specifically are private properties causing in your situation?
    ictus==""

  12. #12
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,214
    Mentioned
    153 Post(s)
    Tagged
    0 Thread(s)
    ServerStorm,

    I was thinking the same thing. I use private methods and variables a lot with my architectures, to simplify methods, such as, I may have a private method that does nothing but resize a larger provided image into a thumbnail (no need to expose this method to extended classes or to the public, as the public just needs to provide a large image).

    So I too am curious why a recommendation would be made to not use private for anything...

  13. #13
    . shoooo... silver trophy logic_earth's Avatar
    Join Date
    Oct 2005
    Location
    CA
    Posts
    9,013
    Mentioned
    8 Post(s)
    Tagged
    0 Thread(s)
    One of the benefits of objects is inheritance, using private disrupts that ability. I have yet to hear a single case where using private offers any benefit other then hampering inheritance.
    Logic without the fatal effects.
    All code snippets are licensed under WTFPL.


  14. #14
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,311
    Mentioned
    19 Post(s)
    Tagged
    1 Thread(s)
    The case both for and against private vs protected: http://fabien.potencier.org/article/...ted-vs-private

  15. #15
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    6 Thread(s)
    Quote Originally Posted by logic_earth View Post
    One of the benefits of objects is inheritance, using private disrupts that ability. I have yet to hear a single case where using private offers any benefit other then hampering inheritance.
    Thank you.

    Quote Originally Posted by Jeff Mott View Post
    The case both for and against private vs protected: http://fabien.potencier.org/article/...ted-vs-private
    Interesting read and not what I expected to hear from Fabien Potencier. Thanks also for this.

    Steve
    ictus==""

  16. #16
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,214
    Mentioned
    153 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    The case both for and against private vs protected: http://fabien.potencier.org/article/...ted-vs-private
    Correct me if I am wrong, but wasn't the point of this article stating that someone who previously never used private for anything, now does, and supports so as it helped his API? All the comments in support for not using private again, just scream it hinders inheritance but with no real proof.

    Maybe, I work too often in APIs where you only expose what is meaningful and makes sense and nothing more. I primarily write components that are interacted with by third parties or services within the local company, so I am constantly asking myself (is this something someone would want/need to override?) If the answer is not a 100% yes, it gets marked as private until a use case comes around asking for it, then I can safely change it to protected/public without breaking prior versions (I rarely have this situation, as typically what I expose allows them to do what they need in a cleaner more realistic approach).

    So did I miss something on why protected is valued more than private? I really want to know more about that point of view with more than "it hinders inheritance". Give me a real case scenario where you used a private method (and you without any hesitation determined it would not or ever need to be extended) and so you had to switch it to protected/public.

  17. #17
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    6 Thread(s)
    Hi cpradio,

    You interpreted the point of the article correctly, and also most of the comments disagreed. This should not confuse Fabien's point, which is 'He used to avoid, even ban the use of private, but due to community team members views and their use of private surprised Fabien as it created clearer API's'. From what I read in your post, you are using private in a similar way. You could look to your own examples ->
    Quote Originally Posted by cpradio View Post
    ...so I am constantly asking myself (is this something someone would want/need to override?) If the answer is not a 100% yes, it gets marked as private until a use case comes around asking for it, then I can safely change it to protected/public without breaking prior versions...
    .

    Steve
    ictus==""

  18. #18
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,214
    Mentioned
    153 Post(s)
    Tagged
    0 Thread(s)
    Unfortunately, the only examples that I have were from early on in my career, where I made incorrect assumptions. I was just trying to see if there are other reasons that I am missing, or general discussion. I've learned two things early on in my career 1) never flat out say that something shouldn't be used; ever. 2) never say you should always do X vs Y.

    There are reasons for the existence of types, functions, patterns, etc. You are bound to encounter the correct reason for something that was barred at some point, know when to use something and when you shouldn't, and when unsure, research it and ask around.

    I am simply trying to learn the mindset that is set against private, as in 1) how did it solve your problem, and 2) what was the after-math, have you had bugs because you exposed a method that someone overrode incorrectly? (or more likely, that a future API broke because it changed the implementation); did things just go much smoother? how so? etc.

  19. #19
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    6 Thread(s)
    Hi cpradio,

    I don't use private very often because it has got in my way when extending object. I am not of the mindset that private is bad it just affects the way I currently code (Although I would be the first to admit that I am not the best OOP programmer here, so I think I don't best understand how it would improve what I do). I can see if you have a class that gets extended and part of it you don't want it to be overwritten and maybe only provide a getter/setter for it then you would declare it private.

    I looked at the solution that Fabien linked to in the article but I could not see how it helped or hindered, but I did see how it forced them to refactor one method into three methods, so maybe that was his point.

    Maybe someone else will have a good example.

    Steve
    ictus==""

  20. #20
    SitePoint Enthusiast
    Join Date
    Sep 2009
    Posts
    45
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Some answers and self evolvement only come by repeating the same question at the occurance of another answer to force analysis of the subtleties of the original question.

    At a technical level "override method" has become an allowable answer due to inheritance and protected methods. The deeper question is why override method and messing with inheritance chains was required and whether having to tamper was a sign of another deeper design issue.

    A lot of the muddiness of this topic comes from the confusion that inheritance(a technical implementation detail) is equal to polymorphism/Liskov substition principle(an external communication/interface detail).

    Anyway I would point that the real answers lie outside of the PHP community as it has a habit of repeating everything our ancestors did however unclean for the sake of self experience sometimes unknowingly rebranding a bad practice with a cool new name. There is decades of information and experience to glean on this topic.

    For gleaning random subjective experience programmers stack exchange is always a good daily read.

    Hint. override method as the most ideal/quick opportunistic answer usually comes from the act of favouring inheritance over composition combined with a muddling of responsibilites.
    http://programmers.stackexchange.com...ance-come-from


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •