Jump to content
iwato

A Nested Class? Or, Inheritance?

Recommended Posts

BACKGROUND:  I am creating a custom class for use with PHPMailer that will hopefully eliminate redundancy in its application and even facilitate my use of the class.  One of my questions deals with inheritance and the other has to do with scope.

My current use of PHPMailer is quite simple.  It consists of a folder containing four files:  three of these are PHP class definitions and one of them is an autoloader.  PHPMailer is called in the following manner:

require_once '../../../PHPMailerAutoload.php';
$mail = new PHPMailer;

Once the instance $mail has been created I have access to a large number of public functions that I have thought to consolidate into a single class of my own making called PHPGlobalMailer.  My construction of this class appears awkward, and I would like to solicit your feedback in an attempt to remove its clumsiness.

class PHPGlobalMailer {
    private $mail;
    private static $char_set = 'UTF-8';
    private static $smtp_debug = 0;
        // SMTP::DEBUG_OFF (0): Disable debugging (default).
        // SMTP::DEBUG_CLIENT (1): Output messages sent by the client.
        // SMTP::DEBUG_SERVER (2): as 1, plus responses received from the server.
        // SMTP::DEBUG_CONNECTION (3): as 2, plus more information about the initial connection (used to diagnose STARTTLS failures.
        // SMTP::DEBUG_LOWLEVEL (4): as 3 (Verbose).
    private static $debug_output = 'html';
        // 'echo': Plain text output (default).
        // 'html': Browser output.
        // 'error_log': Output to php.ini configured error.log.
        // function($str, $level) {echo "Debug Level: " . $level; "Error Message: " .  $str;}

    private static $hostserver = '...';
    private static $server_port = ...;

    private static $smpt_auth = 'true';
    private static $mail_account_name = '...';
    private static $mail_account_password = '...';

    private static $from_mail_address = '...';
    private static $from_mail_name = '...';
    private static $reply_to_address = '...';
    private static $reply_to_name = '...';

    public $email = '';
    public $name = '';
    public $subject = '';
    public $html_message = '';
    public $alt_message = '';

    public function __construct($email, $name, $subject, $html_message, $alt_message) {
        if ($this->mail new PHPMailer) {
            $this->mail->CharSet=self::$char_set;
            $this->mail->isSMPT();
            $this->mail->SMTPDebug = self::$smpt_debug;
            $this->mail->Debugoutput = self::$debug_output;
            $this->mail->Host = self::$hostserver;
            $this->mail->Port = self::$server_port;
            $this->mail->SMPTAuth = self::$smpt_auth;
            $this->mail->Username = self::$mail_account_name;
            $this->mail->Password = self::$mail_account_password;
            $this->mail->setFrom(self::$from_mail_address, self::$from_mail_name);
            $this->mail->addReplyTo(self::$reply_to_address, self::$reply_to_name);
            $this->mail->addAddress($this->email, $this->name);
            $this->mail->Subject = $this->subject;
            $this->mail->msgHTML($html_message);
            $this->mail->altBody = $alt_message;
        } else {
            echo "Error: Failure to create new PHPMailer instance.";
        }
    }
    public function get_character_set() {
        return self::$char_set;
    }
    public function set_character_set($char_set) {
        self::$char_set = $char_set;
    }
    public function get_smtp_debug() {
        return self::$smtp_debug;
    }
    public function set_password($smtp_debug) {
        self::$smtp_debug = $smtp_debug;
    }
    public function get_debug_format() {
        return self::$debug_output;
    }
    public function set_debug_format($debug_output) {
        self::$debug_output = $debug_output;
    }
    public function get_debug_format() {
        return self::$debug_output;
    }
    public function set_debug_format($debug_output) {
        self::$debug_output = $debug_output;
    }
    public function get_mail_instance() {
        return $this->mail;
    }
}

The AWKWARDNESS

1)  In order to make it work I must include PHPMailerAutoload.php file.  Is there someway that I could avoid this, say through inheritance?

