목차
用PHP实现一个高效安全的ftp服务器(二),php实现ftp服务器
 1.实现用户类CUser。
2.共享内存操作类
3.内置的web服务器类
php教程 php手册 用PHP实现一个高效安全的ftp服务器(二),php实现ftp服务器

用PHP实现一个高效安全的ftp服务器(二),php实现ftp服务器

Jun 13, 2016 am 08:48 AM
데이터베이스에 연결

用PHP实现一个高效安全的ftp服务器(二),php实现ftp服务器

接前文。

 

 1.实现用户类CUser。

  用户的存储采用文本形式,将用户数组进行json编码。  

<span>用户文件格式:
 </span>* <span>array</span><span>(
 </span>*         'user1' => <span>array</span><span>(
 </span>*             'pass'=>'',
 *             'group'=>'',
 *             'home'=>'/home/ftp/', <span>//</span><span>ftp主目录</span>
 *             'active'=><span>true</span>,
 *             'expired=>'2015-12-12'<span>,
 *             </span>'description'=>''<span>,
 *             </span>'email' => ''<span>,
 *             </span>'folder'<span>=>array(
 *                     //可以列出主目录下的文件和目录,但不能创建和删除,也不能进入主目录下的目录
 *                     //前1-5位是文件权限,6-9是文件夹权限,10是否继承(inherit)
 *                     array(</span>'path'=>'/home/ftp/','access'=>'RWANDLCNDI'<span>),
 *                     //可以列出/home/ftp/a/下的文件和目录,可以创建和删除,可以进入/home/ftp/a/下的子目录,可以创建和删除。
 *                     array(</span>'path'=>'/home/ftp/a/','access'=>'RWAND-----'<span>),
 *             ),
 *             </span>'ip'<span>=>array(
 *                 </span>'allow'<span>=>array(ip1,ip2,...),//支持*通配符: 192.168.0.*
 *                 </span>'deny'<span>=>array(ip1,ip2,...)
 *             )
 *         ) 
 * )
 * 
 * 组文件格式:
 * array(
 *         </span>'group1'<span>=>array(
 *             </span>'home'=>'/home/ftp/dept1/'<span>,
 *             </span>'folder'<span>=>array(
 * 
 *             ),
 *             </span>'ip'<span>=>array(
 *                 </span>'allow'<span>=>array(ip1,ip2,...),
 *                 </span>'deny'<span>=>array(ip1,ip2,...)
 *            )
 *         )
 * )</span>
로그인 후 복사

  文件夹和文件的权限说明:

 * 文件权限 
 * R读 : 允许用户读取(即下载)文件。该权限不允许用户列出目录内容,执行该操作需要列表权限。 
 * W写: 允许用户写入(即上传)文件。该权限不允许用户修改现有的文件,执行该操作需要追加权限。
 * A追加: 允许用户向现有文件中追加数据。该权限通常用于使用户能够对部分上传的文件进行续传。 
 * N重命名: 允许用户重命名现有的文件。
 * D删除: 允许用户删除文件。 
 * 
 * 目录权限 
 * L列表: 允许用户列出目录中包含的文件。
 * C创建: 允许用户在目录中新建子目录。 
 * N重命名: 允许用户在目录中重命名现有子目录。
 * D删除: 允许用户在目录中删除现有子目录。注意: 如果目录包含文件,用户要删除目录还需要具有删除文件权限。
 * 
 * 子目录权限
 * I继承: 允许所有子目录继承其父目录具有的相同权限。继承权限适用于大多数情况,但是如果访问必须受限于子文件夹,例如实施强制访问控制(Mandatory Access Control)时,则取消继承并为文件夹逐一授予权限。
 * 
로그인 후 복사

  实现代码如下:  

  

class User{ const I = 1; // inherit const FD = 2; // folder delete const FN = 4; // folder rename const FC = 8; // folder create const FL = 16; // folder list const D = 32; // file delete const N = 64; // file rename const A = 128; // file append const W = 256; // file write (upload) const R = 512; // file read (download) private $hash_salt = ''; private $user_file; private $group_file; private $users = array(); private $groups = array(); private $file_hash = ''; public function __construct(){ $this->user_file = BASE_PATH.'/conf/users'; $this->group_file = BASE_PATH.'/conf/groups'; $this->reload(); } /** * 返回权限表达式 * @param int $access * @return string */ public static function AC($access){ $str = ''; $char = array('R','W','A','N','D','L','C','N','D','I'); for($i = 0; $i < 10; $i++){ if($access & pow(2,9-$i))$str.= $char[$i];else $str.= '-'; } return $str; } /** * 加载用户数据 */ public function reload(){ $user_file_hash = md5_file($this->user_file); $group_file_hash = md5_file($this->group_file); if($this->file_hash != md5($user_file_hash.$group_file_hash)){ if(($user = file_get_contents($this->user_file)) !== false){ $this->users = json_decode($user,true); if($this->users){ //folder排序 foreach ($this->users as $user=>$profile){ if(isset($profile['folder'])){ $this->users[$user]['folder'] = $this->sortFolder($profile['folder']); } } } } if(($group = file_get_contents($this->group_file)) !== false){ $this->groups = json_decode($group,true); if($this->groups){ //folder排序 foreach ($this->groups as $group=>$profile){ if(isset($profile['folder'])){ $this->groups[$group]['folder'] = $this->sortFolder($profile['folder']); } } } } $this->file_hash = md5($user_file_hash.$group_file_hash); } } /** * 对folder进行排序 * @return array */ private function sortFolder($folder){ uasort($folder, function($a,$b){ return strnatcmp($a['path'], $b['path']); }); $result = array(); foreach ($folder as $v){ $result[] = $v; } return $result; } /** * 保存用户数据 */ public function save(){ file_put_contents($this->user_file, json_encode($this->users),LOCK_EX); file_put_contents($this->group_file, json_encode($this->groups),LOCK_EX); } /** * 添加用户 * @param string $user * @param string $pass * @param string $home * @param string $expired * @param boolean $active * @param string $group * @param string $description * @param string $email * @return boolean */ public function addUser($user,$pass,$home,$expired,$active=true,$group='',$description='',$email = ''){ $user = strtolower($user); if(isset($this->users[$user]) || empty($user)){ return false; } $this->users[$user] = array( 'pass' => md5($user.$this->hash_salt.$pass), 'home' => $home, 'expired' => $expired, 'active' => $active, 'group' => $group, 'description' => $description, 'email' => $email, ); return true; } /** * 设置用户资料 * @param string $user * @param array $profile * @return boolean */ public function setUserProfile($user,$profile){ $user = strtolower($user); if(is_array($profile) && isset($this->users[$user])){ if(isset($profile['pass'])){ $profile['pass'] = md5($user.$this->hash_salt.$profile['pass']); } if(isset($profile['active'])){ if(!is_bool($profile['active'])){ $profile['active'] = $profile['active'] == 'true' ? true : false; } } $this->users[$user] = array_merge($this->users[$user],$profile); return true; } return false; } /** * 获取用户资料 * @param string $user * @return multitype:|boolean */ public function getUserProfile($user){ $user = strtolower($user); if(isset($this->users[$user])){ return $this->users[$user]; } return false; } /** * 删除用户 * @param string $user * @return boolean */ public function delUser($user){ $user = strtolower($user); if(isset($this->users[$user])){ unset($this->users[$user]); return true; } return false; } /** * 获取用户列表 * @return array */ public function getUserList(){ $list = array(); if($this->users){ foreach ($this->users as $user=>$profile){ $list[] = $user; } } sort($list); return $list; } /** * 添加组 * @param string $group * @param string $home * @return boolean */ public function addGroup($group,$home){ $group = strtolower($group); if(isset($this->groups[$group])){ return false; } $this->groups[$group] = array( 'home' => $home ); return true; } /** * 设置组资料 * @param string $group * @param array $profile * @return boolean */ public function setGroupProfile($group,$profile){ $group = strtolower($group); if(is_array($profile) && isset($this->groups[$group])){ $this->groups[$group] = array_merge($this->groups[$group],$profile); return true; } return false; } /** * 获取组资料 * @param string $group * @return multitype:|boolean */ public function getGroupProfile($group){ $group = strtolower($group); if(isset($this->groups[$group])){ return $this->groups[$group]; } return false; } /** * 删除组 * @param string $group * @return boolean */ public function delGroup($group){ $group = strtolower($group); if(isset($this->groups[$group])){ unset($this->groups[$group]); foreach ($this->users as $user => $profile){ if($profile['group'] == $group) $this->users[$user]['group'] = ''; } return true; } return false; } /** * 获取组列表 * @return array */ public function getGroupList(){ $list = array(); if($this->groups){ foreach ($this->groups as $group=>$profile){ $list[] = $group; } } sort($list); return $list; } /** * 获取组用户列表 * @param string $group * @return array */ public function getUserListOfGroup($group){ $list = array(); if(isset($this->groups[$group]) && $this->users){ foreach ($this->users as $user=>$profile){ if(isset($profile['group']) && $profile['group'] == $group){ $list[] = $user; } } } sort($list); return $list; } /** * 用户验证 * @param string $user * @param string $pass * @param string $ip * @return boolean */ public function checkUser($user,$pass,$ip = ''){ $this->reload(); $user = strtolower($user); if(isset($this->users[$user])){ if($this->users[$user]['active'] && time() <= strtotime($this->users[$user]['expired']) && $this->users[$user]['pass'] == md5($user.$this->hash_salt.$pass)){ if(empty($ip)){ return true; }else{ //ip验证 return $this->checkIP($user, $ip); } }else{ return false; } } return false; } /** * basic auth * @param string $base64 */ public function checkUserBasicAuth($base64){ $base64 = trim(str_replace('Basic ', '', $base64)); $str = base64_decode($base64); if($str !== false){ list($user,$pass) = explode(':', $str,2); $this->reload(); $user = strtolower($user); if(isset($this->users[$user])){ $group = $this->users[$user]['group']; if($group == 'admin' && $this->users[$user]['active'] && time() <= strtotime($this->users[$user]['expired']) && $this->users[$user]['pass'] == md5($user.$this->hash_salt.$pass)){ return true; }else{ return false; } } } return false; } /** * 用户登录ip验证 * @param string $user * @param string $ip * * 用户的ip权限继承组的IP权限。 * 匹配规则: * 1.进行组允许列表匹配; * 2.如同通过,进行组拒绝列表匹配; * 3.进行用户允许匹配 * 4.如果通过,进行用户拒绝匹配 * */ public function checkIP($user,$ip){ $pass = false; //先进行组验证 $group = $this->users[$user]['group']; //组允许匹配 if(isset($this->groups[$group]['ip']['allow'])){ foreach ($this->groups[$group]['ip']['allow'] as $addr){ $pattern = '/'.str_replace('*','\d+',str_replace('.', '\.', $addr)).'/'; if(preg_match($pattern, $ip) && !empty($addr)){ $pass = true; break; } } } //如果允许通过,进行拒绝匹配 if($pass){ if(isset($this->groups[$group]['ip']['deny'])){ foreach ($this->groups[$group]['ip']['deny'] as $addr){ $pattern = '/'.str_replace('*','\d+',str_replace('.', '\.', $addr)).'/'; if(preg_match($pattern, $ip) && !empty($addr)){ $pass = false; break; } } } } if(isset($this->users[$user]['ip']['allow'])){ foreach ($this->users[$user]['ip']['allow'] as $addr){ $pattern = '/'.str_replace('*','\d+',str_replace('.', '\.', $addr)).'/'; if(preg_match($pattern, $ip) && !empty($addr)){ $pass = true; break; } } } if($pass){ if(isset($this->users[$user]['ip']['deny'])){ foreach ($this->users[$user]['ip']['deny'] as $addr){ $pattern = '/'.str_replace('*','\d+',str_replace('.', '\.', $addr)).'/'; if(preg_match($pattern, $ip) && !empty($addr)){ $pass = false; break; } } } } echo date('Y-m-d H:i:s')." [debug]\tIP ACCESS:".' '.($pass?'true':'false')."\n"; return $pass; } /** * 获取用户主目录 * @param string $user * @return string */ public function getHomeDir($user){ $user = strtolower($user); $group = $this->users[$user]['group']; $dir = ''; if($group){ if(isset($this->groups[$group]['home']))$dir = $this->groups[$group]['home']; } $dir = !empty($this->users[$user]['home'])?$this->users[$user]['home']:$dir; return $dir; } //文件权限判断 public function isReadable($user,$path){ $result = $this->getPathAccess($user, $path); if($result['isExactMatch']){ return $result['access'][0] == 'R'; }else{ return $result['access'][0] == 'R' && $result['access'][9] == 'I'; } } public function isWritable($user,$path){ $result = $this->getPathAccess($user, $path); if($result['isExactMatch']){ return $result['access'][1] == 'W'; }else{ return $result['access'][1] == 'W' && $result['access'][9] == 'I'; } } public function isAppendable($user,$path){ $result = $this->getPathAccess($user, $path); if($result['isExactMatch']){ return $result['access'][2] == 'A'; }else{ return $result['access'][2] == 'A' && $result['access'][9] == 'I'; } } public function isRenamable($user,$path){ $result = $this->getPathAccess($user, $path); if($result['isExactMatch']){ return $result['access'][3] == 'N'; }else{ return $result['access'][3] == 'N' && $result['access'][9] == 'I'; } } public function isDeletable($user,$path){ $result = $this->getPathAccess($user, $path); if($result['isExactMatch']){ return $result['access'][4] == 'D'; }else{ return $result['access'][4] == 'D' && $result['access'][9] == 'I'; } } //目录权限判断 public function isFolderListable($user,$path){ $result = $this->getPathAccess($user, $path); if($result['isExactMatch']){ return $result['access'][5] == 'L'; }else{ return $result['access'][5] == 'L' && $result['access'][9] == 'I'; } } public function isFolderCreatable($user,$path){ $result = $this->getPathAccess($user, $path); if($result['isExactMatch']){ return $result['access'][6] == 'C'; }else{ return $result['access'][6] == 'C' && $result['access'][9] == 'I'; } } public function isFolderRenamable($user,$path){ $result = $this->getPathAccess($user, $path); if($result['isExactMatch']){ return $result['access'][7] == 'N'; }else{ return $result['access'][7] == 'N' && $result['access'][9] == 'I'; } } public function isFolderDeletable($user,$path){ $result = $this->getPathAccess($user, $path); if($result['isExactMatch']){ return $result['access'][8] == 'D'; }else{ return $result['access'][8] == 'D' && $result['access'][9] == 'I'; } } /** * 获取目录权限 * @param string $user * @param string $path * @return array * 进行最长路径匹配 * * 返回: * array( * 'access'=>目前权限 * ,'isExactMatch'=>是否精确匹配 * * ); * * 如果精确匹配,则忽略inherit. * 否则应判断是否继承父目录的权限, * 权限位表: * +---+---+---+---+---+---+---+---+---+---+ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | * +---+---+---+---+---+---+---+---+---+---+ * | R | W | A | N | D | L | C | N | D | I | * +---+---+---+---+---+---+---+---+---+---+ * | FILE | FOLDER | * +-------------------+-------------------+ */ public function getPathAccess($user,$path){ $this->reload(); $user = strtolower($user); $group = $this->users[$user]['group']; //去除文件名称 $path = str_replace(substr(strrchr($path, '/'),1),'',$path); $access = self::AC(0); $isExactMatch = false; if($group){ if(isset($this->groups[$group]['folder'])){ foreach ($this->groups[$group]['folder'] as $f){ //中文处理 $t_path = iconv('UTF-8','GB18030',$f['path']); if(strpos($path, $t_path) === 0){ $access = $f['access']; $isExactMatch = ($path == $t_path?true:false); } } } } if(isset($this->users[$user]['folder'])){ foreach ($this->users[$user]['folder'] as $f){ //中文处理 $t_path = iconv('UTF-8','GB18030',$f['path']); if(strpos($path, $t_path) === 0){ $access = $f['access']; $isExactMatch = ($path == $t_path?true:false); } } } echo date('Y-m-d H:i:s')." [debug]\tACCESS:$access ".' '.($isExactMatch?'1':'0')." $path\n"; return array('access'=>$access,'isExactMatch'=>$isExactMatch); } /** * 添加在线用户 * @param ShareMemory $shm * @param swoole_server $serv * @param unknown $user * @param unknown $fd * @param unknown $ip * @return Ambigous > */ public function addOnline(ShareMemory $shm ,$serv,$user,$fd,$ip){ $shm_data = $shm->read(); if($shm_data !== false){ $shm_data['online'][$user.'-'.$fd] = array('ip'=>$ip,'time'=>time()); $shm_data['last_login'][] = array('user' => $user,'ip'=>$ip,'time'=>time()); //清除旧数据 if(count($shm_data['last_login'])>30)array_shift($shm_data['last_login']); $list = array(); foreach ($shm_data['online'] as $k =>$v){ $arr = explode('-', $k); if($serv->connection_info($arr[1]) !== false){ $list[$k] = $v; } } $shm_data['online'] = $list; $shm->write($shm_data); } return $shm_data; } /** * 添加登陆失败记录 * @param ShareMemory $shm * @param unknown $user * @param unknown $ip * @return Ambigous */ public function addAttempt(ShareMemory $shm ,$user,$ip){ $shm_data = $shm->read(); if($shm_data !== false){ if(isset($shm_data['login_attempt'][$ip.'||'.$user]['count'])){ $shm_data['login_attempt'][$ip.'||'.$user]['count'] += 1; }else{ $shm_data['login_attempt'][$ip.'||'.$user]['count'] = 1; } $shm_data['login_attempt'][$ip.'||'.$user]['time'] = time(); //清除旧数据 if(count($shm_data['login_attempt'])>30)array_shift($shm_data['login_attempt']); $shm->write($shm_data); } return $shm_data; } /** * 密码错误上限 * @param unknown $shm * @param unknown $user * @param unknown $ip * @return boolean */ public function isAttemptLimit(ShareMemory $shm,$user,$ip){ $shm_data = $shm->read(); if($shm_data !== false){ if(isset($shm_data['login_attempt'][$ip.'||'.$user]['count'])){ if($shm_data['login_attempt'][$ip.'||'.$user]['count'] > 10 && time() - $shm_data['login_attempt'][$ip.'||'.$user]['time'] < 600){ return true; } } } return false; } /** * 生成随机密钥 * @param int $len * @return Ambigous */ public static function genPassword($len){ $str = null; $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz@!#$%*+-"; $max = strlen($strPol)-1; for($i=0;$i<$len;$i++){ $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数 } return $str; } } View Code

2.共享内存操作类

