Commit 49e7e664 by CnChunfeng

Merge branch 'dev' into 'master'

Dev

See merge request !1
parents b7d3b448 eafc7ab0
Showing with 4569 additions and 55 deletions
<?php
namespace App\Console\Commands;
use App\Model\SystemBulletinModel;
use App\Model\SystemUpdateModel;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
//TODO 上线之后去除注释这个notice ,5。0版本的缺陷 https://blog.csdn.net/weixin_44251615/article/details/93710829
ini_set("error_reporting","E_ALL & ~E_NOTICE");
class CreateNotice extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'notice:create_notice';
/**
* The console command description.
*
* @var string
*/
protected $description = '生成每日六点的公告';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
//查找出今日的所有更新
$date = date('Y-m-d');
$where['begin_time'] = $date;
$where['end_time'] = date('Y-m-d H:i:s');
$systemUpdateModel = new SystemUpdateModel();
//韦伯系统是所有的,通知所有人
$allUpdate = $systemUpdateModel->getWhereObj($where)->get()->toArray();
if (!empty($allUpdate)){
// 查找所有通知用户邮箱和ID
$sendUser = DB::table('t_user_perm')->where('begDate','<=',$date)
->where('endDate','>=',$date)
->select('username','userId')->get();
$this->createNoticeSaveData($allUpdate,$date,'韦伯系统',json_encode($sendUser));
}
//查找所有需要更新的子系统,通知对应系统的人
$allGroup = $systemUpdateModel->getWhereObj($where)->groupBy('system_id')->get()->toArray();
foreach ($allGroup as $value){
// 查找所有通知ID
$sendUser = DB::table('t_user_perm')->where('begDate','<=',$date)
->where('endDate','>=',$date)
->where('bid',$value['system_id'])
->select('username','userId')->get();
$this->createNoticeSaveData($allUpdate,$date,$value['system_name'],json_encode($sendUser));
}
}
private function createNoticeSaveData($allUpdate,$date,$systemName,$sendUser)
{
$saveData['title'] = $date.'系统不停机维护公告';
$saveData['is_send'] = '未发送';
$saveData['create_time'] = time();
$saveData['send_user'] = $sendUser;
$tbodyContent = '';
foreach ($allUpdate as $value){
$tbodyContent .=
'<tr>'.
'<td>'.$date.'</td>'.
'<td>'.$value['system_name'].'</td>'.
'<td>'.$value['update_type'].'</td>'.
'<td>'.$value['update_title'].'</td>'.
'<td>'.$value['update_content'].'</td>'.
'<td>'.$value['product_user'].'</td>'.
'<td>'.$value['version_num'].'</td>'.
'</tr>';
}
$saveData['content'] = '
<h2>猎芯的同事们好</h2>
<h2>以下内容为'.$systemName.'今天全部的更新,请查收</h2>
<table border="1" cellspacing="0" style="width: 1300px;line-height: 50px;table-layout: fixed">
<thead style="background:grey;font-size: 30px;">
<tr>
<td></td>
<td>系统</td>
<td>更新类型</td>
<td>更新标题</td>
<td width="350">更新内容简介</td>
<td>产品负责人</td>
<td>版本号</td>
</tr>
</thead>
<tbody>
'.$tbodyContent.'
</tbody>
</table>
';
(new SystemBulletinModel())->insertGetId($saveData);
}
}
......@@ -21,6 +21,7 @@ class Kernel extends ConsoleKernel
Commands\qj_sf::class,
Commands\Initialization::class,
Commands\Add_field::class,
Commands\CreateNotice::class,
];
/**
......
......@@ -8,6 +8,7 @@
namespace App\Http\Controllers;
use App\Logic\SystemNoticeLogic;
use Illuminate\Http\Request;
use App\Http\Requests;
use DB;
......@@ -32,6 +33,110 @@ class MessageApiController extends Controller
exit();
}
//获取系统更新信息列表
private function systemUpdateList($request)
{
$data = $request->all();
try{
$returnData = (new SystemNoticeLogic())->getSystemUpdateList($data);
echo json_encode(['code'=>0,'count'=>count($returnData),'data'=>$returnData,'msg'=>'获取成功']);
exit();
}catch(\Exception $e){
echo json_encode(['code'=>400,'msg'=>$e->getMessage()]);
exit();
}
}
//获取系统宣讲列表
private function systemPreachList($request)
{
$data = $request->all();
try{
$returnData = (new SystemNoticeLogic())->getSystemPreachList($data);
echo json_encode(['code'=>0,'count'=>count($returnData),'data'=>$returnData,'msg'=>'获取成功']);
exit();
}catch(\Exception $e){
echo json_encode(['code'=>400,'msg'=>$e->getMessage()]);
exit();
}
}
//获取系统公告列表
private function systemBulletinList($request)
{
$data = $request->all();
try{
$returnData = (new SystemNoticeLogic())->getSystemBulletinList($data);
echo json_encode(['code'=>0,'count'=>count($returnData),'data'=>$returnData,'msg'=>'获取成功']);
exit();
}catch(\Exception $e){
echo json_encode(['code'=>400,'msg'=>$e->getMessage()]);
exit();
}
}
//保存系统更新信息
private function saveSystemUpdate($request)
{
$data = $request->all();
try{
(new SystemNoticeLogic())->saveSystemUpdate($data);
$this->Export(0, '操作成功');
}catch(\Exception $e){
$this->Export(400, $e->getMessage());
}
}
//保存系统宣讲信息
private function saveSystemPreach($request)
{
$data = $request->all();
try{
(new SystemNoticeLogic())->saveSystemPreach($data);
$this->Export(0, '操作成功');
}catch(\Exception $e){
$this->Export(400, $e->getMessage());
}
}
//保存系统公告信息
private function saveSystemBulletin($request)
{
$data = $request->all();
try{
(new SystemNoticeLogic())->saveSystemBulletin($data);
$this->Export(0, '操作成功');
}catch(\Exception $e){
$this->Export(400, $e->getMessage());
}
}
//发送公告信息
private function sendNotice($request)
{
$data = $request->all();
try{
(new SystemNoticeLogic())->sendNotice($data);
$this->Export(0, '操作成功');
}catch(\Exception $e){
$this->Export(400, $e->getMessage());
}
}
//获取系统版本号码
private function getSystemUpdateVersion($request)
{
$data = $request->all();
try{
$versionNum = (new SystemNoticeLogic())->getSystemUpdateVersion($data);
$this->Export(0, '获取成功',['version'=>$versionNum]);
}catch(\Exception $e){
$this->Export(400, $e->getMessage());
}
}
//删除消息模板
public function deletetemplate($request){
$data = $request->input();
......
......@@ -8,6 +8,7 @@
namespace App\Http\Controllers;
use App\Logic\SystemNoticeLogic;
use ClassPreloader\Config;
use function GuzzleHttp\Psr7\str;
use Illuminate\Http\Request;
......@@ -91,6 +92,9 @@ function Crumbs($menus, $uri)
return $ret;
}
//TODO 上线之后去除注释这个notice ,5。0版本的缺陷 https://blog.csdn.net/weixin_44251615/article/details/93710829
ini_set("error_reporting","E_ALL & ~E_NOTICE");
class MessageController extends Controller
{
private function templateData(Request $request, $id, $viewid)
......@@ -668,7 +672,7 @@ class MessageController extends Controller
{
$current_domain = $_SERVER['HTTP_HOST'];
if($current_domain === Config('msgconfig.domain_local'))
if($current_domain === Config('msgconfig.domain_local') || $current_domain === 'message.liexindev.net')
{
return Config('msgconfig.config_id_local');
}
......@@ -681,4 +685,38 @@ class MessageController extends Controller
return Config('msgconfig.config_id_release');
}
}
//----------------------------------------通知管理----------------------------------------//
// 系统更新管理
private function systemUpdate(Request $request, $id, $viewid)
{
$data=[
'id'=>$id,
'user'=>SystemNoticeLogic::getAllUser()
];
return view('common', $data);
}
//系统宣讲管理
private function systemPreaching(Request $request, $id, $viewid)
{
$data=[
'id'=>$id,
'user'=>SystemNoticeLogic::getAllUser()
];
return view('common', $data);
}
//系统公告管理
private function systemBulletin(Request $request, $id, $viewid)
{
$data=[
'id'=>$id,
];
return view('common', $data);
}
}
\ No newline at end of file
<?php
/**
* Created by 2022/12/3.
* User: Jone
* Info: 2022/12/3
* Time: 下午3:52
*/
namespace App\Http\Controllers;
use App\Logic\SystemNoticeLogic;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
//TODO 上线之后去除注释这个notice ,5。0版本的缺陷 https://blog.csdn.net/weixin_44251615/article/details/93710829
ini_set("error_reporting","E_ALL & ~E_NOTICE");
class NoticeController extends Controller
{
public function getNotice(Request $request)
{
$data = $request->all();
try{
$returnData = (new SystemNoticeLogic())->getRedisNotice($data);
$this->Export(0, '公告获取成功',$returnData);
}catch(\Exception $e){
$this->Export(400, $e->getMessage());
}
}
private function Export($errcode=0,$errmsg='成功',$data=''){
echo json_encode(['errcode'=>$errcode,'errmsg'=>$errmsg,'data'=>$data]);
exit();
}
}
\ No newline at end of file
......@@ -81,6 +81,9 @@ Route::group(['middleware' => 'web'], function () {
Route::resource('batchapi', 'BatchapiController');
Route::get ('/notice/getNotice','NoticeController@getNotice');
Route::group(['middleware' => 'webapi'], function () {
Route::match(['get', 'post'],'/webapi/{key}', 'WebApiController@Entrance');
});
\ No newline at end of file
<?php
/**
* Created by 2022/12/3.
* User: Jone
* Info: 2022/12/3
* Time: 下午4:08
*/
namespace App\Logic;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
class MqLogic
{
public function pushAmq($exchange, $route_key, $queue_name, $content='')
{
$connection = new AMQPStreamConnection(env('RABBITMQ_HOST'), env('RABBITMQ_PORT'), env('RABBITMQ_LOGIN'), env('RABBITMQ_PASSWORD'), env('RABBITMQ_VHOST')); // 创建连接
$channel = $connection->channel();
$channel->exchange_declare($exchange, 'direct', false, true, false); // 初始化交换机
$channel->queue_declare($queue_name, false, true, false, false); // 初始化队列
$channel->queue_bind($queue_name, $exchange, $route_key); // 将队列与某个交换机进行绑定,并使用路由关键字
$message = new AMQPMessage($content);
$channel->basic_publish($message, '', $queue_name); // 推送消息
$channel->close();
$connection->close();
}
public function pullAmq($queue_name='')
{
$connection = new AMQPStreamConnection(env('RABBITMQ_HOST'), env('RABBITMQ_PORT'), env('RABBITMQ_LOGIN'), env('RABBITMQ_PASSWORD'), env('RABBITMQ_VHOST')); // 创建连接
$channel = $connection->channel();
$message = $channel->basic_get($queue_name); // 取出消息
$channel->basic_ack($message->delivery_info['delivery_tag']); // 确认取出消息后会发送一个ack来确认取出来了,然后会从rabbitmq中将这个消息移除,如果删掉这段代码,会发现rabbitmq中的消息还是没有减少
$channel->close();
$connection->close();
return $message;
}
}
\ No newline at end of file
<?php
/**
* Created by 2022/12/2.
* User: Jone
* Info: 2022/12/2
* Time: 上午10:28
*/
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
class SystemBulletinModel extends Model
{
protected $connection = 'messagemodel';
protected $table = 'system_bulletin';
protected $primaryKey = 'smbn_id';
public $timestamps = false;
public function getWhereObj($data)
{
$obj = self::orderBy('smbn_id','desc');
foreach ($data as $key=>$val){
if ($val === '')continue;
switch ($key){
case 'system_name':
case 'update_type':
case 'version_num':
case 'weonlt_id':
$obj = $obj->where($key,$val);break;
case 'update_title':
case 'update_content':
$obj = $obj->where($key,'like','%'.$val.'%');break;
case 'begin_time':
$obj = $obj->where('create_time','>=',strtotime($val));break;
case 'end_time':
$obj = $obj->where('create_time','<=',strtotime($val));break;
}
}
return $obj;
}
}
\ No newline at end of file
<?php
/**
* Created by 2022/12/2.
* User: Jone
* Info: 2022/12/2
* Time: 上午10:27
*/
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
class SystemPreachModel extends Model
{
protected $connection = 'messagemodel';
protected $table = 'system_preach';
protected $primaryKey = 'smph_id';
public $timestamps = false;
public function getWhereObj($data)
{
$obj = self::orderBy('smph_id','desc');
foreach ($data as $key=>$val){
if ($val === '')continue;
switch ($key){
case 'preach_user':
case 'department_id':
case 'system_id':
$obj = $obj->where($key,$val);break;
case 'company_name':
$obj = $obj->where($key,'like','%'.$val.'%');break;
case 'begin_time':
$obj = $obj->where('create_time','>=',strtotime($val));break;
case 'end_time':
$obj = $obj->where('create_time','<=',strtotime($val));break;
}
}
return $obj;
}
}
\ No newline at end of file
<?php
/**
* Created by 2022/12/2.
* User: Jone
* Info: 2022/12/2
* Time: 上午10:27
*/
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
class SystemUpdateModel extends Model
{
protected $connection = 'messagemodel';
protected $table = 'system_update';
protected $primaryKey = 'smue_id';
public $timestamps = false;
public function getWhereObj($data)
{
$obj = self::orderBy('smue_id','desc');
foreach ($data as $key=>$val){
if ($val === '')continue;
switch ($key){
case 'system_name':
case 'update_type':
case 'version_num':
case 'weonlt_id':
$obj = $obj->where($key,$val);break;
case 'update_title':
case 'update_content':
$obj = $obj->where($key,'like','%'.$val.'%');break;
case 'begin_time':
$obj = $obj->where('create_time','>',strtotime($val));break;
case 'end_time':
$obj = $obj->where('create_time','<=',strtotime($val));break;
}
}
return $obj;
}
}
\ No newline at end of file
......@@ -10,7 +10,8 @@
"justinrainbow/json-schema": "~1.3",
"maatwebsite/excel": "~2.0.0",
"guzzlehttp/guzzle": "^6.3",
"php-amqplib/php-amqplib": "2.6.*"
"php-amqplib/php-amqplib": "2.6.*",
"predis/predis": "^1.1"
},
"require-dev": {
"fzaninotto/faker": "~1.4",
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "3f36d5add2ac2f61e6f91f2203489d32",
"content-hash": "83974b9c610c61acd5280f742258c54f",
"packages": [
{
"name": "classpreloader/classpreloader",
......@@ -1354,6 +1354,78 @@
"time": "2018-11-22T23:07:24+00:00"
},
{
"name": "predis/predis",
"version": "v1.1.10",
"source": {
"type": "git",
"url": "https://github.com/predis/predis.git",
"reference": "a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/predis/predis/zipball/a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e",
"reference": "a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"suggest": {
"ext-curl": "Allows access to Webdis when paired with phpiredis",
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol"
},
"type": "library",
"autoload": {
"psr-4": {
"Predis\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Daniele Alessandri",
"email": "suppakilla@gmail.com",
"homepage": "http://clorophilla.net",
"role": "Creator & Maintainer"
},
{
"name": "Till Krüss",
"homepage": "https://till.im",
"role": "Maintainer"
}
],
"description": "Flexible and feature-complete Redis client for PHP and HHVM",
"homepage": "http://github.com/predis/predis",
"keywords": [
"nosql",
"predis",
"redis"
],
"support": {
"issues": "https://github.com/predis/predis/issues",
"source": "https://github.com/predis/predis/tree/v1.1.10"
},
"funding": [
{
"url": "https://github.com/sponsors/tillkruss",
"type": "github"
}
],
"time": "2022-01-05T17:46:08+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
......@@ -3973,5 +4045,6 @@
"platform": {
"php": ">=5.5.9"
},
"platform-dev": []
"platform-dev": [],
"plugin-api-version": "2.3.0"
}
......@@ -123,6 +123,19 @@ return [
'prefix' => '',
'strict' => false,
],
'messagemodel' => [
'driver' => 'mysql',
'host' => env('DB_HOST_MESSAGE', ''),
'database' => env('DB_DATABASE_MESSAGE', ''),
'username' => env('DB_USERNAME_MESSAGE', ''),
'password' => env('DB_PASSWORD_MESSAGE', ''),
'port' => env('DB_PORT', 3306),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => 'lie_',
'strict' => false,
],
],
......
......@@ -7,6 +7,9 @@
* @author Taylor Otwell <taylorotwell@gmail.com>
*/
//TODO 上线之后去除注释这个notice ,5。0版本的缺陷 https://blog.csdn.net/weixin_44251615/article/details/93710829
ini_set("error_reporting","E_ALL & ~E_NOTICE");
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
......
layui.use(['layer'], function(){
var $ = layui.$;
var layer = layui.layer;
function getNotice(){
if (getcookie('no_notice') == '1'){
return false;
}
var uid = getcookie('oa_user_id');
var host = window.location.host;
//测试的是liexindev.net 正式的是ichunt.net
if (host.indexOf('ichunt.net') != -1) {
requestUrl = 'http://message.ichunt.net/notice/getNotice'
}else{
requestUrl = 'http://message.liexin.net/notice/getNotice'
}
$.ajax({
url: requestUrl,
type: 'get',
data: {user_id:uid,host:host},
dataType:'json',
success: function (resp) {
//请求成功弹出公告,请求失败,设置一个小时不请求的cookie
if (resp.errcode == 0) {
layer.open({
area: ['1000px', '500px'],
title: resp.data.title,
type: 1,
content: resp.data.content,
btn: ['确认', '取消'],
offset:['100px','100px'],
yes: function (index) {
layer.close(index);
},
cancel: function (index) {
layer.close(index);
}
});
return false;
}else{
setCookie('no_notice','1',3600)
}
},
error: function (err) {
console.log(err)
}
});
}
getNotice();
function getcookie(objname){//获取指定名称的cookie的值
var arrstr = document.cookie.split("; ");
for(var i = 0;i < arrstr.length;i ++){
var temp = arrstr[i].split("=");
if(temp[0] == objname) return unescape(temp[1]);
}
}
function setCookie(name, value, seconds) {
seconds = seconds || 0; //seconds有值就直接赋值,没有为0,这个根php不一样。
var expires = "";
if (seconds != 0 ) { //设置cookie生存时间
var date = new Date();
date.setTime(date.getTime()+(seconds*1000));
expires = "; expires="+date.toGMTString();
}
document.cookie = name+"="+escape(value)+expires+"; path=/"; //转码并赋值
}
});
layui.use(['jquery','element', 'layer', 'form','table','laypage','laydate','layedit'], function(){
var $ = layui.$;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var layedit = layui.layedit;
form.render();
//数据表格渲染
var dataList = function(){
table.render({
elem: '#systemBulletinList',
url: '/msgapi/systemBulletinList',
page: true,
limit: 20,
id:'table',
cols: [[ //表头
{field: 'smbn_id', title: '序号',align:'center',width:100},
{field: 'create_time', title: '创建时间',align:'center',width:250},
{field: 'title', title: '公告标题',align:'center',width:250},
{field: 'is_send', title: '状态',align:'center',width:150},
{field: 'active', toolbar: '#active', title: '操作',align:'center',width:150, fixed:'right'}
]]
});
};
dataList();
form.on('submit(load)', function (data) {
//执行重载
table.reload('table', {
page: {
curr: 1
}
, where: data.field
});
return false;
});
$("#addData").click(function () {
$("#data_form_jq")[0].reset();
form.render();
layer.open({
area: ['800px', '500px'],
title: '保存数据',
type: 1,
content: $("#save_form_show"),
btn: ['确认保存', '取消'],
offset:['100px','100px'],
success:function(index){
layeditIndex = layedit.build('content_text',{
height:200,
width:100
}); //建立编辑器
},
yes: function (index) {
$("#content").val(layedit.getContent(layeditIndex));
$("#save_form_click").click();
},
cancel: function (index) {
layer.close(index);
}
});
});
$(document).on('click', '.editData', function () {
var jsonData = $(this).data('json');
$("#content_text").val($(this).data('content'));
form.val("data_form", jsonData);
form.render();
layer.open({
area: ['1000px', '500px'],
title: '保存数据',
type: 1,
content: $("#save_form_show"),
btn: ['确认保存', '取消'],
offset:['100px','100px'],
success:function(index){
layeditIndex = layedit.build('content_text',{
height:200,
}); //建立编辑器
},
yes: function (index) {
$("#content").val(layedit.getContent(layeditIndex));
$("#save_form_click").click();
},
cancel: function (index) {
layer.close(index);
}
});
});
form.on('submit(save_form)', function (data) {
$.ajax({
url: '/msgapi/saveSystemBulletin',
type: 'post',
data: data.field,
dataType:'json',
success: function (resp) {
if (resp.errcode == 0) {
layer.msg(resp.errmsg);
window.location.reload();
return false;
}
layer.msg(resp.errmsg);
},
error: function (err) {
console.log(err)
}
});
return false;
});
$(document).on('click', '.sendNotice', function () {
$("#id").val($(this).data('id'));
layer.open({
area: ['400px', '200px'],
title: '保存数据',
type: 1,
content: $("#notice_form_show"),
btn: ['确认发送', '取消'],
offset:['30px','300px'],
yes: function (index) {
$("#notice_form_click").click();
},
cancel: function (index) {
layer.close(index);
}
});
});
form.on('submit(notice_form)', function (data) {
data.field.id = $("#id").val();
data.field.type = 'system_bulletin';
$.ajax({
url: '/msgapi/sendNotice',
type: 'post',
data: data.field,
dataType:'json',
success: function (resp) {
if (resp.errcode == 0) {
layer.msg(resp.errmsg);
window.location.reload();
return false;
}
layer.msg(resp.errmsg);
},
error: function (err) {
console.log(err)
}
});
return false;
});
});
layui.use(['jquery','element', 'layer', 'form','table','laypage','laydate','layedit'], function(){
var $ = layui.$;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var laydate = layui.laydate;
var layedit = layui.layedit;
laydate.render({
elem: '#begin_time' //指定元素
});
laydate.render({
elem: '#end_time' //指定元素
});
laydate.render({
elem: '#preach_time', //指定元素
format: 'yyyy-MM-dd HH:mm:ss'
,type: 'datetime'
});
form.render();
//数据表格渲染
var dataList = function(){
table.render({
elem: '#systemPreachList',
url: '/msgapi/systemPreachList',
page: true,
limit: 20,
id:'table',
cols: [[ //表头
{field: 'smph_id', title: '序号',align:'center',width:100},
{field: 'preach_time', title: '宣讲时间',align:'center',width:150},
{field: 'system_name', title: '宣讲系统',align:'center',width:150},
{field: 'preach_content', title: '宣讲内容',align:'center',width:150},
{field: 'preach_user', title: '宣讲人',align:'center',width:150},
{field: 'department_name', title: '宣讲部门',align:'center',width:150},
{field: 'preach_feedback', title: '宣讲反馈',align:'center',width:150},
{field: 'active', toolbar: '#active', title: '操作',align:'center',width:150, fixed:'right'}
]]
});
};
dataList();
form.on('submit(load)', function (data) {
//执行重载
table.reload('table', {
page: {
curr: 1
}
, where: data.field
});
return false;
});
$("#addData").click(function () {
$("#data_form_jq")[0].reset();
form.render();
layer.open({
area: ['800px', '600px'],
title: '保存数据',
type: 1,
content: $("#save_form_show"),
btn: ['确认保存', '取消'],
offset:['30px','300px'],
success:function(index){
layeditIndex = layedit.build('preach_content_text',{
height:200,
}); //建立编辑器
},
yes: function (index) {
$("#preach_content").val(layedit.getContent(layeditIndex));
$("#save_form_click").click();
},
cancel: function (index) {
layer.close(index);
}
});
});
$(document).on('click', '.editData', function () {
var jsonData = $(this).data('json');
$("#preach_content_text").val($(this).data('preach_content'));
form.val("data_form", jsonData);
form.render();
layer.open({
area: ['800px', '560px'],
title: '保存数据',
type: 1,
content: $("#save_form_show"),
btn: ['确认保存', '取消'],
offset:['30px','300px'],
success:function(index){
layeditIndex = layedit.build('preach_content_text',{
height:200,
}); //建立编辑器
},
yes: function (index) {
$("#preach_content").val(layedit.getContent(layeditIndex));
$("#save_form_click").click();
},
cancel: function (index) {
layer.close(index);
}
});
});
form.on('submit(save_form)', function (data) {
$.ajax({
url: '/msgapi/saveSystemPreach',
type: 'post',
data: data.field,
dataType:'json',
success: function (resp) {
if (resp.errcode == 0) {
layer.msg(resp.errmsg);
window.location.reload();
return false;
}
layer.msg(resp.errmsg);
},
error: function (err) {
console.log(err)
}
});
return false;
});
$(document).on('click', '.sendNotice', function () {
$("#id").val($(this).data('id'));
layer.open({
area: ['400px', '200px'],
title: '保存数据',
type: 1,
content: $("#notice_form_show"),
btn: ['确认发送', '取消'],
offset:['30px','300px'],
yes: function (index) {
$("#notice_form_click").click();
},
cancel: function (index) {
layer.close(index);
}
});
});
form.on('submit(notice_form)', function (data) {
data.field.id = $("#id").val();
data.field.type = 'system_update';
$.ajax({
url: '/msgapi/sendNotice',
type: 'post',
data: data.field,
dataType:'json',
success: function (resp) {
if (resp.errcode == 0) {
layer.msg(resp.errmsg);
window.location.reload();
return false;
}
layer.msg(resp.errmsg);
},
error: function (err) {
console.log(err)
}
});
return false;
});
});
layui.use(['jquery','element', 'layer', 'form','table','laypage','laydate','layedit'], function(){
var $ = layui.$;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var laydate = layui.laydate;
var layedit = layui.layedit;
laydate.render({
elem: '#begin_time' //指定元素
});
laydate.render({
elem: '#end_time' //指定元素
});
laydate.render({
elem: '#update_time', //指定元素
format: 'yyyy-MM-dd HH:mm:ss'
,type: 'datetime'
});
form.render();
//数据表格渲染
var dataList = function(){
table.render({
elem: '#systemUpdateList',
url: '/msgapi/systemUpdateList',
page: true,
limit: 20,
id:'table',
cols: [[ //表头
{field: 'smue_id', title: '序号',align:'center',width:100},
{field: 'update_time', title: '更新日期',align:'center',width:150},
{field: 'system_name', title: '更新系统',align:'center',width:150},
{field: 'version_num', title: '版本号',align:'center',width:150},
{field: 'update_type', title: '更新类型',align:'center',width:150},
{field: 'update_title', title: '更新标题',align:'center',width:150},
{field: 'update_content', title: '更新内容',align:'center',width:150},
{field: 'product_user', title: '产品负责人',align:'center',width:150},
{field: 'code_user', title: '技术负责人',align:'center',width:180},
{field: 'test_user', title: '测试负责人',align:'center',width:180},
{field: 'bulletin_count', title: '公告发送次数',align:'center',width:180},
{field: 'email_count', title: '邮件发送次数',align:'center',width:180},
{field: 'active', toolbar: '#active', title: '操作',align:'center',width:150, fixed:'right'}
]]
});
};
dataList();
form.on('submit(load)', function (data) {
//执行重载
table.reload('table', {
page: {
curr: 1
}
, where: data.field
});
return false;
});
form.on('select(system_id)', function (data) {
$.ajax({
url: '/msgapi/getSystemUpdateVersion',
type: 'post',
data: {'system_id':data.value},
dataType:'json',
success: function (resp) {
if (resp.errcode == 0) {
$("#version_num").val(resp.data.version);
return false;
}
layer.msg(resp.errmsg);
},
error: function (err) {
console.log(err)
}
});
return false;
});
$("#addData").click(function () {
$("#data_form_jq")[0].reset();
form.render();
layer.open({
area: ['800px', '800px'],
title: '保存数据',
type: 1,
content: $("#save_form_show"),
btn: ['确认保存', '取消'],
offset:['100px','300px'],
success:function(index){
layeditIndex = layedit.build('update_content_text',{
height:200,
}); //建立编辑器
},
yes: function (index) {
$("#update_content").val(layedit.getContent(layeditIndex));
$("#save_form_click").click();
},
cancel: function (index) {
layer.close(index);
}
});
});
$(document).on('click', '.editData', function () {
var jsonData = $(this).data('json');
$("#update_content_text").val($(this).data('update_content'));
form.val("data_form", jsonData);
form.render();
layer.open({
area: ['800px', '760px'],
title: '保存数据',
type: 1,
content: $("#save_form_show"),
btn: ['确认保存', '取消'],
offset:['30px','300px'],
success:function(index){
layeditIndex = layedit.build('update_content_text',{
height:200,
}); //建立编辑器
},
yes: function (index) {
$("#update_content").val(layedit.getContent(layeditIndex));
$("#save_form_click").click();
},
cancel: function (index) {
layer.close(index);
}
});
});
form.on('submit(save_form)', function (data) {
$.ajax({
url: '/msgapi/saveSystemUpdate',
type: 'post',
data: data.field,
dataType:'json',
success: function (resp) {
if (resp.errcode == 0) {
layer.msg(resp.errmsg);
window.location.reload();
return false;
}
layer.msg(resp.errmsg);
},
error: function (err) {
console.log(err)
}
});
return false;
});
$(document).on('click', '.sendNotice', function () {
$("#id").val($(this).data('id'));
layer.open({
area: ['400px', '200px'],
title: '保存数据',
type: 1,
content: $("#notice_form_show"),
btn: ['确认发送', '取消'],
offset:['30px','300px'],
yes: function (index) {
$("#notice_form_click").click();
},
cancel: function (index) {
layer.close(index);
}
});
});
form.on('submit(notice_form)', function (data) {
data.field.id = $("#id").val();
data.field.type = 'system_update';
$.ajax({
url: '/msgapi/sendNotice',
type: 'post',
data: data.field,
dataType:'json',
success: function (resp) {
if (resp.errcode == 0) {
layer.msg(resp.errmsg);
window.location.reload();
return false;
}
layer.msg(resp.errmsg);
},
error: function (err) {
console.log(err)
}
});
return false;
});
});
......@@ -28,6 +28,8 @@
<script src="/js/Message/common.min.js"></script>
@if(empty($into))
<script src="/js/Message/{{$id}}.js"></script>
<script src="/js/Message/getNotice.js"></script>
@endif
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ $sendData['title'] }}</title>
<link href="/js/Message/layui/css/layui.css" rel="stylesheet">
</head>
<body>
<div class="layui-card">
<div class="layui-card-header">{{$sendData['title']}}</div>
<div class="layui-card-body">
{!! $sendData['content'] !!}
</div>
</div>
</body>
</html>
<style>
.filter-label-left{
margin-left: 40px;
margin-top: 20px;
width: 150px;
}
.filter-select{
margin-top: 20px;
width: 150px;
}
</style>
<form class="layui-form layui-box" method="post">
<div class="layui-form-item">
<div class="layui-btn-container" style="text-align: center; margin-top: 10px;">
{{--<button lay-submit lay-filter="load" class="layui-btn" data-type="search">搜索</button>--}}
{{--<button type="button" class="layui-btn layui-btn-normal" id="addData">新增</button>--}}
</div>
</div>
</form>
<table id="systemBulletinList" lay-filter="systemBulletinList"></table>
<script type="text/html" id="active">
<a class="layui-btn layui-btn-xs layui-btn-normal editData" data-json='@{{d.data_json}}' data-content='@{{d.content}}' ><strong>编辑</strong></a>
<a class="layui-btn layui-btn-xs layui-btn-normal sendNotice " data-id='@{{d.smbn_id}}' ><strong>立即发送</strong></a>
</script>
{{--发送信息--}}
<div id="notice_form_show" style="display: none;margin-top: 30px;">
<style>
.fgh .layui-form-label{position: relative;top:5px;width: 100px;}
</style>
<form class="layui-form fgh" >
<div class="layui-form-item">
<div class="layui-inline ">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="checkbox" class="layui-input" lay-verify="required" name="notice_type[]" value="1" title="邮件" >
{{--<input type="checkbox" class="layui-input" lay-verify="required" name="notice_type[]" value="2" title="通知" >--}}
</div>
</div>
</div>
<input type="hidden" id="id" value="0">
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" style="display: none;" id="notice_form_click" lay-filter="notice_form" lay-submit >提交</button>
</div>
</div>
</form>
</div>
{{--保存数据--}}
<div id="save_form_show" style="display: none;margin-top: 30px;">
<style>
.fgh .layui-form-label{position: relative;top:5px;width: 100px;}
</style>
<form class="layui-form fgh" lay-filter="data_form" id="data_form_jq">
<div class="layui-form-item">
<div class="layui-inline ">
<label class="layui-form-label">公告标题:</label>
<div class="layui-input-inline" style="width: 300px;">
<input type="text" class="layui-input" lay-verify="required" name="title" id="title" >
</div>
</div>
</div>
<input type="hidden" name="smbn_id" value="0" id="smbn_id">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label" >宣讲内容:</label>
<div class="layui-input-inline" style="width: 1300px;">
<textarea id="content_text" class="layui-textarea" style="display: none" ></textarea>
<input type="hidden" id="content" name="content">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" style="display: none;" id="save_form_click" lay-filter="save_form" lay-submit >提交</button>
</div>
</div>
</form>
</div>
<style>
.filter-label-left{
margin-left: 40px;
margin-top: 20px;
width: 150px;
}
.filter-select{
margin-top: 20px;
width: 150px;
}
</style>
<form class="layui-form layui-box" method="post">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">更新日期</label>
<div class="layui-input-inline">
<input type="text" name="begin_time" value="" autocomplete="off" placeholder="选择开始时间" class="layui-input" id="begin_time" readonly>
</div>
<div class="layui-form-mid">-</div>
<div class="layui-input-inline">
<input type="text" name="end_time" value="" autocomplete="off" placeholder="选择结束时间" class="layui-input" id="end_time" readonly>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">系统名称</label>
<div class="layui-input-inline">
<select name="system_id" lay-verify="" lay-search>
<option value="">请选择</option>
@foreach(\App\Logic\SystemNoticeLogic::getAllSystemSelect() as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">宣讲部门</label>
<div class="layui-input-inline">
<select name="department_id" lay-verify="" lay-search>
<option value="">请选择</option>
@foreach(\App\Logic\SystemNoticeLogic::getAllUserDepartment() as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">宣讲人</label>
<div class="layui-input-inline">
<input type="text" name="preach_user" placeholder="填写宣讲人" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-btn-container" style="text-align: center; margin-top: 10px;">
<button lay-submit lay-filter="load" class="layui-btn" data-type="search">搜索</button>
<button type="button" class="layui-btn layui-btn-normal" id="addData">新增</button>
</div>
</div>
</form>
<table id="systemPreachList" lay-filter="systemPreachList"></table>
<script type="text/html" id="active">
<a class="layui-btn layui-btn-xs layui-btn-normal editData" data-json='@{{d.data_json}}' data-preach_content='@{{d.preach_content}}' ><strong>编辑</strong></a>
<a class="layui-btn layui-btn-xs layui-btn-normal sendNotice" data-id='@{{d.smph_id}}' ><strong>立即发送</strong></a>
</script>
{{--发送信息--}}
<div id="notice_form_show" style="display: none;margin-top: 30px;">
<style>
.fgh .layui-form-label{position: relative;top:5px;width: 100px;}
</style>
<form class="layui-form fgh" >
<div class="layui-form-item">
<div class="layui-inline ">
<label class="layui-form-label">更新时间:</label>
<div class="layui-input-inline">
<input type="checkbox" class="layui-input" lay-verify="required" name="notice_type[]" value="1" title="邮件" >
<input type="checkbox" class="layui-input" lay-verify="required" name="notice_type[]" value="2" title="通知" >
</div>
</div>
</div>
<input type="hidden" id="id" value="0">
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" style="display: none;" id="notice_form_click" lay-filter="notice_form" lay-submit >提交</button>
</div>
</div>
</form>
</div>
{{--新增数据--}}
<div id="save_form_show" style="display: none;margin-top: 30px;">
<style>
.fgh .layui-form-label{position: relative;top:5px;width: 100px;}
</style>
<form class="layui-form fgh" lay-filter="data_form" id="data_form_jq">
<div class="layui-form-item">
<div class="layui-inline ">
<label class="layui-form-label">宣讲时间:</label>
<div class="layui-input-inline">
<input type="text" class="layui-input" lay-filter="update_time" lay-verify="required" name="preach_time" id="preach_time" >
</div>
</div>
</div>
<input type="hidden" name="smph_id" value="0" id="smph_id">
<div class="layui-form-item">
<label class="layui-form-label">宣讲系统</label>
<div class="layui-input-inline">
<select name="system_id" lay-search lay-verify="required">
@foreach(\App\Logic\SystemNoticeLogic::getAllSystemSelect() as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">宣讲部门</label>
<div class="layui-input-inline">
<select name="department_id" lay-search lay-verify="required">
@foreach(\App\Logic\SystemNoticeLogic::getAllUserDepartment() as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">宣讲人</label>
<div class="layui-input-inline">
<select name="preach_user" lay-search lay-verify="required">
<option value=""></option>
@foreach($user as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label" >宣讲反馈:</label>
<div class="layui-input-inline">
<input type="text" name="preach_feedback" class="layui-input" lay-verify="required" >
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label" >宣讲内容:</label>
<div class="layui-input-inline" style="width: 600px;">
<textarea id="preach_content_text" class="layui-textarea" style="display: none" ></textarea>
<input type="hidden" id="preach_content" name="preach_content">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" style="display: none;" id="save_form_click" lay-filter="save_form" lay-submit >提交</button>
</div>
</div>
</form>
</div>
<style>
.filter-label-left{
margin-left: 40px;
margin-top: 20px;
width: 150px;
}
.filter-select{
margin-top: 20px;
width: 150px;
}
</style>
<form class="layui-form layui-box" >
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">更新日期</label>
<div class="layui-input-inline">
<input type="text" name="begin_time" value="" autocomplete="off" placeholder="选择开始时间" class="layui-input" id="begin_time" readonly>
</div>
<div class="layui-form-mid">-</div>
<div class="layui-input-inline">
<input type="text" name="end_time" value="" autocomplete="off" placeholder="选择结束时间" class="layui-input" id="end_time" readonly>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">系统名称</label>
<div class="layui-input-inline">
<select name="system_id" lay-verify="" lay-search>
<option value="">请选择</option>
@foreach(\App\Logic\SystemNoticeLogic::getAllSystemSelect() as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">更新类型</label>
<div class="layui-input-inline">
<select name="update_type" lay-verify="" lay-search>
<option value="">请选择</option>
<option value="功能更新">功能更新</option>
<option value="BUG优化">BUG优化</option>
<option value="新项目">新项目</option>
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">版本号码</label>
<div class="layui-input-inline">
<input type="text" name="version_num" placeholder="填写版本号码" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">更新标题</label>
<div class="layui-input-inline">
<input type="text" name="update_title" placeholder="填写更新标题" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">更新内容</label>
<div class="layui-input-inline">
<input type="text" name="update_content" placeholder="填写更新内容" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-btn-container" style="text-align: center; margin-top: 10px;">
<button lay-submit lay-filter="load" class="layui-btn">搜索</button>
<button type="button" class="layui-btn layui-btn-normal" id="addData">新增</button>
</div>
</div>
</form>
<table id="systemUpdateList" lay-filter="systemUpdateList"></table>
<script type="text/html" id="active">
<a class="layui-btn layui-btn-xs layui-btn-normal editData" data-json='@{{d.data_json}}' data-update_content='@{{d.update_content}}' ><strong>编辑</strong></a>
<a class="layui-btn layui-btn-xs layui-btn-normal sendNotice" data-id='@{{d.smue_id}}' ><strong>立即发送</strong></a>
</script>
{{--发送信息--}}
<div id="notice_form_show" style="display: none;margin-top: 30px;">
<style>
.fgh .layui-form-label{position: relative;top:5px;width: 100px;}
</style>
<form class="layui-form fgh" >
<div class="layui-form-item">
<div class="layui-inline ">
<label class="layui-form-label">更新时间:</label>
<div class="layui-input-inline">
<input type="checkbox" class="layui-input" lay-verify="required" name="notice_type[]" value="1" title="邮件" >
<input type="checkbox" class="layui-input" lay-verify="required" name="notice_type[]" value="2" title="通知" >
</div>
</div>
</div>
<input type="hidden" id="id" value="0">
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" style="display: none;" id="notice_form_click" lay-filter="notice_form" lay-submit >提交</button>
</div>
</div>
</form>
</div>
{{--新增数据--}}
<div id="save_form_show" style="display: none;margin-top: 30px;">
<style>
.fgh .layui-form-label{position: relative;top:5px;width: 100px;}
</style>
<form class="layui-form fgh" lay-filter="data_form" id="data_form_jq">
<div class="layui-form-item">
<div class="layui-inline ">
<label class="layui-form-label">更新时间:</label>
<div class="layui-input-inline">
<input type="text" class="layui-input" lay-filter="update_time" value="{!! date('Y-m-d H:i:s') !!}" lay-verify="required" name="update_time" id="update_time" >
</div>
</div>
</div>
<input type="hidden" name="smue_id" value="0" id="smue_id">
<div class="layui-form-item">
<label class="layui-form-label">更新系统</label>
<div class="layui-input-inline">
<select name="system_id" lay-filter="system_id" lay-search lay-verify="required">
<option value="">请选择</option>
@foreach(\App\Logic\SystemNoticeLogic::getAllSystemSelect() as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">更新类型</label>
<div class="layui-input-inline">
<select name="update_type" lay-filter="update_type" lay-search lay-verify="required">
<option value="功能更新">功能更新</option>
<option value="BUG优化">BUG优化</option>
<option value="新项目">新项目</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">版本号码</label>
<div class="layui-input-inline">
<input type="text" name="version_num" id="version_num" placeholder="填写版本号码" disabled="disabled" autocomplete="off" class="layui-input" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">更新标题</label>
<div class="layui-input-inline">
<input type="text" name="update_title" lay-filter="update_title" placeholder="填写更新标题" autocomplete="off" class="layui-input" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">产品负责人</label>
<div class="layui-input-inline">
<select name="product_user" lay-search lay-verify="required">
<option value=""></option>
@foreach($user as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">技术负责人</label>
<div class="layui-input-inline">
<select name="code_user" lay-search lay-verify="required">
<option value=""></option>
@foreach($user as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">测试负责人</label>
<div class="layui-input-inline">
<option value=""></option>
<select name="test_user" lay-search lay-verify="required">
@foreach($user as $key=>$value)
<option value="{{$key}}">{{$value}}</option>
@endforeach
</select>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline" >
<label class="layui-form-label" >更新内容:</label>
<div class="layui-input-inline" style="width: 600px;">
<textarea id="update_content_text" class="layui-textarea" style="display: none" ></textarea>
<input type="hidden" id="update_content" name="update_content">
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" style="display: none;" id="save_form_click" lay-filter="save_form" lay-submit >提交</button>
</div>
</div>
</form>
</div>
......@@ -2,6 +2,11 @@
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
exit(1);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit207e6e0c20b937d4ce6e1ee236483f64::getLoader();
......@@ -37,57 +37,130 @@ namespace Composer\Autoload;
*
* @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/
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
......@@ -102,9 +175,11 @@ class ClassLoader
* 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
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
......@@ -147,11 +222,13 @@ class ClassLoader
* 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
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
......@@ -195,8 +272,10 @@ class ClassLoader
* 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
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
......@@ -211,10 +290,12 @@ class ClassLoader
* 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
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
......@@ -234,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
......@@ -256,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
......@@ -276,10 +361,12 @@ class ClassLoader
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
......@@ -296,25 +383,44 @@ class ClassLoader
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
......@@ -323,6 +429,8 @@ class ClassLoader
return true;
}
return null;
}
/**
......@@ -367,6 +475,21 @@ class ClassLoader
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
......@@ -438,6 +561,10 @@ class ClassLoader
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
......
This diff could not be displayed because it is too large.
......@@ -2,20 +2,20 @@
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'1d1b89d124cc9cb8219922c9d5569199' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'1d1b89d124cc9cb8219922c9d5569199' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest.php',
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'e7223560d890eab89cda23685e711e2c' => $vendorDir . '/psy/psysh/src/Psy/functions.php',
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
'58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php',
......
......@@ -2,7 +2,7 @@
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
......
......@@ -2,7 +2,7 @@
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
......@@ -27,6 +27,7 @@ return array(
'SuperClosure\\' => array($vendorDir . '/jeremeamia/SuperClosure/src'),
'Psy\\' => array($vendorDir . '/psy/psysh/src/Psy'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Predis\\' => array($vendorDir . '/predis/predis/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'PhpAmqpLib\\' => array($vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
......
......@@ -13,45 +13,27 @@ class ComposerAutoloaderInit207e6e0c20b937d4ce6e1ee236483f64
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit207e6e0c20b937d4ce6e1ee236483f64', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit207e6e0c20b937d4ce6e1ee236483f64', '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\ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64::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);
}
}
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64::getInitializer($loader));
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
$includeFiles = \Composer\Autoload\ComposerStaticInit207e6e0c20b937d4ce6e1ee236483f64::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire207e6e0c20b937d4ce6e1ee236483f64($fileIdentifier, $file);
}
......@@ -60,11 +42,16 @@ class ComposerAutoloaderInit207e6e0c20b937d4ce6e1ee236483f64
}
}
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequire207e6e0c20b937d4ce6e1ee236483f64($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 50509)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 5.5.9". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}
## Filing bug reports ##
Bugs or feature requests can be posted on the [GitHub issues](http://github.com/predis/predis/issues)
section of the project.
When reporting bugs, in addition to the obvious description of your issue you __must__ always provide
some essential information about your environment such as:
1. version of Predis (check the `VERSION` file or the `Predis\Client::VERSION` constant).
2. version of Redis (check `redis_version` returned by [`INFO`](http://redis.io/commands/info)).
3. version of PHP.
4. name and version of the operating system.
5. when possible, a small snippet of code that reproduces the issue.
__Think about it__: we do not have a crystal ball and cannot predict things or peer into the unknown
so please provide as much details as possible to help us isolating issues and fix them.
__Never__ use GitHub issues to post generic questions about Predis! When you have questions about
how Predis works or how it can be used, please just hop me an email and I will get back to you as
soon as possible.
## Contributing code ##
If you want to work on Predis, it is highly recommended that you first run the test suite in order
to check that everything is OK and report strange behaviours or bugs. When modifying Predis please
make sure that no warnings or notices are emitted by PHP running the interpreter in your development
environment with the `error_reporting` variable set to `E_ALL | E_STRICT`.
The recommended way to contribute to Predis is to fork the project on GitHub, create topic branches
on your newly created repository to fix bugs or add new features (possibly with tests covering your
modifications) and then open a pull request with a description of the applied changes. Obviously you
can use any other Git hosting provider of your preference.
We always aim for consistency in our code base so you should follow basic coding rules as defined by
[PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
and [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
and stick with the conventions used in Predis to name classes and interfaces. Indentation should be
done with 4 spaces and code should be wrapped at 100 columns (please try to stay within this limit
even if the above mentioned official coding guidelines set the soft limit to 120 columns).
Please follow these [commit guidelines](http://git-scm.com/book/ch5-2.html#Commit-Guidelines) when
committing your code to Git and always write a meaningful (not necessarily extended) description of
your changes before opening pull requests.
# Some frequently asked questions about Predis #
________________________________________________
### What is the point of Predis? ###
The main point of Predis is about offering a highly customizable and extensible client for Redis,
that can be easily extended by developers while still being reasonabily fast. With Predis you can
swap almost any class with your own custom implementation: you can have custom connection classes,
new distribution strategies for client-side sharding, or handlers to replace or add Redis commands.
All of this can be achieved without messing with the source code of the library and directly in your
own application. Given the fast pace at which Redis is developed and adds new features, this can be
a great asset since it allows developers to add new and still missing features or commands or change
the standard behaviour of the library without the need to break dependencies in production code (at
least to some degree).
### Does Predis support UNIX domain sockets and persistent connections? ###
Yes. Obviously persistent connections actually work only when using PHP configured as a persistent
process reused by the web server (see [PHP-FPM](http://php-fpm.org)).
### Does Predis support SSL-encrypted connections? ###
Yes. Encrypted connections are mostly useful when connecting to Redis instances exposed by various
cloud hosting providers without the need to configure an SSL proxy, but you should also take into
account the general performances degradation especially during the connect() operation when the TLS
handshake must be performed to secure the connection. Persistent SSL-encrypted connections may help
in that respect, but they are supported only when running on PHP >= 7.0.0.
### Does Predis support transparent (de)serialization of values? ###
No and it will not ever do that by default. The reason behind this decision is that serialization is
usually something that developers prefer to customize depending on their needs and can not be easily
generalized when using Redis because of the many possible access patterns for your data. This does
not mean that it is impossible to have such a feature since you can leverage the extensibility of
this library to define your own serialization-aware commands. You can find more details about how to
do that [on this issue](http://github.com/predis/predis/issues/29#issuecomment-1202624).
### How can I force Predis to connect to Redis before sending any command? ###
Explicitly connecting to Redis is usually not needed since the client initializes connections lazily
only when they are needed. Admittedly, this behavior can be inconvenient in certain scenarios when
you absolutely need to perform an upfront check to determine if the server is up and running and
eventually catch exceptions on failures. Forcing the client to open the underlying connection can be
done by invoking `Predis\Client::connect()`:
```php
$client = new Predis\Client();
try {
$client->connect();
} catch (Predis\Connection\ConnectionException $exception) {
// We could not connect to Redis! Your handling code goes here.
}
$client->info();
```
### How Predis abstracts Redis commands? ###
The approach used to implement Redis commands is quite simple: by default each command follows the
same signature as defined on the [Redis documentation](http://redis.io/commands) which makes things
pretty easy if you already know how Redis works or you need to look up how to use certain commands.
Alternatively, variadic commands can accept an array for keys or values (depending on the command)
instead of a list of arguments. Commands such as [`RPUSH`](http://redis.io/commands/rpush) and
[`HMSET`](http://redis.io/commands/hmset) are great examples:
```php
$client->rpush('my:list', 'value1', 'value2', 'value3'); // plain method arguments
$client->rpush('my:list', ['value1', 'value2', 'value3']); // single argument array
$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // plain method arguments
$client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // single named array
```
An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which modifiers are passed
[using a named array](tests/Predis/Command/KeySortTest.php#L54-L75).
# Speaking about performances... #
_________________________________________________
### Predis is a pure-PHP implementation: it can not be fast enough! ###
It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you a
couple of easy numbers with a simple test that uses a single client and is executed by PHP 5.5.6
against a local instance of Redis 2.8 that runs under Ubuntu 13.10 on a Intel Q6600:
```
21000 SET/sec using 12 bytes for both key and value.
21000 GET/sec while retrieving the very same values.
0.130 seconds to fetch 30000 keys using _KEYS *_.
```
How does it compare with [__phpredis__](http://github.com/nicolasff/phpredis), a nice C extension
providing an efficient client for Redis?
```
30100 SET/sec using 12 bytes for both key and value
29400 GET/sec while retrieving the very same values
0.035 seconds to fetch 30000 keys using "KEYS *"".
```
Wow __phpredis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so
lower numbers are quite expected but there is a fundamental flaw in them: is this really how you are
going to use Redis in your application? Are you really going to send thousands of commands using a
for-loop on each page request using a single client instance? If so... well I guess you are probably
doing something wrong. Also, if you need to `SET` or `GET` multiple keys you should definitely use
commands such as `MSET` and `MGET`. You can also use pipelining to get more performances when this
technique can be used.
There is one more thing: we have tested the overhead of Predis by connecting on a localhost instance
of Redis but how these numbers change when we hit the physical network by connecting to remote Redis
instances?
```
Using Predis:
3200 SET/sec using 12 bytes for both key and value
3200 GET/sec while retrieving the very same values
0.132 seconds to fetch 30000 keys using "KEYS *".
Using phpredis:
3500 SET/sec using 12 bytes for both key and value
3500 GET/sec while retrieving the very same values
0.045 seconds to fetch 30000 keys using "KEYS *".
```
There you go, you get almost the same average numbers and the reason is simple: network latency is a
real performance killer and you cannot do (almost) anything about that. As a disclaimer, remember
that we are measuring the overhead of client libraries implementations and the effects of network
round-trip times, so we are not really measuring how fast Redis is. Redis shines best with thousands
of concurrent clients doing requests! Also, actual performances should be measured according to how
your application will use Redis.
### I am convinced, but performances for multi-bulk responses are still worse ###
Fair enough, but there is an option available if you need even more speed and consists on installing
__[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) and let the
client use it. __phpiredis__ is another C extension that wraps __hiredis__ (the official C client
library for Redis) with a thin layer exposing its features to PHP. You can then choose between two
different connection classes:
- `Predis\Connection\PhpiredisStreamConnection` (using native PHP streams).
- `Predis\Connection\PhpiredisSocketConnection` (requires `ext-socket`).
You will now get the benefits of a faster protocol serializer and parser just by adding a couple of
lines of code:
```php
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array(
'tcp' => 'Predis\Connection\PhpiredisStreamConnection',
'unix' => 'Predis\Connection\PhpiredisSocketConnection',
),
));
```
Dead simple. Nothing changes in the way you use the library in your application. So how fast is it
our basic benchmark script now? There are not much improvements for inline or short bulk responses
like the ones returned by `SET` and `GET`, but the speed for parsing multi-bulk responses is now on
par with phpredis:
```
Fatching 30000 keys with _KEYS *_ using Predis paired with phpiredis::
0.035 seconds from a local Redis instance
0.047 seconds from a remote Redis instance
```
### If I need an extension to get better performances, why not using phpredis? ###
Good question. Generically speaking if you need absolute uber-speed using Redis on the localhost and
you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you
do not need any kind of extensibility or guaranteed backwards compatibility with different versions
of Redis (Predis currently supports from 1.2 up to 2.8 and the current development version), then
using __phpredis__ makes absolutely sense. Otherwise, Predis is perfect for the job and by adding
__phpiredis__ you can get a nice speed bump almost for free.
Copyright (c) 2009-2016 Daniele Alessandri
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.
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/src/Autoloader.php';
Predis\Autoloader::register();
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// This script can be used to automatically generate a file with the scheleton
// of a test case to test a Redis command by specifying the name of the class
// in the Predis\Command namespace (only classes in this namespace are valid).
// For example, to generate a test case for SET (which is represented by the
// Predis\Command\StringSet class):
//
// $ ./bin/generate-command-test --class=StringSet
//
// Here is a list of optional arguments:
//
// --realm: each command has its own realm (commands that operate on strings,
// lists, sets and such) but while this realm is usually inferred from the name
// of the specified class, sometimes it can be useful to override it with a
// custom one.
//
// --output: write the generated test case to the specified path instead of
// the default one.
//
// --overwrite: pre-existing test files are not overwritten unless this option
// is explicitly specified.
// -------------------------------------------------------------------------- //
use Predis\Command\CommandInterface;
use Predis\Command\PrefixableCommandInterface;
class CommandTestCaseGenerator
{
private $options;
public function __construct(array $options)
{
if (!isset($options['class'])) {
throw new RuntimeException("Missing 'class' option.");
}
$this->options = $options;
}
public static function fromCommandLine()
{
$parameters = array(
'c:' => 'class:',
'r::' => 'realm::',
'o::' => 'output::',
'x::' => 'overwrite::'
);
$getops = getopt(implode(array_keys($parameters)), $parameters);
$options = array(
'overwrite' => false,
'tests' => __DIR__.'/../tests/Predis',
);
foreach ($getops as $option => $value) {
switch ($option) {
case 'c':
case 'class':
$options['class'] = $value;
break;
case 'r':
case 'realm':
$options['realm'] = $value;
break;
case 'o':
case 'output':
$options['output'] = $value;
break;
case 'x':
case 'overwrite':
$options['overwrite'] = true;
break;
}
}
if (!isset($options['class'])) {
throw new RuntimeException("Missing 'class' option.");
}
$options['fqn'] = "Predis\\Command\\{$options['class']}";
$options['path'] = "Command/{$options['class']}.php";
$source = __DIR__.'/../src/'.$options['path'];
if (!file_exists($source)) {
throw new RuntimeException("Cannot find class file for {$options['fqn']} in $source.");
}
if (!isset($options['output'])) {
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', 'Test.php', $options['path']));
}
return new self($options);
}
protected function getTestRealm()
{
if (isset($this->options['realm'])) {
if (!$this->options['realm']) {
throw new RuntimeException('Invalid value for realm has been sepcified (empty).');
}
return $this->options['realm'];
}
$fqnParts = explode('\\', $this->options['fqn']);
$class = array_pop($fqnParts);
list($realm,) = preg_split('/([[:upper:]][[:lower:]]+)/', $class, 2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
return strtolower($realm);
}
public function generate()
{
$reflection = new ReflectionClass($class = $this->options['fqn']);
if (!$reflection->isInstantiable()) {
throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed.");
}
if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) {
throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface.");
}
/*
* @var CommandInterface
*/
$instance = $reflection->newInstance();
$buffer = $this->getTestCaseBuffer($instance);
return $buffer;
}
public function save()
{
$options = $this->options;
if (file_exists($options['output']) && !$options['overwrite']) {
throw new RuntimeException("File {$options['output']} already exist. Specify the --overwrite option to overwrite the existing file.");
}
file_put_contents($options['output'], $this->generate());
}
protected function getTestCaseBuffer(CommandInterface $instance)
{
$id = $instance->getId();
$fqn = get_class($instance);
$fqnParts = explode('\\', $fqn);
$class = array_pop($fqnParts) . "Test";
$realm = $this->getTestRealm();
$buffer =<<<PHP
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @group commands
* @group realm-$realm
*/
class $class extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand()
{
return '$fqn';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId()
{
return '$id';
}
/**
* @group disconnected
*/
public function testFilterArguments()
{
\$this->markTestIncomplete('This test has not been implemented yet.');
\$arguments = array(/* add arguments */);
\$expected = array(/* add arguments */);
\$command = \$this->getCommand();
\$command->setArguments(\$arguments);
\$this->assertSame(\$expected, \$command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse()
{
\$this->markTestIncomplete('This test has not been implemented yet.');
\$raw = null;
\$expected = null;
\$command = \$this->getCommand();
\$this->assertSame(\$expected, \$command->parseResponse(\$raw));
}
PHP;
if ($instance instanceof PrefixableCommandInterface) {
$buffer .=<<<PHP
/**
* @group disconnected
*/
public function testPrefixKeys()
{
\$this->markTestIncomplete('This test has not been implemented yet.');
\$arguments = array(/* add arguments */);
\$expected = array(/* add arguments */);
\$command = \$this->getCommandWithArgumentsArray(\$arguments);
\$command->prefixKeys('prefix:');
\$this->assertSame(\$expected, \$command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
\$command = \$this->getCommand();
\$command->prefixKeys('prefix:');
\$this->assertSame(array(), \$command->getArguments());
}
PHP;
}
return "$buffer}\n";
}
}
// ------------------------------------------------------------------------- //
require __DIR__.'/../autoload.php';
$generator = CommandTestCaseGenerator::fromCommandLine();
$generator->save();
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// In order to be able to execute this script to create a PEAR package of Predis
// the `pear` binary must be available and executable in your $PATH.
// The parts used to parse author and version strings are taken from Onion (used
// by this library in the past) just to keep on relying on the package.ini file
// to simplify things. We might consider to switch to using the PEAR classes
// directly in the future.
// -------------------------------------------------------------------------- //
function executeWithBackup($file, $callback)
{
$exception = null;
$backup = "$file.backup";
copy($file, $backup);
try {
call_user_func($callback, $file);
} catch (Exception $exception) {
// NOOP
}
unlink($file);
rename($backup, $file);
if ($exception) {
throw $exception;
}
}
function parseAuthor($string)
{
$author = array();
if (preg_match('/^\s*(.+?)\s*(?:"(\S+)"\s*)?<(\S+)>\s*$/x', $string , $regs)) {
if (count($regs) == 4) {
list($_,$name,$user,$email) = $regs;
$author['name'] = $name;
$author['user'] = $user;
$author['email'] = $email;
} elseif (count($regs) == 3) {
list($_,$name,$email) = $regs;
$author['name'] = $name;
$author['email'] = $email;
}
} else {
$author['name'] = $string;
}
return $author;
}
function parseVersion($string)
{
$version_pattern = '([0-9.]+)';
if (preg_match("/^\s*$version_pattern\s*\$/x", $string, $regs)) {
return array('min' => $regs[1] ?: '0.0.0');
} elseif (preg_match("/^\s*[>=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
return array('min' => $regs[1] ?: '0.0.0');
} elseif (preg_match("/^\s*[<=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
return array('max' => $regs[1]);
} elseif (preg_match("/^\s*$version_pattern\s*<=>\s*$version_pattern\s*\$/x", $string, $regs)) {
return array(
'min' => $regs[1] ?: '0.0.0',
'max' => $regs[2],
);
}
return null;
}
function addRolePath($pkg, $path, $role)
{
if (is_dir($path)) {
$dirRoot = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$dirTree = new RecursiveIteratorIterator($dirRoot, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($dirTree as $fileinfo) {
if ($fileinfo->isFile()) {
addPackageFile($pkg, $fileinfo, $role, $path);
}
}
} else {
foreach (glob($path) as $filename) {
addPackageFile($pkg, new SplFileInfo($filename), $role);
}
}
}
function addPackageFile($pkg, $fileinfo, $role, $baseDir = '')
{
$fileNode = $pkg->contents->dir->addChild('file');
$fileNode->addAttribute('name', $filepath = $fileinfo->getPathname());
$fileNode->addAttribute('role', $role);
$fileNode->addAttribute('md5sum', md5_file($filepath));
$installNode = $pkg->phprelease->filelist->addChild('install');
$installNode->addAttribute('name', $filepath);
$installNode->addAttribute('as', !$baseDir ? basename($filepath) : substr($filepath, strlen($baseDir) + 1));
}
function generatePackageXml($packageINI)
{
$XML = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.4.10" version="2.0"
xmlns="http://pear.php.net/dtd/package-2.0"
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd" />
XML;
$cfg = parse_ini_file($packageINI, true);
$pkg = new SimpleXMLElement($XML);
$pkg->name = $cfg['package']['name'];
$pkg->channel = $cfg['package']['channel'];
$pkg->summary = $cfg['package']['desc'];
$pkg->description = $cfg['package']['desc'];
$author = parseAuthor($cfg['package']['author']);
$pkg->addChild('lead');
$pkg->lead->name = $author['name'];
$pkg->lead->user = $author['user'];
$pkg->lead->email = $author['email'];
$pkg->lead->active = 'yes';
$datetime = new DateTime('now');
$pkg->date = $datetime->format('Y-m-d');
$pkg->time = $datetime->format('H:i:s');
$pkg->addChild('version');
$pkg->version->release = $cfg['package']['version'];
$pkg->version->api = $cfg['package']['version'];
$pkg->addChild('stability');
$pkg->stability->release = $cfg['package']['stability'];
$pkg->stability->api = $cfg['package']['stability'];
$pkg->license = $cfg['package']['license'];
$pkg->notes = '-';
$pkg->addChild('contents')->addChild('dir')->addAttribute('name', '/');
$pkg->addChild('dependencies')->addChild('required');
foreach ($cfg['require'] as $required => $version) {
$version = parseVersion($version);
$pkg->dependencies->required->addChild($required);
if (isset($version['min'])) {
$pkg->dependencies->required->$required->min = $version['min'];
}
if (isset($version['max'])) {
$pkg->dependencies->required->$required->min = $version['max'];
}
}
$pkg->addChild('phprelease')->addChild('filelist');
$pathToRole = array(
'doc' => 'doc', 'docs' => 'doc', 'examples' => 'doc',
'lib' => 'php', 'src' => 'php',
'test' => 'test', 'tests' => 'test',
);
foreach (array_merge($pathToRole, $cfg['roles'] ?: array()) as $path => $role) {
addRolePath($pkg, $path, $role);
}
return $pkg;
}
function rewritePackageInstallAs($pkg)
{
foreach ($pkg->phprelease->filelist->install as $file) {
if (preg_match('/^src\//', $file['name'])) {
$file['as'] = "Predis/{$file['as']}";
}
}
}
function savePackageXml($xml)
{
$dom = new DOMDocument("1.0", "UTF-8");
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
file_put_contents('package.xml', $dom->saveXML());
}
function buildPackage()
{
passthru('pear -q package && rm package.xml');
}
function modifyPhpunitXml($file)
{
$cfg = new SimpleXMLElement($file, null, true);
$cfg[0]['bootstrap'] = str_replace('tests/', '', $cfg[0]['bootstrap']);
$cfg->testsuites->testsuite->directory = str_replace('tests/', '', $cfg->testsuites->testsuite->directory);
$cfg->saveXml($file);
}
// -------------------------------------------------------------------------- //
executeWithBackup(__DIR__.'/../phpunit.xml.dist', function ($file) {
modifyPhpunitXml($file);
$pkg = generatePackageXml('package.ini');
rewritePackageInstallAs($pkg);
savePackageXml($pkg);
buildPackage();
});
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// In order to be able to execute this script to create a Phar archive of Predis,
// the Phar module must be loaded and the "phar.readonly" directive php.ini must
// be set to "off". You can change the values in the $options array to customize
// the creation of the Phar archive to better suit your needs.
// -------------------------------------------------------------------------- //
$options = array(
'name' => 'predis',
'project_path' => __DIR__ . '/../src',
'compression' => Phar::NONE,
'append_version' => true,
);
function getPharFilename($options)
{
$filename = $options['name'];
// NOTE: do not consider "append_version" with Phar compression do to a bug in
// Phar::compress() when renaming phar archives containing dots in their name.
if ($options['append_version'] && $options['compression'] === Phar::NONE) {
$versionFile = @fopen(__DIR__ . '/../VERSION', 'r');
if ($versionFile === false) {
throw new Exception("Could not locate the VERSION file.");
}
$version = trim(fgets($versionFile));
fclose($versionFile);
$filename .= "_$version";
}
return "$filename.phar";
}
function getPharStub($options)
{
return <<<EOSTUB
<?php
Phar::mapPhar('predis.phar');
spl_autoload_register(function (\$class) {
if (strpos(\$class, 'Predis\\\\') === 0) {
\$file = 'phar://predis.phar/'.strtr(substr(\$class, 7), '\\\', '/').'.php';
if (file_exists(\$file)) {
require \$file;
return true;
}
}
});
__halt_compiler();
EOSTUB;
}
// -------------------------------------------------------------------------- //
$phar = new Phar(getPharFilename($options));
$phar->compress($options['compression']);
$phar->setStub(getPharStub($options));
$phar->buildFromDirectory($options['project_path']);
{
"name": "predis/predis",
"type": "library",
"description": "Flexible and feature-complete Redis client for PHP and HHVM",
"keywords": ["nosql", "redis", "predis"],
"homepage": "http://github.com/predis/predis",
"license": "MIT",
"support": {
"issues": "https://github.com/predis/predis/issues"
},
"authors": [
{
"name": "Daniele Alessandri",
"email": "suppakilla@gmail.com",
"homepage": "http://clorophilla.net",
"role": "Creator & Maintainer"
},
{
"name": "Till Krüss",
"homepage": "https://till.im",
"role": "Maintainer"
}
],
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/tillkruss"
}
],
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"suggest": {
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
"ext-curl": "Allows access to Webdis when paired with phpiredis"
},
"autoload": {
"psr-4": {
"Predis\\": "src/"
}
},
"scripts": {
"post-update-cmd": "@php -f tests/apply-patches.php"
}
}
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// Developers can implement Predis\Distribution\DistributorInterface to create
// their own distributors used by the client to distribute keys among a cluster
// of servers.
use Predis\Cluster\Distributor\DistributorInterface;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\Cluster\PredisStrategy;
use Predis\Connection\Aggregate\PredisCluster;
class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
{
private $nodes;
private $nodesCount;
public function __construct()
{
$this->nodes = array();
$this->nodesCount = 0;
}
public function add($node, $weight = null)
{
$this->nodes[] = $node;
++$this->nodesCount;
}
public function remove($node)
{
$this->nodes = array_filter($this->nodes, function ($n) use ($node) {
return $n !== $node;
});
$this->nodesCount = count($this->nodes);
}
public function getSlot($hash)
{
return $this->nodesCount > 1 ? abs($hash % $this->nodesCount) : 0;
}
public function getBySlot($slot)
{
return isset($this->nodes[$slot]) ? $this->nodes[$slot] : null;
}
public function getByHash($hash)
{
if (!$this->nodesCount) {
throw new RuntimeException('No connections.');
}
$slot = $this->getSlot($hash);
$node = $this->getBySlot($slot);
return $node;
}
public function get($value)
{
$hash = $this->hash($value);
$node = $this->getByHash($hash);
return $node;
}
public function hash($value)
{
return crc32($value);
}
public function getHashGenerator()
{
return $this;
}
}
$options = array(
'cluster' => function () {
$distributor = new NaiveDistributor();
$strategy = new PredisStrategy($distributor);
$cluster = new PredisCluster($strategy);
return $cluster;
},
);
$client = new Predis\Client($multiple_servers, $options);
for ($i = 0; $i < 100; ++$i) {
$client->set("key:$i", str_pad($i, 4, '0', 0));
$client->get("key:$i");
}
$server1 = $client->getClientFor('first')->info();
$server2 = $client->getClientFor('second')->info();
if (isset($server1['Keyspace'], $server2['Keyspace'])) {
$server1 = $server1['Keyspace'];
$server2 = $server2['Keyspace'];
}
printf("Server '%s' has %d keys while server '%s' has %d keys.\n",
'first', $server1['db15']['keys'], 'second', $server2['db15']['keys']
);
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// This is an example of how you can easily extend an existing connection class
// and trace the execution of commands for debugging purposes. This can be quite
// useful as a starting poing to understand how your application interacts with
// Redis.
use Predis\Command\CommandInterface;
use Predis\Connection\StreamConnection;
class SimpleDebuggableConnection extends StreamConnection
{
private $tstart = 0;
private $debugBuffer = array();
public function connect()
{
$this->tstart = microtime(true);
parent::connect();
}
private function storeDebug(CommandInterface $command, $direction)
{
$firtsArg = $command->getArgument(0);
$timestamp = round(microtime(true) - $this->tstart, 4);
$debug = $command->getId();
$debug .= isset($firtsArg) ? " $firtsArg " : ' ';
$debug .= "$direction $this";
$debug .= " [{$timestamp}s]";
$this->debugBuffer[] = $debug;
}
public function writeRequest(CommandInterface $command)
{
parent::writeRequest($command);
$this->storeDebug($command, '->');
}
public function readResponse(CommandInterface $command)
{
$response = parent::readResponse($command);
$this->storeDebug($command, '<-');
return $response;
}
public function getDebugBuffer()
{
return $this->debugBuffer;
}
}
$options = array(
'connections' => array(
'tcp' => 'SimpleDebuggableConnection',
),
);
$client = new Predis\Client($single_server, $options);
$client->set('foo', 'bar');
$client->get('foo');
$client->info();
var_export($client->getConnection()->getDebugBuffer());
/* OUTPUT:
array (
0 => 'SELECT 15 -> 127.0.0.1:6379 [0.0008s]',
1 => 'SELECT 15 <- 127.0.0.1:6379 [0.001s]',
2 => 'SET foo -> 127.0.0.1:6379 [0.001s]',
3 => 'SET foo <- 127.0.0.1:6379 [0.0011s]',
4 => 'GET foo -> 127.0.0.1:6379 [0.0013s]',
5 => 'GET foo <- 127.0.0.1:6379 [0.0015s]',
6 => 'INFO -> 127.0.0.1:6379 [0.0019s]',
7 => 'INFO <- 127.0.0.1:6379 [0.0022s]',
)
*/
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// This is a basic example on how to use the Predis\DispatcherLoop class.
//
// To see this example in action you can just use redis-cli and publish some
// messages to the 'events' and 'control' channel, e.g.:
// ./redis-cli
// PUBLISH events first
// PUBLISH events second
// PUBLISH events third
// PUBLISH control terminate_dispatcher
// Create a client and disable r/w timeout on the socket
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
// Return an initialized PubSub consumer instance from the client.
$pubsub = $client->pubSubLoop();
// Create a dispatcher loop instance and attach a bunch of callbacks.
$dispatcher = new Predis\PubSub\DispatcherLoop($pubsub);
// Demonstrate how to use a callable class as a callback for the dispatcher loop.
class EventsListener implements Countable
{
private $events;
public function __construct()
{
$this->events = array();
}
public function count()
{
return count($this->events);
}
public function getEvents()
{
return $this->events;
}
public function __invoke($payload)
{
$this->events[] = $payload;
}
}
// Attach our callable class to the dispatcher.
$dispatcher->attachCallback('events', ($events = new EventsListener()));
// Attach a function to control the dispatcher loop termination with a message.
$dispatcher->attachCallback('control', function ($payload) use ($dispatcher) {
if ($payload === 'terminate_dispatcher') {
$dispatcher->stop();
}
});
// Run the dispatcher loop until the callback attached to the 'control' channel
// receives 'terminate_dispatcher' as a message.
$dispatcher->run();
// Display our achievements!
echo "We received {$events->count()} messages!", PHP_EOL;
// Say goodbye :-)
$version = redis_version($client->info());
echo "Goodbye from Redis $version!", PHP_EOL;
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
$client = new Predis\Client($single_server);
// Plain old SET and GET example...
$client->set('library', 'predis');
$response = $client->get('library');
var_export($response); echo PHP_EOL;
/* OUTPUT: 'predis' */
// Redis has the MSET and MGET commands to set or get multiple keys in one go,
// cases like this Predis accepts arguments for variadic commands both as a list
// of arguments or an array containing all of the keys and/or values.
$mkv = array(
'uid:0001' => '1st user',
'uid:0002' => '2nd user',
'uid:0003' => '3rd user',
);
$client->mset($mkv);
$response = $client->mget(array_keys($mkv));
var_export($response); echo PHP_EOL;
/* OUTPUT:
array (
0 => '1st user',
1 => '2nd user',
2 => '3rd user',
) */
// Predis can also send "raw" commands to Redis. The difference between sending
// commands to Redis the usual way and the "raw" way is that in the latter case
// their arguments are not filtered nor responses coming from Redis are parsed.
$response = $client->executeRaw(array(
'MGET', 'uid:0001', 'uid:0002', 'uid:0003',
));
var_export($response); echo PHP_EOL;
/* OUTPUT:
array (
0 => '1st user',
1 => '2nd user',
2 => '3rd user',
) */
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// Predis can prefix keys found in commands arguments before sending commands to
// Redis, even for complex commands such as SORT, ZUNIONSTORE and ZINTERSTORE.
// Prefixing keys can be useful to create user-level namespaces for you keyspace
// thus reducing the need for separate logical databases in certain scenarios.
$client = new Predis\Client($single_server, array('prefix' => 'nrk:'));
$client->mset(array('foo' => 'bar', 'lol' => 'wut'));
var_export($client->mget('foo', 'lol'));
/*
array (
0 => 'bar',
1 => 'wut',
)
*/
var_export($client->keys('*'));
/*
array (
0 => 'nrk:foo',
1 => 'nrk:lol',
)
*/
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// This example will not work with versions of Redis < 2.6.
//
// Additionally to the EVAL command defined in the current development profile,
// the Predis\Command\ScriptCommand class can be used to build an higher level
// abstraction for "scriptable" commands so that they will appear just like any
// other command on the client-side. This is a quick example used to implement
// INCREX.
use Predis\Command\ScriptCommand;
class IncrementExistingKeysBy extends ScriptCommand
{
public function getKeysCount()
{
// Tell Predis to use all the arguments but the last one as arguments
// for KEYS. The last one will be used to populate ARGV.
return -1;
}
public function getScript()
{
return <<<LUA
local cmd, insert = redis.call, table.insert
local increment, results = ARGV[1], { }
for idx, key in ipairs(KEYS) do
if cmd('exists', key) == 1 then
insert(results, idx, cmd('incrby', key, increment))
else
insert(results, idx, false)
end
end
return results
LUA;
}
}
$client = new Predis\Client($single_server, array(
'profile' => function ($options) {
$profile = $options->getDefault('profile');
$profile->defineCommand('increxby', 'IncrementExistingKeysBy');
return $profile;
},
));
$client->mset('foo', 10, 'foobar', 100);
var_export($client->increxby('foo', 'foofoo', 'foobar', 50));
/*
array (
0 => 60,
1 => NULL,
2 => 150,
)
*/
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// This is a basic example on how to use the Predis\Monitor\Consumer class. You
// can use redis-cli to send commands to the same Redis instance your client is
// connected to, and then type "ECHO QUIT_MONITOR" in redis-cli when you want to
// exit the monitor loop and terminate this script in a graceful way.
// Create a client and disable r/w timeout on the socket.
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
// Use only one instance of DateTime, we will update the timestamp later.
$timestamp = new DateTime();
foreach (($monitor = $client->monitor()) as $event) {
$timestamp->setTimestamp((int) $event->timestamp);
// If we notice a ECHO command with the message QUIT_MONITOR, we stop the
// monitor consumer and then break the loop.
if ($event->command === 'ECHO' && $event->arguments === '"QUIT_MONITOR"') {
echo 'Exiting the monitor loop...', PHP_EOL;
$monitor->stop();
break;
}
echo "* Received {$event->command} on DB {$event->database} at {$timestamp->format(DateTime::W3C)}", PHP_EOL;
if (isset($event->arguments)) {
echo " Arguments: {$event->arguments}", PHP_EOL;
}
}
// Say goodbye :-)
$version = redis_version($client->info());
echo "Goodbye from Redis $version!", PHP_EOL;
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// When you have a whole set of consecutive commands to send to a redis server,
// you can use a pipeline to dramatically improve performances. Pipelines can
// greatly reduce the effects of network round-trips.
$client = new Predis\Client($single_server);
$responses = $client->pipeline(function ($pipe) {
$pipe->flushdb();
$pipe->incrby('counter', 10);
$pipe->incrby('counter', 30);
$pipe->exists('counter');
$pipe->get('counter');
$pipe->mget('does_not_exist', 'counter');
});
var_export($responses);
/* OUTPUT:
array (
0 => Predis\Response\Status::__set_state(array(
'payload' => 'OK',
)),
1 => 10,
2 => 40,
3 => true,
4 => '40',
5 => array (
0 => NULL,
1 => '40',
),
)
*/
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// Starting from Redis 2.0 clients can subscribe and listen for events published
// on certain channels using a Publish/Subscribe (PUB/SUB) approach.
// Create a client and disable r/w timeout on the socket
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
// Initialize a new pubsub consumer.
$pubsub = $client->pubSubLoop();
// Subscribe to your channels
$pubsub->subscribe('control_channel', 'notifications');
// Start processing the pubsup messages. Open a terminal and use redis-cli
// to push messages to the channels. Examples:
// ./redis-cli PUBLISH notifications "this is a test"
// ./redis-cli PUBLISH control_channel quit_loop
foreach ($pubsub as $message) {
switch ($message->kind) {
case 'subscribe':
echo "Subscribed to {$message->channel}", PHP_EOL;
break;
case 'message':
if ($message->channel == 'control_channel') {
if ($message->payload == 'quit_loop') {
echo 'Aborting pubsub loop...', PHP_EOL;
$pubsub->unsubscribe();
} else {
echo "Received an unrecognized command: {$message->payload}.", PHP_EOL;
}
} else {
echo "Received the following message from {$message->channel}:",
PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL;
}
break;
}
}
// Always unset the pubsub consumer instance when you are done! The
// class destructor will take care of cleanups and prevent protocol
// desynchronizations between the client and the server.
unset($pubsub);
// Say goodbye :-)
$version = redis_version($client->info());
echo "Goodbye from Redis $version!", PHP_EOL;
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
use Predis\Collection\Iterator;
// Starting from Redis 2.8, clients can iterate incrementally over collections
// without blocking the server like it happens when a command such as KEYS is
// executed on a Redis instance storing millions of keys. These commands are:
//
// - SCAN (iterates over the keyspace)
// - SSCAN (iterates over members of a set)
// - ZSCAN (iterates over members and ranks of a sorted set)
// - HSCAN (iterates over fields and values of an hash).
// Predis provides a specialized abstraction for each command based on standard
// SPL iterators making it possible to easily consume SCAN-based iterations in
// your PHP code.
//
// See http://redis.io/commands/scan for more details.
//
// Create a client using `2.8` as a server profile (needs Redis 2.8!)
$client = new Predis\Client($single_server, array('profile' => '2.8'));
// Prepare some keys for our example
$client->del('predis:set', 'predis:zset', 'predis:hash');
for ($i = 0; $i < 5; ++$i) {
$client->sadd('predis:set', "member:$i");
$client->zadd('predis:zset', -$i, "member:$i");
$client->hset('predis:hash', "field:$i", "value:$i");
}
// === Keyspace iterator based on SCAN ===
echo 'Scan the keyspace matching only our prefixed keys:', PHP_EOL;
foreach (new Iterator\Keyspace($client, 'predis:*') as $key) {
echo " - $key", PHP_EOL;
}
/* OUTPUT
Scan the keyspace matching only our prefixed keys:
- predis:zset
- predis:set
- predis:hash
*/
// === Set iterator based on SSCAN ===
echo 'Scan members of `predis:set`:', PHP_EOL;
foreach (new Iterator\SetKey($client, 'predis:set') as $member) {
echo " - $member", PHP_EOL;
}
/* OUTPUT
Scan members of `predis:set`:
- member:1
- member:4
- member:0
- member:3
- member:2
*/
// === Sorted set iterator based on ZSCAN ===
echo 'Scan members and ranks of `predis:zset`:', PHP_EOL;
foreach (new Iterator\SortedSetKey($client, 'predis:zset') as $member => $rank) {
echo " - $member [rank: $rank]", PHP_EOL;
}
/* OUTPUT
Scan members and ranks of `predis:zset`:
- member:4 [rank: -4]
- member:3 [rank: -3]
- member:2 [rank: -2]
- member:1 [rank: -1]
- member:0 [rank: 0]
*/
// === Hash iterator based on HSCAN ===
echo 'Scan fields and values of `predis:hash`:', PHP_EOL;
foreach (new Iterator\HashKey($client, 'predis:hash') as $field => $value) {
echo " - $field => $value", PHP_EOL;
}
/* OUTPUT
Scan fields and values of `predis:hash`:
- field:0 => value:0
- field:1 => value:1
- field:2 => value:2
- field:3 => value:3
- field:4 => value:4
*/
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// Predis allows to set Lua scripts as read-only operations for replication.
// This works for both EVAL and EVALSHA and also for the client-side abstraction
// built upon them (Predis\Command\ScriptCommand). This example shows a slightly
// more complex configuration that injects a new script command in the server
// profile used by the new client instance and marks it marks it as a read-only
// operation for replication so that it will be executed on slaves.
use Predis\Command\ScriptCommand;
use Predis\Connection\Aggregate\MasterSlaveReplication;
use Predis\Replication\ReplicationStrategy;
// ------------------------------------------------------------------------- //
// Define a new script command that returns all the fields of a variable number
// of hashes with a single roundtrip.
class HashMultipleGetAll extends ScriptCommand
{
const BODY = <<<LUA
local hashes = {}
for _, key in pairs(KEYS) do
table.insert(hashes, key)
table.insert(hashes, redis.call('hgetall', key))
end
return hashes
LUA;
public function getScript()
{
return self::BODY;
}
}
// ------------------------------------------------------------------------- //
$parameters = array(
'tcp://127.0.0.1:6379/?alias=master',
'tcp://127.0.0.1:6380/?alias=slave',
);
$options = array(
'profile' => function ($options, $option) {
$profile = $options->getDefault($option);
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
return $profile;
},
'replication' => function () {
$strategy = new ReplicationStrategy();
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
$replication = new MasterSlaveReplication($strategy);
return $replication;
},
);
// ------------------------------------------------------------------------- //
$client = new Predis\Client($parameters, $options);
// Execute the following commands on the master server using redis-cli:
// $ ./redis-cli HMSET metavars foo bar hoge piyo
// $ ./redis-cli HMSET servers master host1 slave host2
$hashes = $client->hmgetall('metavars', 'servers');
$replication = $client->getConnection();
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave');
echo 'Is still on slave? ', $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL;
var_export($hashes);
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// Predis supports redis-sentinel to provide high availability in master / slave
// scenarios. The only but relevant difference with a basic replication scenario
// is that sentinel servers can manage the master server and its slaves based on
// their state, which means that they are able to provide an authoritative and
// updated configuration to clients thus avoiding static configurations for the
// replication servers and their roles.
// Instead of connection parameters pointing to redis nodes, we provide a list
// of instances of redis-sentinel. Users should always provide a timeout value
// low enough to not hinder operations just in case a sentinel is unreachable
// but Predis uses a default value of 100 milliseconds for sentinel parameters
// without an explicit timeout value.
//
// NOTE: in real-world scenarios sentinels should be running on different hosts!
$sentinels = array(
'tcp://127.0.0.1:5380?timeout=0.100',
'tcp://127.0.0.1:5381?timeout=0.100',
'tcp://127.0.0.1:5382?timeout=0.100',
);
$client = new Predis\Client($sentinels, array(
'replication' => 'sentinel',
'service' => 'mymaster',
));
// Read operation.
$exists = $client->exists('foo') ? 'yes' : 'no';
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL;
// Write operation.
$client->set('foo', 'bar');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL;
// Read operation.
$bar = $client->get('foo');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL;
/* OUTPUT:
Does 'foo' exist on slave-127.0.0.1:6381? yes.
Now 'foo' has been set to 'bar' on master!
We fetched 'foo' from master and its value is 'bar'.
*/
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// Predis supports master / slave replication scenarios where write operations
// are performed on the master server and read operations are executed against
// one of the slaves. The behavior of commands or EVAL scripts can be customized
// at will. As soon as a write operation is performed the client switches to the
// master server for all the subsequent requests (either reads and writes).
//
// This example must be executed using the second Redis server configured as the
// slave of the first one (see the "SLAVEOF" command).
//
$parameters = array(
'tcp://127.0.0.1:6379?database=15&alias=master',
'tcp://127.0.0.1:6380?database=15&alias=slave',
);
$options = array('replication' => true);
$client = new Predis\Client($parameters, $options);
// Read operation.
$exists = $client->exists('foo') ? 'yes' : 'no';
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL;
// Write operation.
$client->set('foo', 'bar');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL;
// Read operation.
$bar = $client->get('foo');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL;
/* OUTPUT:
Does 'foo' exist on slave? yes.
Now 'foo' has been set to 'bar' on master!
We fetched 'foo' from master and its value is 'bar'.
*/
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// This example demonstrates how to use Predis to save PHP sessions on Redis.
//
// The value of `session.gc_maxlifetime` in `php.ini` will be used by default as
// the TTL for keys holding session data but this value can be overridden when
// creating the session handler instance using the `gc_maxlifetime` option.
//
// NOTE: this class requires PHP >= 5.4 but can be used on PHP 5.3 if a polyfill
// for SessionHandlerInterface is provided either by you or an external package
// like `symfony/http-foundation`.
//
// See http://www.php.net/class.sessionhandlerinterface.php for more details.
//
if (!interface_exists('SessionHandlerInterface')) {
die('ATTENTION: the session handler implemented by Predis requires PHP >= 5.4.0 '.
"or a polyfill for SessionHandlerInterface provided by an external package.\n");
}
// Instantiate a new client just like you would normally do. Using a prefix for
// keys will effectively prefix all session keys with the specified string.
$client = new Predis\Client($single_server, array('prefix' => 'sessions:'));
// Set `gc_maxlifetime` to specify a time-to-live of 5 seconds for session keys.
$handler = new Predis\Session\Handler($client, array('gc_maxlifetime' => 5));
// Register the session handler.
$handler->register();
// We just set a fixed session ID only for the sake of our example.
session_id('example_session_id');
session_start();
if (isset($_SESSION['foo'])) {
echo "Session has `foo` set to {$_SESSION['foo']}", PHP_EOL;
} else {
$_SESSION['foo'] = $value = mt_rand();
echo "Empty session, `foo` has been set with $value", PHP_EOL;
}
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (PHP_SAPI !== 'cli') {
die("Example scripts are meant to be executed locally via CLI.");
}
require __DIR__.'/../autoload.php';
function redis_version($info)
{
if (isset($info['Server']['redis_version'])) {
return $info['Server']['redis_version'];
} elseif (isset($info['redis_version'])) {
return $info['redis_version'];
} else {
return 'unknown version';
}
}
$single_server = array(
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15,
);
$multiple_servers = array(
array(
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15,
'alias' => 'first',
),
array(
'host' => '127.0.0.1',
'port' => 6380,
'database' => 15,
'alias' => 'second',
),
);
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// This is an implementation of an atomic client-side ZPOP using the support for
// check-and-set (CAS) operations with MULTI/EXEC transactions, as described in
// "WATCH explained" from http://redis.io/topics/transactions
//
// First, populate your database with a tiny sample data set:
//
// ./redis-cli
// SELECT 15
// ZADD zset 1 a 2 b 3 c
//
// Then execute this script four times and see its output.
//
function zpop($client, $key)
{
$element = null;
$options = array(
'cas' => true, // Initialize with support for CAS operations
'watch' => $key, // Key that needs to be WATCHed to detect changes
'retry' => 3, // Number of retries on aborted transactions, after
// which the client bails out with an exception.
);
$client->transaction($options, function ($tx) use ($key, &$element) {
@list($element) = $tx->zrange($key, 0, 0);
if (isset($element)) {
$tx->multi(); // With CAS, MULTI *must* be explicitly invoked.
$tx->zrem($key, $element);
}
});
return $element;
}
$client = new Predis\Client($single_server);
$zpopped = zpop($client, 'zset');
echo isset($zpopped) ? "ZPOPed $zpopped" : 'Nothing to ZPOP!', PHP_EOL;
; This file is meant to be used with Onion http://c9s.github.com/Onion/
; For instructions on how to build a PEAR package of Predis please follow
; the instructions at this URL:
;
; https://github.com/c9s/Onion#a-quick-tutorial-for-building-pear-package
;
[package]
name = "Predis"
desc = "Flexible and feature-complete Redis client for PHP and HHVM"
homepage = "http://github.com/nrk/predis"
license = "MIT"
version = "1.1.10"
stability = "stable"
channel = "pear.nrk.io"
author = "Daniele Alessandri \"nrk\" <suppakilla@gmail.com>"
[require]
php = ">= 5.3.9"
pearinstaller = "1.4.1"
[roles]
*.xml.dist = test
*.md = doc
LICENSE = doc
[optional phpiredis]
hint = "Add support for faster protocol handling with phpiredis"
extensions[] = socket
extensions[] = phpiredis
[optional webdis]
hint = "Add support for Webdis"
extensions[] = curl
extensions[] = phpiredis
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
/**
* Implements a lightweight PSR-0 compliant autoloader for Predis.
*
* @author Eric Naeseth <eric@thumbtack.com>
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class Autoloader
{
private $directory;
private $prefix;
private $prefixLength;
/**
* @param string $baseDirectory Base directory where the source files are located.
*/
public function __construct($baseDirectory = __DIR__)
{
$this->directory = $baseDirectory;
$this->prefix = __NAMESPACE__.'\\';
$this->prefixLength = strlen($this->prefix);
}
/**
* Registers the autoloader class with the PHP SPL autoloader.
*
* @param bool $prepend Prepend the autoloader on the stack instead of appending it.
*/
public static function register($prepend = false)
{
spl_autoload_register(array(new self(), 'autoload'), true, $prepend);
}
/**
* Loads a class from a file using its fully qualified name.
*
* @param string $className Fully qualified name of a class.
*/
public function autoload($className)
{
if (0 === strpos($className, $this->prefix)) {
$parts = explode('\\', substr($className, $this->prefixLength));
$filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
if (is_file($filepath)) {
require $filepath;
}
}
}
}
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
use Predis\Command\CommandInterface;
/**
* Interface defining a client-side context such as a pipeline or transaction.
*
* @method $this del(array|string $keys)
* @method $this dump($key)
* @method $this exists($key)
* @method $this expire($key, $seconds)
* @method $this expireat($key, $timestamp)
* @method $this keys($pattern)
* @method $this move($key, $db)
* @method $this object($subcommand, $key)
* @method $this persist($key)
* @method $this pexpire($key, $milliseconds)
* @method $this pexpireat($key, $timestamp)
* @method $this pttl($key)
* @method $this randomkey()
* @method $this rename($key, $target)
* @method $this renamenx($key, $target)
* @method $this scan($cursor, array $options = null)
* @method $this sort($key, array $options = null)
* @method $this ttl($key)
* @method $this type($key)
* @method $this append($key, $value)
* @method $this bitcount($key, $start = null, $end = null)
* @method $this bitop($operation, $destkey, $key)
* @method $this bitfield($key, $subcommand, ...$subcommandArg)
* @method $this bitpos($key, $bit, $start = null, $end = null)
* @method $this decr($key)
* @method $this decrby($key, $decrement)
* @method $this get($key)
* @method $this getbit($key, $offset)
* @method $this getrange($key, $start, $end)
* @method $this getset($key, $value)
* @method $this incr($key)
* @method $this incrby($key, $increment)
* @method $this incrbyfloat($key, $increment)
* @method $this mget(array $keys)
* @method $this mset(array $dictionary)
* @method $this msetnx(array $dictionary)
* @method $this psetex($key, $milliseconds, $value)
* @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method $this setbit($key, $offset, $value)
* @method $this setex($key, $seconds, $value)
* @method $this setnx($key, $value)
* @method $this setrange($key, $offset, $value)
* @method $this strlen($key)
* @method $this hdel($key, array $fields)
* @method $this hexists($key, $field)
* @method $this hget($key, $field)
* @method $this hgetall($key)
* @method $this hincrby($key, $field, $increment)
* @method $this hincrbyfloat($key, $field, $increment)
* @method $this hkeys($key)
* @method $this hlen($key)
* @method $this hmget($key, array $fields)
* @method $this hmset($key, array $dictionary)
* @method $this hscan($key, $cursor, array $options = null)
* @method $this hset($key, $field, $value)
* @method $this hsetnx($key, $field, $value)
* @method $this hvals($key)
* @method $this hstrlen($key, $field)
* @method $this blpop(array|string $keys, $timeout)
* @method $this brpop(array|string $keys, $timeout)
* @method $this brpoplpush($source, $destination, $timeout)
* @method $this lindex($key, $index)
* @method $this linsert($key, $whence, $pivot, $value)
* @method $this llen($key)
* @method $this lpop($key)
* @method $this lpush($key, array $values)
* @method $this lpushx($key, array $values)
* @method $this lrange($key, $start, $stop)
* @method $this lrem($key, $count, $value)
* @method $this lset($key, $index, $value)
* @method $this ltrim($key, $start, $stop)
* @method $this rpop($key)
* @method $this rpoplpush($source, $destination)
* @method $this rpush($key, array $values)
* @method $this rpushx($key, array $values)
* @method $this sadd($key, array $members)
* @method $this scard($key)
* @method $this sdiff(array|string $keys)
* @method $this sdiffstore($destination, array|string $keys)
* @method $this sinter(array|string $keys)
* @method $this sinterstore($destination, array|string $keys)
* @method $this sismember($key, $member)
* @method $this smembers($key)
* @method $this smove($source, $destination, $member)
* @method $this spop($key, $count = null)
* @method $this srandmember($key, $count = null)
* @method $this srem($key, $member)
* @method $this sscan($key, $cursor, array $options = null)
* @method $this sunion(array|string $keys)
* @method $this sunionstore($destination, array|string $keys)
* @method $this zadd($key, array $membersAndScoresDictionary)
* @method $this zcard($key)
* @method $this zcount($key, $min, $max)
* @method $this zincrby($key, $increment, $member)
* @method $this zinterstore($destination, array|string $keys, array $options = null)
* @method $this zrange($key, $start, $stop, array $options = null)
* @method $this zrangebyscore($key, $min, $max, array $options = null)
* @method $this zrank($key, $member)
* @method $this zrem($key, $member)
* @method $this zremrangebyrank($key, $start, $stop)
* @method $this zremrangebyscore($key, $min, $max)
* @method $this zrevrange($key, $start, $stop, array $options = null)
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
* @method $this zrevrank($key, $member)
* @method $this zunionstore($destination, array|string $keys, array $options = null)
* @method $this zscore($key, $member)
* @method $this zscan($key, $cursor, array $options = null)
* @method $this zrangebylex($key, $start, $stop, array $options = null)
* @method $this zrevrangebylex($key, $start, $stop, array $options = null)
* @method $this zremrangebylex($key, $min, $max)
* @method $this zlexcount($key, $min, $max)
* @method $this pfadd($key, array $elements)
* @method $this pfmerge($destinationKey, array|string $sourceKeys)
* @method $this pfcount(array|string $keys)
* @method $this pubsub($subcommand, $argument)
* @method $this publish($channel, $message)
* @method $this discard()
* @method $this exec()
* @method $this multi()
* @method $this unwatch()
* @method $this watch($key)
* @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method $this script($subcommand, $argument = null)
* @method $this auth($password)
* @method $this echo($message)
* @method $this ping($message = null)
* @method $this select($database)
* @method $this bgrewriteaof()
* @method $this bgsave()
* @method $this client($subcommand, $argument = null)
* @method $this config($subcommand, $argument = null)
* @method $this dbsize()
* @method $this flushall()
* @method $this flushdb()
* @method $this info($section = null)
* @method $this lastsave()
* @method $this save()
* @method $this slaveof($host, $port)
* @method $this slowlog($subcommand, $argument = null)
* @method $this time()
* @method $this command()
* @method $this geoadd($key, $longitude, $latitude, $member)
* @method $this geohash($key, array $members)
* @method $this geopos($key, array $members)
* @method $this geodist($key, $member1, $member2, $unit = null)
* @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface ClientContextInterface
{
/**
* Sends the specified command instance to Redis.
*
* @param CommandInterface $command Command instance.
*
* @return mixed
*/
public function executeCommand(CommandInterface $command);
/**
* Sends the specified command with its arguments to Redis.
*
* @param string $method Command ID.
* @param array $arguments Arguments for the command.
*
* @return mixed
*/
public function __call($method, $arguments);
/**
* Starts the execution of the context.
*
* @param mixed $callable Optional callback for execution.
*
* @return array
*/
public function execute($callable = null);
}
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
/**
* Exception class that identifies client-side errors.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ClientException extends PredisException
{
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment