13

I need to create approx. 5-7 classes, every class will contain a lot of members (let us say each class will contain 20 members). I could create them using public access, like:

class A {
    public $myPropertyOne = '';
    public $myPropertyTwo = '';
    ...
}

My preferred way of course to make these members private and create get/set methods for each property. I.e.

class A {
    private $myPropertyOne = '';
    private $myPropertyTwo = '';

    public function getMyPropertyOne() {
            return $this->myPropertyOne;
    }

    public function setMyPropertyOne($myPropertyOne) {
            $this->myPropertyOne = $myPropertyOne;
    }

    public function getMyPropertyTwo() {
            return $this->myPropertyTwo;
    }

    public function setMyPropertyTwo($myPropertyTwo) {
            $this->myPropertyTwo = $myPropertyTwo;
    }
}

But considering a class will have 20 properties, I will have in addition to this add 40 methods. And my concern here is how will this slow down the script and much more memory this will require (remember I am going to have several classes like this).

Another solution could be to use magic functions __set, __get but I don't want to, because the code completion in development IDE will not suggest properties which is crucial for me.

If this would be a compiled language (like C++) I would not have a question and would use the solution with getters, setters but since the PHP is interpreted language I am interested in my scripts to use less RAM and be as fast as possible.

Thanks in advance, any thoughts regarding this question would be much appreciated!


My Opinion

Thank you all for your answers, I just wanted to share my opinion in case someone will look for an answer to this question.

I cannot fully agree with those who say that you should not care about performance as this is task of optimizers, I think this is important factor (well atleast as for me), when we're dealing with interpreted language such as PHP we will always have to think about memory and speed (this all reminds me the time when I was developing system apps for DOS, heh :) and you always have been limited with poor CPU and kilobytes of total RAM so you got happy if you could save an additional byte), in PHP development you have the same picture as regardless of how many server you add, users' count will be always higher so that you always have to decide if you want to follow classic/safe/proper method or to avoid this and get some gain in speed or memory.

So.... my opinion is that the best way here is to use public access for all member and avoid getters/setters for all properties and use private access with get/set methods for properties which requires data validation or initialization before a value will be set.

For example:

class B {
    public $myPropertyOne = '';
    public $myPropertyTwo = '';
    private $myPropertyThree = array();


    public function getMyPropertyThree() {
        return $this->myPropertyThree;
    }

    public function setMyPropertyThree($val) {
        if(!is_array($val)) {
            return;
        }

        $this->myPropertyThree = $val;
    }
}

Thank you for spending time on my question!

user1476490
  • 149
  • 1
  • 6
  • 14
    this is a micro-optimization, and you should *never* worry about it. – Karoly Horvath Jun 23 '12 at 07:28
  • Well, yes but a perspective to add 280 methods (for 7 classes, 20 private members) just for this a little scares me from memory/speed standpoint. And let's say system grows and I will extend those classes to the count of 15.. then I'll get 600 methods. From the other side I realize that these get/set methods will be executed only when needed but at the same time I assume that when PHP will parse source code it will be building a table of methods in memory, will perform syntax analyze and this all may slow down the work of the script a lot, is my assumption right? – user1476490 Jun 23 '12 at 07:39
  • 1
    Memory is relatively cheap and method overhead is only per-Class (not per instance). PHP also does tricks like handling multiple requests in the same process (via mod_php, etc) so the initial load time (as trivial as it may be) can be entirely ignored... in any case, *benchmark first* (under actual conditions) to see what the "slow" parts of a system are. (FWIW, assume that there are 600 methods, and each method "wastes" 1kb. A whopping 600kb is "wasted". Not really that much in the scheme of things... especially considering how much *each variable* in PHP already "wastes" ;-) –  Jun 23 '12 at 07:42
  • 5
    You should not be concerned about parsing time at all. This can be easily eliminated with APC (or some other opcode cache). – linepogl Jun 23 '12 at 07:47
  • Thank you for your answers, however I can't agree with "pst". 600kb per one script load is not that much but let's say a thousand of users load the script, this will result to the usage of ~586 MB in total. And what if 10-20k of users? :) – user1476490 Jun 23 '12 at 07:52
  • @user1476490 "thousand of users" loading the script would mean 1000 _concurrent_ requests (i.e. the first request still running when the last one starts). This is an unlikely scenario. A high loads still does not mean 10-20k users being served _simultaneously_. – lanzz Jun 23 '12 at 08:08
  • maaan, just don't do it. if you that many simultaneous requests/sec, you already have a server cluster handling the traffic, with memcached, APC, maybe hiphop, and with all kinds of other optimizations. And these **stupid** micro-optimizations would be the last thing you would worry about. – Karoly Horvath Jun 23 '12 at 08:17
  • Sorry, I know this is a long dead discussion and forgive me for necro-posting but if you were truly concerned with saving bytes of data, wouldn't you abbreviate "setMyPropertyThree" to something like "sPropThree"? ;) – DevlshOne Oct 25 '12 at 14:29

6 Answers6

5

Simple test shows instances take the same amount of memory, unaffected by the number of methods in a class:

Class with no methods:

class Test1 { }

Class with 20 methods:

class Test2 {

    function test1() { return true; }
    function test2() { return true; }
    function test3() { return true; }
    function test4() { return true; }
    function test5() { return true; }
    function test6() { return true; }
    function test7() { return true; }
    function test8() { return true; }
    function test9() { return true; }
    function test10() { return true; }
    function test11() { return true; }
    function test12() { return true; }
    function test13() { return true; }
    function test14() { return true; }
    function test15() { return true; }
    function test16() { return true; }
    function test17() { return true; }
    function test18() { return true; }
    function test19() { return true; }
    function test20() { return true; }

}