2)  Notice that I am creating an instance of PHPMailer inside my class and then using the instance's public functions to modify it.  Is this proper?  If not, what would be a more appropriate way of constructing my class.

3)  I am concerned that adjustments to an instance of PHPGlobalMailer  will affect other instances created in a similar manner.  For example, were I to change the value of $smpt_debug for a particular instance of PHPGlobalMailer would it not effect the value of that same parameter for other instances of PHPGlobalMailer?  In effect, I would like full control of each instance, but still maintain constancy with regard to the $account_name and $account_password and other values across all instances.

Could you please comment on each of three points.

Roddy

 

 

 

 

 

Edited by iwato

Share this post


Link to post
Share on other sites
Quote

1)  In order to make it work I must include PHPMailerAutoload.php file.  Is there someway that I could avoid this, say through inheritance?

You always need to include a class you're going to use, whether you use autoloading for that or not.  You can't use a class that hasn't been defined and included.  Inheritance has nothing to do with that, if you extend another class then you need to have that class defined before you even define your class.  If your class depends on another class then make sure the class is defined in your own class definition file.  Then you only need to include your class, which will include the other class itself.

Quote

2)  Notice that I am creating an instance of PHPMailer inside my class and then using the instance's public functions to modify it.  Is this proper?

I suppose, but I don't see your class adding anything of value which PHPMailer does not already provide.  You're just providing a more verbose way to do the same things.  You're also using self incorrectly, it should be $this.

Quote

In effect, I would like full control of each instance

You already have that.  You don't have to invent anything new in order to be able to control every instance of every class you make.  That problem has already been solved.

Quote

but still maintain constancy with regard to the $account_name and $account_password and other values across all instances.

If you want to make them constants, you can make them constants.  Otherwise, just set the same value when you create each instance and don't change them.

This is normally not what constants are used for, because if you want to change the constant you have to edit the class file and that kind of defeats the point of classes.  Constants are used for things that a class provides for which the value itself isn't important, like the various debug settings.  Actual data that the class is using, like usernames and passwords, should be set like any other property.  If you don't want them to change, then just don't change them.  They won't automatically change if nothing tells them to change.

 

You may be thinking of the factory pattern, where you have a static class which creates another class, and when you do that you can set whatever values you want to set for every instance of that class before returning the new instance.

Share this post


Link to post
Share on other sites

JSG:

Quote

If your class depends on another class then make sure the class is defined in your own class definition file.  Then you only need to include your class, which will include the other class itself.

Roddy:  So, why not use inheritance and then include both classes outside of the child class?

JSG: 

Quote

I suppose, but I don't see your class adding anything of value which PHPMailer does not already provide.  You're just providing a more verbose way to do the same things.

Roddy:  The idea is to consolidate code so that I only have to copy and paste a single line rather than so many.  Copying code sometimes leads to unwanted omissions or additions that lead to errors that often take forever to discover.  Also, when I go to change my password I only want to have to do it once, and not within third party software that is subject to updates.  Does this not make sense?

JSG:

Quote

You're also using self incorrectly, it should be $this.

Roddy:  it is my understand that self is used to call the values of statically defined variables.  Have a I misunderstood?

JSG:

Quote

You may be thinking of the factory pattern, where you have a static class which creates another class, and when you do that you can set whatever values you want to set for every instance of that class before returning the new instance.

 

Having better understood the problem do you still think that I should explore the factory pattern.  Certainly I recall reading about it already back in 2010 when I was still in Thailand.

Roddy

Share this post


Link to post
Share on other sites

Is JSG sick?  Is he still with W3Schools?

He appears to have disappeared.

Roddy

Share this post


Link to post
Share on other sites

Roddy:  So, why not use inheritance and then include both classes outside of the child class?

When you include the class isn't really the issue, as long as any time you try to create a new object, that class is defined.  Inheritance has nothing to do with the process of including or defining a class, other than the fact that you cannot extend a class that is not defined.

Does this not make sense?

