IYUUAutoReseed初始化版本库v0.2.0

This commit is contained in:
iyuu.cn
2020-01-14 19:17:09 +08:00
commit 822e4a2270
78 changed files with 26926 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
/app/torrent
/app/config/config.php
/php-7.2.12-nts
/*.bat
/*.sh

58
LICENSE Normal file
View File

@@ -0,0 +1,58 @@
木兰宽松许可证, 第1版
2019年8月 http://license.coscl.org.cn/MulanPSL
您对“软件”的复制、使用、修改及分发受木兰宽松许可证第1版“本许可证”的如下条款的约束
0. 定义
“软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
“贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
“法人实体”是指提交贡献的机构及其“关联实体”。
“关联实体”是指对“本许可证”下的一方而言控制、受控制或与其共同受控制的机构此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
“贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
1. 授予版权许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
2. 授予专利许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括仅因您或他人修改“贡献”或其他结合而将必然会侵犯到的专利权利要求。如您或您的“关联实体”直接或间接地(包括通过代理、专利被许可人或受让人),就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
3. 无商标许可
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可但您为满足第4条规定的声明义务而必须使用除外。
4. 分发限制
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
5. 免责声明与责任限制
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
条款结束
如何将木兰宽松许可证第1版应用到您的软件
如果您希望将木兰宽松许可证第1版应用到您的新软件为了方便接收者查阅建议您完成如下三步
1 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
2 请您在软件包的一级目录下创建以“LICENSE”为名的文件将整个许可证文本放入该文件中
3 请将如下声明文本放入每个源文件的头部注释中。
Copyright (c) [2019] [name of copyright holder]
[Software Name] is licensed under the Mulan PSL v1.
You can use this software according to the terms and conditions of the Mulan PSL v1.
You may obtain a copy of Mulan PSL v1 at:
http://license.coscl.org.cn/MulanPSL
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
PURPOSE.
See the Mulan PSL v1 for more details.

141
app/Class/Bencode.php Normal file
View File

@@ -0,0 +1,141 @@
<?php
/**
* Created by PhpStorm.
* User: Rhilip
* Date: 2019/4/21
* Time: 21:12
*/
class Bencode
{
/**
* Decodes a BEncoded string to the following values:
* - Dictionary (starts with d, ends with e)
* - List (starts with l, ends with e
* - Integer (starts with i, ends with e
* - String (starts with number denoting number of characters followed by : and then the string)
*
* @see https://wiki.theory.org/index.php/BitTorrentSpecification
*
* @param string $data
* @param int $pos
* @return mixed
*/
public static function decode($data, &$pos = 0)
{
$start_decode = ($pos === 0);
if ($data[$pos] === 'd') {
$pos++;
$return = [];
while ($data[$pos] !== 'e') {
$key = self::decode($data, $pos);
$value = self::decode($data, $pos);
if ($key === null || $value === null) {
break;
}
if (!is_string($key)) {
throw new RuntimeException('Invalid key type, must be string: ' . gettype($key));
}
$return[$key] = $value;
}
ksort($return);
$pos++;
} elseif ($data[$pos] === 'l') {
$pos++;
$return = [];
while ($data[$pos] !== 'e') {
$value = self::decode($data, $pos);
$return[] = $value;
}
$pos++;
} elseif ($data[$pos] === 'i') {
$pos++;
$digits = strpos($data, 'e', $pos) - $pos;
$return = substr($data, $pos, $digits);
if ($return === '-0') {
throw new RuntimeException('Cannot have integer value -0');
}
$multiplier = 1;
if ($return[0] === '-') {
$multiplier = -1;
$return = substr($return, 1);
}
if (!ctype_digit($return)) {
throw new RuntimeException('Cannot have non-digit values in integer number: ' . $return);
}
$return = $multiplier * ((int)$return);
$pos += $digits + 1;
} else {
$digits = strpos($data, ':', $pos) - $pos;
$len = (int)substr($data, $pos, $digits);
$pos += ($digits + 1);
$return = substr($data, $pos, $len);
$pos += $len;
}
if ($start_decode) {
if ($pos !== strlen($data)) {
throw new RuntimeException('Could not fully decode bencode string');
}
}
return $return;
}
/**
* @param mixed $data
* @return string
*/
public static function encode($data)
{
if (is_array($data)) {
$return = '';
$check = -1;
$list = true;
foreach ($data as $key => $value) {
if ($key !== ++$check) {
$list = false;
break;
}
}
if ($list) {
$return .= 'l';
foreach ($data as $value) {
$return .= self::encode($value);
}
} else {
$return .= 'd';
foreach ($data as $key => $value) {
$return .= self::encode(strval($key));
$return .= self::encode($value);
}
}
$return .= 'e';
} elseif (is_integer($data)) {
$return = 'i' . $data . 'e';
} else {
$return = strlen($data) . ':' . $data;
}
return $return;
}
/**
* Given a path to a file, decode the contents of it
*
* @param string $path
* @return mixed
*/
public static function load($path)
{
if (is_file($path)) {
return self::decode(file_get_contents($path));
}
return null;
}
/**
* Given a path for a file, encode the contents of it
*
* @param string $path
* @param $data
* @return mixed
*/
public static function dump($path, $data)
{
return file_put_contents($path, self::encode($data));
}
}

313
app/Class/Function.php Normal file
View File

@@ -0,0 +1,313 @@
<?php
/**
* 调试函数
*/
function p($data, $echo=true){
$str='******************************'."\n";
// 如果是boolean或者null直接显示文字否则print
if (is_bool($data)) {
$show_data=$data ? 'true' : 'false';
}elseif (is_null($data)) {
$show_data='null';
}else{
$show_data=print_r($data,true);
}
$str.=$show_data;
$str.="\n".'******************************'."\n";
if($echo){
echo $str;
return null;
}
return $str;
}
/**
* 微信推送Server酱
*/
function sc($text='', $desp='')
{
global $configALL;
$token = $configALL['sc.ftqq.com'];
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
$postdata = http_build_query(array(
'text' => $text,
'desp' => $desp
));
$opts = array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
));
$context = stream_context_create($opts);
$result = file_get_contents('http://sc.ftqq.com/'.$token.'.send', false, $context);
return $result;
}
/**
* 微信推送 爱语飞飞
*/
function ff($text='', $desp='')
{
global $configALL;
$token = $configALL['iyuu.cn'];
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
$postdata = http_build_query(array(
'text' => $text,
'desp' => $desp
));
$opts = array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
));
$context = stream_context_create($opts);
$result = file_get_contents('http://iyuu.cn/'.$token.'.send', false, $context);
return $result;
}
/**
* 微信推送 爱语飞飞
* @param array $torrent 种子数组
Array
(
[id] => 118632
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间4时13分]
[details] => https://xxx.me/details.php?id=118632
[download] => https://xxx.me/download.php?id=118632
[filename] => 118632.torrent
[type] => 0
[sticky] => 1
[time] => Array
(
[0] => "2019-11-16 20:41:53">4时13分
[1] => "2019-11-16 14:41:53">1时<br />46分
)
[comments] => 0
[size] => 5232.64MB
[seeders] => 69
[leechers] => 10
[completed] => 93
[percentage] => 100%
[owner] => 匿名
)
*/
function send($site = '', $torrent = array())
{
$br = "\r\n";
$text = $site. ' 免费:' .$torrent['filename']. ',添加成功';
$desp = '主标题:'.$torrent['h1'] . $br;
if ( isset($torrent['title']) ) {
$desp .= '副标题:'.$torrent['title']. $br;
}
if ( isset($torrent['size']) ) {
$desp .= '大小:'.$torrent['size']. $br;
}
if ( isset($torrent['seeders']) ) {
$desp .= '做种数:'.$torrent['seeders']. $br;
}
if ( isset($torrent['leechers']) ) {
$desp .= '下载数:'.$torrent['leechers']. $br;
}
if ( isset($torrent['owner']) ) {
$desp .= '发布者:'.$torrent['owner']. $br;
}
return ff($text, $desp);
}
/**
* @brief 下载种子
* @param string $url 种子URL
* @param string $cookies 模拟登陆的cookie
* @return mixed 返回的数据
*/
function download($url, $cookies, $useragent, $method = 'GET')
{
$header = array(
"Content-Type:application/x-www-form-urlencoded",
'User-Agent: '.$useragent);
$ch = curl_init();
if($method === 'POST'){
curl_setopt($ch, CURLOPT_POST, true );
}
if(stripos($url, 'https://') !== FALSE) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_SSLVERSION, 1);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER,$header);
curl_setopt($ch, CURLOPT_COOKIE,$cookies);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT,60);
curl_setopt($ch, CURLOPT_TIMEOUT,600);
$data = curl_exec($ch);
$status = curl_getinfo($ch);
curl_close($ch);
if (isset($status['http_code']) && $status['http_code'] == 200) {
return $data;
}
if (isset($status['http_code']) && $status['http_code'] == 302) {
return download($status['redirect_url'], $cookies, $useragent);
}
return $data;
}
/**
* @brief 文件大小格式化为MB
* @param string $from 文件大小
* @return int 单位MB
*/
function convertToMB($from){
$number=substr($from,0,-2);
switch(strtoupper(substr($from,-2))){
case "KB":
return $number/1024;
case "MB":
return $number;
case "GB":
return $number*pow(1024,1);
case "TB":
return $number*pow(1024,2);
case "PB":
return $number*pow(1024,3);
default:
return $from;
}
}
/**
* @brief 种子过滤器
* @param string $site 站点标识
* @param array $torrent 种子数组
* Array
(
[id] => 118632
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间4时13分]
[details] => https://xxx.me/details.php?id=118632
[download] => https://xxx.me/download.php?id=118632
[filename] => 118632.torrent
[type] => 0
[sticky] => 1
[time] => Array
(
[0] => "2019-11-16 20:41:53">4时13分
[1] => "2019-11-16 14:41:53">1时<br />46分
)
[comments] => 0
[size] => 5232.64MB
[seeders] => 69
[leechers] => 10
[completed] => 93
[percentage] => 100%
[owner] => 匿名
)
* @return bool 或 string false不过滤
*/
function filter($site = '', $torrent = array()){
global $configALL;
$config = $configALL[$site];
$filter = array();
// 读取配置
if (isset($configALL['default']['filter']) || isset($config['filter'])) {
$filter = isset($config['filter']) && $config['filter'] ? $config['filter'] : $configALL['default']['filter'];
}else {
return false;
}
$filename = $torrent['filename'];
// 兼容性
if ( empty($torrent['size']) ) {
return false;
}
// 大小过滤
$size = convertToMB($torrent['size']);
$min = isset($filter['size']['min']) ? convertToMB($filter['size']['min']) : 0;
$max = isset($filter['size']['max']) ? convertToMB($filter['size']['max']) : 2097152; //默认 2097152MB = 2TB
if ($min > $size || $size > $max) {
return $filename. ' ' .$size. 'MB被大小过滤';
}
// 兼容性
if ( empty($torrent['seeders']) ) {
return false;
}
// 种子数过滤
$seeders = $torrent['seeders'];
$min = isset($filter['seeders']['min']) ? $filter['seeders']['min'] : 1; //默认 1
$max = isset($filter['seeders']['max']) ? $filter['seeders']['max'] : 3; //默认 3
if ($min > $seeders || $seeders > $max) {
return $filename. ' 当前做种' .$seeders. '人,被过滤';
}
// 兼容性
if ( empty($torrent['leechers']) ) {
return false;
}
// 下载数过滤
$leechers = $torrent['leechers'];
$min = isset($filter['leechers']['min']) ? $filter['leechers']['min'] : 0; //默认
$max = isset($filter['leechers']['max']) ? $filter['leechers']['max'] : 30000; //默认
if ($min > $leechers || $leechers > $max) {
return $filename. ' 当前下载' .$leechers. '人,被过滤';
}
// 兼容性
if ( empty($torrent['completed']) ) {
return false;
}
// 完成数过滤
$completed = $torrent['completed'];
$min = isset($filter['completed']['min']) ? $filter['completed']['min'] : 0; //默认
$max = isset($filter['completed']['max']) ? $filter['completed']['max'] : 30000; //默认
if ($min > $completed || $completed > $max) {
return $filename. ' 已完成数' .$completed. '人,被过滤';
}
return false;
}
/**
* 奇数
*/
function oddFilter($var)
{
// 返回$var最后一个二进制位
// 为1则保留奇数的二进制的最后一位肯定是1
return($var & 1);
}
/**
* 偶数
*/
function evenFilter($var)
{
// 返回$var最后一个二进制位
// 为0则保留偶数的二进制的最后一位肯定是0
return(!($var & 1));
}
/**
* 发布员签名
* 注意同时配置iyuu.cn与secret时优先使用secret。
*/
function sign( $timestamp ){
global $configALL;
// 爱语飞飞
$token = isset($configALL['iyuu.cn']) && $configALL['iyuu.cn'] ? $configALL['iyuu.cn'] : '';
// 鉴权
$token = isset($configALL['secret']) && $configALL['secret'] ? $configALL['secret'] : $token;
return sha1($timestamp . $token);
}
/**
* @brief 分离token中的用户uid
* token算法IYUU + uid + T + sha1(openid+time+盐)
* @param string $token 用户请求token
*/
function getUid($token){
//验证是否IYUU开头strpos($token,'T')<15,token总长度小于60(40+10+5)
return (strlen($token)<60)&&(strpos($token,'IYUU')===0)&&(strpos($token,'T')<15) ? substr($token,4,strpos($token,'T')-4): false;
}

332
app/Class/IFile.php Normal file
View File

@@ -0,0 +1,332 @@
<?php
/**
* @brief 文件处理
* @version 0.6
*/
/**
* @class IFile
* @brief IFile 文件处理类
*/
class IFile
{
private $resource = null; //文件资源句柄
/**
* @brief 构造函数,打开资源流,并独占锁定
* @param String $fileName 文件路径名
* @param String $mode 操作方式默认为读操作可供选择的项为r,r+,w+,w+,a,a+
* @note $mod'r' 只读方式打开,将文件指针指向文件头
* 'r+' 读写方式打开,将文件指针指向文件头
* 'w' 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
* 'w+' 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
* 'a' 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
* 'a+' 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
*/
function __construct($fileName,$mode='r')
{
$dirName = dirname($fileName);
$baseName = basename($fileName);
//检查并创建文件夹
self::mkdir($dirName);
$this->resource = fopen($fileName,$mode.'b');
if($this->resource)
{
flock($this->resource,LOCK_EX);
}
}
/**
* @brief 获取文件内容
* @return String 文件内容
*/
public function read()
{
$content = null;
while(!feof($this->resource))
{
$content.= fread($this->resource,1024);
}
return $content;
}
/**
* @brief 文件写入操作
* @param String $content 要写入的文件内容
* @return Int or false 写入的字符数; false:写入失败;
*/
public function write($content)
{
$worldsnum = fwrite($this->resource,$content);
$this->save();
return is_bool($worldsnum) ? false : $worldsnum;
}
/**
* @brief 清空目录下的所有文件
* @return bool false:失败; true:成功;
*/
public static function clearDir($dir)
{
if($dir[0] != '.' && is_dir($dir) && is_writable($dir))
{
$dirRes = opendir($dir);
while( false !== ($fileName = readdir($dirRes)) )
{
if($fileName[0] !== '.')
{
$fullpath = $dir.'/'.$fileName;
if(is_file($fullpath))
{
self::unlink($fullpath);
}
else
{
self::clearDir($fullpath);
rmdir($fullpath);
}
}
}
closedir($dirRes);
return true;
}
else
{
return false;
}
}
/**
* @brief 获取文件信息
* @param String $fileName 文件路径
* @return array or null array:文件信息; null:文件不存在;
*/
public static function getInfo($fileName)
{
if(is_file($fileName))
return stat($fileName);
else
return null;
}
/**
* @brief 创建文件夹
* @param String $path 路径
* @param int $chmod 文件夹权限
* @note $chmod 参数不能是字符串(加引号)否则linux会出现权限问题
*/
public static function mkdir($path,$chmod=0777)
{
return is_dir($path) or (self::mkdir(dirname($path),$chmod) and mkdir($path,$chmod));
}
/**
* @brief 复制文件
* @param String $from 源文件路径
* @param String $to 目标文件路径
* @param String $mod 操作模式c:复制(默认); x:剪切(删除$from文件)
* @return bool 操作结果 true:成功; false:失败;
*/
public static function copy($from,$to,$mode = 'c')
{
$dir = dirname($to);
//创建目录
self::mkdir($dir);
copy($from,$to);
if(is_file($to))
{
if($mode == 'x')
{
self::unlink($from);
}
return true;
}
else
{
return false;
}
}
/**
* @brief 删除文件
* @param String $fileName 文件路径
* @return bool 操作结果 false:删除失败;
*/
public static function unlink($fileName)
{
if(is_file($fileName) && is_writable($fileName))
{
return unlink($fileName);
}
else
return false;
}
/**
* @brief 删除$dir文件夹 或者 其下所有文件
* @param String $dir 文件路径
* @param bool $recursive 是否强制删除如果强制删除则递归删除该目录下的全部文件默认为false
* @return bool true:删除成功; false:删除失败;
*/
public static function rmdir($dir,$recursive = false)
{
if(is_dir($dir) && is_writable($dir))
{
//强制删除
if($recursive == true)
{
self::clearDir($dir);
return self::rmdir($dir,false);
}
//非强制删除
else
{
if(rmdir($dir))
{
return true;
}
else
{
return false;
}
}
}
}
/**
* @brief 获取文件类型
* @param String $fileName 文件名
* @return String $filetype 文件类型
* @note 如果文件不存在返回false,如果文件后缀名不在识别列表之内返回NULL对于docx及elsx格式文档识别在会出现识别为ZIP格式的错误这是office2007的bug目前尚未修复请谨慎使用
*/
public static function getFileType($fileName)
{
$filetype = null;
if(!is_file($fileName))
{
return false;
}
$fileRes = fopen($fileName,"rb");
if(!$fileRes)
{
return false;
}
$bin= fread($fileRes, 2);
fclose($fileRes);
if($bin != null)
{
$strInfo = unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$typelist = self::getTypeList();
foreach($typelist as $val)
{
if(strtolower($val[0]) == strtolower($typeCode))
{
if($val[0] == 8075)
{
return array('zip','docx','xlsx');
}
else
{
return $val[1];
}
}
}
}
return $filetype;
}
/**
* @brief 获取文件类型映射关系
* @return array 文件类型映射关系数组
*/
public static function getTypeList()
{
return array(
array('255216','jpg'),
array('13780','png'),
array('7173','gif'),
array('6677','bmp'),
array('6063','xml'),
array('60104','html'),
array('208207','xls/doc'),
array('8075','zip'),
array('8075','docx'),
array('8075','xlsx'),
array("8297","rar"),
);
}
/**
* @brief 获取文件大小
* @param String $fileName 文件名
* @return Int 文件大小的字节数,如果文件无效则返回 NULL
*/
public static function getFileSize($fileName)
{
return is_file($fileName) ? filesize($fileName):null;
}
/**
* @brief 检测文件夹是否为空
* @param String $dir 路径地址
* @return bool true:$dir为空目录; false:$dir为非空目录;
*/
public static function isEmptyDir($dir)
{
if(is_dir($dir))
{
$isEmpty = true;
$dirRes = opendir($dir);
while(false !== ($fileName = readdir($dirRes)))
{
if($fileName!='.' && $fileName!='..')
{
$isEmpty = false;
break;
}
}
closedir($dirRes);
return $isEmpty;
}
}
/**
* @brief 释放文件锁定
*/
public function save()
{
flock($this->resource,LOCK_UN);
}
/**
* @brief 获取文件扩展名
* @param String $fileName 文件名
* @return String 文件后缀名
*/
public static function getFileSuffix($fileName)
{
$fileInfoArray = pathinfo($fileName);
return $fileInfoArray['extension'];
}
/**
* @brief 析构函数,释放文件连接句柄
*/
function __destruct()
{
if(is_resource($this->resource))
{
fclose($this->resource);
}
}
}

71
app/Class/Oauth.php Normal file
View File

@@ -0,0 +1,71 @@
<?php
/**
* IYUU用户注册、认证
*/
use Curl\Curl;
class Oauth{
// 合作的站点
public static $sites = ['ourbits'];
// 爱语飞飞token
public static $token = '';
// 合作站点用户id
public static $user_id = 0;
// 合作站点密钥
public static $passkey = '';
// 合作站名字
public static $site = '';
/**
* 初始化配置
*/
public static function init(){
global $configALL;
foreach (self::$sites as $name) {
if (isset($configALL[$name]['passkey']) && $configALL[$name]['passkey'] && isset($configALL[$name]['id']) && $configALL[$name]['id'] ) {
self::$token = self::getSign();
self::$user_id = $configALL[$name]['id'];
self::$passkey = sha1( $configALL[$name]['passkey'] ); // 避免泄露用户passkey秘钥
self::$site = $name;
return true;
}
}
echo "-----缺少合作站点登录参数token, user_id, passkey, site \n";
echo "-----当前正在使用测试接口,功能可能会受到限制! \n\n";
return false;
}
/**
* 从配置文件内读取爱语飞飞token作为鉴权参数
*/
public static function getSign(){
global $configALL;
// 爱语飞飞
$token = isset($configALL['iyuu.cn']) && $configALL['iyuu.cn'] ? $configALL['iyuu.cn'] : '';
if (empty($token) || strlen($token)<46) {
echo "缺少辅种接口请求参数爱语飞飞token \n";
echo "请访问https://iyuu.cn 用微信扫码申请并填入配置文件config.php内。\n\n";
exit(1);
}
return $token;
}
/**
* 用户注册与登录
* 作用在服务器端实现微信用户与合作站点用户id的关联
* 参数爱语飞飞token + 合作站点用户id + sha1(合作站点密钥passkey) + 合作站点标识
*/
public static function login($apiUrl = ''){
$is_oauth = self::init();
if ( $is_oauth ) {
$curl = new Curl();
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
$data = [
'token' => self::$token,
'id' => self::$user_id,
'passkey'=> self::$passkey,
'site' => self::$site,
];
$res = $curl->get($apiUrl, $data);
p($res->response);
return true;
}
return false;
}
}

368
app/Class/Rpc.php Normal file
View File

@@ -0,0 +1,368 @@
<?php
/**
* Rpc操作类
*/
class Rpc
{
/**
* 版本号
* @var string
*/
const VER = '0.0.1';
// 下载种子的请求类型 GET POST
public static $method = 'GET';
// RPC连接池
public static $links = array();
/**
* cookie
*/
public static $cookies = '';
/**
* 浏览器 User-Agent
*/
public static $userAgent = '';
/**
* passkey
*/
public static $passkey = '';
/**
* 客户端配置
*/
public static $clients = '';
/**
* 监控目录
*/
public static $watch = '';
/**
* 种子存放路径
*/
public static $torrentDir = '';
/**
* 工作模式
*/
public static $workingMode = '';
// 站点标识
public static $site = '';
/**
* 负载均衡 控制变量
*/
public static $RPC_Key = 0;
/**
* 退出状态码
*/
public static $ExitCode = 0;
/**
* 初始化
*/
public static function init($site = '', $method = 'GET'){
global $configALL;
self::$site = $site;
self::$method = strtoupper($method);
$config = $configALL[$site];
self::$cookies = $config['cookie'];
self::$userAgent = isset($config['userAgent']) && $config['userAgent'] ? $config['userAgent'] : $configALL['default']['userAgent'];
self::$clients = isset($config['clients']) && $config['clients'] ? $config['clients'] : $configALL['default']['clients'];
self::$workingMode = isset($config['workingMode']) && $config['workingMode'] ? $config['workingMode'] : 0;
$watch = isset($config['watch']) && $config['watch'] ? $config['watch'] : $configALL['default']['watch'];
self::$watch = rtrim($watch,'/') . DS;
self::$torrentDir = TORRENT_PATH . $site . DS;
// 建立目录
IFile::mkdir(self::$torrentDir);
self::links();
}
/**
* 连接远端RPC服务器
*
* @param string
* @return array
*/
public static function links()
{
if(self::$workingMode === 1 && empty(self::$links)){
foreach ( self::$clients as $k => $v ){
// 跳过未配置的客户端
if (empty($v['username']) || empty( $v['password'])) {
unset(self::$clients[$k]);
echo "clients_".$k." 用户名或密码未配置,已跳过 \n\n";
continue;
}
try
{
switch($v['type']){
case 'transmission':
self::$links[$k]['rpc'] = new TransmissionRPC($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->sstats();
print $v['type'].''.$v['host']." Rpc连接成功 [{$result->result}] \n";
break;
case 'qBittorrent':
self::$links[$k]['rpc'] = new qBittorrent($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->appVersion();
print $v['type'].''.$v['host']." Rpc连接成功 [{$result}] \n";
break;
case 'uTorrent':
self::$links[$k]['rpc'] = new uTorrent($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->getBuild();
print $v['type'].''.$v['host']." Rpc连接 [{$result}] \n";
break;
default:
echo '[ERROR] '.$v['type'];
exit(1);
break;
}
self::$links[$k]['type'] = $v['type'];
self::$links[$k]['downloadDir'] = $v['downloadDir'];
} catch (Exception $e) {
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
exit(1);
}
}
}
return true;
}
/**
* @brief 添加下载任务
* @param string $torrent 种子元数据
* @param string $save_path 保存路径
* @return bool
*/
public static function add($torrent, $save_path = '', $extra_options = array())
{
switch( (int)self::$workingMode ){
case 0: // watch默认工作模式
// 复制到watch目录
copy($torrent,$save_path);
if(is_file($save_path)){
print "********watch模式下载任务添加成功 \n\n";
return true;
}else {
print "-----watch模式下载任务添加失败!!! \n\n";
}
break;
case 1: //负载均衡模式
try
{
$is_url = false;
if( (strpos($torrent,'http://')===0) || (strpos($torrent,'https://')===0) || (strpos($torrent,'magnet:?xt=urn:btih:')===0) ){
$is_url = true;
}
// 负载均衡
$rpcKey = self::$RPC_Key;
echo '选中:负载均衡'.$rpcKey."\n";
self::rpcSelect();
// 调试
#p($result);
// 下载服务器类型 判断
$type = self::$links[$rpcKey]['type'];
switch($type){
case 'transmission':
if( $is_url ){
echo 'add';
$result = self::$links[$rpcKey]['rpc']->add( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子URL添加下载任务
}else{
echo 'add_metainfo';
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子文件添加下载任务
}
$id = $name = '';
if( isset($result->arguments->torrent_duplicate) ){
$id = $result->arguments->torrent_duplicate->id;
$name = $result->arguments->torrent_duplicate->name;
}elseif( isset($result->arguments->torrent_added) ){
$id = $result->arguments->torrent_added->id;
$name = $result->arguments->torrent_added->name;
}
if(!$id){
print "-----RPC添加种子任务失败 [{$result->result}] \n\n";
}else{
print "********RPC添加下载任务成功 [{$result->result}] (id=$id) \n\n";
// 新添加的任务,开始
self::$links[$rpcKey]['rpc']->start( $id );
return true;
}
break;
case 'qBittorrent':
if( $is_url ){
echo 'add';
$result = self::$links[$rpcKey]['rpc']->add( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子URL添加下载任务
}else{
echo 'add_metainfo';
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子文件添加下载任务
}
if ($result === 'Ok.') {
print "********RPC添加下载任务成功 [{$result}] \n\n";
return true;
} else {
print "-----RPC添加种子任务失败 [{$result}] \n\n";
}
break;
default:
echo '[ERROR] '.$type;
break;
}
} catch (Exception $e) {
die('[ERROR] ' . $e->getMessage() . PHP_EOL);
}
break;
case 2:
echo "\n\n";
# 暂未开放
break;
default:
echo "\n\n";
break;
}
return false;
}
/**
* 负载均衡 选择算法
*
* @param
* @return
*/
public static function rpcSelect()
{
$clientsConut = count(self::$clients);
if( $clientsConut > 1 ){
if( $clientsConut > (self::$RPC_Key+1) ){
self::$RPC_Key++;
}else{
self::$RPC_Key = 0;
}
}
}
/**
* @brief 种子处理函数
* @param array $data 种子数组
* Array
(
[id] => 118632
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDxxx
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间4时13分]
[details] => https://XXX.me/details.php?id=118632
[download] => https://XXX.me/download.php?id=118632
[filename] => 118632.torrent
[type] => 0
[sticky] => 1
[time] => Array
(
[0] => "2019-11-16 20:41:53">4时13分
[1] => "2019-11-16 14:41:53">1时<br />46分
)
[comments] => 0
[size] => 5232.64MB
[seeders] => 69
[leechers] => 10
[completed] => 93
[percentage] => 100%
[owner] => 匿名
)
* @return
*/
public static function call($data = array())
{
foreach ($data as $key => $value) {
// 控制台打印
echo '主标题:'.$value['h1']."\n";
echo '副标题:'.$value['title']."\n";
echo '详情页:'.$value['details']."\n";
if ( $value['type'] != 0 ) {
echo "-----非免费,已忽略! \n\n";
continue;
}
if ( isset($value['hr']) && ($value['hr'] == 1) ) {
echo "-----HR种子已忽略 \n\n";
continue;
}
// 下载任务的可选参数
$extra_options = array();
// 保存的文件名
$filename = $value['id'] . '.torrent';
// 默认watch工作模式复制到此目录
$to = self::$watch . $filename;
// 种子完整存放路径
$torrentFile = self::$torrentDir . $filename;
if(is_file($torrentFile)){
$fileSize = filesize($torrentFile); //失败会返回false 或 00代表上次下载失败
if ( !empty($fileSize) ) {
//种子已经存在
echo '-----存在旧种子:'.$filename."\n\n";
continue;
}
// 删除下载错误的文件
IFile::unlink($torrentFile);
}
// 调用过滤函数
$isFilter = filter(self::$site, $value);
if ( is_string( $isFilter ) ) {
echo "-----" .$isFilter. "\n\n";
continue;
}
//种子不存在
echo '正在下载新种子... '.$value['download']." \n";
// 创建文件、下载种子以二进制写入
$content = '';
$content = download($value['download'], self::$cookies, self::$userAgent, self::$method);
#p($content);
// 文件句柄
$resource = fopen($torrentFile, "wb");
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, $content);
// 关闭
fclose($resource);
// 判断
if(is_bool($worldsnum)){
print "种子下载失败!!! \n\n";
IFile::unlink($torrentFile);
continue;
}else{
print "成功下载种子" . $filename . ',共计:' . $worldsnum . "字节 \n";
sleep(mt_rand(2,10));
$ret = false;
$rpcKey = self::$RPC_Key;
switch( (int)self::$workingMode ){
case 0: //默认工作模式
$ret = self::add($torrentFile, $to);
break;
case 1: //负载均衡模式
$type = self::$links[$rpcKey]['type'];
// 下载服务器类型
switch ($type) {
case 'transmission':
# code...
break;
case 'qBittorrent':
$extra_options['name'] = 'torrents';
$extra_options['filename'] = $filename;
$extra_options['autoTMM'] = 'false'; //关闭自动种子管理
break;
default:
# code...
break;
}
// 种子文件添加下载任务
$ret = self::add($content, $to, $extra_options);
break;
case 2:
echo "\n\n";
# 暂未开放
break;
default:
echo "\n\n";
break;
}
global $configALL;
if( isset($configALL['iyuu.cn']) && ($ret === true) ){
send(self::$site, $value);
}
}
}
return true;
}
}

View File

@@ -0,0 +1,708 @@
<?php
/**
* Transmission bittorrent client/daemon RPC communication class
* Copyright (C) 2010 Johan Adriaans <johan.adriaans@gmail.com>,
* Bryce Chidester <bryce@cobryce.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* PHP version specific information
* version_compare() (PHP 4 >= 4.1.0, PHP 5)
* ctype_digit() (PHP 4 >= 4.0.4, PHP 5)
* stream_context_create (PHP 4 >= 4.3.0, PHP 5)
* PHP Class support (PHP 5) (PHP 4 might work, untested)
*/
/**
* A friendly little version check...
*/
if ( version_compare( PHP_VERSION, TransmissionRPC::MIN_PHPVER, '<' ) )
die( "The TransmissionRPC class requires PHP version {TransmissionRPC::TRANSMISSIONRPC_MIN_PHPVER} or above." . PHP_EOL );
/**
* Transmission bittorrent client/daemon RPC communication class
*
* Usage example:
* <code>
* $rpc = new TransmissionRPC($rpc_url);
* $result = $rpc->add_file( $url_or_path_to_torrent, $target_folder );
* </code>
*
*/
class TransmissionRPC
{
/**
* User agent used in all http communication
*/
const HTTP_UA = 'TransmissionRPC for PHP/0.3';
/**
* Minimum PHP version required
* 5.2.10 implemented the required http stream ignore_errors option
*/
const MIN_PHPVER = '5.2.10';
/**
* The URL to the bittorent client you want to communicate with
* the port (default: 9091) can be set in you Transmission preferences
* @var string
*/
public $url = '';
/**
* If your Transmission RPC requires authentication, supply username here
* @var string
*/
public $username = '';
/**
* If your Transmission RPC requires authentication, supply password here
* @var string
*/
public $password = '';
/**
* Return results as an array, or an object (default)
* @var bool
*/
public $return_as_array = false;
/**
* Print debugging information, default is off
* @var bool
*/
public $debug = false;
/**
* Transmission RPC version
* @var int
*/
protected $rpc_version = 0;
/**
* Transmission uses a session id to prevent CSRF attacks
* @var string
*/
protected $session_id = '';
/**
* Default values for stream context
* @var array
*/
private $default_context_opts = array( 'http' => array(
'user_agent' => self::HTTP_UA,
'timeout' => '60', // Don't want to be too slow
'ignore_errors' => true, // Leave the error parsing/handling to the code
)
);
/**
* Constants for torrent status
*/
const TR_STATUS_STOPPED = 0;
const TR_STATUS_CHECK_WAIT = 1;
const TR_STATUS_CHECK = 2;
const TR_STATUS_DOWNLOAD_WAIT = 3;
const TR_STATUS_DOWNLOAD = 4;
const TR_STATUS_SEED_WAIT = 5;
const TR_STATUS_SEED = 6;
const RPC_LT_14_TR_STATUS_CHECK_WAIT = 1;
const RPC_LT_14_TR_STATUS_CHECK = 2;
const RPC_LT_14_TR_STATUS_DOWNLOAD = 4;
const RPC_LT_14_TR_STATUS_SEED = 8;
const RPC_LT_14_TR_STATUS_STOPPED = 16;
/**
* Start one or more torrents
*
* @param int|array ids A list of transmission torrent ids
*/
public function start ( $ids )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array( "ids" => $ids );
return $this->request( "torrent-start", $request );
}
/**
* Stop one or more torrents
*
* @param int|array ids A list of transmission torrent ids
*/
public function stop ( $ids )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array( "ids" => $ids );
return $this->request( "torrent-stop", $request );
}
/**
* Reannounce one or more torrents
*
* @param int|array ids A list of transmission torrent ids
*/
public function reannounce ( $ids )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array( "ids" => $ids );
return $this->request( "torrent-reannounce", $request );
}
/**
* Verify one or more torrents
*
* @param int|array ids A list of transmission torrent ids
*/
public function verify ( $ids )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array( "ids" => $ids );
return $this->request( "torrent-verify", $request );
}
/**
* Get information on torrents in transmission, if the ids parameter is
* empty all torrents will be returned. The fields array can be used to return certain
* fields. Default fields are: "id", "name", "status", "doneDate", "haveValid", "totalSize".
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
*
* @param array fields An array of return fields
* @param int|array ids A list of transmission torrent ids
*
Request:
{
"arguments": {
"fields": [ "id", "name", "totalSize" ],
"ids": [ 7, 10 ]
},
"method": "torrent-get",
"tag": 39693
}
Response:
{
"arguments": {
"torrents": [
{
"id": 10,
"name": "Fedora x86_64 DVD",
"totalSize": 34983493932,
},
{
"id": 7,
"name": "Ubuntu x86_64 DVD",
"totalSize", 9923890123,
}
]
},
"result": "success",
"tag": 39693
}
*/
public function get ( $ids = array(), $fields = array() )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
if ( count( $fields ) == 0 ) $fields = array( "id", "name", "status", "doneDate", "haveValid", "totalSize" ); // Defaults
$request = array(
"fields" => $fields,
"ids" => $ids
);
return $this->request( "torrent-get", $request );
}
/**
* Set properties on one or more torrents, available fields are:
* "bandwidthPriority" | number this torrent's bandwidth tr_priority_t
* "downloadLimit" | number maximum download speed (in K/s)
* "downloadLimited" | boolean true if "downloadLimit" is honored
* "files-wanted" | array indices of file(s) to download
* "files-unwanted" | array indices of file(s) to not download
* "honorsSessionLimits" | boolean true if session upload limits are honored
* "ids" | array torrent list, as described in 3.1
* "location" | string new location of the torrent's content
* "peer-limit" | number maximum number of peers
* "priority-high" | array indices of high-priority file(s)
* "priority-low" | array indices of low-priority file(s)
* "priority-normal" | array indices of normal-priority file(s)
* "seedRatioLimit" | double session seeding ratio
* "seedRatioMode" | number which ratio to use. See tr_ratiolimit
* "uploadLimit" | number maximum upload speed (in K/s)
* "uploadLimited" | boolean true if "uploadLimit" is honored
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for more information
*
* @param array arguments An associative array of arguments to set
* @param int|array ids A list of transmission torrent ids
*/
public function set ( $ids = array(), $arguments = array() )
{
// See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
if ( !isset( $arguments['ids'] ) ) $arguments['ids'] = $ids; // Any $ids given in $arguments overrides the method parameter
return $this->request( "torrent-set", $arguments );
}
/**
* Add a new torrent
*
* Available extra options:
* key | value type & description
* ---------------------+-------------------------------------------------
* "download-dir" | string path to download the torrent to
* "filename" | string filename or URL of the .torrent file
* "metainfo" | string base64-encoded .torrent content
* "paused" | boolean if true, don't start the torrent
* "peer-limit" | number maximum number of peers
* "bandwidthPriority" | number torrent's bandwidth tr_priority_t
* "files-wanted" | array indices of file(s) to download
* "files-unwanted" | array indices of file(s) to not download
* "priority-high" | array indices of high-priority file(s)
* "priority-low" | array indices of low-priority file(s)
* "priority-normal" | array indices of normal-priority file(s)
*
* Either "filename" OR "metainfo" MUST be included.
* All other arguments are optional.
*
* @param torrent_location The URL or path to the torrent file
* @param save_path Folder to save torrent in
* @param extra options Optional extra torrent options
*/
public function add_file ( $torrent_location, $save_path = '', $extra_options = array() )
{
if(!empty($save_path)) $extra_options['download-dir'] = $save_path;
$extra_options['filename'] = $torrent_location;
return $this->request( "torrent-add", $extra_options );
}
/**
* Add a torrent using the raw torrent data
*
* @param torrent_metainfo The raw, unencoded contents (metainfo) of a torrent
* @param save_path Folder to save torrent in
* @param extra options Optional extra torrent options
*/
public function add_metainfo ( $torrent_metainfo, $save_path = '', $extra_options = array() )
{
$extra_options['download-dir'] = $save_path;
$extra_options['metainfo'] = base64_encode( $torrent_metainfo );
return $this->request( "torrent-add", $extra_options );
}
/* Add a new torrent using a file path or a URL (For backwards compatibility)
* @param torrent_location The URL or path to the torrent file
* @param save_path Folder to save torrent in
* @param extra options Optional extra torrent options
*/
public function add ( $torrent_location, $save_path = '', $extra_options = array() )
{
return $this->add_file( $torrent_location, $save_path, $extra_options );
}
/**
* Remove torrent from transmission
*
* @param bool delete_local_data Also remove local data?
* @param int|array ids A list of transmission torrent ids
*/
public function remove ( $ids, $delete_local_data = false )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array(
"ids" => $ids,
"delete-local-data" => $delete_local_data
);
return $this->request( "torrent-remove", $request );
}
/**
* Move local storage location
*
* @param int|array ids A list of transmission torrent ids
* @param string target_location The new storage location
* @param string move_existing_data Move existing data or scan new location for available data
*/
public function move ( $ids, $target_location, $move_existing_data = true )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array(
"ids" => $ids,
"location" => $target_location,
"move" => $move_existing_data
);
return $this->request( "torrent-set-location", $request );
}
/**
* 3.7. Renaming a Torrent's Path
*
* Method name: "torrent-rename-path"
*
* For more information on the use of this function, see the transmission.h
* documentation of tr_torrentRenamePath(). In particular, note that if this
* call succeeds you'll want to update the torrent's "files" and "name" field
* with torrent-get.
*
* Request arguments:
*
* string | value type & description
* ---------------------------------+-------------------------------------------------
* "ids" | array the torrent torrent list, as described in 3.1
* | (must only be 1 torrent)
* "path" | string the path to the file or folder that will be renamed
* "name" | string the file or folder's new name
* Response arguments: "path", "name", and "id", holding the torrent ID integer
*
* @param int|array ids A 1-element list of transmission torrent ids
* @param string path The path to the file or folder that will be renamed
* @param string name The file or folder's new name
*/
public function rename ( $ids, $path, $name )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $id to an array if only a single id was passed
if ( count( $ids ) !== 1 ) {
throw new TransmissionRPCException( 'A single id is accepted', TransmissionRPCException::E_INVALIDARG );
}
$request = array(
"ids" => $ids,
"path" => $path,
"name" => $name
);
return $this->request( "torrent-rename-path", $request );
}
/**
* Retrieve session statistics
*
* @returns array of statistics
*/
public function sstats ( )
{
return $this->request( "session-stats", array() );
}
/**
* Retrieve all session variables
*
* @returns array of session information
*/
public function sget ( )
{
return $this->request( "session-get", array() );
}
/**
* Set session variable(s)
*
* @param array of session variables to set
*/
public function sset ( $arguments )
{
return $this->request( "session-set", $arguments );
}
/**
* Return the interpretation of the torrent status
*
* @param int The integer "torrent status"
* @returns string The translated meaning
*/
public function getStatusString ( $intstatus )
{
if($this->rpc_version < 14){
if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK_WAIT )
return "Waiting to verify local files";
if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK )
return "Verifying local files";
if( $intstatus == self::RPC_LT_14_TR_STATUS_DOWNLOAD )
return "Downloading";
if( $intstatus == self::RPC_LT_14_TR_STATUS_SEED )
return "Seeding";
if( $intstatus == self::RPC_LT_14_TR_STATUS_STOPPED )
return "Stopped";
}else{
if( $intstatus == self::TR_STATUS_CHECK_WAIT )
return "Waiting to verify local files";
if( $intstatus == self::TR_STATUS_CHECK )
return "Verifying local files";
if( $intstatus == self::TR_STATUS_DOWNLOAD )
return "Downloading";
if( $intstatus == self::TR_STATUS_SEED )
return "Seeding";
if( $intstatus == self::TR_STATUS_STOPPED )
return "Stopped";
if( $intstatus == self::TR_STATUS_SEED_WAIT )
return "Queued for seeding";
if( $intstatus == self::TR_STATUS_DOWNLOAD_WAIT )
return "Queued for download";
}
return "Unknown";
}
/**
* Here be dragons (Internal methods)
*/
/**
* Clean up the request array. Removes any empty fields from the request
*
* @param array array The request associative array to clean
* @returns array The cleaned array
*/
protected function cleanRequestData ( $array )
{
if ( !is_array( $array ) || count( $array ) == 0 ) return null; // Nothing to clean
setlocale( LC_NUMERIC, 'en_US.utf8' ); // Override the locale - if the system locale is wrong, then 12.34 will encode as 12,34 which is invalid JSON
foreach ( $array as $index => $value )
{
if( is_object( $value ) ) $array[$index] = $value->toArray(); // Convert objects to arrays so they can be JSON encoded
if( is_array( $value ) ) $array[$index] = $this->cleanRequestData( $value ); // Recursion
if( empty( $value ) && $value !== 0 ) // Remove empty members
{
unset( $array[$index] );
continue; // Skip the rest of the tests - they may re-add the element.
}
if( is_numeric( $value ) ) $array[$index] = $value+0; // Force type-casting for proper JSON encoding (+0 is a cheap way to maintain int/float/etc)
if( is_bool( $value ) ) $array[$index] = ( $value ? 1 : 0); // Store boolean values as 0 or 1
if( is_string( $value ) ) {
if ( mb_detect_encoding($value,"auto") !== 'UTF-8' ) {
$array[$index] = mb_convert_encoding($value, "UTF-8");
//utf8_encode( $value ); // Make sure all data is UTF-8 encoded for Transmission
}
}
}
return $array;
}
/**
* Clean up the result object. Replaces all minus(-) characters in the object properties with underscores
* and converts any object with any all-digit property names to an array.
*
* @param object The request result to clean
* @returns array The cleaned object
*/
protected function cleanResultObject ( $object )
{
// Prepare and cast object to array
$return_as_array = false;
$array = $object;
if ( !is_array( $array ) ) $array = (array) $array;
foreach ( $array as $index => $value )
{
if( is_array( $array[$index] ) || is_object( $array[$index] ) )
{
$array[$index] = $this->cleanResultObject( $array[$index] ); // Recursion
}
if ( strstr( $index, '-' ) )
{
$valid_index = str_replace( '-', '_', $index );
$array[$valid_index] = $array[$index];
unset( $array[$index] );
$index = $valid_index;
}
// Might be an array, check index for digits, if so, an array should be returned
if ( ctype_digit( (string) $index ) ) { $return_as_array = true; }
if ( empty( $value ) ) unset( $array[$index] );
}
// Return array cast to object
return $return_as_array ? $array : (object) $array;
}
/**
* 执行 rpc 请求
*
* @param $method 请求类型/方法, 详见 $this->allowMethods
* @param array $arguments 附加参数, 可选
* @return mixed
*/
protected function request($method, $arguments = array())
{
// Check the parameters
if ( !is_scalar( $method ) )
throw new TransmissionRPCException( 'Method name has no scalar value', TransmissionRPCException::E_INVALIDARG );
if ( !is_array( $arguments ) )
throw new TransmissionRPCException( 'Arguments must be given as array', TransmissionRPCException::E_INVALIDARG );
$arguments = $this->cleanRequestData( $arguments ); // Sanitize input
// Grab the X-Transmission-Session-Id if we don't have it already
if( !$this->session_id )
if( !$this->GetSessionID() )
throw new TransmissionRPCException( 'Unable to acquire X-Transmission-Session-Id', TransmissionRPCException::E_SESSIONID );
$data = array(
'method' => $method,
'arguments' => $arguments
);
$header = array(
'Content-Type: application/json',
'Authorization: Basic '.base64_encode(sprintf("%s:%s", $this->username, $this->password)),
'X-Transmission-Session-Id: '.$this->session_id
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_TIMEOUT, 600);
$content = curl_exec($ch);
curl_close($ch);
if (!$content) $content = json_encode(array('result' => 'failed'));
return $this->return_as_array ? json_decode( $content, true ) : $this->cleanResultObject( json_decode( $content ) ); // Return the sanitized result
}
/**
* Performs an empty GET on the Transmission RPC to get the X-Transmission-Session-Id
* and store it in $this->session_id
*
* @return string
*/
public function GetSessionID()
{
if( !$this->url )
throw new TransmissionRPCException( "Class must be initialized before GetSessionID() can be called.", TransmissionRPCException::E_INVALIDARG );
// Setup the context
$contextopts = $this->default_context_opts; // Start with the defaults
// Make sure it's blank/empty (reset)
$this->session_id = null;
// Setup authentication (if provided)
if ( $this->username && $this->password )
$contextopts['http']['header'] = sprintf( "Authorization: Basic %s\r\n", base64_encode( $this->username.':'.$this->password ) );
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream context created with options:".
PHP_EOL . print_r( $contextopts, true );
$context = stream_context_create( $contextopts ); // Create the context for this request
if ( ! $fp = @fopen( $this->url, 'r', false, $context ) ) // Open a filepointer to the data, and use fgets to get the result
throw new TransmissionRPCException( 'Unable to connect to '.$this->url, TransmissionRPCException::E_CONNECTION );
// Check the response (headers etc)
$stream_meta = stream_get_meta_data( $fp );
fclose( $fp );
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream meta info: ".
PHP_EOL . print_r( $stream_meta, true );
if( $stream_meta['timed_out'] )
throw new TransmissionRPCException( "Timed out connecting to {$this->url}", TransmissionRPCException::E_CONNECTION );
if( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "401" )
throw new TransmissionRPCException( "Invalid username/password.", TransmissionRPCException::E_AUTHENTICATION );
elseif( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "409" ) // This is what we're hoping to find
{
// Loop through the returned headers and extract the X-Transmission-Session-Id
foreach( $stream_meta['wrapper_data'] as $header )
{
if( strpos( $header, 'X-Transmission-Session-Id: ' ) === 0 )
{
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Session-Id header: ".
PHP_EOL . print_r( $header, true );
$this->session_id = trim( substr( $header, 27 ) );
break;
}
}
if( ! $this->session_id ) { // Didn't find a session_id
throw new TransmissionRPCException( "Unable to retrieve X-Transmission-Session-Id", TransmissionRPCException::E_SESSIONID );
}
} else {
throw new TransmissionRPCException( "Unexpected response from Transmission RPC: ".$stream_meta['wrapper_data'][0] );
}
return $this->session_id;
}
/**
* Takes the connection parameters
*
* TODO: Sanitize username, password, and URL
*
* @param string $url
* @param string $username
* @param string $password
*/
public function __construct( $url = 'http://localhost:9091/transmission/rpc', $username = null, $password = null, $return_as_array = false )
{
// server URL
$this->url = $url;
// Username & password
$this->username = $username;
$this->password = $password;
// Get the Transmission RPC_version
$this->rpc_version = self::sget()->arguments->rpc_version;
// Return As Array
$this->return_as_array = $return_as_array;
// Reset X-Transmission-Session-Id so we (re)fetch one
$this->session_id = null;
}
}
/**
* This is the type of exception the TransmissionRPC class will throw
*/
class TransmissionRPCException extends Exception
{
/**
* Exception: Invalid arguments
*/
const E_INVALIDARG = -1;
/**
* Exception: Invalid Session-Id
*/
const E_SESSIONID = -2;
/**
* Exception: Error while connecting
*/
const E_CONNECTION = -3;
/**
* Exception: Error 401 returned, unauthorized
*/
const E_AUTHENTICATION = -4;
/**
* Exception constructor
*/
public function __construct( $message = null, $code = 0, Exception $previous = null )
{
// PHP version 5.3.0 and above support Exception linking
if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) )
parent::__construct( $message, $code, $previous );
else
parent::__construct( $message, $code );
}
}
?>

