专栏首页陶士涵的菜地[PHP] PHP调用IMAP协议读取邮件类库

[PHP] PHP调用IMAP协议读取邮件类库

socket.php 为连接socket的类库

imap.php 基于socket的imap协议封装

test.php 进行测试

require_once 'socket.php';
require_once 'imap.php';
$imap=new Sina_Mail_Net_Imap("imap.sina.net:143",30,30);
$imap->capability();
$imap->id(array(
    'name'          => 'SinaMail OtherMail Client',
    'version'       => '1',
    'os'            => 'SinaMail OtherMail',
    'os-version'    => '1.0',
));
$imap->login("xxxx@xxxxx","xxxx");
$folders=$imap->getList('', '*');
var_dump($folders);
$status = $imap->select('SENT');
var_dump($status);
$ls = $imap->fetch(array(), array('uid', 'internaldate', 'rfc822.size'));




foreach($ls as $k=>$i){
    $info=$imap->fetch(array($k), array('rfc822'));
}

imap.php

<?php
class Sina_Mail_Net_Imap {
    const MAX_READ_SIZE = 100000000;
    const PATTERN_REQUEST_STRING_SEQUENCE   = '/{\d+}/';
    const PATTERN_RESPONSE_STRING_SEQUENCE  = '/{(\d+)}$/';
    const SEQUENCE_PARAM_NAME   = '[]';
    const PARTIAL_PARAM_NAME    = '<>';
    const PARAM_NO          = 1;
    const PARAM_SINGLE      = 2;
    const PARAM_PAIR        = 4;
    const PARAM_LIST        = 8;
    const PARAM_STRING      = 16;
    const PARAM_NUMBER      = 32;
    const PARAM_DATE        = 64;
    const PARAM_FLAG        = 128;
    const PARAM_SEQUENCE    = 256;
    const PARAM_SEARCH      = 512;
    const PARAM_BODY        = 1024;
    const PARAM_PARTIAL     = 2048;
    const PARAM_EXCLUSIVE   = 4096;
    private static $statusKeywords = array(
        'MESSAGES'      => self::PARAM_NO,
        'RECENT'        => self::PARAM_NO,
        'UIDNEXT'       => self::PARAM_NO,
        'UIDVALIDITY'   => self::PARAM_NO,
        'UNSEEN'        => self::PARAM_NO,
    );
    private static $searchKeywords = array(
        'ALL'           => self::PARAM_NO,
        'ANSWERED'      => self::PARAM_NO,
        'BCC'           => 18, // self::PARAM_SINGLE | self::PARAM_STRING,
        'BEFORE'        => 66, // self::PARAM_SINGLE | self::PARAM_DATE,
        'BODY'          => 18, // self::PARAM_SINGLE | self::PARAM_STRING,
        'CC'            => 18, // self::PARAM_SINGLE | self::PARAM_STRING,
        'DELETED'       => self::PARAM_NO,
        'DRAFT'         => self::PARAM_NO,
        'FLAGGED'       => self::PARAM_NO,
        'FROM'          => 18, // self::PARAM_SINGLE | self::PARAM_STRING,
        'HEADER'        => 20, // self::PARAM_PAIR | self::PARAM_STRING,
        'KEYWORD'       => 130, // self::PARAM_SINGLE | self::PARAM_FLAG,
        'LARGER'        => 34, // self::PARAM_SINGLE | self::PARAM_NUMBER,
        'NEW'           => self::PARAM_NO,
        'NOT'           => 514, // self::PARAM_SINGLE | self::PARAM_SEARCH,
        'OLD'           => self::PARAM_NO,
        'ON'            => 66, // self::PARAM_SINGLE | self::PARAM_DATE,
        'OR'            => 516, // self::PARAM_PAIR | self::PARAM_SEARCH,
        'RECENT'        => self::PARAM_NO,
        'SEEN'          => self::PARAM_NO,
        'SENTBEFORE'    => 66, // self::PARAM_SINGLE | self::PARAM_DATE,
        'SENTON'        => 66, // self::PARAM_SINGLE | self::PARAM_DATE,
        'SENTSINCE'     => 66, // self::PARAM_SINGLE | self::PARAM_DATE,
        'SINCE'         => 66, // self::PARAM_SINGLE | self::PARAM_DATE,
        'SMALLER'       => 34, // self::PARAM_SINGLE | self::PARAM_NUMBER,
        'SUBJECT'       => 18, // self::PARAM_SINGLE | self::PARAM_STRING,
        'TEXT'          => 18, // self::PARAM_SINGLE | self::PARAM_STRING,
        'TO'            => 18, // self::PARAM_SINGLE | self::PARAM_STRING,
        'UID'           => 258, // self::PARAM_SINGLE | self::PARAM_SEQUENCE,
        'UNANSWERED'    => self::PARAM_NO,
        'UNDELETED'     => self::PARAM_NO,
        'UNDRAFT'       => self::PARAM_NO,
        'UNFLAGGED'     => self::PARAM_NO,
        'UNKEYWORD'     => 130, // self::PARAM_SINGLE | self::PARAM_FLAG,
        'UNSEEN'        => self::PARAM_NO,
    );
    private static $fetchKeywords = array(
        'ALL'           => 4097, // self::PARAM_NO | self::PARAM_EXCLUSIVE,
        'FAST'          => 4097, // self::PARAM_NO | self::PARAM_EXCLUSIVE,
        'FULL'          => 4097, // self::PARAM_NO | self::PARAM_EXCLUSIVE,
        'BODY'          => 3075, // self::PARAM_NO | self::PARAM_SINGLE | self::PARAM_BODY | self::PARAM_PARTIAL,
        'BODY.PEEK'     => 3074, // self::PARAM_SINGLE | self::PARAM_BODY | self::PARAM_PARTIAL,
        'BODYSTRUCTURE' => self::PARAM_NO,
        'ENVELOPE'      => self::PARAM_NO,
        'FLAGS'         => self::PARAM_NO,
        'INTERNALDATE'  => self::PARAM_NO,
        'RFC822'        => self::PARAM_NO,
        'RFC822.HEADER' => self::PARAM_NO,
        'RFC822.SIZE'   => self::PARAM_NO,
        'RFC822.TEXT'   => self::PARAM_NO,
        'UID'           => self::PARAM_NO,
    );
    private $sock = null;
    private $timeout = 120;
    private $ts = 0;
    private $tagName = 'A';
    private $tagId = 0;
    private $capabilities = array();
    private $folders = array();
    private $currentFolder = null;
    private $currentCommand = null;
    private $lastSend = '';
    private $lastRecv = '';
    public function __construct($uri, $timeout = null, $connTimeout = null) {
        $this->sock = new Socket($uri, $timeout);        
//        $t = intval($timeout);
//        if ($t > 0) {
//            $this->timeout = $t;
//        }
        $this->connect($connTimeout);        
    }
    public function __destruct() {
    }