Test loop, same for both tests:

$test = array();
$base = memory_get_usage();
for ($i = 0; $i < 10000; $i++) {
    $test[] = new ClassToBeTested();
}
$used = memory_get_usage() - $base;
print("used: $used\n");

Result for class Test1 (no methods):

used: 3157408

Result for class Test2 (20 methods):

used: 3157408

I've run it in two separate scripts, since running the two tests in a single script apparently exposed some PHP internal allocation, and the second test consumed less memory than the first, no matter which one is first or second.

While you surely take more memory for the actual class definition, apparently this cost is incurred only once per class, not per instance. You don't have to worry about the memory usage.

lanzz
  • 42,060
  • 10
  • 89
  • 98
  • I unfortunately cannot paste a lot of code here but here is what I did. I created test1.php with a single class in it, then test2.php with a class and a single method, and then test3.php with class, 20 private properties and then 40 public get/set methods (with all the returns, etc). And output total used RAM at the end, so here is what I got: `test1.php - 653.97 Kbytes` `test2.php - 654.73 Kbytes` `test3.php - 718.16 Kbytes` This is rough but basically I suppose each method/variable not counting the data it holds, eats ~1kb. – user1476490 Jun 23 '12 at 08:10
5

But considering a class will have 20 properties

Having this many properties is usually an indicator of misplaced information. Check whether you can group some of those into Classes of their own.

I will have in addition to this add 40 methods.

Not at all. Unless these classes are dumb data structs, you dont want any Getters and Setters on them because they break encapsulation. Put methods in the public API with which you tell the objects to do things.

And my concern here is how will this slow down the script and much more memory this will require (remember I am going to have several classes like this).

This is not an issue.

Another solution could be to use magic functions __set, __get but I don't want to, because the code completion in development IDE will not suggest properties which is crucial for me.

Modern IDEs can autocomplete on magic methods.

However, if you are already concerned about performance at the microlevel, then you dont want magic methods because those are definitely slower.

Apart from that, Magic Methods are not substitutes for getters and setters but error handlers that get triggered when an inaccessible property or method was called.

Also, magic methods are unobvious and make for hard to read APIs.

Community
  • 1
  • 1
Gordon
  • 312,688
  • 75
  • 539
  • 559
1

To make properties of your class that implemented by magic methods to be highlited by IDE just use @property PHPDoc @property tag, like this:

<?php
/**
* @property int id Blog post ID
* @property string title Blog post Title
*/
class Post {

}

More on PHPDoc' @property here: http://manual.phpdoc.org/HTMLSmartyConverter/PHP/phpDocumentor/tutorial_tags.property.pkg.html

As for other issues questioned - Karoly Horvath' comment fully covers those PHP OOP a lot of setters, getters.

Community
  • 1
  • 1
Evgeniy Chekan
  • 2,615
  • 1
  • 15
  • 23
0

Take in mind that my code considered that properties' name have been declared in lowercase...

  <?php

    class Modelo {
        var $attr1 = "default";
        var $attr2 = 0;


        public function __call($name, $arguments)
        {
            if (method_exists($this, ($method = $name))){
                return $this->$method();
            }
            else{       
                $attribute = split("get",$name);
                if(count($attribute)==2){
                    $attribute = strtolower($attribute[1]);
                    if(isset($this->$attribute)){
                        return ($this->$attribute);
                    }
                }else{
                    $attribute = split("set",$name);
                    if(count($attribute)==2){
                        $attribute = strtolower($attribute[1]);
                        if(isset($this->$attribute) && count($arguments)==1){
                            $this->$attribute=$arguments[0];
                        }else{
                            die("$name number of arguments error: ".join($arguments,","));
                        }
                    }else{
                        die("$name doesn't exist!");
                    }               
                }           
            }
        }


    }

    echo "<pre>";
    $m = new Modelo();
    print_r(
        array(
            "objetct"=>$m
            ,"getAttr1"=>$m->getAttr1()
            ,"getAttr2"=>$m->getAttr2()
        )
    );
    echo "setAttr1\n";
    $m->setAttr1("by set method");
    print_r(
        array(
            "objetct"=>$m
            ,"getAttr1"=>$m->getAttr1()
            ,"getAttr2"=>$m->getAttr2()
        )
    );

    ?>
ivandcl
  • 114
  • 1
  • 4
0

As stated before, it's quite strange that your class should have so many properties. However, it can sometimes (fairly rarely though) happen. But normally, those properties should have a sort of link together : so you could store them in a hashmap and not a property. Then you just neeed one method as a getter.

Now, it will surely be more resources consuming, true. As for autocompletion, use constants : you'll just type something like :

 $my_class->getFromHashMap($parameter)

And when typing your parameter, you'll use the constant as it's stored in the class : here, the autocomplete should be able to help you.

Raveline
  • 2,660
  • 1
  • 24
  • 28
  • Yeah, I currently keep all the properties in associative array but this is not crystal clear for the end user (developer) how to use them all, so I have to use code completion and doc comments plus I want to improve code by validating data when user sets variable. Hash map is a good solution but this will lead to other possible issues (a developer may mistype name of parameter, I will not be able to use refactoring if I want to rename a parameter, etc.) – user1476490 Jun 23 '12 at 08:31
  • Except from data validation, using constants as keys for your hashmap should solve most of the issue you're describing (and you can even prevent, in your setters, using a key that's not defined in your constants). – Raveline Jun 23 '12 at 08:35
0

You could try this:

trait get_set {

public function set($what, $value)
{
    $this->{$what} = $value;
}

public function get($what)
{
    return $this->{$what};
}

}

It will work on public and protected variables. You can add if(!isset($this->{$what})error()

JG Estiot
  • 969
  • 1
  • 10
  • 8