288
app/Class/qBittorrent.php Normal file
View File

@@ -0,0 +1,288 @@
<?php
use Curl\Curl;
/**
* https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation
*/
class qBittorrent
{
private $debug;
private $url;
private $api_version;
private $curl;
protected $delimiter;
private $endpoints = [
'login' => [
'1' => '/login',
'2' => '/api/v2/auth/login'
],
'app_version' => [
'1' => '/version/qbittorrent',
'2' => '/api/v2/app/version'
],
'api_version' => [
'1' => '/version/api',
'2' => '/api/v2/app/webapiVersion'
],
'build_info' => [
'1' => null,
'2' => '/api/v2/app/buildInfo'
],
'preferences' => [
'1' => null,
'2' => '/api/v2/app/preferences'
],
'setPreferences' => [
'1' => null,
'2' => '/api/v2/app/setPreferences'
],
'defaultSavePath' => [
'1' => null,
'2' => '/api/v2/app/defaultSavePath'
],
'torrent_list' => [
'1' => null,
'2' => '/api/v2/torrents/info'
],
'torrent_add' => [
'1' => null,
'2' => '/api/v2/torrents/add'
],
'torrent_delete' => [
'1' => null,
'2' => '/api/v2/torrents/delete'
],
'torrent_pause' => [
'1' => null,
'2' => '/api/v2/torrents/pause'
],
'torrent_resume' => [
'1' => null,
'2' => '/api/v2/torrents/resume'
],
'set_torrent_location' => [
'1' => null,
'2' => '/api/v2/torrents/setLocation'
],
'maindata' => [
'1' => null,
'2' => '/api/v2/sync/maindata'
]
];
public function __construct($url='', $username='', $password='', $api_version = 2, $debug = false)
{
$this->debug = $debug;
$this->url = rtrim($url,'/');
$this->username = $username;
$this->password = $password;
$this->api_version = $api_version;
$this->curl = new Curl();
$this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, false); // 禁止验证证书
$this->curl->setOpt(CURLOPT_SSL_VERIFYHOST, false); // 不检查证书
$this->curl->setOpt(CURLOPT_CONNECTTIMEOUT, 60); // 超时
$this->curl->setOpt(CURLOPT_TIMEOUT, 600); // 超时
// Authenticate and get cookie, else throw exception
if (!$this->authenticate()) {
throw new \Exception("Unable to authenticate with Web Api.");
}
}
public function appVersion()
{
return $this->getData('app_version');
}
public function apiVersion()
{
return $this->getData('api_version');
}
public function buildInfo()
{
return $this->getData('build_info');
}
public function preferences($data = null)
{
if (!empty($data)) {
return $this->postData('setPreferences', ['json' => json_encode($data)]);
}
return $this->getData('preferences');
}
public function torrentList()
{
return $this->getData('torrent_list');
}
/**
* @param array $extra_options
array(
'urls' => '',
'savepath' => '',
'cookie' => '',
'category' => '',
'skip_checking' => true,
'paused' => true,
'root_folder' => true,
)
* @return array
*/
public function add($torrent_url, $save_path = '', $extra_options = array())
{
if(!empty($save_path)) $extra_options['savepath'] = $save_path;
$extra_options['urls'] = $torrent_url;
#$extra_options['skip_checking'] = 'true'; //跳校验
// 关键 上传文件流 multipart/form-data【严格按照api文档编写】
$post_data = $this->buildUrls($extra_options);
#p($post_data);
// 设置请求头
$this->curl->setHeader('Content-Type','multipart/form-data; boundary='.$this->delimiter);
$this->curl->setHeader('Content-Length',strlen($post_data));
return $this->postData('torrent_add', $post_data);
}
public function add_metainfo($torrent_metainfo, $save_path = '', $extra_options = array())
{
if(!empty($save_path)) $extra_options['savepath'] = $save_path;
$extra_options['torrents'] = $torrent_metainfo;
#$extra_options['skip_checking'] = 'true'; //跳校验
// 关键 上传文件流 multipart/form-data【严格按照api文档编写】
$post_data = $this->buildData($extra_options);
#p($post_data);
// 设置请求头
$this->curl->setHeader('Content-Type','multipart/form-data; boundary='.$this->delimiter);
$this->curl->setHeader('Content-Length',strlen($post_data));
return $this->postData('torrent_add', $post_data);
}
public function torrentDelete($hash='', $deleteFiles = false)
{
return $this->postData('torrent_delete', ['hashes' => $hash, 'deleteFiles' => $deleteFiles ? 'true':'false']);
}
public function torrentDeleteAll($deleteFiles = false)
{
$torrents = json_decode($this->torrentList());
$response = '';
foreach ($torrents as $torrent) {
$response .= $this->torrentDelete($torrent->hash, $deleteFiles);
}
return $response;
}
public function torrentPause($hash)
{
return $this->postData('torrent_pause', ['hashes' => $hash]);
}
public function torrentResume($hash)
{
return $this->postData('torrent_resume', ['hashes' => $hash]);
}
public function setTorrentLocation($hash, $location)
{
return $this->postData('set_torrent_location', ['hashes' => $hash, 'location' => $location]);
}
private function getData($endpoint)
{
$this->curl->get($this->url . $this->endpoints[$endpoint][$this->api_version]);
if ($this->debug) {
var_dump($this->curl->request_headers);
var_dump($this->curl->response_headers);
}
if ($this->curl->error) {
return $this->errorMessage();
}
return $this->curl->response;
}
private function postData($endpoint, $data)
{
$this->curl->post($this->url . $this->endpoints[$endpoint][$this->api_version], $data);
if ($this->debug) {
var_dump($this->curl->request_headers);
var_dump($this->curl->response_headers);
}
if ($this->curl->error) {
return $this->errorMessage();
}
return $this->curl->response;
}
private function authenticate()
{
$this->curl->post($this->url . $this->endpoints['login'][$this->api_version], [
'username' => $this->username,
'password' => $this->password
]);
if ($this->debug) {
var_dump($this->curl->request_headers);
var_dump($this->curl->response_headers);
}
// Find authentication cookie and set in curl connection
foreach ($this->curl->response_headers as $header) {
if (preg_match('/SID=(\S[^;]+)/', $header, $matches)) {
$this->curl->setHeader('Cookie', $matches[0]);
return true;
}
};
return false;
}
private function errorMessage()
{
return 'Curl Error Code: ' . $this->curl->error_code . ' (' . $this->curl->response . ')';
}
/**
* 拼接种子urls multipart/form-data
* https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation#add-new-torrent
*/
private function buildUrls($param){
$this->delimiter = uniqid();
$eol = "\r\n";
$data = '';
// 拼接文件流
foreach ($param as $name => $content) {
$data .= "--" . $this->delimiter . $eol;
$data .= 'Content-Disposition: form-data; name' . '="' .$name. '"' . "\r\n\r\n";
$data .= $content . $eol;
}
$data .= "--" . $this->delimiter . "--" . $eol;
return $data;
}
/**
* 拼接种子上传文件流 multipart/form-data
* https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation#add-new-torrent
*/
private function buildData($param){
$this->delimiter = uniqid();
$eol = "\r\n";
$data = '';
$torrents = $param['torrents'];
unset($param['torrents']);
// 拼接文件流
$data .= "--" . $this->delimiter . $eol
. 'Content-Disposition: form-data; ';
foreach ($param as $name => $content) {
$data.= $name . '="' . $content.'"; ';
}
$data .= $eol;
$data .= 'Content-Type: application/x-bittorrent'."\r\n\r\n";
$data .= $torrents . $eol;
$data .= "--" . $this->delimiter . "--" . $eol;
return $data;
}
}

255
app/Class/uTorrent.php Normal file
View File

@@ -0,0 +1,255 @@
<?php
define("UTORRENT_TORRENT_HASH", 0);
define("UTORRENT_TORRENT_STATUS", 1);
define("UTORRENT_TORRENT_NAME", 2);
define("UTORRENT_TORRENT_SIZE", 3);
define("UTORRENT_TORRENT_PROGRESS", 4);
define("UTORRENT_TORRENT_DOWNLOADED", 5);
define("UTORRENT_TORRENT_UPLOADED", 6);
define("UTORRENT_TORRENT_RATIO", 7);
define("UTORRENT_TORRENT_UPSPEED", 8);
define("UTORRENT_TORRENT_DOWNSPEED", 9);
define("UTORRENT_TORRENT_ETA", 10);
define("UTORRENT_TORRENT_LABEL", 11);
define("UTORRENT_TORRENT_PEERS_CONNECTED", 12);
define("UTORRENT_TORRENT_PEERS_SWARM", 13);
define("UTORRENT_TORRENT_SEEDS_CONNECTED", 14);
define("UTORRENT_TORRENT_SEEDS_SWARM", 15);
define("UTORRENT_TORRENT_AVAILABILITY", 16);
define("UTORRENT_TORRENT_QUEUE_POSITION", 17);
define("UTORRENT_TORRENT_REMAINING", 18);
define("UTORRENT_FILEPRIORITY_HIGH", 3);
define("UTORRENT_FILEPRIORITY_NORMAL", 2);
define("UTORRENT_FILEPRIORITY_LOW", 1);
define("UTORRENT_FILEPRIORITY_SKIP", 0);
define("UTORRENT_TYPE_INTEGER", 0);
define("UTORRENT_TYPE_BOOLEAN", 1);
define("UTORRENT_TYPE_STRING", 2);
define("UTORRENT_STATUS_STARTED", 1);
define("UTORRENT_STATUS_CHECKED", 2);
define("UTORRENT_STATUS_START_AFTER_CHECK", 4);
class uTorrent {
// class static variables
private static $base = "%s/gui/%s";
// member variables
public $host;
public $user;
public $pass;
protected $token;
protected $guid;
// constructor
function __construct($host = "", $user = "", $pass = "") {
$this->host = rtrim($host,'/');
$this->user = $user;
$this->pass = $pass;
if (!$this->getToken()) {
//handle error here, don't know how to best do this yet
die('could not get token');
}
}
// performs request
private function makeRequest($request, $decode = true, $options = array()) {
$request = preg_replace('/^\?/', '?token='.$this->token . '&', $request);
$ch = curl_init();
curl_setopt_array($ch, $options);
curl_setopt($ch, CURLOPT_URL, sprintf(self::$base, $this->host, $request));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, $this->user.":".$this->pass);
curl_setopt($ch, CURLOPT_COOKIE, "GUID=".$this->guid);
$req = curl_exec($ch);
curl_close($ch);
return ($decode ? json_decode($req, true) : $req);
}
// implodes given parameter with glue, whether it is an array or not
private function paramImplode($glue, $param) {
return $glue.implode($glue, is_array($param) ? $param : array($param));
}
// gets token, returns true on success
private function getToken() {
$url = sprintf(self::$base, $this->host, 'token.html');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, $this->user.":".$this->pass);
curl_setopt($ch, CURLOPT_HEADER, true);
$output = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
$headers = substr($output, 0, $info['header_size']);
if (preg_match("@Set-Cookie: GUID=([^;]+);@i", $headers, $matches)) {
$this->guid = $matches[1];
}
if (preg_match('/<div id=\'token\'.+>(.*)<\/div>/', $output, $m)) {
$this->token = $m[1];
return true;
}
return false;
}
// returns the uTorrent build number
public function getBuild(){
$json = $this->makeRequest("?");
return $json['build'];
}
// returns an array of files for the specified torrent hash
// TODO:
// - (when implemented in API) allow multiple hashes to be specified
public function getFiles($hash) {
$json = $this->makeRequest("?action=getfiles&hash=".$hash);
return $json['files'];
}
// returns an array of all labels
public function getLabels(){
$json = $this->makeRequest("?list=1");
return $json['label'];
}
// returns an array of the properties for the specified torrent hash
// TODO:
// - (when implemented in API) allow multiple hashes to be specified
public function getProperties($hash) {
$json = $this->makeRequest("?action=getprops&hash=".$hash);
return $json['props'];
}
// returns an array of all settings
public function getSettings() {
$json = $this->makeRequest("?action=getsettings");
return $json['settings'];
}
// returns an array of all torrent jobs and related information
public function getTorrents() {
$json = $this->makeRequest("?list=1");
return $json['torrents'];
}
/**
* Get all the RSS favourites/filters
* @return model\Filter[]
*/
public function getRSSFilters() {
$json = $this->makeRequest("?list=1");
$filters = array();
foreach ($json['rssfilters'] as $filter) {
$filters[] = model\Filter::fromData($filter);
}
return $filters;
}
/**
* Update an RSS filter as retrieved from getRSSFilters
* @param \uTorrent\model\Filter $filter
*/
public function setRSSFilter(model\Filter $filter) {
$request = array_merge(array('action' => 'filter-update'), $filter->toParams());
return $this->makeRequest('?'.http_build_query($request));
}
/**
* Add a new RSS filter
* Requires a utorrent > 2.2.1 (not sure which version exactly)
* @param \uTorrent\model\Filter $filter
* @return int ID of the new filter
*/
public function addRSSFilter(model\Filter $filter) {
$filter->filterId = -1;
$resp = $this->setRSSFilter($filter);
if (!empty($resp['filter_ident'])) {
return $resp['filter_ident'];
} else {
return 0;
}
}
// returns true if WebUI server is online and enabled, false otherwise
public function is_online() {
return is_array($this->makeRequest("?"));
}
// sets the properties for the specified torrent hash
// TODO:
// - allow multiple hashes, properties, and values to be set simultaneously
public function setProperties($hash, $property, $value) {
$this->makeRequest("?action=setprops&hash=".$hash."&s=".$property."&v=".$value, false);
}
// sets the priorities for the specified files in the specified torrent hash
public function setPriority($hash, $files, $priority) {
$this->makeRequest("?action=setprio&hash=".$hash."&p=".$priority.$this->paramImplode("&f=", $files), false);
}
// sets the settings
// TODO:
// - allow multiple settings and values to be set simultaneously
public function setSetting($setting, $value) {
$this->makeRequest("?action=setsetting&s=".$setting."&v=".$value, false);
}
// add a file to the list
public function torrentAdd($filename, &$estring = false) {
$split = explode(":", $filename, 2);
if (count($split) > 1 && (stristr("|http|https|file|magnet|", "|".$split[0]."|") !== false)) {
$this->makeRequest("?action=add-url&s=".urlencode($filename), false);
}
elseif (file_exists($filename)) {
$json = $this->makeRequest("?action=add-file", true, array(CURLOPT_POSTFIELDS => array("torrent_file" => "@".realpath($filename))));
if (isset($json['error'])) {
if ($estring !== false) $estring = $json['error'];
return false;
}
return true;
}
else {
if ($estring !== false) $estring = "File doesn't exist!";
return false;
}
}
// force start the specified torrent hashes
public function torrentForceStart($hash) {
$this->makeRequest("?action=forcestart".$this->paramImplode("&hash=", $hash), false);
}
// pause the specified torrent hashes
public function torrentPause($hash) {
$this->makeRequest("?action=pause".$this->paramImplode("&hash=", $hash), false);
}
// recheck the specified torrent hashes
public function torrentRecheck($hash) {
$this->makeRequest("?action=recheck".$this->paramImplode("&hash=", $hash), false);
}
// start the specified torrent hashes
public function torrentStart($hash) {
$this->makeRequest("?action=start".$this->paramImplode("&hash=", $hash), false);
}
// stop the specified torrent hashes
public function torrentStop($hash) {
$this->makeRequest("?action=stop".$this->paramImplode("&hash=", $hash), false);
}
// remove the specified torrent hashes (and data, if $data is set to true)
public function torrentRemove($hash, $data = false) {
$this->makeRequest("?action=".($data ? "removedata" : "remove").$this->paramImplode("&hash=", $hash), false);
}
}