    public function connect($timeout) {        
        $this->sock->connect($timeout);        
        $this->getResponse();
    }
    
    public function capability() {
        $res = $this->request('capability');
        if (isset($res[0][0]) && $res[0][0] == '*' &&
            isset($res[0][1]) && strcasecmp($res[0][1], 'capability') == 0) {
            for ($i = 2, $n = count($res[0]); $i < $n; ++$i) {
                $this->capabilities[strtoupper($res[0][$i])] = true;
            }
        }        
    }
    
    public function id($data) {
        if (isset($this->capabilities['ID'])) {            
            $this->request('id', array($data));            
        }        
    }
    public function login($username, $password) {
        try {
            $this->request('login', array($username, $password));
        } catch (Exception $ex) {
            throw new Exception($ex->getMessage(), $ex->getCode());
        }
    }
    
    public function logout() {
        $this->request('logout');        
    }
    
    public function getList($reference = '', $wildcard = '') {
        $res = $this->request('list', array($reference, $wildcard));
        foreach ($res as &$r) {
            if (isset($r[0]) && $r[0] == '*' && 
                isset($r[1]) && strcasecmp($r[1], 'list') == 0 && 
                isset($r[4])) {
                $this->folders[$r[4]] = array(
                    'id'    => $r[4],
                    'name'  => mb_convert_encoding($r[4], 'UTF-8', 'UTF7-IMAP'),
                    'path'  => $r[3],
                    'attr'  => $r[2],
                );
            }
        }
        return $this->folders;
    }
    
