为啥要介绍CI的loader?
在一次使用群发邮件的功能时,引用了第三方的发送邮件的类,phpmailer,在使用时出现收件人和发件人在不断增加,然后就去了解了一下CI的loader源码
使用phpmailer的方法
/**
* 发送邮件
* @param $to
* @param string $name
* @param $subject
* @param $content
* @param bool $bcc
* @param array $ccto
* @param string $reply_to
* @return bool
*/
function sendEmail($to, $name = '', $subject, $content, $bcc = FALSE, $ccto = array(), $reply_to='')
{
$CI = &get_instance();
$CI->load->add_package_path(APPPATH . 'third_party/phpmailer/');
$CI->load->library('phpmailer');
$CI->load->config('mail');
$CI->phpmailer->CharSet = $CI->config->item('charset'); // sets charset
$CI->phpmailer->AddReplyTo($reply_to, 'Synbio Technologies');
$CI->phpmailer->IsSMTP(); // telling the class to use SMTP
$CI->phpmailer->SMTPDebug = $CI->config->item('smtp_debug'); // enables SMTP debug information (for testing)
$CI->phpmailer->SMTPAuth = $CI->config->item('smtp_auth'); // enable SMTP authentication
$CI->phpmailer->SMTPSecure = $CI->config->item('smtp_secure'); // sets the prefix to the servier
$CI->phpmailer->Host = $CI->config->item('smtp_host'); // sets GMAIL as the SMTP server
$CI->phpmailer->Port = $CI->config->item('smtp_port'); // set the SMTP port for the GMAIL server
$CI->phpmailer->Username = $CI->config->item('smtp_username'); // GMAIL username
$CI->phpmailer->Password = $CI->config->item('smtp_password'); // GMAIL password
$CI->phpmailer->SetFrom($CI->config->item('smtp_username'), 'Synbio Technologies');
$CI->phpmailer->Subject = $subject;
$CI->phpmailer->MsgHTML($content);
if($ccto)
{
if ($bcc)
{
if(is_array($ccto)){
foreach ($ccto as $value){
$CI->phpmailer->AddBCC($value, $value);
}
}else{
$CI->phpmailer->AddBCC($ccto, $ccto);
}
}
else
{
if(is_array($ccto)){
foreach ($ccto as $value){
$CI->phpmailer->AddCC($value, $value);
}
}else{
$CI->phpmailer->AddCC($ccto, $ccto);
}
}
}
if ($bcc && empty($ccto)) {
if(is_array($to)){
foreach ($to as $key => $value){
$CI->phpmailer->AddBCC($value, (@$name[$key] ? @$name[$key] : $value));
}
}else{
$CI->phpmailer->AddBCC($to, ($name ? $name : $to));
}
} else {
if(is_array($to)){
foreach ($to as $key => $value){
$CI->phpmailer->AddAddress($value, (@$name[$key] ? @$name[$key] : $value));
}
}else{
$CI->phpmailer->AddAddress($to, ($name ? $name : $to));
}
}
// attachment
if(count($attachment) > 0)
{
foreach($attachment as $value)
{
$CI->phpmailer->AddAttachment($value);
}
}
if (!$CI->phpmailer->Send()) {
$CI->phpmailer->ClearAddresses();
$CI->phpmailer->ClearCCs();
return FALSE;
} else {
$CI->phpmailer->ClearAddresses();
$CI->phpmailer->ClearCCs();
return TRUE;
}
}
注意方法结尾部分的代码
$CI->phpmailer->ClearAddresses();
$CI->phpmailer->ClearCCs();
原来是没有这两行代码的,后面我在介绍这两行代码
了解对象在函数中的方式,是传值还是按引用?
这里我就不过多解释了,是引用方式
这里就解释了为什么上面代码群发邮件时会导致收件人和抄送人会增加的原因
$CI->phpmailer->AddAddress($to, ($name ? $name : $to));
$CI->phpmailer->AddCC($ccto, $ccto);
如何解决这个问题
首先我想到的是能不能再使用完phpmailer对象是,释放掉这个对象unset(CI->phpmailer),CI是单例模式,在加载完使用的类之后,使用属性的方式将加载的类作为CI的一个属性,在释放$CI->phpmailer这个属性之后,我就发现多次调用上面发送sendEmail的方法时会报错:
ERROR - 2020-01-07 02:08:45 --> Severity: Warning --> Creating default object from empty value
ERROR - 2020-01-07 02:08:45 --> Severity: error --> Exception: Call to undefined method stdClass::AddReplyTo()
这是上面原因呢?
这就讲到CI的loader类了,CI的loader在加载时使用的是include_once所以在加载一次之后不会重复加载,所以就不能讲phpmailer作为CI的属性再次添加到CI的超级对象中,所以就报错,我们来看下源码:
$CI = &get_instance();
$CI->load->add_package_path(APPPATH . 'third_party/phpmailer/');
$CI->load->library('phpmailer');
$CI->load->config('mail');
这是我上述的方法,看下CI的loader源码:
protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
{
// Get the class name, and while we're at it trim any slashes.
// The directory path can be included as part of the class name,
// but we don't want a leading slash
$class = str_replace('.php', '', trim($class, '/'));
// Was the path included with the class name?
// We look for a slash to determine this
if (($last_slash = strrpos($class, '/')) !== FALSE)
{
// Extract the path
$subdir = substr($class, 0, ++$last_slash);
// Get the filename from the path
$class = substr($class, $last_slash);
}
else
{
$subdir = '';
}
$class = ucfirst($class);
// Is this a stock library? There are a few special conditions if so ...
if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))
{
return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
}
// Let's search for the requested library file and load it.
foreach ($this->_ci_library_paths as $path)
{
// BASEPATH has already been checked for
if ($path === BASEPATH)
{
continue;
}
$filepath = $path.'libraries/'.$subdir.$class.'.php';
// Safety: Was the class already loaded by a previous call?
if (class_exists($class, FALSE))
{
// Before we deem this to be a duplicate request, let's see
// if a custom object name is being supplied. If so, we'll
// return a new instance of the object
if ($object_name !== NULL)
{
$CI =& get_instance();
if ( ! isset($CI->$object_name))
{
return $this->_ci_init_library($class, '', $params, $object_name);
}
}
log_message('debug', $class.' class already loaded. Second attempt ignored.');
return;
}
// Does the file exist? No? Bummer...
elseif ( ! file_exists($filepath))
{
continue;
}
include_once($filepath);
return $this->_ci_init_library($class, '', $params, $object_name);
}
// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
if ($subdir === '')
{
return $this->_ci_load_library($class.'/'.$class, $params, $object_name);
}
// If we got this far we were unable to find the requested class.
log_message('error', 'Unable to load the requested class: '.$class);
show_error('Unable to load the requested class: '.$class);
}
代码中明确指定了include_once一次,在PHP程序中代码都是依次执行的,不管循环多少次,只要前面加载过后面就不会再次加载了。
如何解决发送邮件的收件人和抄送人增加的问题呢
其实在phpmailer中已经给我们方法了
public function ClearAddresses() {
foreach($this->to as $to) {
unset($this->all_recipients[strtolower($to[0])]);
}
$this->to = array();
}
public function ClearCCs() {
foreach($this->cc as $cc) {
unset($this->all_recipients[strtolower($cc[0])]);
}
$this->cc = array();
}
public function ClearBCCs() {
foreach($this->bcc as $bcc) {
unset($this->all_recipients[strtolower($bcc[0])]);
}
$this->bcc = array();
}
public function ClearReplyTos() {
$this->ReplyTo = array();
}
这几个方法在循环发送邮件的时候时候使用就不需要通过unset对象的方法来进行初始化了。所以在程序依次执行时,循环发送邮件调用phpmailer的方法是不需要释法phpmailer这个对象,通过使用他的方法进行收件人和抄送密送等初始化就可以了。