PHP OOP need advice - php

i am building this sms notification system, which will send 10 times free sms based on certain occasion to the web's member, and after a certain member reach 10 times, the system would send a last notification system saying that "this is the last free sms notification", i am currently learning PHP OOP and trying to use an OOP aproach on this
without further a do here's my code:
<?php
class SmsBonus {
//bonus_sms fields = id, member_id, counter, end_status
public static function find_member($id=0){
//query to find a certain member
}
public function add_counter($id=0){
//query to increment the value of counter field
}
public function status_check($id=0){
//query to check whether the given member's counter has reach the number 10
}
public static function send_sms($id, $message){
$found = $this->find_member($id);
$status_check = $this->status_check($id);
if(!empty($found) && !empty($status_check) && $found->counter == 10){
//send the sms notification saying that this member has reach the end of the bonus period
//update this member's end_status table to 1
}else{
//send the regular notification
}
}
}
?>
would this lines:
$found = $this->find_member($id);
$status_check = $this->status_check($id);
work as expected (i cant test this one out because i am currently building this on local)? and is this a best practice regarding OOP aproach ? or am i doing this wrong ?
i need advice, thank you very much.
EDIT:
of course on my original code i declare the class, i am sorry that by not writing it here confuses everybody :D, i am actually looking for a kind of answer (advice) that pointed the way i should implement a best approach (best practice) on my codes (in this case methods), things that i worry about is that i don't meet the requirements such as K.I.S.S or D.R.Y.
UPDATE
i manage to do some modifications based on your suggestions, how is this looks like ?
<?php
class SmsBonus{
//bonus_sms fields = id, member_id, counter, end_status
protected $max_sms = 10;
public $id;
public $member_id;
public $counter;
public $end_status;
public function find_member($id=0){
//query to find a certain member
}
public function add_counter($id=0){
//query to increment the value of counter field
}
public function status_check($id=0){
//query to check whether the given member's counter has reach the number 10
}
public function update_status($id=0){
//query to update when a certain member reach its sms bonus limit
}
protected function can_still_send_sms($member_id){
$found = $this->find_member($member_id);
$status_check = $this->status_check($id);
return !empty($found) && $found->counter < $this->max_sms && !empty($status_check);
}
public function send_sms($id, $message){
$phone = Phone::find_member($id); //
if ($this->can_still_send_sms($id)) {
//send the sms notification saying that this member has reach the end of the bonus period
$this->update_status($id);
}else{
//send the regular notification
$this->add_counter($id);
}
}
}
$sms_bonus = new SmsBonus();
?>

Well, I think OOP is mostly about creating meaningful actions that are easy to reuse and, especially, make it easy to find out what's going on when you revisit your code some months later (or when someone else reads your code, which is more or less the same). Also, when you found your member, you can then perform logic on that, instead of on the id. So, in this case it might be better to create your methods like this, for example:
protected $max_sms_messages = 10;
protected function can_still_send_sms($member){
return !empty($member) && $member->counter < $this->max_sms_messages;
}
public function send_sms($id, $message){
$found = $this->find_member($id);
if ($this->can_still_send_sms($found)) { // or even if($found->can_still_send_sms()), if you want to implement it that way
//send the sms notification saying that this member has reach the end of the bonus period
//update this member's end_status table to 1
}else{
//send the regular notification
}
}
Also, for the record, you can't call non-static methods from static methods.