    public function status($folder, $data) {
        $args = $this->formatArgsForCommand($data, self::$statusKeywords);
        $res = $this->request('status', array($folder, $args));
        $status = array();
        if (!empty($res)) {            
            foreach ($res as &$r) {
                if (isset($r[0]) && $r[0] == '*' && 
                    isset($r[1]) && strcasecmp($r[1], 'status') == 0 && 
                    isset($r[3]) && is_array($r[3])) {
                    for ($i = 0, $n = count($r[3]); $i < $n; $i += 2) {
                        $status[$r[3][$i]] = $r[3][$i + 1];
                    }
                }
            }
        }
        return $status;
    }
    public function select($folder) {
        $res = $this->request('select', array($folder));
        $status = array();
        if (!empty($res)) {
            foreach ($res as $r) {
                if (isset($r[0]) && $r[0] == '*') {
                    if (isset($r[1]) && isset($r[2])) {
                        if (strcasecmp($r[1], 'ok') == 0 && is_array($r[2])) {
                            for ($i = 0, $n = count($i); $i < $n; $i += 2) {
                                $status[$r[2][$i]] = $r[2][$i + 1];
                            }
                        } elseif (ctype_digit($r[1])) {
                            $status[$r[2]] = $r[1];
                        } else {
                            $status[$r[1]] = $r[2];
                        }    
                    }
                }
            }
        }
        $this->currentFolder = $folder;
        return $status;
    }
    public function search($data) {     
        $args = $this->formatArgsForCommand($data, self::$searchKeywords, true);                
        $res = $this->request('search', $args);
        $ls = array();
        foreach ($res as &$r) {
            if (isset($r[0]) && $r[0] == '*' && 
                isset($r[1]) && strcasecmp($r[1], 'search') == 0) {
                for ($i = 2, $n = count($r); $i < $n; ++$i) {
                    $ls[] = $r[$i];
                }
            }
        }
        return $ls;
    }
    public function fetch($seq, $data) {                        
        $seqStr = $this->formatSequence($seq);        
        $args = $this->formatArgsForCommand($data, self::$fetchKeywords);                                
        $res = $this->request('fetch', array($seqStr, $args));
//        var_dump($res);
        $ls = array();
        foreach ($res as &$r) {
            if (isset($r[0]) && $r[0] == '*' &&
                isset($r[1]) && is_numeric($r[1]) && 
                isset($r[2]) && strcasecmp($r[2], 'fetch') == 0 &&
                isset($r[3]) && is_array($r[3])) {
                $a = array();
                for ($i = 0, $n = count($r[3]); $i < $n; $i += 2) {
                    $key = $r[3][$i];
                    if (((strcasecmp($key, 'BODY') == 0 && isset($args['BODY']) && is_array($args['BODY'])) || 
                         (strcasecmp($key, 'BODY.PEEK') == 0 && isset($args['BODY.PEEK']) && is_array($args['BODY.PEEK']))) && 
                        is_array($r[3][$i + 1])) {
                        $key = trim($this->formatRequestArray(array($key => $r[3][$i + 1]), $placeHolder, 0), '()');
                        $i++;
                    } else {
                        $key = $r[3][$i];
                    }
                    $a[$key] = $r[3][$i + 1];
                }
                if (!empty($a)) {
                    $ls[$r[1]] = $a;
                }
            }
        }
        return $ls;
    }
    private function nextTag() {
        $this->tagId++;
        return sprintf('%s%d', $this->tagName, $this->tagId);
    }
    private function request($cmd, $args = array()) {                
        $this->currentCommand = strtoupper(trim($cmd));
        $tag = $this->nextTag();
        $req = $tag . ' ' . $this->currentCommand;
        
        // 格式化参数列表
        $strSeqList = array();
        if (is_array($args)) {
            $argStr = $this->formatRequestArray($args, $strSeqList);                
        } else {                
            $argStr = $this->formatRequestString($args, $strSeqList);
        }
        //$argStr = $this->makeRequest($args, $strSeqList);
        $subReqs = array();
        if (isset($argStr[0])) {
            $req .= ' ' . $argStr;
            // 如果参数中包括需要序列化的数据,根据序列化标识{length}将命令拆分成多条
            if (!empty($strSeqList) && preg_match_all(self::PATTERN_REQUEST_STRING_SEQUENCE, $req, $matches, PREG_OFFSET_CAPTURE)) {
                $p = 0;
                foreach ($matches[0] as $m) {
                    $e = $m[1] + strlen($m[0]);
                    $subReqs[] = substr($req, $p, $e - $p);                    
                    $p = $e;
                }
                $subReqs[] = substr($req, $p);
                // 校验序列化标识与需要序列化的参数列表数量是否一致
                if (count($subReqs) != count($strSeqList) + 1) {
                    $subReqs = null;
                }
            }
        }
                
        if (empty($subReqs)) {
            // 处理单条命令
            $this->sock->writeLine($req);
            $this->lastSend = $req;        
            $res = $this->getResponse($tag);
        } else {            
            // 处理多条命令
            $this->lastSend = '';
            foreach ($subReqs as $id => $req) {
                $this->sock->writeLine($req);                          
                $this->lastSend .= $req;
                $res = $this->getResponse($tag);                
                if (isset($res[0][0]) && $res[0][0] == '+') {
                    $this->sock->write($strSeqList[$id]);                
                    $this->lastSend .= "\r\n" . $strSeqList[$id];
                } else {
                    // 如果服务器端返回其他相应,则定制后续执行
                    break;
                }
            }                
        }
        return $res;
    }
        
    private function formatRequestString($s, &$strSeqList) {
        $s = trim($s);
        $needQuote = false;
        if (!isset($s[0])) {
            $needQuote = true;
        } elseif ($this->currentCommand == 'ID') {    
            $needQuote = true;
        } else {
            // 参数包含多行时,需要进行序列化
            if (strpos($s, "\r") !== false || strpos($s, "\n") !== false) {                
                $strSeqList[] = $s;
                $s = sprintf('{%d}', strlen($s));
            } else {
                // 参数包含双引号或空格时,需要将使用双引号括起来
                if (strpos($s, '"') !== false) {
                    $s = addcslashes($s, '"');
                    $needQuote = true;
                }
                if (strpos($s, ' ') !== false) {
                    $needQuote = true;
                }
            }
        }
        if ($needQuote) {
            return sprintf('"%s"', $s);
        } else {
            return $s;
        }
    }
    private function formatRequestArray($arr, &$strSeqList, $level = -1) {
        $a = array();
        foreach ($arr as $k => $v) {
            $isBody = false;
            $supportPartial = false;
            $partialStr = '';
            if ($this->currentCommand == 'FETCH') {
                // 识别是否body命令,是否可以包含<partial>
                $kw = strtoupper($k);            
                if (isset(self::$fetchKeywords[$kw]) && (self::$fetchKeywords[$kw] & self::PARAM_BODY) > 0) {
                    $isBody = true;
                }
                if (isset(self::$fetchKeywords[$kw]) && (self::$fetchKeywords[$kw] & self::PARAM_PARTIAL) > 0) {
                    $supportPartial = true;
                }
            }                        
            if (is_array($v)) {                        
                if ($supportPartial && isset($v[self::PARTIAL_PARAM_NAME]) && is_array($v[self::PARTIAL_PARAM_NAME])) {                    
                    // 处理包含<partial>的命令
                    foreach ($v[self::PARTIAL_PARAM_NAME] as $spos => $mlen) {
                        $partialStr =  sprintf('<%d.%d>', $spos, $mlen);
                    }                    
                    unset($v[self::PARTIAL_PARAM_NAME]);
                }
                $s = $this->formatRequestArray($v, $strSeqList, $level + 1);
            } else {
                $s = $this->formatRequestString($v, $strSeqList);
            }            
            if (!is_numeric($k)) {
                // 字典方式需要包含键名
                $k = $this->formatRequestString($k, $strSeqList);
                if ($isBody) {
                    $s = $k . $s;                    
                } else {
                    $s = $k . ' ' . $s;
                }
                // 包含<partial>
                if ($supportPartial) {
                    $s .= $partialStr;
                }
            }
            $a[] = $s;
        }
        if ($level < 0) {
            return implode(' ', $a);
        } elseif (($level % 2) == 0) {
            return sprintf('(%s)', implode(' ', $a));
        } else {
            return sprintf('[%s]', implode(' ', $a));
        }
    }
    private function formatSequence($seq) {
        $n = count($seq);
        if ($n == 0) {
            return '1:*';            
        } elseif ($n == 1) {
            if (isset($seq[0])) {
                return strval($seq[0]);
            } else {
                foreach ($seq as $k => $v) {
                    return $k . ':' . $v;
                }
            }
        } else {
            return implode(',', $seq);
        }
    }
    private function formatArgsForCommand(&$data, &$fields, $asList = false) {
        $args = array();
        foreach ($data as $k => $v) {            
            if (is_numeric($k)) {
                // 无值参数
                $name = strtoupper($v);
                if (isset($fields[$name])) {
                    // 对于排他性属性,直接返回
                    if (($fields[$name] & self::PARAM_EXCLUSIVE) > 0) {
                        return $name;
                    } elseif (($fields[$name] & self::PARAM_NO) > 0) {                
                        $args[] = $name;
                    }
                }
            } elseif ($k == self::SEQUENCE_PARAM_NAME) {
                // 序列
                $args[] = $this->formatSequence($v);                
            } else {
                $name = strtoupper($k);
                if (isset($fields[$name])) {
                    $paramType = $fields[$name];                    
                    // 格式化参数类型
                    if (($paramType & self::PARAM_DATE) > 0) {
                        $v = date('j-M-Y', $v);
                    } elseif (($paramType & self::PARAM_SEQUENCE) > 0) {
                        $v = $this->formatSequence($v);
                    }                        

                    // 根据参数定义拼组参数列表
                    if (($paramType & self::PARAM_SINGLE) > 0) {
                        // 单值参数
                        if ($asList) {
                            $args[] = $name;
                            $args[] = $v;
                        } else {
                            $args[$name] = $v;
                        }
                    } elseif (($paramType & self::PARAM_PAIR) > 0) {
                        // 键值对参数
                        if (is_array($v)) {
                            foreach ($v as $x => $y) {
                                $pk = $x;
                                $pv = $y;
                                break;
                            }
                        } else {
                            $pk = $v;
                            $pv = '';
                        }
                        if ($asList) {
                            $args[] = $name;
                            $args[] = $pk;
                            $args[] = $pv;
                        } else {
                            $args[$name] = array($pk => $pv);
                        }
                    } elseif (($paramType & self::PARAM_LIST) > 0) {
                        // 列表参数
                        if ($asList) {
                            $args[] = $name;
                            foreach ($v as $i) {
                                $args[] = $i;
                            }
                        } else {
                            $args[$name] = $v;
                        }
                    } elseif (($paramType & self::PARAM_NO) > 0) {
                        // 无值参数
                        $args[] = $name;
                    }
                }
            }
        }
        return $args;
    }
    private function getResponse($tag = null) {
        $r = array();
        $readMore = true;  
        while ($readMore) {
            $ln = trim($this->sock->readLine());            
            if (!isset($ln[0])) {
                // connection closed or read empty string, throw exception to avoid dead loop and reconnect
                throw new Exception('read response failed');
            }
            
            $matches = null;
            $strSeqKey = null;
            $strSeq = null;
            if (preg_match(self::PATTERN_RESPONSE_STRING_SEQUENCE, $ln, $matches)) {
                $strSeqKey = $matches[0];
                $this->readSequence($ln, $strSeq, $matches[1]);
            }
            $this->lastRecv = $ln;
            
            // 区分处理不同种响应
            switch ($ln[0]) {
                case '*':
                    $r[] = $this->parseLine($ln, $strSeqKey, $strSeq);                                        
                    if (!$tag) {
                        $readMore = false;
                    }
                    break;
                case $this->tagName:
                    $r[] = $this->parseLine($ln);
                    if ($tag) {
                        $readMore = false;
                    } else {
                        
                    }
                    break;
                case '+':
                    $r[] = $this->parseLine($ln);
                    $readMore = false;
                    break;
                default:
                    $r[] = $ln;
                    break;
            }                        
        }        
        
        //var_dump($this->lastSend, $this->lastRecv);
        
        // 无响应数据
        if (empty($r)) {
            throw new Exception('no response');
        }
                                                
        $last = $r[count($r) - 1];
        if (isset($last[0]) && $last[0] == '+') {
            // 继续发送请求数据
        } else {
            if ($tag) {
                if (!isset($last[0]) || strcasecmp($last[0], $tag) != 0) {                    
                    throw new Exception('tag no match');
                }           
            } else {
                if (!isset($last[0]) || strcasecmp($last[0], '*') != 0) {                    
                    throw new Exception('untag no match');
                }
            }
            if (isset($last[1])) {
                // 处理响应出错的情况
                if (strcasecmp($last[1], 'bad') == 0) {        
                    throw new Exception(implode(' ', $last));
                } elseif (strcasecmp($last[1], 'no') == 0) {
                    throw new Exception(implode(' ', $last));
                }
            }
            //$this->currentCommand = null;
        }
        
        return $r;
    }
    