Of course it does.  Most applications have a single global include file, which defines whatever you might need on any page, which includes including any class definitions you might use and instantiating objects that are generally single-purpose and which all pages are likely to use, like the database object or a session or user object.  If I'm writing a new page for my application then I include a single file at the top, and I have everything in the application available to use.

That being said, you don't want to simplify something so much that it becomes inflexible.  All of those lines that you might need to write to use a certain object are there because you don't always need to do the same thing.  If you do, then sure, write a helper class for it.

it is my understand that self is used to call the values of statically defined variables.  Have a I misunderstood?

No, I didn't notice that you had some static properties and others not.  Although it feels a little weird to use a programmatic technique, like static properties, to signify that these are things that you aren't going to change in code, only by editing the file.  I would prefer class constants instead.

Having better understood the problem do you still think that I should explore the factory pattern.

If you don't know what it is, you should at least understand it so that if it makes sense to use it, you'll know it's an option.

Share this post


Link to post
Share on other sites

Thank you for responding.  I hope that you are feeling well and that everything is still good at W3Schools.

Please consider the following simplified piece of code and respond to the question that follows.  It is a heuristic test case, if you will.

PHP Class Inclusion

require_once('./_utilities/php/PHPMailerAutoload.php');
require_once('./_utilities/php/class.globalmailer.php');

PHPGlobalMailer CLASS

<?php
    class PHPGlobalMailer extends PHPMailer {
	
		private $mail;

		const hostserver = "vps.antistate.io";
		const smtp_port = 587;
		
		private static $char_set = 'UTF-8'; 
		
		public function __construct($email, $username) {
			$this->mail = new PHPMailer();
		}
		
		$this->mail->CharSet=self::$char_set;
		
	}
?>

SAMPLE APPLICATION

$email = 'anonymous@antistate.io';
$username = 'Anonymous';
$user_mail = new PHPGlobalMailer($email, $username);

Please answer the following questions:

BACKGROUND:  In the absence of the PHPGlobalMailer helping class, the character set of an instance of the PHPMailer class would be set by the following several lines of code:

require_once('./_utilities/php/PHPMailerAutoload.php');
$mail = new PHPMailer();
$mail->CharSet = 'ISO-8859-1';

QUESTION ONE:  Since the class PHPGlobalMailer has inherited the class PHPMailer is it even necessary to instantiate an instance of PHPMailer within the PHPGlobalMailer class in order to get an instance of PHPGlobalMailer to function, as if it were an instance of PHPMailer?

QUESTION TWO:  Using the PHPGlobalMailer class with or without the instantiation of a PHPMailer object would the following piece of code change the character set for the instance $user_mail?  If so, would it change the character set only for the instance of $user_mail and no other?

$user_mail->CharSet = 'ISO-8859-1';

It is my sincere hope that the above two questions are a clear expression of my confusion.

Roddy

 

 

 

Share this post


Link to post
Share on other sites

To answer your first question, there is no need to instantiate PHPMailer inside your class. PHPGlobalMailer is a PHPMailer, that is the meaning of inheritance.

To answer your second question, the character set only applies to the instance $user_mail and not to any other mailer instance. That line will also override the value provided in the constructor.

Your PHPGlobalMailer class, as shown above, does not do anything different from PHPMailer, you need to make a few fixes.

  • The line $this->mail->CharSet=self::$char_set; must be inside the constructor. You cannot have loose lines of code inside a class.
  • The above line of code should probably be changed to $this->CharSet=self::$char_set; since you don't need the $mail property.
  • If the $char_set property is not being used anywhere else except the constructor then you should just delete it altogether and putting the literal string in the constructor so that it looks like this: $this->CharSet = 'UTF-8';
  • You're not doing anything with the ($email, $username) arguments of the constructor. You should write some code that uses them.
  • The constructor should call the constructor of the parent class as in the following example:
public function __construct() {
  parent::__construct();
  //
  // ... The rest of your code here ...
  // 
}

Share this post


Link to post
Share on other sites

In the absence of the PHPGlobalMailer helping class, the character set of an instance of the PHPMailer class would be set by the following several lines of code:

Well, it's 1 extra line of code.  In both cases you need to define the class and create the object.  And it's fine to do what you want, assuming that you're only ever going to use that to send UTF-8 emails.  That's what I meant about simplifying something until it becomes inflexible.  I don't see a technical reason to make the character set anything other than a regular private property with getter/setter methods, and a default value of UTF-8, which you can change if you want to otherwise it will be UTF-8.  If you want to make a helper class just to set default values that's fine, but I wouldn't remove functionality by making things static or constants when you might have a situation where you need to change one.  Ideally, you want to change the class definition as little as possible.  If you're changing the definition often then you've made it too restrictive.

This is a case for the factory pattern where you don't have to change the properties at all, ever.  Put things like the SMTP server in a config file, and use a factory class to create a new class with the defined config settings.  If you need to change anything that the application uses, like database or email settings, you only have to change the config file, not the class definitions.

Since the class PHPGlobalMailer has inherited the class PHPMailer i s it even necessary to instantiate an instance of PHPMailer within the PHPGlobalMailer class in order to get an instance of PHPGlobalMailer to function

No, in fact it is redundant to instantiate an instance of the parent class and isn't advised.  All of the properties and methods of the parent class are available in the child class.  But if you're going to extend another class, be careful about clobbering existing property or method names.  If you only extend a class to set your own defaults for various properties, that's fine too.  You don't need to implement any methods.

I hope that you are feeling well and that everything is still good at W3Schools.

Everything is fine, I was just on a short vacation.

Share this post


Link to post
Share on other sites

COMMENT:  Based upon the above discussion, my desire to accommodate my site's own specific needs with a minimum of additional study  I have come up with the following tentative solution.  As my site will send mail for a variety of purposes from a variety of email accounts -- some of which are repeatedly used, some of which are not,  I have decided to create a different class for each purpose.  The underlying logic is that it is easier to copy classes than it is to write a multi-purpose class that satisfies all of my PHPMailer needs.

PURPORTED SATISFIED OBJECTIVES -- CONVENIENCE

  1. If ever there is a change of server, I can enter into the class and change the values of two constants knowing well that the changes will be passed along to all instances of PHPMailer.
  2. I can use the same class structure to create new classes for new default email accounts by simply changing the name of the class and the default values of the corresponding email account name and password.
  3. I can use the same class for a variety of different messages from the same email account and for a large number of different users with different email addresses.
  4. I can change the password at any time for all instances of the class no matter where they are employed within my site.
  5. I have full access to the content of any single instance where access would likely be advantageous to have including changing the subject heading of the mail or the content of the message.
  6. With three simple lines of code I can employ PHPMailer in nearly any place that I desire.