View File

@@ -0,0 +1,352 @@
<?php
/**
* 技术讨论及后续更新请加入QQ群
群名称IYUU自动辅种交流
QQ群号859882209
* 手动配置方法请查看https://www.iyuu.cn/archives/324/
*/
return array(
// 1.爱语飞飞 微信通知配置
'iyuu.cn' => 'IYUU',
// 2.server酱 微信通知配置
'sc.ftqq.com' => '',
// 3.发布员鉴权
'secret' => '',
// 4.全局默认配置
'default' => array(
// 5.【必须配置】浏览器UA打开http://demo.iyuu.cn 复制过来即可
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
// 6.【自动辅种必须配置】全局客户端设置(条目不够可以复制,用不到的请删除)
'clients' => array(
// 全局客户端设置 开始
array(
'type' => 'transmission', // 支持transmission、qBittorrent
'host' => 'http://127.0.0.1:9091/transmission/rpc',
'username' => '',
'password' => '',
//'move' =>array(
// 'type' => 2, // 0保持不变1减2加 3直接替换
// 'path' =>array(
// '/sda1' => '/volume1',
// ),
//),
),
// (条目不够可以复制,用不到的请删除)
array(
'type' => 'qBittorrent', // 支持transmission、qBittorrent
'host' => 'http://www.baidu.com:8083',
'username' => '',
'password' => '',
),
// 全局客户端设置 结束
),
'CONNECTTIMEOUT'=> 60,
'TIMEOUT' => 600,
),
/**
* 以下为各站点的独立配置(互不影响、互不冲突)
* 自动辅种需要配置各站的passkey没有配置passkey的站点会自动跳过
*/
// m-team 序号1
'm-team' => array(
// 14.m-team的cookie 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => 'tp=',
// 15.m-team的passkey 【必须配置】
'passkey' => '',
// 种子Tracker的IP地址选择 可选ipv4ipv6
'ip_type' => 'ipv4',
),
// keepfrds 序号2
'keepfrds' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// ourbits 序号3
'ourbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'id' => 0, // 用户ID
'is_vip' => 0, // 是否具有VIP或特殊权限0 普通1 VIP
),
// HDSky 序号4
'hdsky' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// pter 序号5
'pter' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// tjupt 序号6
'tjupt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdhome 序号7
'hdhome' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// btschool 序号8
'btschool' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// PTHome 序号9
'pthome' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hddolby 序号10
'hddolby' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// TorrentCCF 序号11
'torrentccf' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// PTMSG 序号12
'ptmsg' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// MoeCat 序号13
'moecat' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
// 种子Tracker的IP地址选择 可选ipv4ipv6
'ip_type' => 'ipv4',
),
// totheglory 序号14
'ttg' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// nanyangpt 序号15
'nanyangpt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// springsunday.net 序号16
'ssd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// yingk 序号17
'yingk' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdcity 序号18
'hdcity' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置 cuhash
'passkey' => '',
),
// 52pt.site 序号19
'52pt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// brobits.cc 序号20
'brobits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// www.beitai.pt 序号21
'beitai' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// pt.eastgame.org 序号22
'eastgame' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// pt.soulvoice.club 序号23
'soulvoice' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// chdbits 序号24
'chdbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// leaguehd 序号25
'leaguehd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// ptsbao.club 序号26
'ptsbao' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdchina 序号27
'hdchina' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdarea 序号28
'hdarea' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdtime 序号29
'hdtime' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// 1ptba 序号30
'1ptba' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hd4fans 序号31
'hd4fans' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdbug 序号32
'hdbug' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// opencd 序号33
'opencd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdstreet 序号34
'hdstreet' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// joyhd 序号35
'joyhd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// dmhy 序号36
'dmhy' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// upxin 序号37
'upxin' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// oshen 序号38
'oshen' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// discfan 序号39
'discfan' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdzone 序号40
'hdzone' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// cnscg 序号41
'cnscg' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// nicept 序号42
'nicept' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// 配置文件结束
);

1
app/config/version.php Normal file
View File

@@ -0,0 +1 @@
<?php return '20191224.1010';

42
app/init.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
//----------------------------------
// 公共入口文件
//----------------------------------
// 定义目录
defined('ROOT_PATH') or define("ROOT_PATH", dirname(__DIR__));
defined('APP_PATH') or define('APP_PATH', __DIR__);
define('DS', DIRECTORY_SEPARATOR);
define('TORRENT_PATH', APP_PATH.DS.'torrent'.DS);
// 严格开发模式
error_reporting( E_ALL );
#ini_set('display_errors', 1);
// 永不超时
ini_set('max_execution_time', 0);
set_time_limit(0);
// 内存限制,如果外面设置的内存比 /etc/php/php-cli.ini 大,就不要设置了
if (intval(ini_get("memory_limit")) < 1024)
{
ini_set('memory_limit', '1024M');
}
if( PHP_SAPI != 'cli' )
{
exit("You must run the CLI environment\n");
}
// 设置时区
date_default_timezone_set('Asia/Shanghai');
// 系统配置
if( file_exists( APP_PATH."/config/config.php" ) )
{
// 配置(全局变量)
$configALL = require_once APP_PATH."/config/config.php";
}else{
// 示例配置
$configALL = require_once APP_PATH . '/config/config.sample.php';
}
require_once ROOT_PATH . '/vendor/autoload.php';

10
composer.json Normal file
View File

@@ -0,0 +1,10 @@
{
"require": {
"owner888/phpspider": "^2.1",
"curl/curl": "^2.2"
},
"autoload": {
"classmap":["app/Class"],
"files": ["app/Class/Function.php"]
}
}

131
composer.lock generated Normal file
View File

@@ -0,0 +1,131 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "32806a4860870f6306b69cf349584387",
"packages": [
{
"name": "curl/curl",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/php-mod/curl.git",
"reference": "d22086dd2eee5ca02e4c29b9a5bdf3645bfdbbff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-mod/curl/zipball/d22086dd2eee5ca02e4c29b9a5bdf3645bfdbbff",
"reference": "d22086dd2eee5ca02e4c29b9a5bdf3645bfdbbff",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"ext-curl": "*",
"php": "^5.6 | ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "~2.1"
},
"type": "library",
"autoload": {
"psr-0": {
"Curl": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Hassan Amouhzi",
"email": "hassan@anezi.net",
"homepage": "http://hassan.amouhzi.com"
},
{
"name": "php-curl-class",
"homepage": "https://github.com/php-curl-class"
},
{
"name": "user52",
"homepage": "https://github.com/user52"
}
],
"description": "cURL class for PHP",
"homepage": "https://github.com/php-mod/curl",
"keywords": [
"curl",
"dot"
],
"time": "2018-12-04T19:47:03+00:00"
},
{
"name": "owner888/phpspider",
"version": "v2.1.6",
"source": {
"type": "git",
"url": "https://github.com/owner888/phpspider.git",
"reference": "e6021148adec201418c16ba26f39bc013ba5b4d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/owner888/phpspider/zipball/e6021148adec201418c16ba26f39bc013ba5b4d9",
"reference": "e6021148adec201418c16ba26f39bc013ba5b4d9",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.5.0"
},
"suggest": {
"ext-pcntl、ext-redis": "For better performance. "
},
"type": "library",
"autoload": {
"psr-4": {
"phpspider\\": "./"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Seatle Yang",
"email": "seatle@foxmail.com",
"homepage": "http://www.phpspider.org",
"role": "Developer"
}
],
"description": "The PHPSpider Framework.",
"homepage": "http://www.phpspider.org",
"keywords": [
"framework",
"phpspider"
],
"time": "2018-08-15T08:04:29+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

612
iyuu.cn.php Normal file
View File

@@ -0,0 +1,612 @@
<?php
/**
* IYUUAutoReseed自动辅种
*/
use Curl\Curl;
require_once __DIR__ . '/app/init.php';
iyuuAutoReseed::init();
$hashArray = iyuuAutoReseed::get();
if ( iyuuAutoReseed::$move != null ) {
echo "种子移动完毕,请重新编辑配置,再尝试辅种! \n\n";
exit;
}
iyuuAutoReseed::call($hashArray);
iyuuAutoReseed::wechatMessage();
/**
* IYUUAutoReseed自动辅种类
*/
class iyuuAutoReseed
{
/**
* 版本号
* @var string
*/
const VER = '0.1.0';
/**
* RPC连接池
* @var array
*/
public static $links = array();
/**
* 客户端配置
* @var array
*/
public static $clients = array();
/**
* 不辅种的站点 'ourbits','hdchina'
* @var array
*/
public static $noReseed = [];
/**
* 缓存路径
* @var string
*/
public static $cacheDir = TORRENT_PATH.'cache'.DS;
public static $cacheHash = TORRENT_PATH.'cachehash'.DS;
/**
* API接口配置
* @var string
* @var array
*/
public static $apiUrl = 'http://iyuu.cn:2122';
public static $endpoints = array(
'add' => '/api/add',
'update' => '/api/update',
'reseed' => '/api/reseed',
'login' => '/login',
);
/**
* 退出状态码
* @var int
*/
public static $ExitCode = 0;
/**
* 客户端转移做种 状态码[请把transmission配置为第一个客户端]
* @var array
*/
public static $move = null;
/**
* 微信消息体
* @var array
*/
public static $wechatMsg = array(
'hashCount' => 0, // 提交给服务器的hash总数
'sitesCount' => 0, // 可辅种站点总数
'reseedCount' => 0, // 返回的总数据
'reseedSuccess' => 0, // 成功:辅种成功(会加入缓存,哪怕种子在校验中,下次也会过滤)
'reseedError' => 0, // 错误:辅种失败(可以重试)
'reseedRepeat' => 0, // 重复:客户端已做种
'reseedSkip' => 0, // 跳过因未设置passkey而跳过
'reseedPass' => 0, // 忽略:因上次成功添加、存在缓存,而跳过
);
/**
* 初始化
* @return void
*/
public static function init(){
global $configALL;
self::$clients = isset($configALL['default']['clients']) && $configALL['default']['clients'] ? $configALL['default']['clients'] : array();
echo "程序正在初始化运行参数... \n";
// 递归删除上次历史记录
IFile::rmdir(self::$cacheDir, true);
// 建立目录
IFile::mkdir(self::$cacheDir);
IFile::mkdir(self::$cacheHash);
// 连接全局客户端
self::links();
// 合作站点自动注册鉴权
Oauth::login(self::$apiUrl . self::$endpoints['login']);
}
/**
* 连接远端RPC服务器
*
* @param string
* @return bool
*/
public static function links()
{
if(empty(self::$links)){
foreach ( self::$clients as $k => $v ){
// 跳过未配置的客户端
if (empty($v['username']) || empty( $v['password'])) {
unset(self::$clients[$k]);
echo "clients_".$k." 用户名或密码未配置,已跳过 \n\n";
continue;
}
try
{
switch($v['type']){
case 'transmission':
self::$links[$k]['rpc'] = new TransmissionRPC($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->sstats();
print $v['type'].''.$v['host']." Rpc连接 [{$result->result}] \n";
break;
case 'qBittorrent':
self::$links[$k]['rpc'] = new qBittorrent($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->appVersion();
print $v['type'].''.$v['host']." Rpc连接 [{$result}] \n";
break;
default:
echo '[ERROR] '.$v['type'];
exit(1);
break;
}
self::$links[$k]['type'] = $v['type'];
// 检查是否转移种子的做种客户端?
if ( isset($v['move']) && $v['move'] ) {
self::$move = array($k,$v['type']);
}
} catch (Exception $e) {
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
exit(1);
}
}
}
return true;
}
/**
* 从客户端获取种子的哈希列表
* @var array
*/
public static function get(){
$hashArray = array();
foreach ( self::$clients as $k => $v ){
$result = array();
$res = $info_hash = array();
$json = $sha1 = '';
try
{
switch($v['type']){
case 'transmission':
$ids = $fields = array();
#$fields = array( "id", "status", "name", "hashString", "downloadDir", "torrentFile" );
$fields = array( "id", "status", "hashString", "downloadDir");
$result = self::$links[$k]['rpc']->get($ids, $fields);
if ( empty($result->result) || $result->result != 'success' ){
// 获取种子列表 失败
echo "获取种子列表失败原因可能是transmission暂时无响应请稍后重试 \n";
break;
}
if( empty($result->arguments) ){
echo "未获取到需要辅种的数据,请多多保种,然后重试! \n";
break;
}
// 对象转数组
$res = object_array($result->arguments->torrents);
// 过滤,只保留正常做种
$res = array_filter($res, "filterStatus");
// 提取数组hashString
$info_hash = array_column($res, 'hashString');
// 升序排序
sort($info_hash);
// 微信模板消息 统计
self::$wechatMsg['hashCount'] += count($info_hash);
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
// 去重 应该从文件读入,防止重复提交
$sha1 = sha1( $json );
if ( isset($hashArray['sha1']) && (in_array($sha1, $hashArray['sha1']) != false) ) {
break;
}
// 组装返回数据
$hashArray['hash']['clients_'.$k] = $json;
$hashArray['sha1'][] = $sha1;
// 变换数组hashString为键
self::$links[$k]['hash'] = array_column($res, "downloadDir", 'hashString');
#p(self::$links[$k]['hash']);exit;
break;
case 'qBittorrent':
$result = self::$links[$k]['rpc']->torrentList();
$res = json_decode($result,true);
if ( empty($res) ) {
echo "未获取到需要辅种的数据,请多多保种,然后重试! \n";
break;
}
#p($res);exit;
// 过滤,只保留正常做种
$res = array_filter($res, "qbfilterStatus");
// 提取数组hashString
$info_hash = array_column($res, 'hash');
// 升序排序
sort($info_hash);
// 微信模板消息 统计
self::$wechatMsg['hashCount'] += count($info_hash);
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
// 去重 应该从文件读入,防止重复提交
$sha1 = sha1( $json );
if ( isset($hashArray['sha1']) && (in_array($sha1, $hashArray['sha1']) != false) ) {
break;
}
// 组装返回数据
$hashArray['hash']['clients_'.$k] = $json;
$hashArray['sha1'][] = $sha1;
// 变换数组hash为键
self::$links[$k]['hash'] = array_column($res, "save_path", 'hash');
#p(self::$links[$k]['hash']);exit;
break;
default:
echo '[ERROR] '.$v['type'];
exit(1);
break;
}
// 是否执行转移种子做种客户端?
if ( self::$move != null && (empty($v['move'])) ) {
self::move($res, $v['type']);
}
} catch (Exception $e) {
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
exit(1);
}
}
return $hashArray;
}
/**
* @brief 添加下载任务
* @param string $torrent 种子元数据
* @param string $save_path 保存路径
* @return bool
*/
public static function add($rpcKey, $torrent, $save_path = '', $extra_options = array())
{
try
{
$is_url = false;
if( (strpos($torrent,'http://')===0) || (strpos($torrent,'https://')===0) || (strpos($torrent,'magnet:?xt=urn:btih:')===0) ){
$is_url = true;
}
// 下载服务器类型
$type = self::$links[$rpcKey]['type'];
// 判断
switch( $type ){
case 'transmission':
$extra_options['paused'] = true;
if( $is_url ){
$result = self::$links[$rpcKey]['rpc']->add( $torrent, $save_path, $extra_options ); // 种子URL添加下载任务
} else{
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, $save_path, $extra_options ); // 种子元数据添加下载任务
}
if(isset($result->result) && $result->result == 'success'){
$id = $name = '';
if( isset($result->arguments->torrent_duplicate) ){
$id = $result->arguments->torrent_duplicate->id;
$name = $result->arguments->torrent_duplicate->name;
}elseif( isset($result->arguments->torrent_added) ){
$id = $result->arguments->torrent_added->id;
$name = $result->arguments->torrent_added->name;
}
print "********RPC添加下载任务成功 [{$result->result}] (id=$id) \n";
if( $is_url ){
print "种子:".$torrent. "\n";
}
print "名字:".$name."\n\n";
return true;
}else{
$errmsg = isset($result->result) ? $result->result : '未知错误,请稍后重试!';
print "-----RPC添加种子任务失败 [{$errmsg}] \n";
if( $is_url ){
print "种子:".$torrent. "\n";
}
}
break;
case 'qBittorrent':
$extra_options['paused'] = 'true';
$extra_options['autoTMM'] = 'false'; //关闭自动种子管理
if( $is_url ){
$result = self::$links[$rpcKey]['rpc']->add( $torrent, $save_path, $extra_options ); // 种子URL添加下载任务
} else{
$extra_options['name'] = 'torrents';
$extra_options['filename'] = rand(1,4294967200).'.torrent';
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, $save_path, $extra_options ); // 种子元数据添加下载任务
}
if ($result === 'Ok.') {
print "********RPC添加下载任务成功 [{$result}] \n\n";
return true;
} else {
print "-----RPC添加种子任务失败 [{$result}] \n\n";
}
break;
default:
echo '[ERROR] '.$type;
break;
}
} catch (Exception $e) {
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
}
return false;
}
/**
* 正常做种的种子在各下载器的互相转移
*/
public static function move($torrent=array(), $type = 'qBittorrent'){
switch($type){
case 'transmission':
break;
case 'qBittorrent':
foreach ($torrent as $k => $v) {
// 路径转换
#$v['save_path'] = '/volume3' . $v['save_path']; // docker路径转换
self::add(self::$move[0], $v['magnet_uri'], $v['save_path'] );
}
break;
default:
echo '[ERROR] '.$type;
break;
}
}
/**
* @brief 提交种子hash给远端API用来获取辅种数据
* @param array $hashArray 种子hash数组
* @return
*/
public static function call($hashArray = array())
{
global $configALL;
$resArray = $sites = array();
$curl = new Curl();
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
// 签名
$hashArray['timestamp'] = time();
// 爱语飞飞token
$hashArray['sign'] = Oauth::getSign();
$hashArray['version'] = self::VER;
// 写日志
if (true) {
// 文件句柄
$resource = fopen(self::$cacheDir.'hashString.txt', "wb");
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, p($hashArray, false));
fclose($resource);
}
// 发起请求
echo "正在提交辅种信息…… \n";
$res = $curl->post(self::$apiUrl . self::$endpoints['reseed'], $hashArray);
$resArray = json_decode($res->response, true);
// 写日志
if(true){
// 文件句柄
$resource = fopen(self::$cacheDir.'reseed.txt', "wb");
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, p($resArray, false));
fclose($resource);
}
// 判断返回值
if ( isset($resArray['errmsg']) && ($resArray['errmsg'] == 'ok') ) {
echo "辅种信息提交成功!!! \n\n";
}else{
$errmsg = isset($resArray['errmsg']) ? $resArray['errmsg'] : '远端服务器无响应,请稍后重试!';
echo '-----辅种失败,原因:' .$errmsg. " \n\n";
exit(1);
}
// 可辅种站点信息列表
$sites = $resArray['sites'];
self::$wechatMsg['sitesCount'] = count($sites);
#p($sites);
// 按客户端循环辅种 开始
foreach (self::$links as $k => $v) {
$reseed = $infohash_Dir = array();
// info_hash 对应的下载目录
$infohash_Dir = self::$links[$k]['hash'];
if (empty($resArray['clients_'.$k])) {
echo "clients_".$k."没有查询到可辅种数据 \n\n";
continue;
}
#p($infohash_Dir);
// 当前客户端辅种数据
$reseed = $resArray['clients_'.$k];
foreach ($reseed as $info_hash => $vv) {
// 当前种子哈希对应的目录
$downloadDir = $infohash_Dir[$info_hash];
foreach ($vv['torrent'] as $id => $value) {
// 匹配的辅种数据累加
self::$wechatMsg['reseedCount']++;
// 站点id
$sitesID = $value['sid'];
$url = $_url = '';
$download_page = $details_url = '';
// 页面规则
$download_page = str_replace('{}', $value['torrent_id'], $sites[$sitesID]['download_page']);
$_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$download_page;
if ( empty($configALL[$sites[$sitesID]['site']]['passkey']) ) {
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置passkey已跳过' . "\n\n";
self::$wechatMsg['reseedSkip']++;
continue;
}
// 种子URL组合方式区分
switch ($sites[$sitesID]['site']) {
case 'ttg':
$url = $_url."/". $configALL[$sites[$sitesID]['site']]['passkey'];
break;
case 'm-team':
$ip_type = '';
if (isset($configALL[$sites[$sitesID]['site']]['ip_type'])) {
$ip_type = $configALL[$sites[$sitesID]['site']]['ip_type'] == 'ipv6' ? '&ipv6=1' : '';
}
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'] . $ip_type. "&https=1";
break;
case 'moecat':
$ip_type = '';
if (isset($configALL[$sites[$sitesID]['site']]['ip_type'])) {
$ip_type = $configALL[$sites[$sitesID]['site']]['ip_type'] == 'ipv6' ? '&ipv6=1' : '';
}
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'] . $ip_type. "&https=1";
break;
case 'hdchina':
if ( empty($configALL[$sites[$sitesID]['site']]['cookie']) ) {
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置cookie已跳过' . "\n\n";
self::$wechatMsg['reseedSkip']++;
break;
}
$cookie = isset($configALL[$sites[$sitesID]['site']]['cookie']) ? $configALL[$sites[$sitesID]['site']]['cookie'] : '';
$userAgent = $configALL['default']['userAgent'];
// 拼接URL
$details_page = str_replace('{}', $value['torrent_id'], 'details.php?id={}&hit=1');
$details_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$details_page;
$details_html = download($details_url, $cookie, $userAgent);
print "种子详情页:".$details_url. "\n";
// 提取种子下载地址
$download_page = str_replace('{}', '', $sites[$sitesID]['download_page']);
$offset = strpos($details_html, $download_page);
$urlTemp = substr($details_html, $offset, 50);
// 种子地址
$_url = substr($urlTemp,0,strpos($urlTemp,'">'));
$_url = 'https://' .$sites[$sitesID]['base_url']. '/' . $_url;
print "种子下载页:".$_url. "\n";
$url = download($_url, $cookie, $userAgent);
break;
case 'hdcity':
if ( empty($configALL[$sites[$sitesID]['site']]['cookie']) ) {
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置cookie已跳过' . "\n\n";
self::$wechatMsg['reseedSkip']++;
break;
}
$cookie = isset($configALL[$sites[$sitesID]['site']]['cookie']) ? $configALL[$sites[$sitesID]['site']]['cookie'] : '';
$userAgent = $configALL['default']['userAgent'];
print "种子:".$_url. "\n";
if ( isset($configALL[$sites[$sitesID]['site']]['cuhash']) ) {
// 已获取cuhash
# code...
}else {
// 获取cuhash
$html = download('https://' .$sites[$sitesID]['base_url']. '/pt', $cookie, $userAgent);
// 提取种子下载地址
$offset = strpos($html,'cuhash=');
$len = strlen('cuhash=');
$cuhashTemp = substr($html,$offset+$len,40);
$configALL[$sites[$sitesID]['site']]['cuhash'] = substr($cuhashTemp,0,strpos($cuhashTemp,'"'));
}
$url = $_url."&cuhash=". $configALL[$sites[$sitesID]['site']]['cuhash'];
// 城市下载种子时会302转向
$url = download($url, $cookie, $userAgent);
break;
default:
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'];
break;
}
/**
* 检查站点是否可以辅种
*/
// 判断是否具有VIP或特殊权限
$is_vip = isset($configALL[$sites[$sitesID]['site']]['is_vip']) && $configALL[$sites[$sitesID]['site']]['is_vip'] ? 1 : 0;
if ( (in_array($sites[$sitesID]['site'], self::$noReseed)==false) || $is_vip ) {
/**
* 可以辅种
*/
if ( isset($infohash_Dir[$value['info_hash']]) ) {
// 与客户端现有种子重复
echo '-------与客户端现有种子重复:'.$_url."\n\n";
self::$wechatMsg['reseedRepeat']++;
continue;
}else{
// 判断上次是否成功添加?
if ( is_file(self::$cacheHash . $value['info_hash'].'.txt') ) {
echo '-------当前种子上次辅种已成功添加,已跳过!'.$_url."\n\n";
self::$wechatMsg['reseedPass']++;
continue;
}
// 把拼接的种子URL推送给下载器
$ret = false;
// 成功返回true
$ret = self::add($k, $url, $downloadDir);
// 添加成功的种子以infohash为文件名写入缓存
if ($ret) {
// 成功的种子
// 文件句柄
$resource = fopen(self::$cacheHash . $value['info_hash'].'.txt', "wb");
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, $url);
fclose($resource);
self::$wechatMsg['reseedSuccess']++;
continue;
}else{
// 失败的种子
// 站点类型判断
switch ($sites[$sitesID]['site']) {
case 'hdcity':
echo '当前' .$sites[$sitesID]['site']. '站点是配置cuhash(不是passkey),添加成功说明配置正确!如果添加任务失败,请查阅常见问题!!' . "\n";
break;
default:
break;
}
// 失败累加
self::$wechatMsg['reseedError']++;
continue;
}
}
}else{
/**
* 不辅种
*/
echo '-------已跳过不辅种的站点:'.$_url."\n\n";
// 写入日志文件,供用户手动辅种
if ( !isset($infohash_Dir[$value['info_hash']]) ) {
// 站点类型判断
switch ($sites[$sitesID]['site']) {
case 'hdchina':
$url = $_url;
break;
default:
break;
}
// 文件句柄
$resource = fopen(self::$cacheDir . $sites[$sitesID]['site'].'.txt', 'a');
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, 'clients_'.$k."\n".$downloadDir."\n".$url."\n".$details_url."\n\n");
fclose($resource);
}
}
}
}
}
// 按客户端循环辅种 结束
}
/**
*
*/
public static function wechatMessage(){
$br = "\r\n";
$text = 'IYUU自动辅种-统计报表';
$desp = '总做种:'.self::$wechatMsg['hashCount'] . ' [客户端正在做种的hash总数]' .$br;
$desp .= '返回数据:'.self::$wechatMsg['reseedCount']. ' [服务器返回的可辅种数据]' .$br;
$desp .= '支持站点:'.self::$wechatMsg['sitesCount']. ' [当前支持自动辅种的站点数量]' .$br;
$desp .= '成功:'.self::$wechatMsg['reseedSuccess']. ' [辅种成功会把hash加入缓存]' .$br;
$desp .= '失败:'.self::$wechatMsg['reseedError']. ' [下载器下载种子失败或网络超时引起,可以重试]' .$br;
$desp .= '重复:'.self::$wechatMsg['reseedRepeat']. ' [客户端已做种]' .$br;
$desp .= '跳过:'.self::$wechatMsg['reseedSkip']. ' [未设置passkey]' .$br;
$desp .= '忽略:'.self::$wechatMsg['reseedPass']. ' [成功添加存在缓存]' .$br;
return ff($text, $desp);
}
}
/**
* transmission过滤函数只保留正常做种
*/
function filterStatus( $v ){
return isset($v['status']) && $v['status']===6;
}
/**
* qBittorrent过滤函数只保留正常做种
*/
function qbfilterStatus( $v ){
if( isset($v['state']) && in_array($v['state'], array('uploading','stalledUP','pausedUP','queuedUP','checkingUP','forcedUP')) ){
return true;
}
return false;
}
//PHP stdClass Object转array
function object_array($array) {
if(is_object($array)) {
$array = (array)$array;
}
if(is_array($array)) {
foreach($array as $key=>$value) {
$array[$key] = object_array($value);
}
}
return $array;
}
// 对象转数组
function object2array(&$object) {
return json_decode( json_encode( $object ), true );
}