You need to wrap your code in a class declaration
class SMSNotification {
...
}
And you may also want to create a constructor
function __construct() {
One reason for this is so that you can set private variables to the class when it is instantiated.
you instantiate the class like this:
$sms = SMSNotification()
Will you be connecting this to a database for the counter increment. As what you would normally do with an oop approach is have a seperate class that handles that connection, so that if you wanted to build on this whole project then everything would be connecting to a database the same way.
The two lines of code you pasted are a bit different:
$found = $this->find_member($id);
you have made find_member a static function (which is probably what i would have done) so that you can call the function without create a new class object. That being said it is not of value $this as it is not part of the current instantiated class. So you will need to call it like (using my example of SMSNotification):
$found = SMSNotification::find_member($id);
This tells php to look for a static function called find_member
The other line of code should work fine:
$status_check = $this->status_check($id);

According to OOP you can't call $this->find_member($id) on a static member function. Besides you didn't declare any class at all, so $this is meaningless (as far as I remember PHP). You probalby wanted to declare some SmsClient class which would be initialized from db query filling in member variables. Your static find_member($id=0) function would query db by id and return initialized object of SmsClient with its id = $id
class SmsClient
{
private $id;
private $nSmsSent;
public __construct($id)
{
$res = DAL->GetClient($id);
//initialize vars here
}
public send_sms(...)
{
$this->nSmsSent++;
}
}

Listen to dewi.
Anyway, a nice way to test if you're using the right syntax is commenting out the content of the find_member() and status_check() functions and make them return some arbitrary values: if the values are actually returned, you're doing it right.

Related

PHP Classes - How to call them properly

Here's what I'm pretty sure I can do, just don't know how to do it.
class My_Looping_Class {
public function __construct() {
$this->vars = new My_Vars_Class(); //maybe this goes here???
for($y=0;$y<=10;$y++) {
$this->do_loop();
}
}
private function do_loop() {
//in this loop the value of $x in My_Var_Class gets incremented each loop but I'm not exactly
//sure how to call it. Something like this????
$this->vars->x++;
}
}
class My_Var_Class {
public $x = 0;
}
class My_Looping_Class_Copy extends My_Looping_Class {
// Here I need to be able to read and echo the value of x in My_Var_Class each time it
//changes but again, I'm unsure of how to call it.
}
new My_Looping_Class_Copy();
Tried to post this with just the code above and it complained I didn't have enough details. So, here goes:
What I'm attempting to do is to write a web crawler similar to PHPCrawl. In PHPCrawl you basically set the parameters for the crawler (crawl depth, follow redirects, timeout, etc.), set the url, call a "go" function in the main class and it starts crawling pages. As it crawls each page it updates another class that contains all of the result variables, like response time, links found, etc. After it finishes each page crawl, through a class extension, you're able to access all of those variables and process them as you please. After that, it crawls another url it found and updates the result variables.
I tried reading through the code but got lost pretty quick as to how the author did this. The code above is just a very basic example of how PHPCrawl works.
Here's a link to the PHPCrawl example code: http://phpcrawl.cuab.de/example.html What I'm trying to duplicate is the handleDocumentInfo($DocInfo) function.
If I understand you correctly, here's an example of how you can get the value of a property of one class and use it in another one. Extending that other one, instantiating it, and getting the new value of the property:
class My_Looping_Class {
public $xCopy;
public function __construct() {
$varClass = new My_Var_Class();
$this->xCopy = $varClass->x + 10; // Here i add 10 to this class propert 'xCopy'. Loop or do whatever you want with it instead.
}
}
class My_Var_Class {
public $x = 33; // Just some number as an example.
}
class My_Looping_Class_Copy extends My_Looping_Class {
}
$loopClassCopy = new My_Looping_Class_Copy();
echo $loopClassCopy->xCopy; // Should output 43 (33 + 10)
Or see this working example.

How to implement a good MVC pattern with PHP/MySQL without loosing SQL request time/server memory ? (good practices)

I want to implement a real pattern MVC for my php controllers. Especially, i want to split Model and API by creating java "bean" equivalent in PHP (objects made for business organization) and an API using these business objects.
For exemple, my basic object is the Member.
The question is : where i request my database ?
Do I request all the members proprities right at __construct, and i simply access them with the getters OR i do nothing in the __construct and I call the database in every getter function ?
People tell me that the 1st solution is better, though, if i want only a specific information in my controller, i will create a Member with all the informations computed right at construct (bad memory management). In the 2nd case, if i want several members proprities, i will do several SQL request which will increase my server execution time.
1st solution :
public function __construct($ID_membre,$sql)
{
$this->ID_membre = $ID_membre;
$res = mysql_get("select * from membres where ID_membre = $ID_membre",$sql);
if(!$res)
throw new Exceptions\MyDefaultException("no member for this Id");
$this->firstName = $res['first_name'];
$this->lastName = $res['last_name'];
$this->mail = $res['mail'];
$this->gender = $res['gender'];
// ....
$this->sql = $sql;
}
public function getLastName()
{
return $this->lastName;
}
public function getMail()
{
return $this->mail;
}
public function getGender()
{
return $this->gender;
}
// ....
2nd solution :
public function __construct($ID_membre,$sql)
{
$this->ID_membre = $ID_membre;
$res = mysql_get("select count(*) from membres where ID = $ID_membre",$sql);
if($res == 0)
throw new Exceptions\MyDefaultException("no member with this id");
$this->sql = $sql;
}
public function getLastName()
{
mysql_get("select name from members where ID = {$this->id}",$this->sql);
return $this->lastName;
}
public function getMail()
{
mysql_get("select mail from members where ID = {$this->id}",$this->sql);
return $this->mail;
}
public function getGender()
{
mysql_get("select gender from members where ID = {$this->id}",$this->sql);
return $this->gender;
}
In this context, good old SQL custom request within controllers are perfect to not waste time or memory, because they are customs. So, why doing such request are so poorly viewed nowsaday ? And if big organizations such as Fb or Google do MVC with database, how they manage to not waste any time/memory while splitting Model and controllers ?
This is a classic problem, which can even get worse if you want one property of many members.
The standard answer is that solution 1 is better. Requesting one row from a database doesn't take much longer than requesting one value from a database, so it makes sense to ask a whole row at once. That is unless your database rows get very big. That should however not occur in good database design. If your rows get so big that they hamper efficiency then it is probably time to split the table.
Now back to the problem I mentioned at the start of this answer. You haven't solved this. My suggestion would be to make two classes: One with solution 1, dealing with one row, and one with solution 2 dealing with multiple rows.
So both solutions have their place, it just that solution 2 is almost always inefficient for dealing with one row, and I haven't even talked about the amount of extra coding it requires.

When should I set my class properties in PHP - in constructor or as and when they are needed?

I am learning OOP PHP and I am struggling to know when to set my variables.
I've got a bunch of variables in my class which are used by some but not all functions. The code below shows how if a function needs a variable it
1) Checks if it is currently set
2) If it is not set, it runs whatever function sets that variable
Is this right, or should I set all variables using the __constructor function?
Thanks so much in advance - laura.
class Person {
private $user_id;
private $eye_color;
public function __construct ($user_id) {
$this->user_id = $user_id;
}
public function eyeColor () {
$this->eye_color = get_user_meta($this->user_id, '_eye_color', 'true');
}
public function describeEyes () {
// If Eye Color Is not set, set it
if (!isset($this->eye_color)) {
$this->eyeColor();
}
$eye_decription = 'The user has beautiful eyes, they are' . $this->eye_color;
return $eye_decription;
}
public function describeFace () {
// If Eye Color Is not set, set it
if (!isset($this->eye_color)) {
$this->eyeColor();
}
$face_decription = 'The user has a nice face and beautiful eyes, they are' . $this->eye_color;
return $face_decription;
}
}
I don't think there's a unique way of doing this and it depend on each case.
If the function loading the data (in your case eyeColor()) is expensive in terms of resources or time, then making it only run when required is a good choice. If it's something simple then you can run it in the constructor.
Also keep in mind that if you are going to use the eye_color may times, each time you need it you'll be testing a conditional clause to see if it's already loaded. So if you use this value too many times it could be better to load it in the constructor and save those conditional testings.
If you are sure you'll use it at least once better put it in the constructor.
In your case, I would argue that your Object Composition of the Person class is off. If "eye color" belongs to the user, then that's where it should remain, and it should only be exposed by the Person class, not duplicated across both classes. See the below code changes:
class Person {
private $user_id;
public function __construct($user_id)
{
$this->user_id = $user_id;
}
public function eyeColor()
{
return get_user_meta($this->user_id, '_eye_color', 'true');
}
public function describeEyes()
{
return 'The user has beautiful eyes, they are' . $this->eyeColor();
}
public function describeFace()
{
return 'The user has a nice face and beautiful eyes, they are' . $this->eyeColor();
}
}
A few things to note here:
We are only ever setting user_id via the constructor. This implies that a Person object cannot exist without having a user_id, which achieves the composition you are after.
We are exposing get_user_meta($this->user_id, '_eye_color', 'true') via the function eyeColor(), meaning that the eye color of a Person is actually just a reference to the user's eye color attribute.
We will never have a mismatch between a persons eye color and the underlying user's eye color, as we cannot change a person's eye color independently of the user's eye color.
I would argue further that you should, instead of passing in a user_id, pass in a User object, but this would depend on how OO your code is and how far you want to take it.