<?php
    class PHPLetterMailer extends PHPMailer {
	
		const hostserver = "vps.antistate.io";
		const smtp_port = 587;
		
		private $charset = 'UTF-8';
		
		private $smpt_debug = 0;
		private $smpt_output = 'html';
		private $smpt_auth = 'true';

		private $email_account = '...';
		private $password = '...';
		private $sender_addr = 'newsletter@grammarcaptive.com';
		private $sender_name = 'Grammar Captive';
		private $replyto_addr = 'admin@grammarcaptive.com';
		private $replyto_name = 'Grammar Captive Administration';
		
		public $this->subject = '';
		public $html_message = '';
		public $alt_message = '';
		
		public function __construct($username, $email) {
			parent::__construct()

			$this->addAddress($email, $username);

			$this->Host = self::hostserver;
			$this->Port = self::smpt_port;

			$this->isSMTP();

			$this->SMTPDebug = $this->smpt_debug;
			$this->Debugoutput = $this->smpt_output;
			$this->SMTPAuth = $this->smpt_auth;

			$this->Username = $this->email_account;
			$this->Password = $this->password;
			$this->CharSet = $this->charset;
			
			$this->setFrom($this->sender_addr, $this->sender_name);		
			$this->addReplyTo($this->replyto_addr, $this->replyto_name);

			$this->Subject = $this->subject;
			$this->msgHTML($this->html_message);
			$this->AltBody = $this->alt_message;
		}
		
		public function set_letter_contents($subject, $html_message, $alt_message) {
			$this->Subject = $subject;
			$this->msgHTML($html_message);
			$this->AltBody = $alt_message;
		}
		
		public function get_letter_contents() {
			return array($this->$subject, $this->html_message, $this->alt_message);
		}
		
		public function set_mail_account($mail_account, $password) {
			$this->mail_account = $mail_account;
			$this->password = $password;
		}
		
		public function set_sender($sender_addr, $sender_name) {
			$this->sender_addr = $sender_addr;	
			$this->sender_name = $sender_name;	
		)
		
		public function get_sender() {
			return array($this->sender_addr, $this->sender_name); 
		}
		
		public function set_replyto($replyto_addr, $replyto_name) {
			$this->replyto_addr = $replyto_addr;
			$this->replyto_name = $replyto_name;
		}
		
		public function get_replyto() {
			return array($this->replyto_addr, $this->replyto_name); 
		}		
				
		public function set_charset($charset) {
			$this->charset = $charset;
		}
		
		public function get_charset() {
			return $this->charset;
		}
		
		public function set_smpt_debug($debug, $output, $auth) {
			$this->smpt_debug = $debug;
			$this->smpt_output = $output;
			$this->smpt_auth = $auth;
		}
		
		public function get_smpt_debug() {
			return array ($this->smpt_debug, $this->smpt_output, $this->smpt_auth);
		}
	}
?>

SAMPLE EXECUTION

require_once('./_utilities/php/PHPMailerAutoload.php');
require_once('./_utilities/php/class.phplettermailer.php');
$email = 'name@sample.addr';
$username = 'sample_name';
$subject = 'Effortless Deployment';
$html_content = '<!DOCTYPE html><html> ... <table><tr><td>Content Here</td></tr></table ... </html>';
$alternate_message = 'Content Here';
$anInstance = new PHPLetterMailer($username, $email);
$anInstance->set_letter_content($subject, $html_content, $alternate_message);
if (!$anInstance->send()) {
    echo "PHPLetterMailer Error: " . $anInstance->ErrorInfo;
} else {
    header('Location: ./gate_confirmation.html');
    echo "Message sent!";
}

QUESTION:  Have I achieved my purported objectives?

Share this post


Link to post
Share on other sites

If ever there is a change of server, I can enter into the class and change the values of two constants knowing well that the changes will be passed along to all instances of PHPMailer.

There's a way to do that while only maintaining a single class.

I can use the same class structure to create new classes for new default email accounts by simply changing the name of the class and the default values of the corresponding email account name and password.

There's a way to do that while only maintaining a single class.

I can use the same class for a variety of different messages from the same email account and for a large number of different users with different email addresses.

Reusability is a goal of any class, it might even be the most important goal.

I can change the password at any time for all instances of the class no matter where they are employed within my site.

There's a way to do that while only maintaining a single class.

I have full access to the content of any single instance where access would likely be advantageous to have including changing the subject heading of the mail or the content of the message.

I can't think of a practical class design where you wouldn't have this ability.

With three simple lines of code I can employ PHPMailer in nearly any place that I desire.

Lines of code are pretty arbitrary as a metric.  The best code is code that is easy to understand, not necessarily code that is short.

Have I achieved my purported objectives?

That's obviously up to you to determine, you know how I feel about copying and pasting classes and changing the properties.  That's an obvious violation of the reusability goal.

Consider these usage scenarios:

require_once 'include/global.init.php';

$newsletter = PHPMailerFactory::create(
  $config::NEWSLETTER_HOST, 
  $config::NEWSLETTER_USER, 
  $config::NEWSLETTER_PASSWORD, 
  $to, 
  $from
);
$newsletter->setSubject($newsletterSubject);
$newsletter->setBody($newsletterBody);
$newsletter->send();