    private function readSequence(&$ln, &$strSeq, $seqLength) {
        // 对于字符串序列,读取完整内容后再拼接响应        
        $readLen = 0;
        $st = microtime(true);
        // 网络请求多次读取字符串序列内容,直到读好为止
        while ($readLen < $seqLength) {
            $sb = $this->sock->read($seqLength - $readLen);
            if (isset($sb[0])) {
                $strSeq .= $sb;
                $readLen = strlen($strSeq);
            }                    
            if ((microtime(true) - $st) > $this->timeout) {
                throw new Exception('read sequence timeout');
            }
        }
        // 读取字符串序列后的剩余命令
        $leftLn = rtrim($this->sock->readLine());
        $ln = $ln . $leftLn;
    }
    
    private function parseLine($ln, $strSeqKey = null, $strSeq = null) {
        $r = array();
        $p =& $r;
        $stack = array();
        $token = '';
        $escape = false;
        $inQuote = false;
        for ($i = 0, $n = strlen($ln); $i < $n; ++$i) {
            $ch = $ln[$i];
            if ($ch == '"') {
                // 处理双引号括起的字符串
                if (!$inQuote) {
                    $inQuote = true;
                } else {
                    $inQuote = false;
                }
            } elseif ($inQuote) {
                // 对于括起的字符串,处理双引号转义
                if ($ch == '\\') {
                    if (!$escape && isset($ln[$i + 1]) && $ln[$i + 1] == '"') {               
                        $token .= '"';
                        $i++;
                    } else {
                        $token .= $ch;
                        $escape = !$escape;
                    }                    
                } else {
                    $token .= $ch;
                }
            } elseif ($ch == ' ' || 
                $ch == '(' || $ch == ')' || 
                $ch == '[' || $ch == ']') {
                // 处理子列表
                if (isset($token[0])) {
                    // 将字符串序列标识:{length},替换为真实字符串
                    if ($strSeqKey && $token == $strSeqKey) {
                        $p[] = $strSeq;
                    } else {
                        $p[] = $token;
                    }
                    $token = '';
                }                
                if ($ch == '(' || $ch == '[') {                    
                    $p[] = array();
                    $stack[] =& $p;
                    $p =& $p[count($p) - 1];                                        
                } elseif ($ch == ')' || $ch == ']') {        
                    $p =& $stack[count($stack) - 1];
                    array_pop($stack);
                }
            } else {                
                // 处理字符串字面量
                $token .= $ch;
            }            
        }
        if (isset($token[0])) {
            // 将字符串序列标识:{length},替换为真实字符串
            if ($strSeqKey && $token == $strSeqKey) {
                $p[] = $strSeq;
            } else {
                $p[] = $token;
            }
        }
        return $r;
    }
}