How best to initialise this class?

On my site at the beginning of every script I include a "bootstrap" script which queries a few things from the database, does some calculations and then loads the variables into constants that I define one by one.
Some examples are:
define("SITE_ID", $site_id); // $site_id is pulled from a field in the database
define("SITE_NAME", $site_name);
// pulled from a field in the same row as the above
define("STOCK_IDS", $stock_ids);
//computed array of stock id integers from a different query.
//I perform logic on the array after the query before putting it in the definition
define("ANALYTICS_ENABLED", false);
// this is something I define myself and isnt "pulled" from a database
Now, I have many functions on the site. One example function is get_stock_info. And it references the STOCK_IDS constant.
What I want to do is have a class which has the above constants in it and the get_stock_info function.
Would the best approach to be have an empty class "site", create an instance of it and then afterwards define the static variables above one by one? Or is that not a good way and should I move all of of my logic which pulls from the database and calculates SITE_ID, STOCK_IDS, ANALYTICS_ENABLED etc into the constructor instead?
Ultimately I want the class to contain all of the above info and then I would be able to use class methods such as site::get_stock_info() and those methods will have access to the constants via self:: or this.
There's a lot more I want to do than that but that would give me enough to figure the rest out.
I think this approach isn't the best. You should consider not using constants as your values aren't constant. For your case it is better to have a class with classic getters methods.
Something like this:
class SiteInfo
{
private $siteId;
private $siteName;
private $stockIds;
private $analyticsEnabled;
public function __construct()
{
// Results from the database
$results = $query->execute();
$this->siteId = $results['siteId'];
$this->siteName = $results['siteName'];
$this->stockIds = $results['stockIds'];
$this->analyticsEnabled = $results['analyticsEnabled'];
}
public function getSiteId()
{
return $this->siteId;
}
public function getSiteName()
{
return $this->siteName;
}
public function getStockIds()
{
return $this->stockIds;
}
public function isAnalyticsEnabled()
{
return $this->analyticsEnabled;
}
}