$verifyEmail = PHPMailerFactory::create(
  $config::SITE_HOST, 
  $config::SITE_USER, 
  $config::SITE_PASSWORD, 
  $to, 
  $from
);
$verifyEmail->setSubject($verifyEmailSubject);
$verifyEmail->setBody($verifyEmailBody);
$verifyEmail->send();

$forgotPassword = PHPMailerFactory::create(
  $config::SITE_HOST, 
  $config::SITE_USER, 
  $config::SITE_PASSWORD, 
  $to, 
  $from
);
$forgotPassword->setSubject($forgotPasswordSubject);
$forgotPassword->setBody($forgotPasswordBody);
$forgotPassword->send();

Consider also the need to make changes.  If you want to add a new feature, or if you decide that the Simple Mail Transport Protocol is SMTP and not SMPT, in how many places do you want to make that addition or correction?  When you can do what you're trying to do with a single class, what exactly are you gaining by duplicating your classes?  If you're not gaining anything, then why are you doing it?

If you really want to go ahead with what you plan, you're not doing it the right way.  You should probably create an abstract class which has everything that is common to every other class that you only need to implement in one place, with placeholder methods for the things that will differ.  Each of your other classes would implement the abstract class.  You could also create a single parent class that every other class will extend, again keeping everything that doesn't change in the same place.  If you find yourself copying and pasting the exact same chunks of code around, you're doing something wrong.  The goals behind object-oriented programming are encapsulation, reusability, and extensibility.  Those are what you should have in mind, not the minimum number of lines to do some arbitrary task.

Share this post


Link to post
Share on other sites

Yes,  I have reviewed your most recent post and can understand well the gist of what you are proposing.  I am at a loss, however, when it comes to your first line of code -- namely,

require_once 'include/global.init.php';

What is the purpose of this statement?

Roddy

Share this post


Link to post
Share on other sites

You understand what include, require, include_once, and require_once do, right?  That is just including the main include file for my application.  That file includes all other files which are necessary to run the application and defines any classes and data structures that the application expects to be defined.  We include that file on any new page and then have access to the entire application.  It's the only file we ever need to include to get all functionality that we need.

Share this post


Link to post
Share on other sites

This is basically what the global.init.php file for our application looks like:

<?php

define('LMS_DEBUG', ('development' == '##DEV##'));
define('LMS_VERSION', '9.17.0');

error_reporting(E_ALL ^ E_STRICT);
ini_set('error_log', __DIR__ . '/error.log');

require_once 'FirePHPCore/fb.php';

$php_command_line = strpos(php_sapi_name(), 'apache') === false && (strpos(php_sapi_name(), 'cgi') !== false || strpos(php_sapi_name(), 'cli') !== false);

/** @var array $config */
require_once 'global.config.php';

// check for SSL and update http_root if necessary
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
    $config['http_root'] = str_replace('http://', 'https://', $config['http_root']);
}
$config['http_root'] = rtrim($config['http_root'], '/');

// If pw_version is not set, use default of 3
if (!isset($config['pw_version'])) {
    $config['pw_version'] = 3;
}

date_default_timezone_set($config['timezone']);
if (!$php_command_line) {
    session_name('lms8');
    $cookie_info = session_get_cookie_params();
    session_set_cookie_params($cookie_info['lifetime'], '/', $config['cookie_domain'], $cookie_info['secure'], $cookie_info['httponly']);
}

require_once 'functions.global.php';

require_once 'class.db.php';
$db = new tc_lms_database($config['db_host'], $config['db_user'], $config['db_pass'], $config['db_name']);

require_once 'class.pdo.php';
$pdo = new tc_lms_pdo($config['db_host'], $config['db_user'], $config['db_pass'], $config['db_name']);

require_once 'factory.passwordhash.php';

require_once 'class.lang.php';
$lang = new tc_lms_lang();

if (!$php_command_line) {
    require_once 'class.session.php';
    $sess = new tc_lms_session();
} else {
    $sess = null;
}

require_once 'class.userfield.php';
require_once 'class.userfields.php';
$uf = new tc_lms_userfields();