// end of php

socket.php

<?php
class Socket{
    const DEFAULT_READ_SIZE = 8192;
    const CRTL = "\r\n";
    private $uri = null;
    private $timeout = null;
    private $sock = null;
    private $connected = false;
    public function __construct($uri, $timeout = null){
        $this->uri = $uri;
        $this->timeout = $this->formatTimeout($timeout);
    }

    public function connect($timeout = null, $retryTimes = null){
        if ($this->connected) {
            $this->close();
        }

        $connTimeout = $this->formatTimeout($timeout, $this->timeout);
        $retryTimes = intval($retryTimes);
        if ($retryTimes < 1) {
            $retryTimes = 1;
        }
        for ($i = 0; $i < $retryTimes; ++$i) {
            $this->sock = stream_socket_client(
                $this->uri, $errno, $error, $connTimeout);
            if ($this->sock) {
                break;
            }
        }
        if (!$this->sock) {

        }
        stream_set_timeout($this->sock, $this->timeout);
        $this->connected = true;
    }

    public function read($size){
        assert($this->connected);

        $buf = fread($this->sock, $size);
        if ($buf === false) {
            $this->handleReadError();
        }

        return $buf;
    }

    public function readLine(){
        assert($this->connected);
        $buf = '';
        while (true) {
            $s = fgets($this->sock, self::DEFAULT_READ_SIZE);
            if ($s === false) {
                $this->checkReadTimeout();
                break;
            }
            $n = strlen($s);
            if (!$n) {
                break;
            }
            $buf .= $s;
            if ($s[$n - 1] == "\n") {
                break;
            }
        }

        return $buf;
    }

