万网2年免费虚拟主机无法使用SMTP的解决方案
随着拥有万网免费2年免费虚拟主机的站长越来越多,万网2年免费虚拟主机上搭建Wordpress无法发送邮件的问题(止步于WordPress 4.2.3版本,4.3版本已经修复)困扰着很多站长。这里给下解决方案。
Wordpress是使用一个开源库Phpmailer来进行邮件的收发,这个库由三个位于wp-includes目录下的文件组成:
class-phpmailer.php class-smtp.php class-pop3.php
在这三个文件中,class-phpmailer.php是主文件,class-pop3.php负责POP方式收邮件,class-smtp.php负责SMTP方式发送邮件。而万网的两年限免主机不能发送邮件便是出在了class-smtp.php上的一个函数上。
在phpmailer的版本迭代中,在较早版本中使用fsockopen函数创建与SMTP服务器创建连接发送邮件,而在之后较新的版本和Wordpress所包含的版本中,不知道为啥变成了stream_socket_client,然而这俩函数在参数和功能上异常相似。
非常不幸,万网把stream_socket_client直接封掉了,不允许启用或者使用,下图是用雅黑php探针对万网主机进行检测得到的禁用函数列表:
不过万幸的是fsockopen还能够开启使用,那么修复这个问题的关键就是将核心的连接SMTP的代码从使用stream_socket_client改为使用fsockopen函数。
我们看一下PHP Manual给的解释:
resource fsockopen(string $hostname [,int $port=-1[,int &$errno[,string &$errstr[,float $timeout =ini_get("default_socket_timeout") ]]]]); /* 参数 hostname 如果安装了OpenSSL,那么你也许应该在你的主机名地址前面添加访问协议ssl://或者是tls://,从而可以使用基于TCP/IP协议的SSL或者TLS的客户端连接到远程主机。 port 端口号。如果对该参数传一个-1,则表示不使用端口,例如unix://。 errno 如果传入了该参数,holds the system level error number that occurred in the system-level connect() call。 如果errno的返回值为0,而且这个函数的返回值为FALSE,那么这表明该错误发生在套接字连接(connect())调用之前,导致连接失败的原因最大的可能是初始化套接字的时候发生了错误。 errstr 错误信息将以字符串的信息返回。 timeout 设置连接的时限,单位为秒。 */
resource stream_socket_client(string $remote_socket[,int &$errno[,string &$errstr[,float $timeout=ini_get("default_socket_timeout")[,int $flags=STREAM_CLIENT_CONNECT[,resource $context]]]]]) /* 参数 remote_socket Address to the socket to connect to.(地址到套接字连接。) errno Will be set to the system level error number if connection fails.(将被设置为系统级的错误数量如果连接失败。) errstr Will be set to the system level error message if the connection fails.(将被设置为系统级的错误消息如果连接失败。 ) timeout Number of seconds until the connect() system call should timeout.(Connect函数调用后的超时时间) Note: This parameter only applies when not making asynchronous connection attempts. (仅适用于非异步请求) Note: To set a timeout for reading/writing data over the socket, use the stream_set_timeout(), as the timeout only applies while making connecting the socket.(设置读写数据的套接字超时请使用stream_set_timeout函数) flags Bitmask field which may be set to any combination of connection flags. Currently the select of connection flags is limited to STREAM_CLIENT_CONNECT (default), STREAM_CLIENT_ASYNC_CONNECT and STREAM_CLIENT_PERSISTENT.(位掩码字段可以设置为任意组合连接标志。 目前连接标志的选择是有限的 STREAM_CLIENT_CONNECT (默认), STREAM_CLIENT_ASYNC_CONNECT 和 STREAM_CLIENT_PERSISTENT 。) context A valid context resource created with stream_context_create().(资源创建一个有效的上下文 stream_context_create().) -----翻译来自有道词典------ */
我们发现,这两个函数的返回值都是Resource,参数都由地址,端口号,错误信息等内容组成.而且fsockopen需要的参数要少于stream_soket_client.
经过摸索,在class-smtp.php 200行找到了如下代码:
$socket_context = stream_context_create($options); //Suppress errors; connection failures are handled at a higher level $this->smtp_conn = @stream_socket_client( $host . ":" . $port, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $socket_context );
根据PHP Manual给出的两个函数帮助,可以将上面的代码转化为
$this->smtp_conn = fsockopen($host,$port,$errno,$errstr,$timeout);
之后保存即可。
最后安装一枚WordPress的SMTP插件,例如WP-SMTP之类的插件,然后尽情的使用邮件系统吧!
2015年8月19日补充:
今天是个值得纪念的日子,为什么呢?因为今天我升级了Wordpress 4.3,在升级内核后我做了一下对SMTP代码的例行检查,发现Wordpress官方已经修复了这个bug,也就是意味着我们万网的站长以后可以不用更改核心代码也可以直接使用SMTP功能了!
在class-smtp.php的247~297行便是做了更改的内容,由之前的单一的使用stream_socket_client函数改为了使用前利用function_exist函数判断函数可用性后再调用的方式.
2016年5月23日补充:
最近又群友在Wordpress综合交流区里边又问起了关于Wordpress发送邮件的问题.有一些群友的主机因为mail函数被禁用而改用Wordpress的SMTP插件,不管是WP-SMTP-Mail或者是什么插件,发现均不能正常的发送邮件,然而在注册界面上仍然显示成功发送了邮件,也没有任何的错误提示,而错误日志中也没有任何的新增错误信息.这里我分享一个测试的方法.
这里用到的方法是在wordpress开发中常用的一个方法:创建一个单独的测试文件用于针对开发的模块进行模块化测试.
在wordpress中,整个网站的API由wp-load.php来统一加载但不调用任何函数,在引入所有API函数之后,调用主函数wp()启动整个网站的运行.在调试邮件发送的时候是针对wordpress自身函数wp_mail函数进行调试.wp_mail本身会支持使用phpmailer进行SMTP代发.
首先在wordpress根目录(包含wp-content的那个目录)中创建一个名为wp-mail-test.php的文件,内容如下:
<?php require 'wp-load.php'; //引入所有的Wordpress API函数?> <?php wp_mail("改为自己的一个Email地址","测试邮件标题","测试内容:当您收到这封邮件的时候,您的wp_mail函数已经可以正常使用了!"); //尝试发送一封邮件?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Wordpress 邮件页面</title> <head> <body> <pre> <?php print_r($_GLOBALS['phpmailer']->ErrorInfo); //输出phpmailer的错误信息,去掉->ErrorInfo可以查看完整的phpmailer对象内容?> </pre> </body> </html>
随后在浏览器中访问http://域名/wp-mail-test.php,随后可以看到phpmailer类的错误信息,例如下图:
这里的错误含义是SMTP身份校验失败.这些错误信息如果不知道是什么原因可以使用关键字 phpmailer+完整的错误信息进行搜索,一般都可以得到不能发送邮件的原因.如果提示的是SMTP:Could not Create Connection之类的内容,那么就要检查一下wordpress版本和本文所涉及的内容了.
版权声明:
本解决方案最早于2015年3月3日在Wordpress大学问答系统正式发表(点击查看),之后由畅萌代替本人在Wordpress大学上正式发表一篇文章《解决万网主机下 WordPress 无法发送邮件的问题》。
本方法最早的引用是看到在百度贴吧Wordpress吧中有数位吧友求救,就在贴吧中首次引用此方案(点击查看),这次没有明确修改方法。
之后也看到一些博客有写此方法:
《万网免费空间SMTP设置指南》@千弦月
《万网等虚拟主机不能SMTP发信的解决方案》@知言博客
《万网免费虚拟主机SMTP无法发送邮件的解决方法》@客家网络服务平台
希望在本文之后引用的博主请添加本文和以上所有博客链接,以保护我们合法权益!
博主,你好,我分别按照你给出的方案进行了修改,但然后提示不支持mail函数,能否给与指导下,非常感谢。
莫名奇妙中给解决了,都不知道咋整的。
如果提示不支持mail函数的话,需要先装一个SMTP插件或者根据网上的教程通过在主题的functions.php中添加一些代码将wordpress的发信器由mail()换成phpmailer,然后才能够适用本文.同时我并不推荐按照此文方法去改核心代码(每次更新完,4.3之前,都要重新改很麻烦),推荐更新到Wordpress 4.3或者更新的版本(已经采用两个函数选择其中能用的作为发信器).
嗯,下次试试。
另外,对于代码高亮你推荐使用什么插件,以及怎么用?我在网上找了很多,但是装上后都不好用。