  这个相对简单,使用php的shmop扩展即可。

  

class ShareMemory{ private $mode = 0644; private $shm_key; private $shm_size; /** * 构造函数 */ public function __construct(){ $key = 'F'; $size = 1024*1024; $this->shm_key = ftok(__FILE__,$key); $this->shm_size = $size + 1; } /** * 读取内存数组 * @return array|boolean */ public function read(){ if(($shm_id = shmop_open($this->shm_key,'c',$this->mode,$this->shm_size)) !== false){ $str = shmop_read($shm_id,1,$this->shm_size-1); shmop_close($shm_id); if(($i = strpos($str,"\0")) !== false)$str = substr($str,0,$i); if($str){ return json_decode($str,true); }else{ return array(); } } return false; } /** * 写入数组到内存 * @param array $arr * @return int|boolean */ public function write($arr){ if(!is_array($arr))return false; $str = json_encode($arr)."\0"; if(strlen($str) > $this->shm_size) return false; if(($shm_id = shmop_open($this->shm_key,'c',$this->mode,$this->shm_size)) !== false){ $count = shmop_write($shm_id,$str,1); shmop_close($shm_id); return $count; } return false; } /** * 删除内存块,下次使用时将重新开辟内存块 * @return boolean */ public function delete(){ if(($shm_id = shmop_open($this->shm_key,'c',$this->mode,$this->shm_size)) !== false){ $result = shmop_delete($shm_id); shmop_close($shm_id); return $result; } return false; } } View Code

 

3.内置的web服务器类