require_once 'class.config.php';
$opts = new tc_lms_config();

require_once 'class.datacache.php';
$cache = new tc_lms_datacache($config['cache_timeout']);

require_once 'class.lms.global.php';
$global = new tc_lms_global($db, $pdo, $lang, $sess, $uf, $opts, $cache);
if (!is_null($sess)) {
    require_once 'factory.userpermissions.php';
    $perms = tc_lms_permissionsFactory::build($global, $sess);
    $global->setPerms($perms);
}

require_once 'class.certification.php';

$client_id     = $opts->get_opt('vimeo_client_id', 'd9efe06e6da5d0632d45223eff');
$client_secret = $opts->get_opt('vimeo_client_secret', 'jXvr4piScy0AZrlwYvCPlfgWysgyQxwuUS1j92UKeRz+JqreWHontAZpRXi/5JECvF9PZqilpVhlkaFSsE');
require_once 'vimeo-1.2.6/autoload.php';
$vimeo = new \Vimeo\Vimeo($client_id, $client_secret);
if ($access_token = $opts->get_opt('vimeo_access_token')) {
    $vimeo->setToken($access_token);
}

/**
 * Note: In order to overwrite a function, it must be wrapped in a function_exists statement.
 */
$file_list = [
    'functions.usergroups.php',
    'functions.images.php',
    'functions.content.php',
    'functions.learning_tracks.php',
    'functions.emails.php',
    'functions.dates.php',
    'functions.change.logs.php',
];
foreach ($file_list as $filename) {
    $custom_path = sprintf('%s/%s/%s/include/', dirname(__DIR__), getenv('LMS_ENV'), getenv('LMS_USER'));
    if (is_readable($custom_path . $filename)) {
        require_once $custom_path . $filename;
    } else {
        require_once $filename;
    }
}
require_once 'Json.php';

// Set Html Purifier @link http://htmlpurifier.org/docs
require_once __DIR__ . '/htmlpurifier-4.8.0/library/HTMLPurifier.auto.php';
$html_purifier_config = HTMLPurifier_Config::createDefault();
$html_purifier_def = $html_purifier_config->getHTMLDefinition(true);  // Turn off caching.
$html_purifier_def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');  // White list target attribute for links.
$html_purifier = new HTMLPurifier($html_purifier_config);

 

Share this post


Link to post
Share on other sites

OK.   In the following block of code, for example, I assume that create() is a static function defined in a class called PHPMailerFactory that inherits the class PHPMailer from a file that has been included by your global.init.php file. Also, I can understand that your create() function takes a series of arguments that are used to satisfy the specifications of an instance of PHPMailerFactor aka PHPMailer and the eventual sending of an email.  What I do not understand is how the values of those arguments are assigned.

$newsletter = PHPMailerFactory::create(
  $config::NEWSLETTER_HOST, 
  $config::NEWSLETTER_USER, 
  $config::NEWSLETTER_PASSWORD, 
  $to, 
  $from
);

To what, for example, does $config::NEWSLETTER_HOST refer?  Is it a constant predefined in PHPMailerFactory that you are accessing by a variable called $config to which you assigned the class PHPMailerFactor something on the order of

$config = 'PHPMailerFactory';

where

class PHPMailerFactory {
	...
	const NEWSLETTER_HOST = 'some_hostname.com';
	const NEWSLETTER_USER = 'some_accountname';
	const NEWSLETTER_PSWD = 'some_password';
	...

	create( ..., ..., ...) {...}
}

Or, does $config refer to some sort of configuration class in which the values of some_hostname.com, some_accountname, and some_password are stored on the order of

class ConfigureThisHost {
    const NEWSLETTER_HOST = 'some_hostname.com';
	const NEWSLETTER_USER = 'some_accountname';
	const NEWSLETTER_PSWD = 'some_password';
	
}

$config = 'ConfigureThisHost';
$config::NEWSLETTER_HOST;

Please clarify and explain why you have done what you are suggesting.

Roddy

Share this post


Link to post
Share on other sites