101
readme.md Normal file
View File

@@ -0,0 +1,101 @@
## 功能
IYUU自动辅种工具目前能对国内大部分的PT站点自动辅种支持下载器集群支持多盘位支持多下载目录支持远程连接等。
## 原理
IYUU自动辅种工具英文名IYUUAutoReseed是一款PHP语言编写的Private Tracker辅种脚本通过计划任务或常驻内存按指定频率调用transmission、qBittorrent下载软件的API接口提取正在做种的info_hash提交到服务器API接口根据API接口返回的数据拼接种子连接提交给下载器自动辅种各个站点。
## 优势
- 全程自动化,无需人工干预;
- 支持多盘位,多做种目录,多下载器,支持远程连接下载器;
- 辅种精确度高,精度可配置;
- 支持微信通知,消息即时达;
- 自动对合集包,进行拆包辅种(暂未开发)
## 支持的下载器
1. transmission
2. qBittorrent
## 支持自动辅种的站点
学校、杜比、家园、天空、朋友、馒头、萌猫、我堡、猫站、铂金家、烧包、北洋、TCCF、南洋、TTG、映客、城市、52pt、brobits、备胎、SSD、CHD、ptmsg、leaguehd、聆音、瓷器、hdarea、eastgame(TLF)、1ptba、hdtime、hd4fans、opencd、hdbug、hdstreet、joyhd、u2、upxin(HDU)、oshen、discfan(GZT)、cnscg圣城。
## 运行环境
所有具备PHP运行环境的所有平台官方下载的记得开启crul、fileinfo、mbstring这3个扩展。
例如Linux、Windows、MacOS
1. Windows下安装php环境https://www.php.net/downloads
## 下载源码
- github仓库https://github.com/ledccn/IYUUAutoReseed
- 码云仓库https://gitee.com/ledc/IYUUAutoReseed
## 使用方法
详见Wiki https://gitee.com/ledc/IYUUAutoReseed/wikis
## 需求提交/错误反馈
- 点击链接加入群聊【IYUU自动辅种交流】[https://jq.qq.com/?_wv=1027&k=5JOfOlM][1]
- QQ群859882209
- issues https://gitee.com/ledc/IYUUAutoReseed/issues
## 捐助开发者
如果觉得我的付出,节约了您的宝贵时间,请随意打赏一杯咖啡!或者一杯水!
如果喜欢,请帮忙在[Github](https://github.com/ledccn/IYUUAutoReseed)或[码云](https://gitee.com/ledc/IYUUAutoReseed)给个Star也可以对IYUUAutoReseed进行[捐赠](https://gitee.com/ledc/IYUUAutoReseed#%E6%8D%90%E5%8A%A9%E5%BC%80%E5%8F%91%E8%80%85)哦 ^_^。
**您所有的打赏将用于服务器续期,增加服务的延续性。**
![微信打赏.png][2]
## 捐赠者列表
感谢以下捐赠者,排名不分先后!
|名字 | 金额 | 时间|
| - | :-: | ---- |
| 祭 | ¥6元 | 2019年12月10日18:02 |
| 未署名 | ¥88.88元 | 2019年12月16日20:38 |
| 当下丶 [阿里云1H2G VPS]2021.9.17 | ¥1300元 | 2019年12月16日16:00 |
| xzs | ¥20元 | 2019年12月24日11:29 |
| loveB杉 | ¥20元 | 2019年12月24日20:59 |
| 风少 | ¥20元 | 2019年12月24日23:30 |
| 小夏 | ¥1元 | 2019年12月25日11:38 |
| 优つ伤 | ¥50元 | 2019年12月25日19:21 |
| Nice | ¥20元 | 2019年12月27日12:54 |
| 木腕清(天才) | ¥10元 | 2019年12月28日11:26 |
| @希望功能越来越多 | ¥20元 | 2019年12月28日17:29 |
| 竹节香附 | ¥20元 | 2019年12月28日18:21 |
| 李元芳 | ¥6.66 | 2019年12月30日16:19 |
| Ge(附言:client0修正) | ¥20元 | 2019年12月31日12:02 |
| 怪叔叔 | ¥20元 | 2019年12月31日15:46 |
| Shaopeng | ¥10元 | 2020年1月1日18:57 |
| III感谢大佬的软件 | ¥10元 | 2020年1月1日22:34 |
| 子不语 | ¥10元 | 2020年1月3日13:31 |
| 寒山先生 | ¥100元 | 2020年1月3日20:35 |
| 阿腾 | ¥20元 | 2020年1月3日22:37 |
| 手动滑稽 | ¥23.33元 | 2020年1月4日01:38 |
| 凭樊 | ¥5元 | 2020年1月4日17:58 |
| Mocar | ¥10元 | 2020年1月4日20:03 |
| Throne | ¥10元 | 2020年1月4日20:09 |
| JeSsiE杰西 | ¥200元 | 2020年1月5日09:48 |
| 人生五十载 | ¥30元 | 2020年1月5日12:29 |
| C陈奕轰隆隆 | ¥20元 | 2020年1月5日15:55 |
| 寒山先生 | ¥100元 | 2020年1月6日12:17 |
| 244574970 | ¥20元 | 2020年1月6日16:18 |
| Shaopeng | ¥10元 | 2020年1月6日22:01 |
| 轲 | ¥387元 | 2020年1月7日20:34 |
| 纸鸢 | ¥2元 | 2020年1月9日11:45 |
| 寒山先生 | ¥100元 | 2020年1月9日11:51 |
| 王浩淼 | ¥50元 | 2020年1月9日11:53 |
| 寒山先生 | ¥100元 | 2020年1月11日11:47 |
补充说明:
1. 此明细不是为了竞价排名,而是以公开、透明的制度说明所捐赠资源的使用情况和去处;
2. 所捐赠的资源不属于任何个人,而应作为项目或者开发团队的所需开销;
3. 如果捐赠了却不希望您的名字出现在这里,可以联系我们进行相应处理;
4. 更新有延时,如未能及时更新,可联系我。
[1]: https://jq.qq.com/?_wv=1027&k=5JOfOlM
[2]: https://www.iyuu.cn/usr/uploads/2019/12/801558607.png

7
vendor/autoload.php vendored Normal file
View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6::getLoader();

445
vendor/composer/ClassLoader.php vendored Normal file
View File

@@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

21
vendor/composer/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

17
vendor/composer/autoload_classmap.php vendored Normal file
View File

@@ -0,0 +1,17 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Bencode' => $baseDir . '/app/Class/Bencode.php',
'IFile' => $baseDir . '/app/Class/IFile.php',
'Oauth' => $baseDir . '/app/Class/Oauth.php',
'Rpc' => $baseDir . '/app/Class/Rpc.php',
'TransmissionRPC' => $baseDir . '/app/Class/TransmissionRPC.php',
'TransmissionRPCException' => $baseDir . '/app/Class/TransmissionRPC.php',
'qBittorrent' => $baseDir . '/app/Class/qBittorrent.php',
'uTorrent' => $baseDir . '/app/Class/uTorrent.php',
);

10
vendor/composer/autoload_files.php vendored Normal file
View File

@@ -0,0 +1,10 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'818e4acb2b433594ec9a26bacb71084d' => $baseDir . '/app/Class/Function.php',
);

10
vendor/composer/autoload_namespaces.php vendored Normal file
View File

@@ -0,0 +1,10 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Curl' => array($vendorDir . '/curl/curl/src'),
);

10
vendor/composer/autoload_psr4.php vendored Normal file
View File

@@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'phpspider\\' => array($vendorDir . '/owner888/phpspider'),
);

70
vendor/composer/autoload_real.php vendored Normal file
View File

@@ -0,0 +1,70 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire902220bdd481fe56c25750cdf0255dd6($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire902220bdd481fe56c25750cdf0255dd6($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

58
vendor/composer/autoload_static.php vendored Normal file
View File

@@ -0,0 +1,58 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit902220bdd481fe56c25750cdf0255dd6
{
public static $files = array (
'818e4acb2b433594ec9a26bacb71084d' => __DIR__ . '/../..' . '/app/Class/Function.php',
);
public static $prefixLengthsPsr4 = array (
'p' =>
array (
'phpspider\\' => 10,
),
);
public static $prefixDirsPsr4 = array (
'phpspider\\' =>
array (
0 => __DIR__ . '/..' . '/owner888/phpspider',
),
);
public static $prefixesPsr0 = array (
'C' =>
array (
'Curl' =>
array (
0 => __DIR__ . '/..' . '/curl/curl/src',
),
),
);
public static $classMap = array (
'Bencode' => __DIR__ . '/../..' . '/app/Class/Bencode.php',
'IFile' => __DIR__ . '/../..' . '/app/Class/IFile.php',
'Oauth' => __DIR__ . '/../..' . '/app/Class/Oauth.php',
'Rpc' => __DIR__ . '/../..' . '/app/Class/Rpc.php',
'TransmissionRPC' => __DIR__ . '/../..' . '/app/Class/TransmissionRPC.php',
'TransmissionRPCException' => __DIR__ . '/../..' . '/app/Class/TransmissionRPC.php',
'qBittorrent' => __DIR__ . '/../..' . '/app/Class/qBittorrent.php',
'uTorrent' => __DIR__ . '/../..' . '/app/Class/uTorrent.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$prefixesPsr0;
$loader->classMap = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$classMap;
}, null, ClassLoader::class);
}
}

119
vendor/composer/installed.json vendored Normal file
View File

@@ -0,0 +1,119 @@
[
{
"name": "curl/curl",
"version": "2.2.0",
"version_normalized": "2.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-mod/curl.git",
"reference": "d22086dd2eee5ca02e4c29b9a5bdf3645bfdbbff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-mod/curl/zipball/d22086dd2eee5ca02e4c29b9a5bdf3645bfdbbff",
"reference": "d22086dd2eee5ca02e4c29b9a5bdf3645bfdbbff",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"ext-curl": "*",
"php": "^5.6 | ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "~2.1"
},
"time": "2018-12-04T19:47:03+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Curl": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Hassan Amouhzi",
"email": "hassan@anezi.net",
"homepage": "http://hassan.amouhzi.com"
},
{
"name": "php-curl-class",
"homepage": "https://github.com/php-curl-class"
},
{
"name": "user52",
"homepage": "https://github.com/user52"
}
],
"description": "cURL class for PHP",
"homepage": "https://github.com/php-mod/curl",
"keywords": [
"curl",
"dot"
]
},
{
"name": "owner888/phpspider",
"version": "v2.1.6",
"version_normalized": "2.1.6.0",
"source": {
"type": "git",
"url": "https://github.com/owner888/phpspider.git",
"reference": "e6021148adec201418c16ba26f39bc013ba5b4d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/owner888/phpspider/zipball/e6021148adec201418c16ba26f39bc013ba5b4d9",
"reference": "e6021148adec201418c16ba26f39bc013ba5b4d9",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.5.0"
},
"suggest": {
"ext-pcntl、ext-redis": "For better performance. "
},
"time": "2018-08-15T08:04:29+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"phpspider\\": "./"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Seatle Yang",
"email": "seatle@foxmail.com",
"homepage": "http://www.phpspider.org",
"role": "Developer"
}
],
"description": "The PHPSpider Framework.",
"homepage": "http://www.phpspider.org",
"keywords": [
"framework",
"phpspider"
]
}
]