Is this the correct way to do this PHP class?

Hello I am just learning more about using classes in PHP. I know the code below is crap but I need help.
Can someone just let me know if I am going in the right direction.
My goal is to have this class included into a user profile page, when a new profile object is created, I would like for it to retrieve all the profile data from mysql, then I would like to be able to display any item on the page by just using something like this
$profile = New Profile;
echo $profile->user_name;
Here is my code so far, please tell me what is wrong so far or if I am going in the right direction?
Also instead of using echo $profile->user_name; for the 50+ profile mysql fileds, sometimes I need to do stuff with the data, for example the join date and birthdate have other code that must run to convert them, also if a record is empty then I would like to show an alternative value, so with that knowlege, should I be using methods? Like 50+ different methods?
<?PHP
//Profile.class.php file
class Profile
{
//set some profile variables
public $userid;
public $pic_url;
public $location_lat;
public $location_long;
public $user_name;
public $f_name;
public $l_name;
public $country;
public $usa_state;
public $other_state;
public $zip_code;
public $city;
public $gender;
public $birth_date;
public $date_create;
public $date_last_visit;
public $user_role;
public $photo_url;
public $user_status;
public $friend_count;
public $comment_count;
public $forum_post_count;
public $referral_count;
public $referral_count_total;
public $setting_public_profile;
public $setting_online;
public $profile_purpose;
public $profile_height;
public $profile_body_type;
public $profile_ethnicity;
public $profile_occupation;
public $profile_marital_status;
public $profile_sex_orientation;
public $profile_home_town;
public $profile_religion;
public $profile_smoker;
public $profile_drinker;
public $profile_kids;
public $profile_education;
public $profile_income;
public $profile_headline;
public $profile_about_me;
public $profile_like_to_meet;
public $profile_interest;
public $profile_music;
public $profile_television;
public $profile_books;
public $profile_heroes;
public $profile_here_for;
public $profile_counter;
function __construct($session)
{
// coming soon
}
//get profile data
function getProfile_info(){
$sql = "SELECT user_name,f_name,l_name,country,usa_state,other_state,zip_code,city,gender,birth_date,date_created,date_last_visit,
user_role,photo_url,user_status,friend_count,comment_count,forum_post_count,referral_count,referral_count_total,
setting_public_profile,setting_online,profile_purpose,profile_height,profile_body_type,profile_ethnicity,
profile_occupation,profile_marital_status,profile_sex_orientation,profile_home_town,profile_religion,
profile_smoker,profile_drinker,profile_kids,profile_education,profile_income,profile_headline,profile_about_me,
profile_like_to_meet,profile_interest,profile_music,profile_television,profile_books,profile_heroes,profile_here_for,profile_counter
FROM users WHERE user_id=$profileid AND user_role > 0";
$result_profile = Database::executequery($sql);
if ($profile = mysql_fetch_assoc($result_profile)) {
//result is found so set some variables
$this->user_name = $profile['user_name'];
$this->f_name = $profile['f_name'];
$this->l_name = $profile['l_name'];
$this->country = $profile['country'];
$this->usa_state = $profile['usa_state'];
$this->other_state = $profile['other_state'];
$this->zip_code = $profile['zip_code'];
$this->city = $profile['city'];
$this->gender = $profile['gender'];
$this->birth_date = $profile['birth_date'];
$this->date_created = $profile['date_created'];
$this->date_last_visit = $profile['date_last_visit'];
$this->user_role = $profile['user_role'];
$this->photo_url = $profile['photo_url'];
$this->user_status = $profile['user_status'];
$this->friend_count = $profile['friend_count'];
$this->comment_count = $profile['comment_count'];
$this->forum_post_count = $profile['forum_post_count'];
$this->referral_count = $profile['referral_count'];
$this->referral_count_total = $profile['referral_count_total'];
$this->setting_public_profile = $profile['setting_public_profile'];
$this->setting_online = $profile['setting_online'];
$this->profile_purpose = $profile['profile_purpose'];
$this->profile_height = $profile['profile_height'];
$this->profile_body_type = $profile['profile_body_type'];
$this->profile_ethnicity = $profile['profile_ethnicity'];
$this->profile_occupation = $profile['profile_occupation'];
$this->profile_marital_status = $profile['profile_marital_status'];
$this->profile_sex_orientation = $profile['profile_sex_orientation'];
$this->profile_home_town = $profile['profile_home_town'];
$this->profile_religion = $profile['profile_religion'];
$this->profile_smoker = $profile['profile_smoker'];
$this->profile_drinker = $profile['profile_drinker'];
$this->profile_kids = $profile['profile_kids'];
$this->profile_education = $profile['profile_education'];
$this->profile_income = $profile['profile_income'];
$this->profile_headline = $profile['profile_headline'];
$this->profile_about_me = $profile['profile_about_me'];
$this->profile_like_to_meet = $profile['profile_like_to_meet'];
$this->profile_interest = $profile['profile_interest'];
$this->profile_music = $profile['profile_music'];
$this->profile_television = $profile['profile_television'];
$this->profile_books = $profile['profile_books'];
$this->profile_heroes = $profile['profile_heroes'];
$this->profile_here_for = $profile['profile_here_for'];
$this->profile_counter = $profile['profile_counter'];
}
//this part is not done either...........
return $this->pic_url;
}
}
You might want to take a look at PHP's magic methods which allow you to create a small number of methods (typically "get" and "set" methods), which you can then use to return/set a large number of private/protected variables very easily. You could then have eg the following code (abstract but hopefully you'll get the idea):
class Profile
{
private $_profile;
// $_profile is set somewhere else, as per your original code
public function __get($name)
{
if (array_key_exists($name, $this->_profile)) {
return $this->_profile[$name];
}
}
public function __set($name, $value)
{
// you would normally do some sanity checking here too
// to make sure you're not just setting random variables
$this->_profile[$name] = $value;
}
}
As others have suggested as well, maybe looking into something like an ORM or similar (Doctrine, ActiveRecord etc) might be a worthwhile exercise, where all the above is done for you :-)
Edit: I should probably have mentioned how you'd access the properties after you implement the above (for completeness!)
$profile = new Profile;
// setting
$profile->user_name = "JoeBloggs";
// retrieving
echo $profile->user_name;
and these will use the magic methods defined above.
You should look into making some kind of class to abstract this all, so that your "Profile" could extend it, and all that functionality you've written would already be in place.
You might be interested in a readymade solution - these are called object relational mappers.
You should check out PHP ActiveRecord, which should easily allow you to do something like this without writing ORM code yourself.
Other similar libraries include Doctrine and Outlet.
Don't use a whole bunch of public variables. At worst, make it one variable, such as $profile. Then all the fields are $profile['body_type'] or whatever.
This looks like a Data Class to me, which Martin Fowler calls a code smell in his book Refactoring.
Data classes are like children. They are okay as a starting point, but to participate as a grownup object, they need to take some responsibility.
He points out that, as is the case here,
In the early stages these classes may have public fields. If so, you should immediately Encapsulate Field before anyone notices.
If you turn your many fields into one or several several associative arrays, then Fowler's advice is
check to see whether they are properly encapsulated and apply Encapsulate Collection if they aren't. Use Remove Setting Method on any field that should not be changed.
Later on, when you have your Profile class has been endowed with behaviors, and other classes (its clients) use those behaviors, it may make sense to move some of those behaviors (and any associated data) to the client classes using Move Method.
If you can't move a whole method, use Extract Method to create a method that can be moved. After a while, you can start using Hide Method on the getters and setters.
Normally, a class would be created to abstract the things you can do to an object (messages you can send). The way you created it is more like a dictionary: a one-to-one mapping of the PHP syntaxis to the database fields. There's not much added value in that: you insert one extra layer of indirection without having a clear benefit.
Rather, the class would have to contain what's called a 'state', containing e.g. an id field of a certain table, and some methods (some...) to e.g. "addressString()", "marriedTo()", ....
If you worry about performance, you can cache the fields of the table, which is a totally different concern and should be implemented by another class that can be aggregated (a Cache class or whatsoever).
The main OO violation I see in this design is the violation of the "Tell, don't ask" principle.

Categories