To what, for example, does $config::NEWSLETTER_HOST refer?

It would be an example of a mail server host.

Is it a constant predefined in PHPMailerFactory

It is a constant in an object called config.

Or, does $config refer to some sort of configuration class

That's right, some data structure which holds any value that may be considered application configuration data.  If you move your application to a new environment, you change the config and nothing else.  You don't have to rewrite any classes.  Maybe it's stored in an array, or a text file, or something else that the application reads configuration data from and creates the data structure that you can access from anywhere in the application.  Even better would be a static class with constants so you don't even need to define an instance of it, it's always in scope.

Share this post


Link to post
Share on other sites

So, if one places all of the non-volatile data in a PHP configuration class file what are the best ways to include the information contained within into the PHPMailerFactory class?

Having to include all of the non-volatile information in the factory class via a constructor function is awkward at best.

As a general practice can/does one extend to the factory class the configuration class?  Say,

class PHPMailerFactory extend PHPMailer PHPConfig {
	...
}

Roddy

Share this post


Link to post
Share on other sites

Well, what do you mean by "non-volatile."  If that is data that will never ever change, even across servers, which is actually non-volatile, then you can just make it a property in the class.  Otherwise, it's volatile, and the common way for reusability and extensibility is to pass it as parameters, awkward or not.

If you want other methods, then you could hard-code calls to your config class inside the other classes, but now you've introduced a requirement that your new class relies on another class being defined and implemented.  Maybe that's a fine tradeoff though.  So, inside the constructor of one class, you could make calls to something like a static config class, e.g. config::get_value('smtp_host').  But, like I said, that's going to make things less reusable.  The better way is to pass those values as parameters.

There are other ways though...

Quote

As a general practice can/does one extend to the factory class the configuration class?

PHP is a single-inheritance language, but you can use something like a trait.

http://php.net/manual/en/language.oop5.traits.php

You could make a ConfigTrait trait, and your ConfigTrait would read your config values from wherever they're stored and set up a data structure.  Then, you could make a config class which uses the config trait, and you can use that config class to access config settings in any arbitrary piece of code.  Maybe your ConfigTrait has a method called getConfigValue, so when you make a config class that uses the ConfigTrait, the config class would also be able to use the same getConfigValue method. 

If you then make your mailer, or any other, class use the same ConfigTrait, then those classes will also have a getConfigValue method that you can use to get the config values inside your constructor or any other method.  Traits are how PHP deals with being a single-inheritance language.  A class can use multiple traits.

In our application, for example, we have a userAwareTrait.  For any class which needs to access the logged-in user, they just use the userAwareTrait and then they can use something like $this->getUser() to access the logged-in user.  Those classes don't need any other code other than the line to use the userAwareTrait, and they have all of the properties and methods which that trait defines.

Just be aware of naming conflicts.  If you have a config trait, call your method getConfigValue, not just getValue.

  • Thanks 1

Share this post


Link to post
Share on other sites

I followed up on your link and discovered something that was invented after I left Thailand.  I believe this is the path that I will take.

I do have one remaining question, though.  Is the absence of parentheses in the following sample code a misprint.  If not, when are the parentheses required and not required?

<?php
trait PropertiesTrait {
    public $x = 1;
}

class PropertiesExample {
    use PropertiesTrait;
}

$example = new PropertiesExample;
$example->x;
?>

The Suspicious Line of Code

$example = new PropertiesExample;

Roddy

Share this post


Link to post
Share on other sites

I don't know what the difference is, maybe leaving out the parens causes the constructor not to be executed.  That should be easy enough to test though.

Share this post


Link to post
Share on other sites

Actually, I cannot test it -- well, at least not on my own machine.  Traits were introduced with PHP 5.4, and I am currently running with PHP 5.3.6.  The test will have to wait until I upload my completed files to my host server.

Thanks, anyway.  I thought you might know.

Roddy

Share this post


Link to post
Share on other sites

I don't think that creating a new object without parentheses requires a trait, I didn't see that mentioned in the manual.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×