11
vendor/curl/curl/.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
vendor/*
*.orig
.buildpath
.project
.settings/*
.idea/*
composer.lock
*~
tests/phpunit_report/*
/.settings/
/.php_cs.cache

113
vendor/curl/curl/.gitlab-ci.yml vendored Normal file
View File

@@ -0,0 +1,113 @@
stages:
- build
- test
build-test-server:
image: docker:latest
stage: build
services:
- docker:dind
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker build --pull -t "$CI_REGISTRY_IMAGE:server-test" tests/server
- docker push "$CI_REGISTRY_IMAGE:server-test"
only:
changes:
- tests/server
tests-php5.6:
image: alpine:3.7
stage: test
services:
- name: "$CI_REGISTRY_IMAGE:server-test"
alias: server_test
script:
- apk add --no-cache php5-cli php5-curl php5-gd php5-phar php5-json php5-openssl php5-dom php5-xml php5-zlib
- ln -s /usr/bin/php5 /usr/bin/php
- php --version
- if [ ! -f composer.phar ]; then DOWLOAD_COMPOSER=1 ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php composer-setup.php ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "unlink('composer-setup.php');" ; fi;
- php composer.phar install
- vendor/bin/phpcs --warning-severity=0 --standard=PSR2 src
- vendor/bin/phpunit
cache:
key: php5.6
paths:
- composer.phar
- vendor
tests-php7.0:
image: alpine:3.5
stage: test
services:
- name: "$CI_REGISTRY_IMAGE:server-test"
alias: server_test
script:
- apk add --no-cache php7 php7-curl php7-gd php7-phar php7-json php7-openssl php7-dom php7-mbstring
- ln -s /usr/bin/php7 /usr/bin/php
- php --version
- if [ ! -f composer.phar ]; then DOWLOAD_COMPOSER=1 ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php composer-setup.php ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "unlink('composer-setup.php');" ; fi;
- php composer.phar install
- vendor/bin/phpcs --warning-severity=0 --standard=PSR2 src
- nohup php -S localhost:8000 -t tests/server/php-curl-test > phpd.log 2>&1 &
- vendor/bin/phpunit
cache:
key: php7.0
paths:
- composer.phar
- vendor
tests-php7.1:
image: alpine:3.7
stage: test
services:
- name: "$CI_REGISTRY_IMAGE:server-test"
alias: server_test
script:
- apk add --no-cache php7-cli php7-curl php7-gd php7-phar php7-json php7-openssl php7-dom php7-simplexml php7-tokenizer php7-mbstring php7-xml
- php --version
- if [ ! -f composer.phar ]; then DOWLOAD_COMPOSER=1 ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php composer-setup.php ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "unlink('composer-setup.php');" ; fi;
- php composer.phar install
- vendor/bin/phpcs --warning-severity=0 --standard=PSR2 src
- nohup php -S localhost:8000 -t tests/server/php-curl-test > phpd.log 2>&1 &
- vendor/bin/phpunit
cache:
key: php7.1
paths:
- composer.phar
- vendor
tests-php7.2:
image: alpine:3.8
stage: test
services:
- name: "$CI_REGISTRY_IMAGE:server-test"
alias: server_test
script:
- apk add --no-cache php7-cli php7-curl php7-gd php7-phar php7-json php7-openssl php7-dom php7-simplexml php7-tokenizer php7-mbstring php7-xml
- php --version
- if [ ! -f composer.phar ]; then DOWLOAD_COMPOSER=1 ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php composer-setup.php ; fi;
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "unlink('composer-setup.php');" ; fi;
- php composer.phar install
- vendor/bin/phpcs --warning-severity=0 --standard=PSR2 src
- nohup php -S localhost:8000 -t tests/server/php-curl-test > phpd.log 2>&1 &
- vendor/bin/phpunit
cache:
key: php7.2
paths:
- composer.phar
- vendor

20
vendor/curl/curl/LICENSE vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 php-mod
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

125
vendor/curl/curl/README.md vendored Normal file
View File

@@ -0,0 +1,125 @@
# PHP Curl Class
This library provides an object-oriented wrapper of the PHP cURL extension.
If you have questions or problems with installation or usage [create an Issue](https://github.com/php-mod/curl/issues).
## Installation
In order to install this library via composer run the following command in the console:
```sh
composer require curl/curl
```
or add the package manually to your composer.json file in the require section:
```json
"curl/curl": "^2.0"
```
## Usage examples
```php
$curl = new Curl\Curl();
$curl->get('http://www.example.com/');
```
```php
$curl = new Curl\Curl();
$curl->get('http://www.example.com/search', array(
'q' => 'keyword',
));
```
```php
$curl = new Curl\Curl();
$curl->post('http://www.example.com/login/', array(
'username' => 'myusername',
'password' => 'mypassword',
));
```
```php
$curl = new Curl\Curl();
$curl->setBasicAuthentication('username', 'password');
$curl->setUserAgent('');
$curl->setReferrer('');
$curl->setHeader('X-Requested-With', 'XMLHttpRequest');
$curl->setCookie('key', 'value');
$curl->get('http://www.example.com/');
if ($curl->error) {
echo $curl->error_code;
}
else {
echo $curl->response;
}
var_dump($curl->request_headers);
var_dump($curl->response_headers);
```
```php
$curl = new Curl\Curl();
$curl->setOpt(CURLOPT_RETURNTRANSFER, TRUE);
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, FALSE);
$curl->get('https://encrypted.example.com/');
```
```php
$curl = new Curl\Curl();
$curl->put('http://api.example.com/user/', array(
'first_name' => 'Zach',
'last_name' => 'Borboa',
));
```
```php
$curl = new Curl\Curl();
$curl->patch('http://api.example.com/profile/', array(
'image' => '@path/to/file.jpg',
));
```
```php
$curl = new Curl\Curl();
$curl->delete('http://api.example.com/user/', array(
'id' => '1234',
));
```
```php
$curl->close();
```
```php
// Example access to curl object.
curl_set_opt($curl->curl, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1');
curl_close($curl->curl);
```
```php
// Example of downloading a file or any other content
$curl = new Curl\Curl();
// open the file where the request response should be written
$file_handle = fopen($target_file, 'w+');
// pass it to the curl resource
$curl->setOpt(CURLOPT_FILE, $file_handle);
// do any type of request
$curl->get('https://github.com');
// disable writing to file
$curl->setOpt(CURLOPT_FILE, null);
// close the file for writing
fclose($file_handle);
```
## Testing
In order to test the library:
1. Create a fork
2. Clone the fork to your machine
3. Install the depencies `composer install`
4. Run the unit tests `./vendor/bin/phpunit tests`

36
vendor/curl/curl/composer.json vendored Normal file
View File

@@ -0,0 +1,36 @@
{
"name": "curl/curl",
"description": "cURL class for PHP",
"keywords": ["dot", "curl"],
"homepage": "https://github.com/php-mod/curl",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "php-curl-class",
"homepage": "https://github.com/php-curl-class"
},
{
"name": "Hassan Amouhzi",
"email": "hassan@anezi.net",
"homepage": "http://hassan.amouhzi.com"
},
{
"name": "user52",
"homepage": "https://github.com/user52"
}
],
"require": {
"php": "^5.6 | ^7.0",
"ext-curl": "*"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "~2.1"
},
"autoload": {
"psr-0": {
"Curl": "src/"
}
}
}

24
vendor/curl/curl/phpunit.xml.dist vendored Normal file
View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="true"
verbose="false"
bootstrap="vendor/autoload.php"
>
<php>
<ini name="display_errors" value="on"/>
</php>
<testsuites>
<testsuite name="PHP MP4Box Tests Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>

719
vendor/curl/curl/src/Curl/Curl.php vendored Normal file
View File

@@ -0,0 +1,719 @@
<?php
namespace Curl;
/**
* An object-oriented wrapper of the PHP cURL extension.
*
* This library requires to have the php cURL extensions installed:
* https://php.net/manual/curl.setup.php
*
* Example of making a get request with parameters:
*
* ```php
* $curl = new Curl\Curl();
* $curl->get('http://www.example.com/search', array(
* 'q' => 'keyword',
* ));
* ```
*
* Example post request with post data:
*
* ```php
* $curl = new Curl\Curl();
* $curl->post('http://www.example.com/login/', array(
* 'username' => 'myusername',
* 'password' => 'mypassword',
* ));
* ```
*
* @see https://php.net/manual/curl.setup.php
*/
class Curl
{
// The HTTP authentication method(s) to use.
/**
* @var string Type AUTH_BASIC
*/
const AUTH_BASIC = CURLAUTH_BASIC;
/**
* @var string Type AUTH_DIGEST
*/
const AUTH_DIGEST = CURLAUTH_DIGEST;
/**
* @var string Type AUTH_GSSNEGOTIATE
*/
const AUTH_GSSNEGOTIATE = CURLAUTH_GSSNEGOTIATE;
/**
* @var string Type AUTH_NTLM
*/
const AUTH_NTLM = CURLAUTH_NTLM;
/**
* @var string Type AUTH_ANY
*/
const AUTH_ANY = CURLAUTH_ANY;
/**
* @var string Type AUTH_ANYSAFE
*/
const AUTH_ANYSAFE = CURLAUTH_ANYSAFE;
/**
* @var string The user agent name which is set when making a request
*/
const USER_AGENT = 'PHP Curl/1.9 (+https://github.com/php-mod/curl)';
private $_cookies = array();
private $_headers = array();
/**
* @var resource Contains the curl resource created by `curl_init()` function
*/
public $curl;
/**
* @var bool Whether an error occured or not
*/
public $error = false;
/**
* @var int Contains the error code of the curren request, 0 means no error happend
*/
public $error_code = 0;
/**
* @var string If the curl request failed, the error message is contained
*/
public $error_message = null;
/**
* @var bool Whether an error occured or not
*/
public $curl_error = false;
/**
* @var int Contains the error code of the curren request, 0 means no error happend.
* @see https://curl.haxx.se/libcurl/c/libcurl-errors.html
*/
public $curl_error_code = 0;
/**
* @var string If the curl request failed, the error message is contained
*/
public $curl_error_message = null;
/**
* @var bool Whether an error occured or not
*/
public $http_error = false;
/**
* @var int Contains the status code of the current processed request.
*/
public $http_status_code = 0;
/**
* @var string If the curl request failed, the error message is contained
*/
public $http_error_message = null;
/**
* @var string|array TBD (ensure type) Contains the request header informations
*/
public $request_headers = null;
/**
* @var string|array TBD (ensure type) Contains the response header informations
*/
public $response_headers = array();
/**
* @var string Contains the response from the curl request
*/
public $response = null;
/**
* @var bool Whether the current section of response headers is after 'HTTP/1.1 100 Continue'
*/
protected $response_header_continue = false;
/**
* Constructor ensures the available curl extension is loaded.
*
* @throws \ErrorException
*/
public function __construct()
{
if (!extension_loaded('curl')) {
throw new \ErrorException('The cURL extensions is not loaded, make sure you have installed the cURL extension: https://php.net/manual/curl.setup.php');
}
$this->init();
}
// private methods
/**
* Initializer for the curl resource.
*
* Is called by the __construct() of the class or when the curl request is reseted.
* @return self
*/
private function init()
{
$this->curl = curl_init();
$this->setUserAgent(self::USER_AGENT);
$this->setOpt(CURLINFO_HEADER_OUT, true);
$this->setOpt(CURLOPT_HEADER, false);
$this->setOpt(CURLOPT_RETURNTRANSFER, true);
$this->setOpt(CURLOPT_HEADERFUNCTION, array($this, 'addResponseHeaderLine'));
return $this;
}
/**
* Handle writing the response headers
*
* @param resource $curl The current curl resource
* @param string $header_line A line from the list of response headers
*
* @return int Returns the length of the $header_line
*/
public function addResponseHeaderLine($curl, $header_line)
{
$trimmed_header = trim($header_line, "\r\n");
if ($trimmed_header === "") {
$this->response_header_continue = false;
} elseif (strtolower($trimmed_header) === 'http/1.1 100 continue') {
$this->response_header_continue = true;
} elseif (!$this->response_header_continue) {
$this->response_headers[] = $trimmed_header;
}
return strlen($header_line);
}
// protected methods
/**
* Execute the curl request based on the respectiv settings.
*
* @return int Returns the error code for the current curl request
*/
protected function exec()
{
$this->response_headers = array();
$this->response = curl_exec($this->curl);
$this->curl_error_code = curl_errno($this->curl);
$this->curl_error_message = curl_error($this->curl);
$this->curl_error = !($this->curl_error_code === 0);
$this->http_status_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
$this->http_error = in_array(floor($this->http_status_code / 100), array(4, 5));
$this->error = $this->curl_error || $this->http_error;
$this->error_code = $this->error ? ($this->curl_error ? $this->curl_error_code : $this->http_status_code) : 0;
$this->request_headers = preg_split('/\r\n/', curl_getinfo($this->curl, CURLINFO_HEADER_OUT), null, PREG_SPLIT_NO_EMPTY);
$this->http_error_message = $this->error ? (isset($this->response_headers['0']) ? $this->response_headers['0'] : '') : '';
$this->error_message = $this->curl_error ? $this->curl_error_message : $this->http_error_message;
return $this->error_code;
}
/**
* @param array|object|string $data
*/
protected function preparePayload($data)
{
$this->setOpt(CURLOPT_POST, true);
if (is_array($data) || is_object($data)) {
$skip = false;
foreach ($data as $key => $value) {
// If a value is an instance of CurlFile skip the http_build_query
// see issue https://github.com/php-mod/curl/issues/46
// suggestion from: https://stackoverflow.com/a/36603038/4611030
if ($value instanceof \CurlFile) {
$skip = true;
}
}
if (!$skip) {
$data = http_build_query($data);
}
}
$this->setOpt(CURLOPT_POSTFIELDS, $data);
}
/**
* Set auth options for the current request.
*
* Available auth types are:
*
* + self::AUTH_BASIC
* + self::AUTH_DIGEST
* + self::AUTH_GSSNEGOTIATE
* + self::AUTH_NTLM
* + self::AUTH_ANY
* + self::AUTH_ANYSAFE
*
* @param int $httpauth The type of authentication
*/
protected function setHttpAuth($httpauth)
{
$this->setOpt(CURLOPT_HTTPAUTH, $httpauth);
}
// public methods
/**
* @deprecated calling exec() directly is discouraged
*/
public function _exec()
{
return $this->exec();
}
// functions
/**
* Make a get request with optional data.
*
* The get request has no body data, the data will be correctly added to the $url with the http_build_query() method.
*
* @param string $url The url to make the get request for
* @param array $data Optional arguments who are part of the url
* @return self
*/
public function get($url, $data = array())
{
if (count($data) > 0) {
$this->setOpt(CURLOPT_URL, $url.'?'.http_build_query($data));
} else {
$this->setOpt(CURLOPT_URL, $url);
}
$this->setOpt(CURLOPT_HTTPGET, true);
$this->exec();
return $this;
}
/**
* Make a post request with optional post data.
*
* @param string $url The url to make the post request
* @param array $data Post data to pass to the url
* @return self
*/
public function post($url, $data = array())
{
$this->setOpt(CURLOPT_URL, $url);
$this->preparePayload($data);
$this->exec();
return $this;
}
/**
* Make a put request with optional data.
*
* The put request data can be either sent via payload or as get paramters of the string.
*
* @param string $url The url to make the put request
* @param array $data Optional data to pass to the $url
* @param bool $payload Whether the data should be transmitted trough payload or as get parameters of the string
* @return self
*/
public function put($url, $data = array(), $payload = false)
{
if (! empty($data)) {
if ($payload === false) {
$url .= '?'.http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');
$this->exec();
return $this;
}
/**
* Make a patch request with optional data.
*
* The patch request data can be either sent via payload or as get paramters of the string.
*
* @param string $url The url to make the patch request
* @param array $data Optional data to pass to the $url
* @param bool $payload Whether the data should be transmitted trough payload or as get parameters of the string
* @return self
*/
public function patch($url, $data = array(), $payload = false)
{
if (! empty($data)) {
if ($payload === false) {
$url .= '?'.http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH');
$this->exec();
return $this;
}
/**
* Make a delete request with optional data.
*
* @param string $url The url to make the delete request
* @param array $data Optional data to pass to the $url
* @param bool $payload Whether the data should be transmitted trough payload or as get parameters of the string
* @return self
*/
public function delete($url, $data = array(), $payload = false)
{
if (! empty($data)) {
if ($payload === false) {
$url .= '?'.http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE');
$this->exec();
return $this;
}
// setters
/**
* Pass basic auth data.
*
* If the the rquested url is secured by an httaccess basic auth mechanism you can use this method to provided the auth data.
*
* ```php
* $curl = new Curl();
* $curl->setBasicAuthentication('john', 'doe');
* $curl->get('http://example.com/secure.php');
* ```
*
* @param string $username The username for the authentification
* @param string $password The password for the given username for the authentification
* @return self
*/
public function setBasicAuthentication($username, $password)
{
$this->setHttpAuth(self::AUTH_BASIC);
$this->setOpt(CURLOPT_USERPWD, $username.':'.$password);
return $this;
}
/**
* Provide optional header informations.
*
* In order to pass optional headers by key value pairing:
*
* ```php
* $curl = new Curl();
* $curl->setHeader('X-Requested-With', 'XMLHttpRequest');
* $curl->get('http://example.com/request.php');
* ```
*
* @param string $key The header key
* @param string $value The value for the given header key
* @return self
*/
public function setHeader($key, $value)
{
$this->_headers[$key] = $key.': '.$value;
$this->setOpt(CURLOPT_HTTPHEADER, array_values($this->_headers));
return $this;
}
/**
* Provide a User Agent.
*
* In order to provide you cusomtized user agent name you can use this method.
*
* ```php
* $curl = new Curl();
* $curl->setUserAgent('My John Doe Agent 1.0');
* $curl->get('http://example.com/request.php');
* ```
*
* @param string $useragent The name of the user agent to set for the current request
* @return self
*/
public function setUserAgent($useragent)
{
$this->setOpt(CURLOPT_USERAGENT, $useragent);
return $this;
}
/**
* @deprecated Call setReferer() instead
*/
public function setReferrer($referrer)
{
$this->setReferer($referrer);
return $this;
}
/**
* Set the HTTP referer header.
*
* The $referer informations can help identify the requested client where the requested was made.
*
* @param string $referer An url to pass and will be set as referer header
* @return self
*/
public function setReferer($referer)
{
$this->setOpt(CURLOPT_REFERER, $referer);
return $this;
}
/**
* Set contents of HTTP Cookie header.
*
* @param string $key The name of the cookie
* @param string $value The value for the provided cookie name
* @return self
*/
public function setCookie($key, $value)
{
$this->_cookies[$key] = $value;
$this->setOpt(CURLOPT_COOKIE, http_build_query($this->_cookies, '', '; '));
return $this;
}
/**
* Set customized curl options.
*
* To see a full list of options: http://php.net/curl_setopt
*
* @see http://php.net/curl_setopt
*
* @param int $option The curl option constante e.g. `CURLOPT_AUTOREFERER`, `CURLOPT_COOKIESESSION`
* @param mixed $value The value to pass for the given $option
*/
public function setOpt($option, $value)
{
return curl_setopt($this->curl, $option, $value);
}
/**
* Get customized curl options.
*
* To see a full list of options: http://php.net/curl_getinfo
*
* @see http://php.net/curl_getinfo
*
* @param int $option The curl option constante e.g. `CURLOPT_AUTOREFERER`, `CURLOPT_COOKIESESSION`
* @param mixed $value The value to check for the given $option
*/
public function getOpt($option)
{
return curl_getinfo($this->curl, $option);
}
/**
* Return the endpoint set for curl
*
* @see http://php.net/curl_getinfo
*
* @return string of endpoint
*/
public function getEndpoint()
{
return $this->getOpt(CURLINFO_EFFECTIVE_URL);
}
/**
* Enable verbositiy.
*
* @todo As to keep naming convention it should be renamed to `setVerbose()`
*
* @param string $on
* @return self
*/
public function verbose($on = true)
{
$this->setOpt(CURLOPT_VERBOSE, $on);
return $this;
}
/**
* Reset all curl options.
*
* In order to make multiple requests with the same curl object all settings requires to be reset.
* @return self
*/
public function reset()
{
$this->close();
$this->_cookies = array();
$this->_headers = array();
$this->error = false;
$this->error_code = 0;
$this->error_message = null;
$this->curl_error = false;
$this->curl_error_code = 0;
$this->curl_error_message = null;
$this->http_error = false;
$this->http_status_code = 0;
$this->http_error_message = null;
$this->request_headers = null;
$this->response_headers = array();
$this->response = null;
$this->init();
return $this;
}
/**
* Closing the current open curl resource.
* @return self
*/
public function close()
{
if (is_resource($this->curl)) {
curl_close($this->curl);
}
return $this;
}
/**
* Close the connection when the Curl object will be destroyed.
*/
public function __destruct()
{
$this->close();
}
/**
* Was an 'info' header returned.
* @return bool
*/
public function isInfo()
{
return $this->http_status_code >= 100 && $this->http_status_code < 200;
}
/**
* Was an 'OK' response returned.
* @return bool
*/
public function isSuccess()
{
return $this->http_status_code >= 200 && $this->http_status_code < 300;
}
/**
* Was a 'redirect' returned.
* @return bool
*/
public function isRedirect()
{
return $this->http_status_code >= 300 && $this->http_status_code < 400;
}
/**
* Was an 'error' returned (client error or server error).
* @return bool
*/
public function isError()
{
return $this->http_status_code >= 400 && $this->http_status_code < 600;
}
/**
* Was a 'client error' returned.
* @return bool
*/
public function isClientError()
{
return $this->http_status_code >= 400 && $this->http_status_code < 500;
}
/**
* Was a 'server error' returned.
* @return bool
*/
public function isServerError()
{
return $this->http_status_code >= 500 && $this->http_status_code < 600;
}
/**
* Get a specific response header key or all values from the response headers array.
*
* Usage example:
*
* ```php
* $curl = (new Curl())->get('http://example.com');
*
* echo $curl->getResponseHeaders('Content-Type');
* ```
*
* Or in order to dump all keys with the given values use:
*
* ```php
* $curl = (new Curl())->get('http://example.com');
*
* var_dump($curl->getResponseHeaders());
* ```
*
* @param string $headerKey Optional key to get from the array.
* @return bool|string
* @since 1.9
*/
public function getResponseHeaders($headerKey = null)
{
$headers = array();
$headerKey = strtolower($headerKey);
foreach ($this->response_headers as $header) {
$parts = explode(":", $header, 2);
$key = isset($parts[0]) ? $parts[0] : null;
$value = isset($parts[1]) ? $parts[1] : null;
$headers[trim(strtolower($key))] = trim($value);
}
if ($headerKey) {
return isset($headers[$headerKey]) ? $headers[$headerKey] : false;
}
return $headers;
}
public function getResponse()
{
return $this->response;
}
public function getErrorCode()
{
return $this->curl_error_code;
}
public function getErrorMessage()
{
return $this->curl_error_message;
}
public function getHttpStatus()
{
return $this->http_status_code;
}
}

277
vendor/curl/curl/tests/CurlTest.php vendored Normal file
View File

@@ -0,0 +1,277 @@
<?php
namespace Curl;
class CurlTest extends \PHPUnit_Framework_TestCase
{
const TEST_URL = 'http://server_test';
/**
*
* @var Curl
*/
protected $curl;
function setUp() {
$this->curl = new Curl();
$this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, FALSE);
$this->curl->setOpt(CURLOPT_SSL_VERIFYHOST, FALSE);
}
function server($request_method, $data='') {
$request_method = strtolower($request_method);
$this->curl->$request_method(self::TEST_URL . '/server.php', $data);
return $this->curl->response;
}
public function testExtensionLoaded() {
$this->assertTrue(extension_loaded('curl'));
}
public function testUserAgent() {
$this->curl->setUserAgent(Curl::USER_AGENT);
$this->assertEquals(Curl::USER_AGENT, $this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_USER_AGENT',
)));
}
public function testGet() {
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'GET');
}
public function testPostRequestMethod() {
$this->assertTrue($this->server('POST', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'POST');
}
public function testPostData() {
$this->assertTrue($this->server('POST', array(
'test' => 'post',
'key' => 'test',
)) === 'post');
}
public function testPostMultidimensionalData() {
$data = array(
'key' => 'file',
'file' => array(
'wibble',
'wubble',
'wobble',
),
);
$this->curl->post(self::TEST_URL . '/post_multidimensional.php', $data);
$this->assertEquals(
'key=file&file%5B0%5D=wibble&file%5B1%5D=wubble&file%5B2%5D=wobble',
$this->curl->response);
}
public function testPostFilePathUpload()
{
$file_path = $this->get_png();
$data = array(
'key' => 'image',
'image' => '@' . $file_path,
);
$this->curl->setOpt(CURLOPT_RETURNTRANSFER, true);
$this->curl->post(self::TEST_URL . '/post_file_path_upload.php', $data);
$this->assertEquals(
array(
'request_method' => 'POST',
'key' => 'image',
'mime_content_type' => 'ERROR', // Temp change the image response, but assuming this is not fixing the issue indeed.
//'mime_content_type' => 'image/png'
),
json_decode($this->curl->response, true));
unlink($file_path);
}
public function testPutRequestMethod() {
$this->assertTrue($this->server('PUT', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'PUT');
}
public function testPutData() {
$this->assertTrue($this->server('PUT', array(
'test' => 'put',
'key' => 'test',
)) === 'put');
}
public function testPutFileHandle() {
$png = $this->create_png();
$tmp_file = $this->create_tmp_file($png);
$this->curl->setOpt(CURLOPT_PUT, TRUE);
$this->curl->setOpt(CURLOPT_INFILE, $tmp_file);
$this->curl->setOpt(CURLOPT_INFILESIZE, strlen($png));
$this->curl->put(self::TEST_URL . '/server.php', array(
'test' => 'put_file_handle',
));
fclose($tmp_file);
$this->assertTrue($this->curl->response === 'image/png');
}
public function testDelete() {
$this->assertTrue($this->server('DELETE', array(
'test' => 'server',
'key' => 'REQUEST_METHOD',
)) === 'DELETE');
$this->assertTrue($this->server('DELETE', array(
'test' => 'delete',
'key' => 'test',
)) === 'delete');
}
public function testBasicHttpAuth() {
$data = array();
$this->curl->get(self::TEST_URL . '/http_basic_auth.php', $data);
$this->assertEquals('canceled', $this->curl->response);
$username = 'myusername';
$password = 'mypassword';
$this->curl->setBasicAuthentication($username, $password);
$this->curl->get(self::TEST_URL . '/http_basic_auth.php', $data);
$this->assertEquals(
'{"username":"myusername","password":"mypassword"}',
$this->curl->response);
}
public function testReferrer() {
$this->curl->setReferer('myreferrer');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_REFERER',
)) === 'myreferrer');
}
public function testDeprecatedReferrer() {
$this->curl->setReferrer('myreferrer');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_REFERER',
)) === 'myreferrer');
}
public function testCookies() {
$this->curl->setCookie('mycookie', 'yum');
$this->assertTrue($this->server('GET', array(
'test' => 'cookie',
'key' => 'mycookie',
)) === 'yum');
}
public function testError() {
$this->curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 2000);
$this->curl->get('http://1.2.3.4/');
$this->assertTrue($this->curl->error === TRUE);
$this->assertTrue($this->curl->curl_error === TRUE);
$this->assertTrue($this->curl->curl_error_code === CURLE_OPERATION_TIMEOUTED);
}
public function testHeaders() {
$this->curl->setHeader('Content-Type', 'application/json');
$this->curl->setHeader('X-Requested-With', 'XMLHttpRequest');
$this->curl->setHeader('Accept', 'application/json');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'CONTENT_TYPE',
)) === 'application/json');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_X_REQUESTED_WITH',
)) === 'XMLHttpRequest');
$this->assertTrue($this->server('GET', array(
'test' => 'server',
'key' => 'HTTP_ACCEPT',
)) === 'application/json');
}
public function testHeadersWithContinue() {
$headers = file(dirname(__FILE__) . '/data/response_headers_with_continue.txt');
$this->curl->response_headers = array();
foreach($headers as $header_line) {
$this->curl->addResponseHeaderLine(null, $header_line);
}
$expected_headers = array_values(array_filter(array_map(function($l) { return trim($l, "\r\n"); }, array_slice($headers, 1))));
$this->assertEquals($expected_headers, $this->curl->response_headers);
}
public function testReset()
{
$curl = $this->getMockBuilder(get_class($this->curl))->getMock();
$curl->expects($this->once())->method('reset')->with();
// lets make small request
$curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 2000);
$curl->get('http://1.2.3.4/');
$curl->reset();
$this->assertFalse($curl->error);
$this->assertSame(0, $curl->error_code);
$this->assertNull($curl->error_message);
$this->assertFalse($curl->curl_error);
$this->assertSame(0, $curl->curl_error_code);
$this->assertNull($curl->curl_error_message);
$this->assertFalse($curl->http_error);
$this->assertSame(0, $curl->http_status_code);
$this->assertNull($curl->http_error_message);
$this->assertNull($curl->request_headers);
$this->assertEmpty($curl->response_headers);
$this->assertNull($curl->response);
}
function create_png() {
// PNG image data, 1 x 1, 1-bit colormap, non-interlaced
ob_start();
imagepng(imagecreatefromstring(base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7')));
$raw_image = ob_get_contents();
ob_end_clean();
return $raw_image;
}
function create_tmp_file($data) {
$tmp_file = tmpfile();
fwrite($tmp_file, $data);
rewind($tmp_file);
return $tmp_file;
}
function get_png() {
$tmp_filename = tempnam('/tmp', 'php-curl-class.');
file_put_contents($tmp_filename, $this->create_png());
return $tmp_filename;
}
}

View File

@@ -0,0 +1,13 @@
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Server: nginx/1.1.19
Date: Fri, 11 Aug 2017 13:22:00 GMT
Content-Type: image/jpeg
Content-Length: 62574
Connection: close
Cache-Control: max-age=7257600
Expires: Fri, 03 Nov 2017 13:22:00 GMT
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Option: DENY

BIN
vendor/curl/curl/tests/data/test.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,9 @@
FROM alpine:3.7
RUN apk add --no-cache php5-cli php5-curl php5-gd php5-phar php5-json php5-openssl php5-dom
COPY php-curl-test php-curl-test
EXPOSE 80
CMD ["php5", "-S", "0.0.0.0:80", "-t", "php-curl-test"]

View File

@@ -0,0 +1,37 @@
<?php
// The commands
$commands = array(
'cd ../../.. && git pull',
);
// Run the commands for output
$output = '';
foreach($commands AS $command){
// Run it
$tmp = shell_exec($command);
// Output
$output .= "<span style=\"color: #6BE234;\">\$</span> <span style=\"color: #729FCF;\">{$command}\n</span>";
$output .= htmlentities(trim($tmp)) . "\n";
}
// Make it pretty for manual user access (and why not?)
?>
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>GIT DEPLOYMENT SCRIPT</title>
</head>
<body style="background-color: #000000; color: #FFFFFF; font-weight: bold; padding: 0 10px;">
<pre>
. ____ . ____________________________
|/ \| | |
[| <span style="color: #FF0000;">&hearts; &hearts;</span> |] | Git Deployment Script v0.1 |
|___==___| / &copy; oodavid 2012 |
|____________________________|
<?php echo $output; ?>
</pre>
</body>
</html>

View File

@@ -0,0 +1,14 @@
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'canceled';
exit;
}
header('Content-Type: application/json');
echo json_encode(array(
'username' => $_SERVER['PHP_AUTH_USER'],
'password' => $_SERVER['PHP_AUTH_PW'],
));