    public function readAll(){
        assert($this->connected);
        $buf = '';
        while (true) {
            $s = fread($this->sock, self::DEFAULT_READ_SIZE);
            if ($s === false) {
                $this->handleReadError();
            }

            if (!isset($s[0])) {
                break;
            }

            $buf .= $s;
        }

        return $buf;
    }

    public function write($s){
        assert($this->connected);
        $n = strlen($s);
        $w = 0;
        while ($w < $n) {
            $buf = substr($s, $w, self::DEFAULT_READ_SIZE);
            $r = fwrite($this->sock, $buf);
            if (!$r) {
                $this->close();
            }
            $w += $r;
        }
    }
    public function writeLine($s){
        $this->write($s . self::CRTL);
    }
    public function close() {
        if ($this->connected) {
            fclose($this->sock);
            $this->connected = false;
        }
    }
    private function formatTimeout($timeout, $default = null){
        $t = intval($timeout);
        if ($t <= 0) {
            if (!$default) {
                $t = ini_get('default_socket_timeout');
            } else {
                $t = $default;
            }
        }
        return $t;
    }

    private function checkReadTimeout(){
        $meta = stream_get_meta_data($this->sock);
        if (isset($meta['timed_out'])) {
            $this->close();
        }
    }

    private function handleReadError(){
        $this->checkReadTimeout();
        $this->close();
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何通过爬虫爬取公众号的活跃度

    这篇文章主要来介绍下如何通过爬虫技术来爬取测试相关公众号的信息,接着通过对爬取的信息进行过滤处理给出测试公众号活跃度的一个列表。这里活跃度会以月发文的数量来进行...

    测试邦
  • Jmeter 创建一个web测试计划

    下载地址:http://jmeter.apache.org/download_jmeter.cgi

    授客
  • CanHackMe | CTF平台搭建记录

    在Github上发现了一个开源的CTF平台,界面很好看,而且是php写的,所以决定搭建一下折腾折腾。

    天钧
  • Linux 的 Crond(二)

    最近由于工作中用到了crond,之前对crond不是很了解,只知道咋用,但是这次需要考虑好多情况,所以又深入了解了一下crond,下面就以下几个问题来谈谈cro...

    lin_zone
  • NATS用Go入门实践

    NATS系统通常由具有nats或tls协议的标准URL标识,例如, NATS://demo.nats.io。NATS系统可以是单个服务器,小型集群或全局超级集群...

    陌无崖
  • loadrunner 脚本开发-url解码

    value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;

    授客
  • 腾讯面试官是这样问布隆过滤器的

    假设遇到这样一个问题:一个网站有 20 亿 url 存在一个黑名单中,这个黑名单要怎么存?若此时随便输入一个 url,你如何快速判断该 url 是否在这个黑名单...

    用户2769421
  • CTF从入门到提升(十五)自包含

    1、Phpinfo()包含文件返回临时文件名,我们是在文件上传环节进行包含,如果不删除临时文件我们可以生成临时文件,

    牛油果
  • 虚拟主机能用宝塔服务器管理系统吗?

    虚拟主机能用宝塔吗?不可以的,宝塔面板是一款服务器运维管理面板,云服务器的话可以借助宝塔用来管理维护,而虚拟主机不能。其实也不需要,主要有以下几点:

    用户6191802
  • NATS多种连接Golang实践

    如果因为任何原因断开连接,大多数(如果不是全部)客户端库将重新连接到NATS系统。重新连接逻辑可能因库而异,因此请检查客户端库的文档。

    陌无崖

扫码关注云+社区

领取腾讯云代金券