  这个主要是嵌入在ftp的http服务器类,功能不是很完善,进行ftp的管理还是可行的。不过需要注意的是,这个实现与apache等其他http服务器运行的方式可能有所不同。代码是驻留内存的。

  

class CWebServer{ protected $buffer_header = array(); protected $buffer_maxlen = 65535; //最大POST尺寸 const DATE_FORMAT_HTTP = 'D, d-M-Y H:i:s T'; const HTTP_EOF = "\r\n\r\n"; const HTTP_HEAD_MAXLEN = 8192; //http头最大长度不得超过2k const HTTP_POST_MAXLEN = 1048576;//1m const ST_FINISH = 1; //完成,进入处理流程 const ST_WAIT = 2; //等待数据 const ST_ERROR = 3; //错误,丢弃此包 private $requsts = array(); private $config = array(); public function log($msg,$level = 'debug'){ echo date('Y-m-d H:i:s').' ['.$level."]\t" .$msg."\n"; } public function __construct($config = array()){ $this->config = array( 'wwwroot' => __DIR__.'/wwwroot/', 'index' => 'index.php', 'path_deny' => array('/protected/'), ); } public function onReceive($serv,$fd,$data){ $ret = $this->checkData($fd, $data); switch ($ret){ case self::ST_ERROR: $serv->close($fd); $this->cleanBuffer($fd); $this->log('Recevie error.'); break; case self::ST_WAIT: $this->log('Recevie wait.'); return; default: break; } //开始完整的请求 $request = $this->requsts[$fd]; $info = $serv->connection_info($fd); $request = $this->parseRequest($request); $request['remote_ip'] = $info['remote_ip']; $response = $this->onRequest($request); $output = $this->parseResponse($request,$response); $serv->send($fd,$output); if(isset($request['head']['Connection']) && strtolower($request['head']['Connection']) == 'close'){ $serv->close($fd); } unset($this->requsts[$fd]); $_REQUEST = $_SESSION = $_COOKIE = $_FILES = $_POST = $_SERVER = $_GET = array(); } /** * 处理请求 * @param array $request * @return array $response * * $request=array( * 'time'=> * 'head'=>array( * 'method'=> * 'path'=> * 'protocol'=> * 'uri'=> * //other http header * '..'=>value * ) * 'body'=> * 'get'=>(if appropriate) * 'post'=>(if appropriate) * 'cookie'=>(if appropriate) * * * ) */ public function onRequest($request){ if($request['head']['path'][strlen($request['head']['path']) - 1] == '/'){ $request['head']['path'] .= $this->config['index']; } $response = $this->process($request); return $response; } /** * 清除数据 * @param unknown $fd */ public function cleanBuffer($fd){ unset($this->requsts[$fd]); unset($this->buffer_header[$fd]); } /** * 检查数据 * @param unknown $fd * @param unknown $data * @return string */ public function checkData($fd,$data){ if(isset($this->buffer_header[$fd])){ $data = $this->buffer_header[$fd].$data; } $request = $this->checkHeader($fd, $data); //请求头错误 if($request === false){ $this->buffer_header[$fd] = $data; if(strlen($data) > self::HTTP_HEAD_MAXLEN){ return self::ST_ERROR; }else{ return self::ST_WAIT; } } //post请求检查 if($request['head']['method'] == 'POST'){ return $this->checkPost($request); }else{ return self::ST_FINISH; } } /** * 检查请求头 * @param unknown $fd * @param unknown $data * @return boolean|array */ public function checkHeader($fd, $data){ //新的请求 if(!isset($this->requsts[$fd])){ //http头结束符 $ret = strpos($data,self::HTTP_EOF); if($ret === false){ return false; }else{ $this->buffer_header[$fd] = ''; $request = array(); list($header,$request['body']) = explode(self::HTTP_EOF, $data,2); $request['head'] = $this->parseHeader($header); $this->requsts[$fd] = $request; if($request['head'] == false){ return false; } } }else{ //post 数据合并 $request = $this->requsts[$fd]; $request['body'] .= $data; } return $request; } /** * 解析请求头 * @param string $header * @return array * array( * 'method'=>, * 'uri'=> * 'protocol'=> * 'name'=>value,... * * * * } */ public function parseHeader($header){ $request = array(); $headlines = explode("\r\n", $header); list($request['method'],$request['uri'],$request['protocol']) = explode(' ', $headlines[0],3); foreach ($headlines as $k=>$line){ $line = trim($line); if($k && !empty($line) && strpos($line,':') !== false){ list($name,$value) = explode(':', $line,2); $request[trim($name)] = trim($value); } } return $request; } /** * 检查post数据是否完整 * @param unknown $request * @return string */ public function checkPost($request){ if(isset($request['head']['Content-Length'])){ if(intval($request['head']['Content-Length']) > self::HTTP_POST_MAXLEN){ return self::ST_ERROR; } if(intval($request['head']['Content-Length']) > strlen($request['body'])){ return self::ST_WAIT; }else{ return self::ST_FINISH; } } return self::ST_ERROR; } /** * 解析请求 * @param unknown $request * @return Ambigous */ public function parseRequest($request){ $request['time'] = time(); $url_info = parse_url($request['head']['uri']); $request['head']['path'] = $url_info['path']; if(isset($url_info['fragment']))$request['head']['fragment'] = $url_info['fragment']; if(isset($url_info['query'])){ parse_str($url_info['query'],$request['get']); } //parse post body if($request['head']['method'] == 'POST'){ //目前只处理表单提交 if (isset($request['head']['Content-Type']) && substr($request['head']['Content-Type'], 0, 33) == 'application/x-www-form-urlencoded' || isset($request['head']['X-Request-With']) && $request['head']['X-Request-With'] == 'XMLHttpRequest'){ parse_str($request['body'],$request['post']); } } //parse cookies if(!empty($request['head']['Cookie'])){ $params = array(); $blocks = explode(";", $request['head']['Cookie']); foreach ($blocks as $b){ $_r = explode("=", $b, 2); if(count($_r)==2){ list ($key, $value) = $_r; $params[trim($key)] = trim($value, "\r\n \t\""); }else{ $params[$_r[0]] = ''; } } $request['cookie'] = $params; } return $request; } public function parseResponse($request,$response){ if(!isset($response['head']['Date'])){ $response['head']['Date'] = gmdate("D, d M Y H:i:s T"); } if(!isset($response['head']['Content-Type'])){ $response['head']['Content-Type'] = 'text/html;charset=utf-8'; } if(!isset($response['head']['Content-Length'])){ $response['head']['Content-Length'] = strlen($response['body']); } if(!isset($response['head']['Connection'])){ if(isset($request['head']['Connection']) && strtolower($request['head']['Connection']) == 'keep-alive'){ $response['head']['Connection'] = 'keep-alive'; }else{ $response['head']['Connection'] = 'close'; } } $response['head']['Server'] = CFtpServer::$software.'/'.CFtpServer::VERSION; $out = ''; if(isset($response['head']['Status'])){ $out .= 'HTTP/1.1 '.$response['head']['Status']."\r\n"; unset($response['head']['Status']); }else{ $out .= "HTTP/1.1 200 OK\r\n"; } //headers foreach($response['head'] as $k=>$v){ $out .= $k.': '.$v."\r\n"; } //cookies if($_COOKIE){ $arr = array(); foreach ($_COOKIE as $k => $v){ $arr[] = $k.'='.$v; } $out .= 'Set-Cookie: '.implode(';', $arr)."\r\n"; } //End $out .= "\r\n"; $out .= $response['body']; return $out; } /** * 处理请求 * @param unknown $request * @return array */ public function process($request){ $path = $request['head']['path']; $isDeny = false; foreach ($this->config['path_deny'] as $p){ if(strpos($path, $p) === 0){ $isDeny = true; break; } }
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

PHP를 사용하여 데이터베이스의 처음 몇 개의 레코드를 읽는 방법은 무엇입니까? PHP를 사용하여 데이터베이스의 처음 몇 개의 레코드를 읽는 방법은 무엇입니까? Mar 22, 2024 am 10:03 AM

PHP를 사용하여 데이터베이스의 처음 몇 개의 레코드를 읽는 방법은 무엇입니까? 웹 애플리케이션을 개발할 때 데이터베이스에서 데이터를 읽어 사용자에게 표시해야 하는 경우가 많습니다. 때로는 전체 내용이 아닌 데이터베이스의 처음 몇 개의 레코드만 표시해야 하는 경우도 있습니다. 이 기사에서는 PHP를 사용하여 데이터베이스의 처음 몇 개의 레코드를 읽는 방법과 특정 코드 예제를 제공하는 방법을 설명합니다. 먼저, 데이터베이스에 연결하고 작업하려는 테이블을 선택했다고 가정합니다. 다음은 간단한 데이터베이스 연결 예입니다.

Java로 데이터베이스에 연결할 때 SQLException을 처리하는 방법은 무엇입니까? Java로 데이터베이스에 연결할 때 SQLException을 처리하는 방법은 무엇입니까? Jun 24, 2023 pm 09:23 PM

Java 프로그램에서 데이터베이스에 연결하는 것은 매우 일반적인 작업입니다. 기성 클래스 라이브러리와 도구를 사용하여 데이터베이스에 연결할 수 있지만 프로그램 개발 중에 다양한 비정상적인 상황이 계속 발생할 수 있으며 그 중 SQLException이 그 중 하나입니다. SQLException은 Java에서 제공하는 예외 클래스로 쿼리문 오류, 테이블 존재하지 않음, 연결 끊김 등 데이터베이스에 접근할 때 발생하는 오류를 설명합니다. Java 프로그래머, 특히 JDBC(Java Data

Go 언어로 데이터베이스에 연결하는 방법 Go 언어로 데이터베이스에 연결하는 방법 Dec 12, 2023 pm 03:51 PM

Go 언어는 데이터베이스 드라이버 가져오기, 데이터베이스 연결 설정, SQL 문 실행, 준비된 문 사용 및 트랜잭션 처리를 통해 데이터베이스에 연결합니다. 자세한 소개: 1. 데이터베이스 드라이버를 가져오고 github.com/go-sql-driver/mysql 패키지를 사용하여 MySQL 데이터베이스에 연결합니다. 2. 데이터베이스 연결을 설정하고 데이터베이스 주소, 사용자를 포함한 데이터베이스 연결 정보를 제공합니다. 이름, 비밀번호 등 sql.Open 함수를 통해 데이터베이스 연결 등을 설정합니다.

Go 언어를 사용하여 데이터베이스에 연결: 애플리케이션 성능 및 효율성 향상 Go 언어를 사용하여 데이터베이스에 연결: 애플리케이션 성능 및 효율성 향상 Jan 23, 2024 am 08:57 AM

Go 언어를 사용하여 데이터베이스에 연결: 애플리케이션 성능 및 효율성 향상 애플리케이션이 개발되고 사용자 수가 증가함에 따라 데이터 저장 및 처리가 점점 더 중요해지고 있습니다. 애플리케이션의 성능과 효율성을 향상시키기 위해서는 데이터베이스를 적절하게 연결하고 운영하는 것이 중요한 부분입니다. 빠르고 안정적이며 동시성이 뛰어난 개발 언어인 Go 언어는 데이터베이스를 처리할 때 효율적인 성능을 제공할 수 있는 잠재력을 가지고 있습니다. 이 기사에서는 Go 언어를 사용하여 데이터베이스에 연결하는 방법을 소개하고 몇 가지 코드 예제를 제공합니다. Go 언어를 사용하여 데이터베이스 드라이버 설치

PHP를 사용하여 제품 재고 계산을 구현하는 단계 및 기술 PHP를 사용하여 제품 재고 계산을 구현하는 단계 및 기술 Aug 18, 2023 am 08:39 AM

PHP에서 제품 재고를 구현하는 단계 및 기술 전자상거래 업계에서 제품 재고 관리는 매우 중요한 작업입니다. 적시에 정확한 재고 계산을 통해 판매 지연, 고객 불만 및 재고 오류로 인한 기타 문제를 피할 수 있습니다. 이 기사에서는 PHP를 사용하여 제품 재고 계산을 구현하는 방법에 대한 단계와 기술을 소개하고 코드 예제를 제공합니다. 1단계: 데이터베이스 생성 먼저, 제품 정보를 저장할 데이터베이스를 생성해야 합니다. "inventory"라는 데이터베이스를 생성한 다음 "prod"라는 데이터베이스를 생성합니다.

Go 언어 시작하기: 데이터베이스 연결의 기본 개념 Go 언어 시작하기: 데이터베이스 연결의 기본 개념 Jan 23, 2024 am 08:17 AM

Go 언어 배우기: 데이터베이스 연결에 대한 기본 지식, 구체적인 코드 예제가 필요합니다. Go 언어는 간결하고 효율적인 기능으로 인해 점점 더 많은 개발자가 사랑하고 사용하고 있습니다. 개발 과정에서 데이터 읽기, 쓰기, 업데이트, 삭제 등의 작업을 수행하기 위해 데이터베이스와의 연결을 설정해야 하는 경우가 많습니다. 따라서 Go 언어로 데이터베이스에 연결하는 방법을 배우는 것은 매우 중요한 기술입니다. 데이터베이스 드라이버 Go 언어에서는 데이터베이스에 연결하려면 데이터베이스 드라이버가 필요합니다. 현재 Go 언어의 주요 데이터베이스 드라이버는 다음과 같습니다.

MySQL Jar 패키지의 중요한 기능은 무엇입니까? MySQL Jar 패키지의 중요한 기능은 무엇입니까? Mar 01, 2024 pm 09:45 PM

제목: MySQL Jar 패키지의 중요한 기능은 무엇인가요? MySQL은 많은 Java 개발자가 애플리케이션을 개발할 때 사용하는 널리 사용되는 관계형 데이터베이스 관리 시스템입니다. Java 프로젝트에서 MySQL 데이터베이스와 상호 작용하기 위해서는 일반적으로 MySQL에서 제공하는 공식 Java 드라이버 Jar 패키지가 사용됩니다. MySQL의 Jar 패키지에는 많은 중요한 기능이 있습니다. 이 기사에서는 그 중 일부를 소개하고 구체적인 코드 예제를 제공합니다. 1. MyS에 연결

MSSQL 데이터베이스에 연결하기 위해 Ubuntu 시스템에 PHP를 설치하고 구성하는 방법 MSSQL 데이터베이스에 연결하기 위해 Ubuntu 시스템에 PHP를 설치하고 구성하는 방법 Feb 29, 2024 am 10:06 AM

MSSQL 데이터베이스에 연결하기 위해 Ubuntu 시스템에 PHP를 설치하고 구성하는 것은 특히 웹 애플리케이션을 개발할 때 일반적인 작업입니다. 이 기사에서는 특정 코드 예제를 제공하면서 Ubuntu 시스템에서 PHP, MSSQL 확장을 설치하고 데이터베이스 연결을 구성하는 방법을 소개합니다. 1단계: PHP 및 MSSQL 확장 설치 PHP 설치 먼저 Ubuntu 시스템에 PHP가 설치되어 있는지 확인해야 합니다. PHP는 다음 명령으로 설치할 수 있습니다: sudoaptu

See all articles