View File

@@ -0,0 +1,21 @@
<?php
$request_method = isset($_SERVER['REQUEST_METHOD']) ?
$_SERVER['REQUEST_METHOD'] : '';
$data_values = $request_method === 'POST' ? $_POST : $_GET;
$key = isset($data_values['key']) ? $data_values['key'] : '';
$response = array();
$response['request_method'] = $request_method;
$response['key'] = $key;
if(isset($_FILES[$key])) {
$response['mime_content_type'] = mime_content_type($_FILES[$key]['tmp_name']);
} else {
$response['mime_content_type'] = 'ERROR';
}
echo json_encode($response);

View File

@@ -0,0 +1,4 @@
<?php
$http_raw_post_data = file_get_contents('php://input');
echo $http_raw_post_data;

View File

@@ -0,0 +1,31 @@
<?php
$request_method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '';
$data_values = $request_method === 'POST' ? $_POST : $_GET;
$test = isset($data_values['test']) ? $data_values['test'] : '';
$key = isset($data_values['key']) ? $data_values['key'] : '';
if ($test === 'put_file_handle') {
$tmp_filename = tempnam('/tmp', 'php-curl-class.');
file_put_contents($tmp_filename, file_get_contents('php://input'));
echo mime_content_type($tmp_filename);
unlink($tmp_filename);
exit;
}
header('Content-Type: text/plain');
$data_mapping = array(
'cookie' => '_COOKIE',
'delete' => '_GET',
'post' => '_POST',
'put' => '_GET',
'server' => '_SERVER',
);
if(isset($data_mapping[$test])) {
$data = ${$data_mapping[$test]};
$value = isset($data[$key]) ? $data[$key] : '';
echo $value;
} else {
echo "Error.";
}

52
vendor/owner888/phpspider/README.md vendored Normal file
View File

@@ -0,0 +1,52 @@
# phpspider -- PHP蜘蛛爬虫框架
《我用爬虫一天时间“偷了”知乎一百万用户只为证明PHP是世界上最好的语言 》所使用的程序
phpspider是一个爬虫开发框架。使用本框架你不用了解爬虫的底层技术实现爬虫被网站屏蔽、有些网站需要登录或验证码识别才能爬取等问题。简单几行PHP代码就可以创建自己的爬虫利用框架封装的多进程Worker类库代码更简洁执行效率更高速度更快。
demo目录下有一些特定网站的爬取规则只要你安装了PHP环境代码就可以在命令行下直接跑。 对爬虫感兴趣的开发者可以加QQ群一起讨论147824717。
下面以糗事百科为例, 来看一下我们的爬虫长什么样子:
```
$configs = array(
'name' => '糗事百科',
'domains' => array(
'qiushibaike.com',
'www.qiushibaike.com'
),
'scan_urls' => array(
'http://www.qiushibaike.com/'
),
'content_url_regexes' => array(
"http://www.qiushibaike.com/article/\d+"
),
'list_url_regexes' => array(
"http://www.qiushibaike.com/8hr/page/\d+\?s=\d+"
),
'fields' => array(
array(
// 抽取内容页的文章内容
'name' => "article_content",
'selector' => "//*[@id='single-next-link']",
'required' => true
),
array(
// 抽取内容页的文章作者
'name' => "article_author",
'selector' => "//div[contains(@class,'author')]//h2",
'required' => true
),
),
);
$spider = new phpspider($configs);
$spider->start();
```
爬虫的整体框架就是这样, 首先定义了一个$configs数组, 里面设置了待爬网站的一些信息, 然后通过调用```$spider = new phpspider($configs);```和```$spider->start();```来配置并启动爬虫.
#### 运行界面如下:
![](http://www.epooll.com/zhihu/pachong.gif)
更多详细内容,移步到:
[开发文档](http://doc.phpspider.org)

View File

@@ -0,0 +1,77 @@
<?php
/**
* This file is part of phpspider.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author seatle<seatle@foxmail.com>
* @copyright seatle<seatle@foxmail.com>
* @link http://www.phpspider.org/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace phpspider;
/**
* autoloader.
*/
class autoloader
{
/**
* Autoload root path.
*
* @var string
*/
protected static $_autoload_root_path = '';
/**
* Set autoload root path.
*
* @param string $root_path
* @return void
*/
public static function set_root_path($root_path)
{
self::$_autoload_root_path = $root_path;
}
/**
* Load files by namespace.
*
* @param string $name
* @return boolean
*/
public static function load_by_namespace($name)
{
$class_path = str_replace('\\', DIRECTORY_SEPARATOR, $name);
if (strpos($name, 'phpspider\\') === 0)
{
$class_file = __DIR__ . substr($class_path, strlen('phpspider')) . '.php';
}
else
{
if (self::$_autoload_root_path)
{
$class_file = self::$_autoload_root_path . DIRECTORY_SEPARATOR . $class_path . '.php';
}
if (empty($class_file) || !is_file($class_file))
{
$class_file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . "$class_path.php";
}
}
if (is_file($class_file))
{
require_once($class_file);
if (class_exists($name, false))
{
return true;
}
}
return false;
}
}
spl_autoload_register('\phpspider\autoloader::load_by_namespace');

38
vendor/owner888/phpspider/composer.json vendored Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "owner888/phpspider",
"type": "library",
"keywords": [
"framework",
"phpspider"
],
"homepage": "http://www.phpspider.org",
"license": "MIT",
"description": "The PHPSpider Framework.",
"authors": [
{
"name": "Seatle Yang",
"email": "seatle@foxmail.com",
"homepage": "http://www.phpspider.org",
"role": "Developer"
}
],
"support": {
"email": "seatle@foxmail.com",
"issues": "https://github.com/owner888/phpspider/issues",
"forum": "http://wenda.phpspider.org/",
"wiki": "http://doc.phpspider.org/",
"source": "https://github.com/owner888/phpspider"
},
"require": {
"php": ">=5.5.0"
},
"suggest": {
"ext-pcntl、ext-redis": "For better performance. "
},
"autoload": {
"psr-4": {
"phpspider\\": "./"
}
},
"minimum-stability": "dev"
}

View File

@@ -0,0 +1,64 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider缓存类文件
//----------------------------------
class cache
{
// 多进程下面不能用单例模式
//protected static $_instance;
/**
* 获取实例
*
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-04-10 22:55
*/
public static function init()
{
if(extension_loaded('Redis'))
{
$_instance = new Redis();
}
else
{
$errmsg = "extension redis is not installed";
log::add($errmsg, "Error");
return null;
}
// 这里不能用pconnect会报错Uncaught exception 'RedisException' with message 'read error on connection'
$_instance->connect($GLOBALS['config']['redis']['host'], $GLOBALS['config']['redis']['port'], $GLOBALS['config']['redis']['timeout']);
// 验证
if ($GLOBALS['config']['redis']['pass'])
{
if ( !$_instance->auth($GLOBALS['config']['redis']['pass']) )
{
$errmsg = "Redis Server authentication failed!!";
log::add($errmsg, "Error");
return null;
}
}
// 不序列化的话不能存数组用php的序列化方式其他语言又不能读取所以这里自己用json序列化了性能还比php的序列化好1.4倍
//$_instance->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // don't serialize data
//$_instance->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // use built-in serialize/unserialize
//$_instance->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // use igBinary serialize/unserialize
$_instance->setOption(Redis::OPT_PREFIX, $GLOBALS['config']['redis']['prefix'] . ":");
return $_instance;
}
}

View File

@@ -0,0 +1,55 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider公共入口文件
//----------------------------------
//namespace phpspider\core;
// Display errors.
ini_set('display_errors', 'on');
// Reporting all.
error_reporting(E_ALL);
// 永不超时
ini_set('max_execution_time', 0);
set_time_limit(0);
// 内存限制,如果外面设置的内存比 /etc/php/php-cli.ini 大,就不要设置了
if (intval(ini_get("memory_limit")) < 1024)
{
ini_set('memory_limit', '1024M');
}
if( PHP_SAPI != 'cli' )
{
exit("You must run the CLI environment\n");
}
// Date.timezone
if (!ini_get('date.timezone'))
{
date_default_timezone_set('Asia/Shanghai');
}
//核心库目录
define('CORE', dirname(__FILE__));
define('PATH_ROOT', CORE."/../");
define('PATH_DATA', CORE."/../data");
define('PATH_LIBRARY', CORE."/../library");
//系统配置
//if( file_exists( PATH_ROOT."/config/inc_config.php" ) )
//{
//require PATH_ROOT."/config/inc_config.php";
//}

579
vendor/owner888/phpspider/core/db.php vendored Normal file
View File

@@ -0,0 +1,579 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider数据库类文件
//----------------------------------
namespace phpspider\core;
class db
{
private static $configs = array();
private static $rsid;
private static $links = array();
private static $link_name = 'default';
private static $autocommiting = false;
public static function _init()
{
// 获取配置
$config = self::$link_name == 'default' ? self::_get_default_config() : self::$configs[self::$link_name];
// 创建连接
if (empty(self::$links[self::$link_name]) || empty(self::$links[self::$link_name]['conn']))
{
// 第一次连接初始化fail和pid
if (empty(self::$links[self::$link_name]))
{
self::$links[self::$link_name]['fail'] = 0;
self::$links[self::$link_name]['pid'] = function_exists('posix_getpid') ? posix_getpid() : 0;
//echo "progress[".self::$links[self::$link_name]['pid']."] create db connect[".self::$link_name."]\n";
}
self::$links[self::$link_name]['conn'] = mysqli_connect($config['host'], $config['user'], $config['pass'], $config['name'], $config['port']);
if(mysqli_connect_errno())
{
self::$links[self::$link_name]['fail']++;
$errmsg = 'Mysql Connect failed['.self::$links[self::$link_name]['fail'].']: ' . mysqli_connect_error();
echo util::colorize(date("H:i:s") . " {$errmsg}\n\n", 'fail');
log::add($errmsg, "Error");
// 连接失败5次中断进程
if (self::$links[self::$link_name]['fail'] >= 5)
{
exit(250);
}
self::_init($config);
}
else
{
mysqli_query(self::$links[self::$link_name]['conn'], " SET character_set_connection=utf8, character_set_results=utf8, character_set_client=binary, sql_mode='' ");
}
}
else
{
$curr_pid = function_exists('posix_getpid') ? posix_getpid() : 0;
// 如果父进程已经生成资源就释放重新生成,因为多进程不能共享连接资源
if (self::$links[self::$link_name]['pid'] != $curr_pid)
{
self::clear_link();
}
}
}
/**
* 重新设置连接
* 传空的话就等于关闭数据库再连接
* 在多进程环境下如果主进程已经调用过了,子进程一定要调用一次 clear_link否则会报错
* Error while reading greeting packet. PID=19615这是两个进程互抢一个连接句柄引起的
*
* @param array $config
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-03-29 00:51
*/
public static function clear_link()
{
if(self::$links)
{
foreach(self::$links as $k=>$v)
{
@mysqli_close($v['conn']);
unset(self::$links[$k]);
}
}
// 注意,只会连接最后一个,不过貌似也够用了啊
self::_init();
}
/**
* 改变链接为指定配置的链接(如果不同时使用多个数据库,不会涉及这个操作)
* @parem $link_name 链接标识名
* @parem $config 多次使用时, 这个数组只需传递一次
* config 格式与 $GLOBALS['config']['db'] 一致
* @return void
*/
public static function set_connect($link_name, $config = array())
{
self::$link_name = $link_name;
if (!empty($config))
{
self::$configs[self::$link_name] = $config;
}
else
{
if (empty(self::$configs[self::$link_name]))
{
throw new Exception("You not set a config array for connect!");
}
}
}
/**
* 还原为默认连接(如果不同时使用多个数据库,不会涉及这个操作)
* @parem $config 指定配置默认使用inc_config.php的配置
* @return void
*/
public static function set_connect_default()
{
$config = self::_get_default_config();
self::set_connect('default', $config);
}
/**
* 获取默认配置
*/
protected static function _get_default_config()
{
if (empty(self::$configs['default']))
{
if (!is_array($GLOBALS['config']['db']))
{
exit('db.php _get_default_config()' . '没有mysql配置');
}
self::$configs['default'] = $GLOBALS['config']['db'];
}
return self::$configs['default'];
}
/**
* 返回查询游标
* @return rsid
*/
protected static function _get_rsid($rsid = '')
{
return $rsid == '' ? self::$rsid : $rsid;
}
public static function autocommit($mode = false)
{
if ( self::$autocommiting )
{
return true;
}
self::$autocommiting = true;
self::_init();
return mysqli_autocommit(self::$links[self::$link_name]['conn'], $mode);
}
public static function begin_tran()
{
return self::autocommit(false);
}
public static function commit()
{
mysqli_commit(self::$links[self::$link_name]['conn']);
self::autocommit(true);
return true;
}
public static function rollback()
{
mysqli_rollback(self::$links[self::$link_name]['conn']);
self::autocommit(true);
return true;
}
public static function query($sql)
{
$sql = trim($sql);
// 初始化数据库
self::_init();
self::$rsid = @mysqli_query(self::$links[self::$link_name]['conn'], $sql);
if (self::$rsid === false)
{
// 不要每次都ping浪费流量浪费性能执行出错了才重新连接
$errno = mysqli_errno(self::$links[self::$link_name]['conn']);
if ($errno == 2013 || $errno == 2006)
{
$errmsg = mysqli_error(self::$links[self::$link_name]['conn']);
log::add($errmsg, "Error");
@mysqli_close(self::$links[self::$link_name]['conn']);
self::$links[self::$link_name]['conn'] = null;
return self::query($sql);
}
$errmsg = "Query SQL: ".$sql;
log::add($errmsg, "Warning");
$errmsg = "Error SQL: ".mysqli_error(self::$links[self::$link_name]['conn']);
log::add($errmsg, "Warning");
$backtrace = debug_backtrace();
array_shift($backtrace);
$narr = array('class', 'type', 'function', 'file', 'line');
$err = "debug_backtrace\n";
foreach($backtrace as $i => $l)
{
foreach($narr as $k)
{
if( !isset($l[$k]) )
{
$l[$k] = '';
}
}
$err .= "[$i] in function {$l['class']}{$l['type']}{$l['function']} ";
if($l['file']) $err .= " in {$l['file']} ";
if($l['line']) $err .= " on line {$l['line']} ";
$err .= "\n";
}
log::add($err);
return false;
}
else
{
return self::$rsid;
}
}
public static function fetch($rsid = '')
{
$rsid = self::_get_rsid($rsid);
$row = mysqli_fetch_array($rsid, MYSQLI_ASSOC);
return $row;
}
public static function get_one($sql)
{
if (!preg_match("/limit/i", $sql))
{
$sql = preg_replace("/[,;]$/i", '', trim($sql)) . " limit 1 ";
}
$rsid = self::query($sql);
if ($rsid === false)
{
return array();
}
$row = self::fetch($rsid);
self::free($rsid);
return $row;
}
public static function get_all($sql)
{
$rsid = self::query($sql);
if ($rsid === false)
{
return array();
}
while ( $row = self::fetch($rsid) )
{
$rows[] = $row;
}
self::free($rsid);
return empty($rows) ? false : $rows;
}
public static function free($rsid)
{
return mysqli_free_result($rsid);
}
public static function insert_id()
{
return mysqli_insert_id(self::$links[self::$link_name]['conn']);
}
public static function affected_rows()
{
return mysqli_affected_rows(self::$links[self::$link_name]['conn']);
}
public static function insert($table = '', $data = null, $return_sql = false)
{
$items_sql = $values_sql = "";
foreach ($data as $k => $v)
{
$v = stripslashes($v);
$v = addslashes($v);
$items_sql .= "`$k`,";
$values_sql .= "\"$v\",";
}
$sql = "Insert Ignore Into `{$table}` (" . substr($items_sql, 0, -1) . ") Values (" . substr($values_sql, 0, -1) . ")";
if ($return_sql)
{
return $sql;
}
else
{
if (self::query($sql))
{
return mysqli_insert_id(self::$links[self::$link_name]['conn']);
}
else
{
return false;
}
}
}
public static function insert_batch($table = '', $set = NULL, $return_sql = FALSE)
{
if (empty($table) || empty($set))
{
return false;
}
$set = self::strsafe($set);
$fields = self::get_fields($table);
$keys_sql = $vals_sql = array();
foreach ($set as $i=>$val)
{
ksort($val);
$vals = array();
foreach ($val as $k => $v)
{
// 过滤掉数据库没有的字段
if (!in_array($k, $fields))
{
continue;
}
// 如果是第一个数组把key当做插入条件
if ($i == 0 && $k == 0)
{
$keys_sql[] = "`$k`";
}
$vals[] = "\"$v\"";
}
$vals_sql[] = implode(",", $vals);
}
$sql = "Insert Ignore Into `{$table}`(".implode(", ", $keys_sql).") Values (".implode("), (", $vals_sql).")";
if ($return_sql) return $sql;
$rt = self::query($sql);
$insert_id = self::insert_id();
$return = empty($insert_id) ? $rt : $insert_id;
return $return;
}
public static function update_batch($table = '', $set = NULL, $index = NULL, $where = NULL, $return_sql = FALSE)
{
if (empty($table) || is_null($set) || is_null($index))
{
// 不要用exit会中断程序
return false;
}
$set = self::strsafe($set);
$fields = self::get_fields($table);
$ids = array();
foreach ($set as $val)
{
ksort($val);
// 去重其实不去也可以因为相同的when只会执行第一个后面的就直接跳过不执行了
$key = md5($val[$index]);
$ids[$key] = $val[$index];
foreach (array_keys($val) as $field)
{
if ($field != $index)
{
$final[$field][$key] = 'When `'.$index.'` = "'.$val[$index].'" Then "'.$val[$field].'"';
}
}
}
//$ids = array_values($ids);
// 如果不是数组而且不为空,就转数组
if (!is_array($where) && !empty($where))
{
$where = array($where);
}
$where[] = $index.' In ("'.implode('","', $ids).'")';
$where = empty($where) ? "" : " Where ".implode(" And ", $where);
$sql = "Update `".$table."` Set ";
$cases = '';
foreach ($final as $k => $v)
{
// 过滤掉数据库没有的字段
if (!in_array($k, $fields))
{
continue;
}
$cases .= '`'.$k.'` = Case '."\n";
foreach ($v as $row)
{
$cases .= $row."\n";
}
$cases .= 'Else `'.$k.'` End, ';
}
$sql .= substr($cases, 0, -2);
// 其实不带 Where In ($index) 的条件也可以的
$sql .= $where;
if ($return_sql) return $sql;
$rt = self::query($sql);
$insert_id = self::affected_rows();
$return = empty($affected_rows) ? $rt : $affected_rows;
return $return;
}
public static function update($table = '', $data = array(), $where = null, $return_sql = false)
{
$sql = "UPDATE `{$table}` SET ";
foreach ($data as $k => $v)
{
$v = stripslashes($v);
$v = addslashes($v);
$sql .= "`{$k}` = \"{$v}\",";
}
if (!is_array($where))
{
$where = array($where);
}
// 删除空字段,不然array("")会成为WHERE
foreach ($where as $k => $v)
{
if (empty($v))
{
unset($where[$k]);
}
}
$where = empty($where) ? "" : " Where " . implode(" And ", $where);
$sql = substr($sql, 0, -1) . $where;
if ($return_sql)
{
return $sql;
}
else
{
if (self::query($sql))
{
return mysqli_affected_rows(self::$links[self::$link_name]['conn']);
}
else
{
return false;
}
}
}
public static function delete($table = '', $where = null, $return_sql = false)
{
// 小心全部被删除了
if (empty($where))
{
return false;
}
$where = 'Where ' . (!is_array($where) ? $where : implode(' And ', $where));
$sql = "Delete From `{$table}` {$where}";
if ($return_sql)
{
return $sql;
}
else
{
if (self::query($sql))
{
return mysqli_affected_rows(self::$links[self::$link_name]['conn']);
}
else
{
return false;
}
}
}
public static function ping()
{
if (!mysqli_ping(self::$links[self::$link_name]['conn']))
{
@mysqli_close(self::$links[self::$link_name]['conn']);
self::$links[self::$link_name]['conn'] = null;
self::_init();
}
}
public static function strsafe($array)
{
$arrays = array();
if(is_array($array)===true)
{
foreach ($array as $key => $val)
{
if(is_array($val)===true)
{
$arrays[$key] = self::strsafe($val);
}
else
{
//先去掉转义,避免下面重复转义了
$val = stripslashes($val);
//进行转义
$val = addslashes($val);
//处理addslashes没法处理的 _ % 字符
//$val = strtr($val, array('_'=>'\_', '%'=>'\%'));
$arrays[$key] = $val;
}
}
return $arrays;
}
else
{
$array = stripslashes($array);
$array = addslashes($array);
//$array = strtr($array, array('_'=>'\_', '%'=>'\%'));
return $array;
}
}
// 这个是给insert、update、insert_batch、update_batch用的
public static function get_fields($table)
{
// $sql = "SHOW COLUMNS FROM $table"; //和下面的语句效果一样
$rows = self::get_all("Desc `{$table}`");
$fields = array();
foreach ($rows as $k => $v)
{
// 过滤自增主键
// if ($v['Key'] != 'PRI')
if ($v['Extra'] != 'auto_increment')
{
$fields[] = $v['Field'];
}
}
return $fields;
}
public static function table_exists($table_name)
{
$sql = "SHOW TABLES LIKE '" . $table_name . "'";
$rsid = self::query($sql);
$table = self::fetch($rsid);
if (empty($table))
{
return false;
}
return true;
}
}

101
vendor/owner888/phpspider/core/init.php vendored Normal file
View File

@@ -0,0 +1,101 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider公共入口文件
//----------------------------------
// 严格开发模式
error_reporting( E_ALL );
//ini_set('display_errors', 1);
// 永不超时
ini_set('max_execution_time', 0);
set_time_limit(0);
// 内存限制,如果外面设置的内存比 /etc/php/php-cli.ini 大,就不要设置了
if (intval(ini_get("memory_limit")) < 1024)
{
ini_set('memory_limit', '1024M');
}
if( PHP_SAPI != 'cli' )
{
exit("You must run the CLI environment\n");
}
// 设置时区
date_default_timezone_set('Asia/Shanghai');
// 引入PATH_DATA
require_once __DIR__ . '/constants.php';
// 核心库目录
define('CORE', dirname(__FILE__));
define('PATH_ROOT', CORE."/../");
define('PATH_DATA', CORE."/../data");
define('PATH_LIBRARY', CORE."/../library");
// 系统配置
if( file_exists( PATH_ROOT."/config/inc_config.php" ) )
{
require PATH_ROOT."/config/inc_config.php";
}
require CORE.'/log.php';
require CORE.'/requests.php';
require CORE.'/selector.php';
require CORE.'/util.php';
require CORE.'/db.php';
require CORE.'/cache.php';
require CORE."/worker.php";
require CORE."/phpspider.php";
// 启动的时候生成data目录
util::path_exists(PATH_DATA);
util::path_exists(PATH_DATA."/lock");
util::path_exists(PATH_DATA."/log");
util::path_exists(PATH_DATA."/cache");
util::path_exists(PATH_DATA."/status");
function autoload($classname) {
set_include_path(PATH_ROOT.'/library/');
spl_autoload($classname); //replaces include/require
}
spl_autoload_extensions('.php');
spl_autoload_register('autoload');
/**
* 自动加载类库处理
* @return void
*/
//function __autoload( $classname )
//{
//$classname = preg_replace("/[^0-9a-z_]/i", '', $classname);
//if( class_exists ( $classname ) ) {
//return true;
//}
//$classfile = $classname.'.php';
//try
//{
//if ( file_exists ( PATH_LIBRARY.'/'.$classfile ) )
//{
//require PATH_LIBRARY.'/'.$classfile;
//}
//else
//{
//throw new Exception ( 'Error: Cannot find the '.$classname );
//}
//}
//catch ( Exception $e )
//{
//log::error($e->getMessage().'|'.$classname);
//exit();
//}
//}

119
vendor/owner888/phpspider/core/log.php vendored Normal file
View File

@@ -0,0 +1,119 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider日志类文件
//----------------------------------
namespace phpspider\core;
// 引入PATH_DATA
require_once __DIR__ . '/constants.php';
class log
{
public static $log_show = false;
public static $log_type = false;
public static $log_file = "data/phpspider.log";
public static $out_sta = "";
public static $out_end = "";
public static function note($msg)
{
self::$out_sta = self::$out_end = "";
self::msg($msg, 'note');
}
public static function info($msg)
{
self::$out_sta = self::$out_end = "";
self::msg($msg, 'info');
}
public static function warn($msg)
{
self::$out_sta = self::$out_end = "";
if (!util::is_win())
{
self::$out_sta = "\033[33m";
self::$out_end = "\033[0m";
}
self::msg($msg, 'warn');
}
public static function debug($msg)
{
self::$out_sta = self::$out_end = "";
if (!util::is_win())
{
self::$out_sta = "\033[36m";
self::$out_end = "\033[0m";
}
self::msg($msg, 'debug');
}
public static function error($msg)
{
self::$out_sta = self::$out_end = "";
if (!util::is_win())
{
self::$out_sta = "\033[31m";
self::$out_end = "\033[0m";
}
self::msg($msg, 'error');
}
public static function msg($msg, $log_type)
{
if ($log_type != 'note' && self::$log_type && strpos(self::$log_type, $log_type) === false)
{
return false;
}
if ($log_type == 'note')
{
$msg = self::$out_sta. $msg . "\n".self::$out_end;
}
else
{
$msg = self::$out_sta.date("Y-m-d H:i:s")." [{$log_type}] " . $msg .self::$out_end. "\n";
}
if(self::$log_show)
{
echo $msg;
}
file_put_contents(self::$log_file, $msg, FILE_APPEND | LOCK_EX);
}
/**
* 记录日志 XXX
* @param string $msg
* @param string $log_type Note|Warning|Error
* @return void
*/
public static function add($msg, $log_type = '')
{
if ($log_type != '')
{
$msg = date("Y-m-d H:i:s")." [{$log_type}] " . $msg . "\n";
}
if(self::$log_show)
{
echo $msg;
}
//file_put_contents(PATH_DATA."/log/".strtolower($log_type).".log", $msg, FILE_APPEND | LOCK_EX);
file_put_contents(PATH_DATA."/log/error.log", $msg, FILE_APPEND | LOCK_EX);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1388
vendor/owner888/phpspider/core/queue.php vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,998 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
// +----------------------------------------------------------------------
// | GET请求
// | requests::get('http://www.test.com');
// | SERVER
// | $_GET
// +----------------------------------------------------------------------
// | POST请求
// | $data = array('name'=>'request');
// | requests::post('http://www.test.com', $data);
// | SERVER
// | $_POST
// +----------------------------------------------------------------------
// | POST RESTful请求
// | $data = array('name'=>'request');
// | $data_string = json_encode($data);
// | requests::set_header("Content-Type", "application/json");
// | requests::post('http://www.test.com', $data_string);
// | SERVER
// | file_get_contents('php://input')
// +----------------------------------------------------------------------
// | POST 文件上传
// | $data = array('file1'=>''./data/phpspider.log'');
// | requests::post('http://www.test.com', null, $data);
// | SERVER
// | $_FILES
// +----------------------------------------------------------------------
// | 代理
// | requests::set_proxy(array('223.153.69.150:42354'));
// | $html = requests::get('https://www.test.com');
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider请求类文件
//----------------------------------
namespace phpspider\core;
if (!function_exists('curl_file_create'))
{
function curl_file_create($filename, $mimetype = '', $postname = '')
{
return "@$filename;filename="
. ($postname ?: basename($filename))
. ($mimetype ? ";type=$mimetype" : '');
}
}
class requests
{
const VERSION = '2.0.1';
protected static $ch = null;
/**** Public variables ****/
/* user definable vars */
public static $timeout = 15;
public static $encoding = null;
public static $input_encoding = null;
public static $output_encoding = null;
public static $cookies = array(); // array of cookies to pass
// $cookies['username'] = "seatle";
public static $rawheaders = array(); // array of raw headers to send
public static $domain_cookies = array(); // array of cookies for domain to pass
public static $hosts = array(); // random host binding for make request faster
public static $headers = array(); // headers returned from server sent here
public static $useragents = array("requests/2.0.0"); // random agent we masquerade as
public static $client_ips = array(); // random ip we masquerade as
public static $proxies = array(); // random proxy ip
public static $raw = ""; // head + body content returned from server sent here
public static $head = ""; // head content
public static $content = ""; // The body before encoding
public static $text = ""; // The body after encoding
public static $info = array(); // curl info
public static $history = 302; // http request status before redirect. ex:30x
public static $status_code = 0; // http request status
public static $error = ""; // error messages sent here
/**
* set timeout
* $timeout 为数组时会分别设置connect和read
*
* @param init or array $timeout
* @return
*/
public static function set_timeout($timeout)
{
self::$timeout = $timeout;
}
/**
* 设置代理
* 如果代理有多个,请求时会随机使用
*
* @param mixed $proxies
* array (
* 'socks5://user1:pass2@host:port',
* 'socks5://user2:pass2@host:port'
*)
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public static function set_proxy($proxy)
{
self::$proxies = is_array($proxy) ? $proxy : array($proxy);
}
/**
* 删除代理
* 因为每个链接信息里面都有代理信息,有的链接需要,有的不需要,所以必须提供一个删除功能
*
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2018-07-16 17:59
*/
public static function del_proxy()
{
self::$proxies = array();
}
/**
* 自定义请求头部
* 请求头内容可以用 requests::$rawheaders 来获取
* 比如获取Content-Typerequests::$rawheaders['Content-Type']
*
* @param string $headers
* @return void
*/
public static function set_header($key, $value)
{
self::$rawheaders[$key] = $value;
}
/**
* 设置全局COOKIE
*
* @param string $cookie
* @return void
*/
public static function set_cookie($key, $value, $domain = '')
{
if (empty($key))
{
return false;
}
if (!empty($domain))
{
self::$domain_cookies[$domain][$key] = $value;
}
else
{
self::$cookies[$key] = $value;
}
return true;
}
/**
* 批量设置全局cookie
*
* @param mixed $cookies
* @param string $domain
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function set_cookies($cookies, $domain = '')
{
$cookies_arr = explode(';', $cookies);
if (empty($cookies_arr))
{
return false;
}
foreach ($cookies_arr as $cookie)
{
$cookie_arr = explode('=', $cookie, 2);
$key = $cookie_arr[0];
$value = empty($cookie_arr[1]) ? '' : $cookie_arr[1];
if (!empty($domain))
{
self::$domain_cookies[$domain][$key] = $value;
}
else
{
self::$cookies[$key] = $value;
}
}
return true;
}
/**
* 获取单一Cookie
*
* @param mixed $name cookie名称
* @param string $domain 不传则取全局cookie就是手动set_cookie的cookie
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function get_cookie($name, $domain = '')
{
if (!empty($domain) && !isset(self::$domain_cookies[$domain]))
{
return '';
}
$cookies = empty($domain) ? self::$cookies : self::$domain_cookies[$domain];
return isset($cookies[$name]) ? $cookies[$name] : '';
}
/**
* 获取Cookie数组
*
* @param string $domain 不传则取全局cookie就是手动set_cookie的cookie
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function get_cookies($domain = '')
{
if (!empty($domain) && !isset(self::$domain_cookies[$domain]))
{
return array();
}
return empty($domain) ? self::$cookies : self::$domain_cookies[$domain];
}
/**
* 删除Cookie
*
* @param string $domain 不传则删除全局Cookie
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function del_cookie($key, $domain = '')
{
if (empty($key))
{
return false;
}
if (!empty($domain) && !isset(self::$domain_cookies[$domain]))
{
return false;
}
if (!empty($domain))
{
if (isset(self::$domain_cookies[$domain][$key]))
{
unset(self::$domain_cookies[$domain][$key]);
}
}
else
{
if (isset(self::$cookies[$key]))
{
unset(self::$cookies[$key]);
}
}
return true;
}
/**
* 删除Cookie
*
* @param string $domain 不传则删除全局Cookie
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function del_cookies($domain = '')
{
if (!empty($domain) && !isset(self::$domain_cookies[$domain]))
{
return false;
}
if ( empty($domain) )
{
self::$cookies = array();
}
else
{
if (isset(self::$domain_cookies[$domain]))
{
unset(self::$domain_cookies[$domain]);
}
}
return true;
}
/**
* 设置随机的user_agent
*
* @param string $useragent
* @return void
*/
public static function set_useragent($useragent)
{
self::$useragents = is_array($useragent) ? $useragent : array($useragent);
}
/**
* set referer
*
*/
public static function set_referer($referer)
{
self::$rawheaders['Referer'] = $referer;
}
/**
* 设置伪造IP
* 传入数组则为随机IP
* @param string $ip
* @return void
*/
public static function set_client_ip($ip)
{
self::$client_ips = is_array($ip) ? $ip : array($ip);
}
/**
* 删除伪造IP
*
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2018-07-16 17:59
*/
public static function del_client_ip()
{
self::$client_ips = array();
}
/**
* 设置中文请求
*
* @param string $lang
* @return void
*/
public static function set_accept_language($lang = 'zh-CN')
{
self::$rawheaders['Accept-Language'] = $lang;
}
/**
* 设置Hosts
* 负载均衡到不同的服务器如果对方使用CDN采用这个是最好的了
*
* @param string $hosts
* @return void
*/
public static function set_hosts($host, $ips = array())
{
$ips = is_array($ips) ? $ips : array($ips);
self::$hosts[$host] = $ips;
}
/**
* 分割返回的header和body
* header用来判断编码和获取Cookie
* body用来判断编码得到编码前和编码后的内容
*
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function split_header_body()
{
$head = $body = '';
$head = substr(self::$raw, 0, self::$info['header_size']);
$body = substr(self::$raw, self::$info['header_size']);
// http header
self::$head = $head;
// The body before encoding
self::$content = $body;
//$http_headers = array();
//// 解析HTTP数据流
//if (!empty(self::$raw))
//{
//self::get_response_cookies($domain);
//// body里面可能有 \r\n\r\n但是第一个一定是HTTP Header去掉后剩下的就是body
//$array = explode("\r\n\r\n", self::$raw);
//foreach ($array as $k=>$v)
//{
//// post 方法会有两个http headerHTTP/1.1 100 Continue、HTTP/1.1 200 OK
//if (preg_match("#^HTTP/.*? 100 Continue#", $v))
//{
//unset($array[$k]);
//continue;
//}
//if (preg_match("#^HTTP/.*? \d+ #", $v))
//{
//$header = $v;
//unset($array[$k]);
//$http_headers = self::get_response_headers($v);
//}
//}
//$body = implode("\r\n\r\n", $array);
//}
// 设置了输出编码的转码,注意: xpath只支持utf-8iso-8859-1 不要转他本身就是utf-8
$body = self::encoding($body); //自动转码
// 转码后
self::$encoding = self::$output_encoding;
// The body after encoding
self::$text = $body;
return array($head, $body);
}
/**
* 获得域名相对应的Cookie
*
* @param mixed $header
* @param mixed $domain
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function get_response_cookies($header, $domain)
{
// 解析Cookie并存入 self::$cookies 方便调用
preg_match_all("/.*?Set\-Cookie: ([^\r\n]*)/i", $header, $matches);
$cookies = empty($matches[1]) ? array() : $matches[1];
// 解析到Cookie
if (!empty($cookies))
{
$cookies = implode(';', $cookies);
$cookies = explode(';', $cookies);
foreach ($cookies as $cookie)
{
$cookie_arr = explode('=', $cookie, 2);
// 过滤 httponly、secure
if (count($cookie_arr) < 2)
{
continue;
}
$cookie_name = !empty($cookie_arr[0]) ? trim($cookie_arr[0]) : '';
if (empty($cookie_name))
{
continue;
}
// 过滤掉domain路径
if (in_array(strtolower($cookie_name), array('path', 'domain', 'expires', 'max-age')))
{
continue;
}
self::$domain_cookies[$domain][trim($cookie_arr[0])] = trim($cookie_arr[1]);
}
}
}
/**
* 获得response header
* 此方法占时没有用到
*
* @param mixed $header
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function get_response_headers($header)
{
$headers = array();
$header_lines = explode("\n", $header);
if (!empty($header_lines))
{
foreach ($header_lines as $line)
{
$header_arr = explode(':', $line, 2);
$key = empty($header_arr[0]) ? '' : trim($header_arr[0]);
$val = empty($header_arr[1]) ? '' : trim($header_arr[1]);
if (empty($key) || empty($val))
{
continue;
}
$headers[$key] = $val;
}
}
self::$headers = $headers;
return self::$headers;
}
/**
* 获取编码
* @param $string
* @return string
*/
public static function get_encoding($string)
{
$encoding = mb_detect_encoding($string, array('UTF-8', 'GBK', 'GB2312', 'LATIN1', 'ASCII', 'BIG5', 'ISO-8859-1'));
return strtolower($encoding);
}
/**
* 移除页面head区域代码
* @param $html
* @return mixed
*/
private static function _remove_head($html)
{
return preg_replace('/<head.+?>.+<\/head>/is', '<head></head>', $html);
}
/**
* 简单的判断一下参数是否为一个URL链接
* @param string $str
* @return boolean
*/
private static function _is_url($url)
{
//$pattern = '/^http(s)?:\\/\\/.+/';
$pattern = "/\b(([\w-]+:\/\/?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|\/)))/";
if (preg_match($pattern, $url))
{
return true;
}
return false;
}
/**
* 初始化 CURL
*
*/
public static function init()
{
if (!is_resource ( self::$ch ))
{
self::$ch = curl_init ();
curl_setopt( self::$ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( self::$ch, CURLOPT_HEADER, false );
curl_setopt( self::$ch, CURLOPT_USERAGENT, "phpspider-requests/".self::VERSION );
// 如果设置了两个时间,就分开设置
if (is_array(self::$timeout))
{
curl_setopt( self::$ch, CURLOPT_CONNECTTIMEOUT, self::$timeout[0] );
curl_setopt( self::$ch, CURLOPT_TIMEOUT, self::$timeout[1]);
}
else
{
curl_setopt(self::$ch, CURLOPT_CONNECTTIMEOUT, ceil(self::$timeout / 2));
curl_setopt(self::$ch, CURLOPT_TIMEOUT, self::$timeout);
}
curl_setopt(self::$ch, CURLOPT_MAXREDIRS, 5); //maximum number of redirects allowed
// 在多线程处理场景下使用超时选项时会忽略signals对应的处理函数但是无耐的是还有小概率的crash情况发生
curl_setopt( self::$ch, CURLOPT_NOSIGNAL, true);
}
return self::$ch;
}
/**
* get 请求
*/
public static function get($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'get', $fields, NULL, $allow_redirects, $cert);
}
/**
* post 请求
* $fields 有三种类型:1、数组2、http query3、json
* 1、array('name'=>'yangzetao')
* 2、http_build_query(array('name'=>'yangzetao'))
* 3、json_encode(array('name'=>'yangzetao'))
* 前两种是普通的post可以用$_POST方式获取
* 第三种是post stream( json rpc其实就是webservice )
* 虽然是post方式但是只能用流方式 http://input 后者 $HTTP_RAW_POST_DATA 获取
*
* @param mixed $url
* @param array $fields
* @param mixed $proxies
* @static
* @access public
* @return void
*/
public static function post($url, $fields = array(), $files = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'POST', $fields, $files, $allow_redirects, $cert);
}
public static function put($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'PUT', $fields, $allow_redirects, $cert);
}
public static function delete($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'DELETE', $fields, $allow_redirects, $cert);
}
// 响应HTTP头域里的元信息
// 此方法被用来获取请求实体的元信息而不需要传输实体主体entity-body
// 此方法经常被用来测试超文本链接的有效性,可访问性,和最近的改变。.
public static function head($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
self::request($url, 'HEAD', $fields, $allow_redirects, $cert);
}
public static function options($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'OPTIONS', $fields, $allow_redirects, $cert);
}
public static function patch($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'PATCH', $fields, $allow_redirects, $cert);
}
/**
* request
*
* @param mixed $url 请求URL
* @param string $method 请求方法
* @param array $fields 表单字段
* @param array $files 上传文件