月度归档:2015年05月

PHP在线源码阅读小工具

需求:可以随时阅读源代码,有基本的登录验证(大体上可以在手机平板上联网既可以阅读)。大体上,需要拉一个目录树出来,在点击文件时把文件内容返回显示,做一个弹出验证框验证登录。

实现代码:

<?php
///
class ReadDirTree {
	public $tree = array();
	public $treeView = '';
	
	public function __construct($source='') {
		if(!empty($source) && is_dir($source)){
			$this->tree = $this->getTree($source);
		}
	}
	
	public function getTree($source) {
		$dir = array();
		if(is_dir($source)){
			if ($dh = opendir($source)) {
				while (($file = readdir($dh)) !== false) {
					if(($file == '.') || ($file == '..') || (substr($file,0,1) == '.')){ continue; }
	
					if(is_dir($source.'/'.$file)){
						$dir[$file] = $this->getTree($source.'/'.$file);
					}
				}
				rewinddir($dh);
				while (($file = readdir($dh)) !== false) {
					if(is_file($source.'/'.$file)){
						$ext = pathinfo($file);
						$ext = $ext['extension'];
						if(($file == '.') || ($file == '..') || (($ext != '') && in_array(strtolower($ext),array('gif','jpg','jpeg','png','ico')))){ continue; }
						$dir[] = $file;
					}
				}
					
				closedir($dh);
			}
		}
		return $dir;
	}
	
	// 携带文件路径
	public function setDirTreeView($tree,$parent='/'){	
		$tmp = $parent;
		$this->treeView .= '<ul>';
		foreach($tree as $idx=>$vle){
			if(is_array($vle)){
				if($parent == '/'){
					$parent = '/'.$idx;
				}else{
					$parent = $parent.'/'.$idx;
				}
				
				$this->treeView .= '<li dir="'.$parent.'">'.$idx;
				$this->setDirTreeView($vle,$parent);
				$this->treeView .= '</li>';
			}else{
				$this->treeView .= '<li data-jstree=\'{"icon":"jstree-file"}\' file="'.$parent.'/'.$vle.'">'.$vle.'</li>';
			}
			$parent = $tmp;
		}
		$this->treeView .= '</ul>';
		
		return $this->treeView;
	}	
	
	public function getDirTreeView(){
		if(empty($this->treeView) && !empty($this->tree) && is_array($this->tree)){
			$this->setDirTreeView($this->tree);
		}
		return $this->treeView;
	}
}

///
$ajax = false;
if(isset($_SERVER["HTTP_X_REQUESTED_WITH"]) && strtolower($_SERVER["HTTP_X_REQUESTED_WITH"])=="xmlhttprequest"){
	$ajax = true;
}

///
$basePath = '/www/web/erp/lib/Zend';
$mod = '';
if(isset($_REQUEST['mod']) && (trim($_REQUEST['mod']) != '')){
}
$treeView = (new ReadDirTree($basePath))->getDirTreeView();

///
session_start();
if(!isset($_SESSION['vlogin']) || ($_SESSION['vlogin'] != 'login')){
	if($ajax){
		header("Content-type:text/html;charset=utf-8");
		echo json_encode(array('success'=>0,'errCode'=>'1010','errMsg'=>'未授权请求.','data'=>array()));
		exit;
	}
	if(($_SERVER['PHP_AUTH_USER'] == 'admin') && ($_SERVER['PHP_AUTH_PW'] == 'xxxxxx')){
		$_SESSION['vlogin'] = 'login';
	}else{
		header("Content-type:text/html;charset=utf-8");
		header('WWW-Authenticate: Basic realm="admin xxxxxx"');
		header('HTTP/1.0 401 Unauthorized');
		echo 'Unauthorized.';
	}
}

///
$fileContent = '...';
if(isset($_POST['file'])){
	$file = $basePath.trim($_POST['file']);
	$fileContent = highlight_file($file,true);	
}

if($ajax){
	//sleep(10);
	header("Content-type:text/html;charset=utf-8");
	echo json_encode(array('success'=>1,'errCode'=>'','errMsg'=>'','data'=>$fileContent));
	exit;
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<!-- jQuery库,大于等于1.9.0 -->
<script src="jquery-1.10.2.min.js"></script>
<script src="jquery.blockUI.js"></script>
<!-- 引入jstree -->
<script src="jstree.min.js"></script>

<!-- 引入jstree的皮肤样式 -->
<link rel="stylesheet" href="themes/default/style.min.css" />
<link rel="stylesheet" href="themes/default-dark/style.min.css" />
<style>
body{  padding:0px; margin:0px; }
table{ padding:0px; margin:0px; }
</style>
<script>
$(function(){
	function loading() {
		if ($("#overlay").length>0 && $("#ajaxbusyspinner").length>0) {
	    	$("#overlay").show();
	    	$("#ajaxbusyspinner").show();
	    } else {
	    	var bheight = $("body").height()+$(document).height();
	        var bwidth=$("body").width();
	        var overlay = $('<div style="z-index:10001;background-color:#666;opacity:.5;filter:Alpha(Opacity=50);position:absolute;padding:0px;margin:0px;left:0;top:0;bottom:0" id="overlay"></div>');
	        overlay.css({width:bwidth, height:bheight, display:"block"}).appendTo($("body"));
	        overlay.dblclick(closeLoading);
	        $('<div id="ajaxbusyspinner" style="overflow-y:auto;background-color:#FFFFAA;position:fixed;left:0;bottom:0;display:block;height:32px;margin:0;padding:0;text-align:center;width:100%;z-index:10002;"><div style="width:45%;text-align:right;float:left;"><img style="border-width: 0px;" alt="Loading..." src="loading.gif" /></div><div style="float:left;margin-top:5px;width:55%;text-align:left;line-height:100%; vertical-align: middle; font-size:16px;">&nbsp;正在处理,请等候...</div></div>').appendTo($("body"));
	    }
	}
	
	function closeLoading() {
		$("#overlay").hide();
		$("#ajaxbusyspinner").hide();	
	}
		
	$('#jstree').jstree({
		'core' : {
			'themes' : {
				'responsive' : false,
				'variant' : 'small',
				'stripes' : true
			}
		}
	}); 
	$('#jstree').on("changed.jstree", function (e, data) {
		var file = data.node.li_attr.file;
		if(file !== undefined){
			$.ajax({ 
				url:'index.php',
				async:false,
				type:"POST",
				data:{'file':file,'mod':'<?php echo $mod;?>'},
				dataType:"json",
				beforeSend:loading,
				complete:closeLoading,
				success:function(data){
					if(data.success){
						$("#containerView").html(data.data);
					}else{
						window.location.href = 'index.php';
					}
				},
				error:function(){
					alert("Error occurred");
				}
			});
		}
	});
});
</script>
</head>
<body style="position:relative">
<table style="position:relative; z-index:100;">
	<tr>
		<td style="vertical-align: top;">
		<div style="width: 220px; overflow-x:scroll;" id="jstree">
		<?php 
		echo $treeView;
		?>
		</div>
		</td>
		<td style="vertical-align: top;" id="containerView">
			<?php 
			echo $fileContent;
			?>
		</td>
	</tr>
</table>
</body>
</html>

程序基本符合预期,当然还有显示上可以改观的地方。参考网址:目录树阅读 Demo

永久链接:http://blog.ifeeline.com/1678.html

PHP读取文件目录树

扫描目录是一个递归过程,由于扫描目录时,视乎是按照字母顺序读出文件的(包含目录),所有出现目录和文件相互交叉的情况,如果直接输出这个数,感觉不怎么好,观察到很多工具,都是先排列目录然后再排列文件的,所以可以先把目录读出来,然后把文件读出来,合并在一起就是一个目录的显示。写了一个类,是为备用:

class ReadDirTree {
	public $tree = array();
	public $treeView = '';
	
	public function __construct($source='') {
		if(!empty($source) && is_dir($source)){
			$this->tree = $this->getTree($source);
		}
	}
	
	public function getTree($source) {
		$dir = array();
		if(is_dir($source)){
			if ($dh = opendir($source)) {
				while (($file = readdir($dh)) !== false) {
                                        // 以点开头的文件夹跳过
					if(($file == '.') || ($file == '..') || (substr($file,0,1) == '.')){ continue; }
	
					if(is_dir($source.'/'.$file)){
						$dir[$file] = $this->getTree($source.'/'.$file);
					}
				}
                                // 移到开始
				rewinddir($dh);
				while (($file = readdir($dh)) !== false) {
						
					if(is_file($source.'/'.$file)){
						$ext = pathinfo($file);
						$ext = $ext['extension'];
                                                // 不读取图片文件
						if(($file == '.') || ($file == '..') || (($ext != '') && in_array(strtolower($ext),array('gif','jpg','jpeg','png','ico')))){ continue; }
						
						$dir[] = $file;
					}
				}
					
				closedir($dh);
			}
		}
		return $dir;
	}
	
	// 携带文件路径
	public function setDirTreeView($tree,$parent='/'){	
		$tmp = $parent;
		$this->treeView .= '<ul>';
		foreach($tree as $idx=>$vle){
			if(is_array($vle)){
				if($parent == '/'){
					$parent = '/'.$idx;
				}else{
					$parent = $parent.'/'.$idx;
				}
				
				$this->treeView .= '<li dir="'.$parent.'">'.$idx;
				$this->setDirTreeView($vle,$parent);
				$this->treeView .= '</li>';
			}else{
				$this->treeView .= '<li data-jstree=\'{"icon":"jstree-file"}\' file="'.$parent.'/'.$vle.'">'.$vle.'</li>';
			}
			$parent = $tmp;
		}
		$this->treeView .= '</ul>';
		
		return $this->treeView;
	}	
	
	public function getDirTreeView(){
		if(empty($this->treeView) && !empty($this->tree) && is_array($this->tree)){
			$this->setDirTreeView($this->tree);
		}
		return $this->treeView;
	}
}

使用:

//传递一个目录路径到构造函数,直接调用getDirTreeView()就可以获取目录树格式化的输出。
$treeView = (new ReadDirTree($basePath))->getDirTreeView();

永久链接:http://blog.ifeeline.com/1675.html

Javascript的undefined和null

Javascript中的数据类型有undefined,boolean,number,string,object。没错,undefined是一种数据类型。任何没有定义的变量 和 定义未赋值的变量都是undefined类型,而且它的值是undefined,表示值没有定义未知。

对于一个变量只要赋值了,只要不是赋值undefined,那么它就不是undefined;变量还可以赋值undefined,如果这样做,那么这个变量相当于没有定义过 或 定义了没有赋值,所以这个操作无意义。

对于null值,它的类型应该是null,但是JS中为了兼容性,typeof一个赋值了null的变量时,返回object。对于null,表示空值,可以把null赋值给变量,用来表示这个变量是空的,这个跟undefined的区别在于,undefined表示未声明或声明了未赋值,而null表示变量声明并且赋值了null值。

通常,可以预定义一个变量并赋值为null,表示变量已经预定义。我们也可以把null看做是对象类型的一个特殊值。只是一个特殊值而已,不同于undefined它还包含其它信息(变量未声明 声明未赋值)

另外,NaN是数字类型的特殊值,表示不是数字。

	var a0=null;
	var a1=123;
	var a2="123";
	var a3=[1,2,3];
	var a4=function(){};
	var a5=true;
	
	console.log(typeof a);				//undefined
	console.log(typeof a0);				//object
	console.log(typeof a1);				//number
	console.log(typeof a2);				//string
	console.log(typeof a3);				//object
	console.log(typeof a4);				//function
	console.log(typeof a5);				//boolean
	
	console.log(undefined == null);		//true
	console.log(undefined === null);	//false
	
	var a=undefined;					
	var b=null;
	console.log(a);						//undefined
	console.log(typeof a)				//undefined
	console.log(b);						//null
	console.log(typeof b);				//object

	console.log(undefined in window);	//true

客户端上传插件 fine uploader

fine uploader以前是基于jQuery的,后来完全还用纯JS,目前看到的最新版本还要授权。所以这里说的是老旧版本。

先是一段接收文件的逻辑:

<?php 
	if(!empty($_FILES['Filedata'])) {
		sleep(1);
		
		$tmpname = $_FILES['Filedata']['tmp_name'];
		$name = $_FILES['Filedata']['name'];
		
		if(is_uploaded_file($tmpname)) {
			move_uploaded_file($tmpname, 'D:/wamp/'.$name);
		}
		echo json_encode(array('success'=>'OK','file'=>$_FILES['Filedata']));
		exit;
	}
?>

接下来就是客户端的脚本:

<!-- CSS -->
<link rel="stylesheet" type="text/css" href="css/fineuploader-3.8.2.min.css" />

<!-- JS  -->
<script type="text/javascript" src="js/jquery-1.8.2.js"></script>
<script type="text/javascript" src="js/jquery.fineuploader-3.8.2.min.js"></script>

<script type="text/javascript">
$(function(){
	var fileTotal = 0;
	var manualuploader = new qq.FineUploader({
		element: $('#manual-fine-uploader')[0],
		multiple: true,
		request: {
			inputName: 'Filedata'
		},
		text: {
			uploadButton: '选择文件...',
			failUpload: '上传失败...',
			cancelButton: '取消'
                },
		validation: {
			allowedExtensions: ['xls|xlsx'],
			//acceptFiles: ['image/jpeg'],
			//sizeLimit: 309600,
		},
		messages: {
			typeError: '只能选择xls格式的文件!'
		},
		autoUpload:false,
		callbacks: {
			onUpload: function(id, fileName) {
			},
			onComplete: function(id, fileName, responseJSON) {
				var len = $(".qq-upload-list > li").length;
				fileTotal++;

				if(len == fileTotal){
					//manualuploader.reset();
					alert("上传成功.");
				}
				//if(responseJSON != '') {}
			}
		}
	});
		
	$("#upload_btn").on('click',function(){
		fileTotal = 0;
		manualuploader.setEndpoint('index.php');
		manualuploader.uploadStoredFiles();
	});

	$("#reset_btn").on('click',function(){
		fileTotal = 0;
		manualuploader.reset();
	});
	
});
</script>

代码参考以上实现即可。有几点说明,element指定了要填充的父元素,插件产生的HTML全部填入这个元素中; inputName就是文件域的名称,服务端也是根据这个名称抓取上传文件的内容的;如果选择了多个文件上传,那么客户端实际模拟多次提交;服务端只要返回JSON数据,如果客户端不做任何处理,那就认为是上传文件成功,所以需要在onComplete回调函数中根据返回的JSON数据进行判断;选择的多个文件全部放入.qq-upload-list的ul中,每个li代表一个文件,如果失败,对应的li会打上一个叫qq-upload-fail的类,成功就是qq-upload-success,所以我们可以通过返回的JSON进行处理。
——————————————–
如果通过firebug查看生成的HTML代码,有一个file文件域,并且有一个mutiple属性,这个属性可以让弹出的文件选择框可以选择多个文件,这个是HTML5引入的特征,但是很不幸,IE不支持。所以在使用IE时无法选择多个文件。实际上,这里的这个文件域,也仅仅是为了弹出个框而已(在没有引入第三方扩展情况下,没有其它方法可以弹出文件选择框),实际的文件上传,是正儿八经的模拟表单进行文件上传而已,只不过它是通过Ajax的方式POST数据而已。
——————————————–
用到的压缩包:
fine uploader demo

jQuery UI 插件 multiselect

这个插件可以使传统的多选变得更加友好,特别是提供了一个过滤的功能,感觉比较不错。Github地址:https://github.com/ehynds/jquery-ui-multiselect-widget,看这里代码的提交,视乎也已经很久没有更新了。

基本使用:

<!-- CSS -->
<link rel="stylesheet" type="text/css" href="css/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="css/jquery.multiselect.css" />
<link rel="stylesheet" type="text/css" href="css/jquery.multiselect.filter.css" />

<!-- JS  -->
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jquery-ui.js"></script>
<script type="text/javascript" src="js/jquery.multiselect.js"></script>
<script type="text/javascript" src="js/jquery.multiselect.filter.js"></script>

<script type="text/javascript">
$(function(){
	
	// 单选 没有太多意义
	$("#select1").multiselect({
		multiple: false,
		header: "选择一个",
		noneSelectedText: "没有选择",
		selectedList: 1
	});
	
	// 基本用法
	$("#select2").multiselect({
		checkAllText:'全选',		// 全选文本
		uncheckAllText:'清除',	        // 全部不选文本
		noneSelectedText:'当前没有选择', // 没有选中时的提示文本
		selectedText:'选择了#个',
			
	}); 
	
	// 头部显示 隐藏 与自定义
	$("#select3").multiselect({
		header: false
	});
	
	// 应用过滤器
	$("#select4").multiselect({
		checkAllText:'全选',
		uncheckAllText:'清除',
		noneSelectedText:'当前没有选择',
		selectedText:'选择了#个',
	}).multiselectfilter({
		label:'过滤',
		width:'120',
		placeholder:'请输入文本',
		autoReset:false
	});
	$("#select5").multiselect().multiselectfilter();
	
	var conf = {
		// 基本配置
		header:true, 					// true false string, 显示 隐藏 设置头部字符串
		height:175,					// 下拉框的高度
		minWidth:225,					// 最小宽度
		checkAllText:'Check All',		        // 全选文本
		uncheckAllText:'Uncheck All',	                // 全部不选文本
		noneSelectedText:'Select options',              // 没有选中时的提示文本
		selectedText:'#selected',		        // selectedList为false时,选中时显示的文本,#表示多少个
		selectedList:false,				// 是否显示选中项的内容
		show:'',					// 设置打开效果
		hide:'',					// 设置隐藏效果
		autoOpen:false,					// 自动弹出
		multiple:true,					// 是否多选
		classes:'',					// 添加类
		position:{},					// 显示位置设置 position:{my:'center',at:'center'}
		
		// 事件
		create:function(event,ui){},
		beforeopen:function(event,ui){},
		open:function(event,ui){},
		beforeclose:function(event,ui){},
		close:function(event,ui){},
		checkall:function(event,ui){},
		uncheckall:function(event,ui){},
		optgrouptoggle:function(event,ui){},
		click:function(event,ui){}
	};
	
	// 已经也可以通过这个方式绑定回调函数到事件
	$("#multiselect").bind("multiselectopen", function(event, ui){
	    // event handler here
	});
	
	// 按照jQuery UI惯例,可以用如下方式调用方法
	$("#multiselect").multiselect("method_name");
	/* method_name 列表
	open
	close
	refresh
	disable		// 禁用,不可用
	enable		// 启用
	checkAll	// 全选
	uncheckAll	// 清除选择
	isOpen		// 是否打开
	getChecked	// 获取所有选中
	getButton
	widget		// 
	option		// 
	destroy
	*/
	
	// 应用过滤器
	// 引入jquery.multiselect.filter.css 和 jquery.multiselect.filter.js
	var filter = {
		label:'Filter',			//文本
		width:'100px',			//宽度
		placeholder:'Enter keywords',	//占位符
		autoReset:false
	};
	
});
</script>

jquery.ui.multiselect

Multiselect是基于jQuery UI的,所以jQuery 和 jQuery UI要新引入(包括它的CSS),而multiselect.filter又是基于multiselect的插件,所以对应的JS和CSS都需要引入。

在使用上,和使用select元素一样(记得添加multiple=”multiple”属性,表示可以多少),另外,在PHP中,这个select应该命名为xxx[]这样的形式,这样表单POST过去时就可以以一个数组的方式组织选中的值(否则只会接收最后一个值):

// select命名为multi_select[]
Array
(
    [multi_select] => Array
        (
            [0] => green
            [1] => blue
            [2] => orange
        )

)

以下是一些在线例子(来自官方压缩包):
http://blog.ifeeline.com/jquery/ui/multiselect/index.htm

永久链接:http://blog.ifeeline.com/1664.html

Javascript 插件 alertify.js

地址:http://fabien-d.github.io/alertify.js/,它可以用来替代Javascript中生硬的弹框,比如alert()。不过alertify已经停止维护,但由于项目中使用这个东西,故而稍研究一下。

在使用上先引入CSS和JS:

<link rel="stylesheet" href="PATH_TO_FILE/alertify.css" />
<link rel="stylesheet" href="PATH_TO_FILE/alertify.default.css" />

<script src="PATH_TO_FILE/alertify.min.js"></script>

还可以引入jQuery,不过alertify不依赖jQuery(纯JS)。

先大概看看alertify.js:

(function (global, undefined) {
	"use strict";

	var document = global.document,
	    Alertify;

	Alertify = function () {
                // .....
		return {
			alert   : function (message, fn, cssClass) { _alertify.dialog(message, "alert", fn, "", cssClass); return this; },
			confirm : function (message, fn, cssClass) { _alertify.dialog(message, "confirm", fn, "", cssClass); return this; },
			extend  : _alertify.extend,
			init    : _alertify.init,
			log     : function (message, type, wait) { _alertify.log(message, type, wait); return this; },
			prompt  : function (message, fn, placeholder, cssClass) { _alertify.dialog(message, "prompt", fn, placeholder, cssClass); return this; },
			success : function (message, wait) { _alertify.log(message, "success", wait); return this; },
			error   : function (message, wait) { _alertify.log(message, "error", wait); return this; },
			set     : function (args) { _alertify.set(args); },
			labels  : _alertify.labels,
			debug   : _alertify.handleErrors
		};
	};
	// AMD and window support
	if (typeof define === "function") {
		define([], function () { return new Alertify(); });
	} else if (typeof global.alertify === "undefined") {
		global.alertify = new Alertify();
	}
}(this));

典型的自执行函数,把this传递给global,this就是当前的window对象,再获取document对象。内部定义了Alertify对象,这个对象内部定义了所有可以的方法,最后在window上附加一个叫alertify的属性,它指向了Alertify对象,注意看Alertify内部的return语句,这样全局就可以直接使用alertify.xxx()方法了。

注意看set方法,实际传递到_alertify.set(args),这个_alertify内部定义的变量有:

			/**
			 * Labels object
			 * @type {Object}
			 */
			labels : {
				ok     : "OK",
				cancel : "Cancel"
			},

			/**
			 * Delay number
			 * @type {Number}
			 */
			delay : 5000,

			/**
			 * Whether buttons are reversed (default is secondary/primary)
			 * @type {Boolean}
			 */
			buttonReverse : false,

			/**
			 * Which button should be focused by default
			 * @type {String}	"ok" (default), "cancel", or "none"
			 */
			buttonFocus : "ok",

			/**
			 * Set the transition event on load
			 * @type {[type]}
			 */
			transition : undefined,

这个就是可以定制的内容。所以我们第一步就是配置一下(也可以使用默认的,不需要配置)

alertify.set({
				labels : {
					ok     : "确定",
					cancel : "取消"
				},
				delay : 5000, //错误提示框消失时间
				buttonReverse : false,
				buttonFocus   : "ok"
			});

接下来就是具体API的使用了,压缩包中的例子:

	<script>
		function reset () {
			$("#toggleCSS").attr("href", "../themes/alertify.default.css");
			alertify.set({
				labels : {
					ok     : "OK",
					cancel : "Cancel"
				},
				delay : 5000,
				buttonReverse : false,
				buttonFocus   : "ok"
			});
		}

		// ==============================
		// Standard Dialogs
		$("#alert").on( 'click', function () {
			reset();
			alertify.alert("This is an alert dialog");
			return false;
		});

		$("#confirm").on( 'click', function () {
			reset();
			alertify.confirm("This is a confirm dialog", function (e) {
				if (e) {
					alertify.success("You've clicked OK");
				} else {
					alertify.error("You've clicked Cancel");
				}
			});
			return false;
		});

		$("#prompt").on( 'click', function () {
			reset();
			alertify.prompt("This is a prompt dialog", function (e, str) {
				if (e) {
					alertify.success("You've clicked OK and typed: " + str);
				} else {
					alertify.error("You've clicked Cancel");
				}
			}, "Default Value");
			return false;
		});

		// ==============================
		// Ajax
		$("#ajax").on("click", function () {
			reset();
			alertify.confirm("Confirm?", function (e) {
				if (e) {
					alertify.alert("Successful AJAX after OK");
				} else {
					alertify.alert("Successful AJAX after Cancel");
				}
			});
		});

		// ==============================
		// Standard Dialogs
		$("#notification").on( 'click', function () {
			reset();
			alertify.log("Standard log message");
			return false;
		});

		$("#success").on( 'click', function () {
			reset();
			alertify.success("Success log message");
			return false;
		});

		$("#error").on( 'click', function () {
			reset();
			alertify.error("Error log message");
			return false;
		});

		// ==============================
		// Custom Properties
		$("#delay").on( 'click', function () {
			reset();
			alertify.set({ delay: 10000 });
			alertify.log("Hiding in 10 seconds");
			return false;
		});

		$("#forever").on( 'click', function () {
			reset();
			alertify.log("Will stay until clicked", "", 0);
			return false;
		});

		$("#labels").on( 'click', function () {
			reset();
			alertify.set({ labels: { ok: "Accept", cancel: "Deny" } });
			alertify.confirm("Confirm dialog with custom button labels", function (e) {
				if (e) {
					alertify.success("You've clicked OK");
				} else {
					alertify.error("You've clicked Cancel");
				}
			});
			return false;
		});

		$("#focus").on( 'click', function () {
			reset();
			alertify.set({ buttonFocus: "cancel" });
			alertify.confirm("Confirm dialog with cancel button focused", function (e) {
				if (e) {
					alertify.success("You've clicked OK");
				} else {
					alertify.error("You've clicked Cancel");
				}
			});
			return false;
		});

		$("#order").on( 'click', function () {
			reset();
			alertify.set({ buttonReverse: true });
			alertify.confirm("Confirm dialog with reversed button order", function (e) {
				if (e) {
					alertify.success("You've clicked OK");
				} else {
					alertify.error("You've clicked Cancel");
				}
			});
			return false;
		});

		// ==============================
		// Custom Log
		$("#custom").on( 'click', function () {
			reset();
			alertify.custom = alertify.extend("custom");
			alertify.custom("I'm a custom log message");
			return false;
		});

		// ==============================
		// Custom Themes
		$("#bootstrap").on( 'click', function () {
			reset();
			$("#toggleCSS").attr("href", "../themes/alertify.bootstrap.css");
			alertify.prompt("Prompt dialog with bootstrap theme", function (e) {
				if (e) {
					alertify.success("You've clicked OK");
				} else {
					alertify.error("You've clicked Cancel");
				}
			}, "Default Value");
			return false;
		});
	</script>

一、alertify.alert(“字符串”)弹出提示框;alertify.confirm(“字符串”,function(e){})弹出确认框,通过回调函数传递的e判断是确认还是取消;alertify.prompt(“字符串”,function(e,str){})弹出一个输入确认框,通过回调函数的e判断是确认还是取消,str获得输入的字符串;

二、alertify.log(“字符串”,,持续时间);alertify.success(“字符串”);alertify.error(“字符串”);

最后,alertify.success(“字符串”)这类函数弹出的框无法自定义,大概需要自己去修改样式文件。

永久链接:http://blog.ifeeline.com/1659.html

Smarty模板引擎学习笔记

官网:
http://www.smarty.net/

引擎目录结构:
libs
/plugins 插件
/sysplugins 系统插件(内置函数)
/Autoloader.php 类自动加载
/Smarty.class.php 模板引擎(需要加载此文件)
/SmartyBC.class.php

使用:

require('/.../libs/Smarty.class.php');
$smarty = new Smarty;

配置:

public $auto_literal = true; //auto literal on delimiters with whitspace
public $error_unassigned = false; // 变量未分配时显示错误
public $use_include_path = false; // 包含文件时是否搜索include_path
private $template_dir = array('./templates/') //**************
public $joined_template_dir = './templates/';
public $joined_config_dir = './configs/';
public $default_template_handler_func = null;
public $default_config_handler_func = null;
public $default_plugin_handler_func = null;
private $compile_dir = './templates_c/'; //**************
private $plugins_dir = null; //**************
private $cache_dir = './cache/'; //**************
private $config_dir = array('./configs/'); //**************
public $force_compile = false; //强制编译(开发环境可以开启)
public $compile_check = true;
public $use_sub_dirs = false; //编译和缓存文件使用子目录组织
public $allow_ambiguous_resources = false;
public $merge_compiled_includes = false;
public $inheritance_merge_compiled_includes = true;
public $force_cache = false; //强制缓存
public $left_delimiter = "{"; //左分隔符
public $right_delimiter = "}"; //右分隔符
public $security_class = 'Smarty_Security';
public $security_policy = null;
public $php_handling = self::PHP_PASSTHRU; //PHP标签的处理方式
public $allow_php_templates = false; //是否允许PHP模板
public $direct_access_security = true;
public $debugging = false; //是否开启调试
public $debugging_ctrl = 'NONE';
public $smarty_debug_id = 'SMARTY_DEBUG';
public $debug_tpl = null;
public $error_reporting = null; //错误报告
public $get_used_tags = false;
public $config_overwrite = true;
public $config_booleanize = true;
public $config_read_hidden = false;
public $compile_locking = true;
public $cache_locking = false;
public $locking_timeout = 10;
public $default_resource_type = 'file';
public $caching_type = 'file';
public $properties = array();
public $default_config_type = 'file';
public $source_objects = array();
public $template_objects = array();
public $resource_caching = false;
public $template_resource_caching = true;
public $cache_modified_check = false;
public $registered_plugins = array();
public $plugin_search_order = array('function', 'block', 'compiler', 'class');
public $registered_objects = array();
public $registered_classes = array();
public $registered_filters = array();
public $registered_resources = array();
public $_resource_handlers = array();
public $registered_cache_resources = array();
public $_cacheresource_handlers = array();
public $autoload_filters = array();
public $default_modifiers = array();
public $escape_html = false; //对变量的输出自动escape_html
//来自父类(低版本中也直接定义)
public $caching = false; //是否开启缓存
public $cache_lifetime = 3600; //缓存时间

实际使用:
以上是配置可用项目,大部分保留默认即可,不过声明为private的变量,设置和获取是通过__set和__get进行的,这几个变量一般只需要设置如下四个($plugins_dir不用)

$smarty-&gt;template_dir = 'html/template/v1'.DIRECTORY_SEPARATOR; //模板文件目录
$smarty-&gt;compile_dir = 'smarty/templates_c'.DIRECTORY_SEPARATOR; //编译后文件目录
$smarty-&gt;config_dir = 'smarty/configs'.DIRECTORY_SEPARATOR; //配置文件目录(针对Smarty)
$smarty-&gt;cache_dir = 'smarty/cache'.DIRECTORY_SEPARATOR; //缓存文件目录

以下是可能使用的配置:

$force_compile = false; //***启用强制编译,在开发时可以设置为true
$use_sub_dirs = false; //编译和缓存文件使用子目录组织,一般默认即可,如果按照子目录来存放,就设置为true
$force_cache = false; //强制缓存
$left_delimiter = "{"; //左分隔符
$right_delimiter = "}"; //右分隔符
$debugging = false; //***是否开启调试,在开发时可以设置为true
$caching = false; //是否开启缓存
$cache_lifetime = 3600; //缓存时间,如果开启了缓存,这里调整缓存的时间

快速入门:
1 {config_load file=”test.conf” section=”setup”},加载$config_dir目录中的的test.conf文件中的setup节:

title = Welcome to Smarty!
cutoff_size = 40

[setup]
bold = true

注意这个有分节的概念,是继承关系,这些配置中的变量,在模板中需要通过##圈起来访问,比如#title#

2 {include file=”header.tpl” title=foo},包含$template_dir模板目录中的header.tpl,可以在后面赋值变量,比如例子中的title,就是模板中用到的$title变量,赋值为foo。

3 {* bold and title are read from the config file *} 注释语法

4 管道符
比如{#title#|capitalize},#title#输入到capitalize
比如{$smarty.now|date_format:”%Y-%m-%d %H:%M:%S”},时间应用格式

5 {$SCRIPT_NAME} 与 {$smarty.server.SCRIPT_NAME}一样,$smarty.server映射了$_SERVER

6 {ldelim}$Name{rdelim},表示$left_delimiter和$right_delimiter,其中的内容当做纯文本

7 {section}

{section name=sec1 loop=$contacts}
phone: {$contacts[sec1].phone}
fax: {$contacts[sec1].fax}
cell: {$contacts[sec1].cell}
{/section}

类似foreach数组$contacts,下标为sec1。

{section name=outer loop=$FirstName}
{if $smarty.section.outer.index is odd by 2}
{$smarty.section.outer.rownum} . {$FirstName[outer]} {$LastName[outer]}
{else}
{$smarty.section.outer.rownum} * {$FirstName[outer]} {$LastName[outer]}
{/if}
{sectionelse}
none
{/section}

这个形式有点奇葩,outer是$FirstName的下标对象(记录了index rownum这些信息),$smarty.section.outer.index获取到outer的循环编号,{$smarty.section.outer.rownum}获取outer的号,区别是一个从0开始,一个从1开始。{sectionelse}可以来分之。

8 {strip}{/strip}去除标签空格

9 html生成器
{html_select_date start_year=1998 end_year=2010} 年月日选择器
{html_select_time use_24_hours=false} 时间选择器
{html_options values=$option_values selected=$option_selected output=$option_output} 下来选择,values对应值,output对应显示的名称

基本语法——————————————————————
[注释]

{* Comments *}

{* this multiline smarty
comment is
not sent to browser
*}

{*********************************************************
Multi line comment block with credits block
@ author: bg@example.com
@ maintainer: support@example.com
@ para: var that sets block style
@ css: the style output
**********************************************************}

[变量]
配置文件变量是一个不用美元符号$,而是用#号包围着变量(#hashmarks#),或者是一个$smarty.config形式的变量。

数学和嵌入标签:

{$smarty.server.SERVER_NAME} //$_SERVER['SERVER_NAME']
{$x+$y}
{assign var=foo value=$x+$y} // 属性中的变量,分配变量时不要使用$符号
{$foo[$x+3]} // 变量作为数组索引
{$foo={counter}+3} // 标签里面嵌套标签,使用变量是如果用标签嵌套,也不需要使用$符号
{$foo="this is message {counter}"} // 引号里面使用标签

定义数组:

{assign var=foo value=[1,2,3]}
{assign var=foo value=['y'=&gt;'yellow','b'=&gt;'blue']}
{assign var=foo value=[1,[9,8],3]} // 可以嵌套

Smarty “dot” 语法 (注意: 嵌入的{}用来解决指代不明的情况):

{$foo.a.b.c} =&gt; $foo['a']['b']['c']
{$foo.a.$b.c} =&gt; $foo['a'][$b]['c'] // with variable index
{$foo.a.{$b+4}.c} =&gt; $foo['a'][$b+4]['c'] // 表达式作为索引
{$foo.a.{$b.c}} =&gt; $foo['a'][$b['c']] // 嵌套索引

PHP式语法, “dot”语法外的另一种选择:

{$foo[1]} // normal access
{$foo['bar']}
{$foo['bar'][1]}
{$foo[$x+$x]} // index may contain any expression
{$foo[$bar[1]]} // nested index
{$foo[section_name]} // smarty {section} access, not array access! 访问Smarty节块变量,而非访问数组

可变变量:

$foo // normal variable
$foo_{$bar} // variable name containing other variable
$foo_{$x+$y} // variable name containing expressions
$foo_{$bar}_buh_{$blar} // variable name with multiple segments 用在多段变量名中
{$foo_{$x}} // will output the variable $foo_1 if $x has a value of 1 注意是输出变量,而非值

对象链:

{$object-&gt;method1($x)-&gt;method2($y)}

直接php函数:

{time()} //译注:如果直接使用模版变量符号引用php函数,该函数应有返回值。

[函数]
{funcname attr1=”val” attr2=”val”}
内置函数将在smarty内部工作,例如{if}、{section}和{strip},不能修改他们。

[属性]
大多数函数都带有自己的属性以便于明确说明或者修改他们的行为,smarty函数的属性很像HTML中的属性。静态数值不需要加引号,但是字符串建议使用引号。可以使用普通smarty变量,也可以使用带调节器的变量作为属性值,它们也不用加引号。你甚至可以使用php函数返回值和复杂表达式作为属性值。
一些属性用到了布尔值(true或false),它们表明为真或为假。如果没有为这些属性赋布尔值,那么默认使用true为其值。

[双引号里嵌入变量]
如果字符串中包含变量,需要使用双引号,当变量包含点时需要使用反引号圈起来,当包含管道符时,需要使用定界符号圈起来。

{func var="test `$foo.bar` test"|escape} // modifiers outside quotes! 调节器在引号外
{func var="test {$foo|escape} test"} // modifiers inside quotes! 调节器在引号内
{func var="test {time()} test"} // PHP function result ******
{func var="test {counter} test"} // plugin result
{func var="variable foo is {if !$foo}not {/if} defined"} // Smarty block function *****

[数学运算]
[忽略Smarty解析]
在Smarty模版,如果‘{’和‘}’大括号里包含有空格那么整个{}内容会被忽略,你可以设置Smarty类变量$auto_literal=false来取消这种规则。
{literal}…{/literal}块被用来忽略模版语法的解析,你也可以用{ldelim}、{rdelim}标签或{$smarty.ldelim}、{$smarty.rdelim}变量来忽略个别大括号(译注:后面两种方法主要用来在模版中输出左右大括号)。

变量——————————————————————————
[关联数组]
访问关联数组变量

$smarty = new Smarty;
$smarty-&gt;assign('Contacts',
array('fax' =&gt; '555-222-9876',
'email' =&gt; 'zaphod@slartibartfast.com',
'phone' =&gt; array('home' =&gt; '555-444-3333',
'cell' =&gt; '555-111-1234')));
$smarty-&gt;display('index.tpl');

index.tpl:

{$Contacts.fax}

{$Contacts.email}

{* you can print arrays of arrays as well *} {* 同样可以用于多维数组 *}
{$Contacts.phone.home}

{$Contacts.phone.cell}

[数组索引]
通过索引访问数组

<!--?php $smarty--->assign('Contacts', array(
'555-222-9876',
'zaphod@slartibartfast.example.com',
array(
'555-444-3333',
'555-111-1234'
)
));
$smarty-&gt;display('index.tpl');
?&gt;

index.tpl source:
{$Contacts[0]}

{$Contacts[1]}

{* you can print arrays of arrays as well *}
{$Contacts[2][0]}

{$Contacts[2][1]}

[对象]

name: {$person-&gt;name}

email: {$person-&gt;email}

OUTPUT:

name: Zaphod Beeblebrox

email: zaphod@slartibartfast.com

[变量范围]
http://www.php100.com/manual/smarty3/language.variables.scopes.html

[从配置文件读取的变量]
加载配置文件后,配置文件中的变量需要用两个井号”#”包围或者是smarty的保留变量$smarty.config.来调用(下节将讲到),第二种语法在变量作为属性值嵌入至引号的时候非常有用,详细可参考双引号里值的嵌入。

(举例 {include file=”#includefile#”} 这样#includefile#将被当作字符处理,而不表示配置文件变量,但可以这样表示 {include file=”`$smarty.config.includefile`”}不要忘了加“,当然也可用{$smarty.config.includefile}表示)。

[{$smarty}保留变量]
1 Request variables

{$smarty.get.page}
{$smarty.post.page}
{$smarty.cookies.username}
{$smarty.server.SERVER_NAME}
{$smarty.env.PATH}
{$smarty.session.id}
{$smarty.request.username}

2 {$smarty.now}

{$smarty.now|date_format:'%Y-%m-%d %H:%M:%S'}

3 {$smarty.const} 直接访问常量

<!--?php // the constant defined in php define('MY_CONST_VAL','CHERRIES'); ?-->
Output the constant in a template with
{$smarty.const.MY_CONST_VAL}

4 {$smarty.capture}
可以通过{$smarty.capture}变量捕获内置的{capture}…{/capture}模版输出。

5 {$smarty.config}
{$smarty.config}可以取得配置变量。{$smarty.config.foo}是{#foo#}的同义词。

6 {$smarty.section}
{$smarty.section}用来指向{section}循环的属性,里面包含一些有用的值,比如.first/.index等。

7 {$smarty.template}
返回经过处理的当前模板名(不包括目录)。

8 {$smarty.current_dir}
返回经过处理的当前模板目录名。

9 {$smarty.version}

10 {$smarty.block.child}

11 {$smarty.block.parent}

12 {$smarty.ldelim}, {$smarty.rdelim}
这两者变量用来打印left-delimiter和right-delimiter的字面值,等同于{ldelim}、{rdelim}。

变量调节器-—————————————————————————-
变量调节器作用于变量、自定义函数或字符串。 变量调节器的用法是:‘|’符号右接调节器名称。 变量调节器可接收附加参数影响其行为。 参数位于调节器右边,并用‘:’符号分开。

[首字符大写]

<!--?php $smarty--->assign('articleTitle', 'next x-men film, x3, delayed.');
?&gt;

Where the template is:
{$articleTitle}
{$articleTitle|capitalize}
{$articleTitle|capitalize:true}

Will output:
next x-men film, x3, delayed.
Next X-Men Film, x3, Delayed.
Next X-Men Film, X3, Delayed.

[连接字符串]

<!--?php $smarty--->assign('articleTitle', "Psychics predict world didn't end");
?&gt;

index.tpl:

{$articleTitle|cat:" yesterday."}

OUTPUT:

Psychics predict world didn't end yesterday.

[字符计数]

<!--?php $smarty--->assign('articleTitle', 'Cold Wave Linked to Temperatures.');
?&gt;

Where template is:
{$articleTitle}
{$articleTitle|count_characters}
{$articleTitle|count_characters:true}

Will output:

Cold Wave Linked to Temperatures.
29
33

[计算段数]
[计算句数]
[计算词数]

<!--?php $smarty--->assign('articleTitle', 'Dealers Will Hear Car Talk at Noon.');
?&gt;

Where template is:
{$articleTitle}
{$articleTitle|count_words}

This will output:
Dealers Will Hear Car Talk at Noon.
7

[格式化日期]
http://www.php100.com/manual/smarty3/language.modifier.date.format.html

[默认值]
Where template is:
{$articleTitle|default:’no title’}
{$myTitle|default:’no title’}
{$email|default:’No email address available’}

[转义]
escape作用于变量,用以html、url、单引号、十六进制、十六进制实体、javascript、邮件的转码或转义。默认为html转义。
http://www.php100.com/manual/smarty3/language.modifier.escape.html

[缩进]

[小写 ]

Where template is:
{$articleTitle}
{$articleTitle|lower}

[换行符替换成]

Where the template is:
{$articleTitle|nl2br}

[ 正则替换]
使用正则表达式在变量中搜索和替换,语法来自Php的preg_replace()函数。

Where template is:
{* replace each carriage return回车, tab and new line with a space *}
{$articleTitle}
{$articleTitle|regex_replace:"/[\r\t\n]/":" "}

[替换]
一种在变量中进行简单的搜索和替换字符串的处理。等同于php的str_replace()函数。

{$articleTitle}
{$articleTitle|replace:'Garden':'Vineyard'}
{$articleTitle|replace:' ':' '}

[插空 ]
[字符串格式化]

{$number}
{$number|string_format:"%.2f"}
{$number|string_format:"%d"}

[去除(多余空格)]
用一个空格或一个给定字符替换所有重复空格、换行和制表符。(注意:如果想要去除模板文本中的区块,请使用{strip}函数。)

{$articleTitle}
{$articleTitle|strip}
{$articleTitle|strip:' '}

[去除html标签]

{$articleTitle}
{$articleTitle|strip_tags} {* same as {$articleTitle|strip_tags:true} *}
{$articleTitle|strip_tags:false}

[截取]

{$articleTitle}
{$articleTitle|truncate}
{$articleTitle|truncate:30}
{$articleTitle|truncate:30:""}
{$articleTitle|truncate:30:"---"}
{$articleTitle|truncate:30:"":true}
{$articleTitle|truncate:30:"...":true}
{$articleTitle|truncate:30:'..':true:true}

This will output:

Two Sisters Reunite after Eighteen Years at Checkout Counter.
Two Sisters Reunite after Eighteen Years at Checkout Counter.
Two Sisters Reunite after...
Two Sisters Reunite after
Two Sisters Reunite after---
Two Sisters Reunite after Eigh
Two Sisters Reunite after E...
Two Sisters Re..ckout Counter.

[大写]

{$articleTitle}
{$articleTitle|upper}

[行宽约束]
http://www.php100.com/manual/smarty3/language.modifier.wordwrap.html

内置函数-
——————————————————————————
1 {$var=…} 变量赋值
这是{assign}函数的简写版,你可以直接赋值给模版,也可以为数组元素赋值。
2 {append} 追加
{append}用于在模板执行期间建立或追加模板变量数组。
3 {assign} 赋值
{assign}用来在模板运行时为模板变量赋值。
4 {block} 块
5 {call} 调用
6 {capture}捕获
{capture}用来捕获模板输出的数据并将其存储到一个变量里,而不是将它们输出到页面。任何在{capture name=”foo”}和{/capture}之间的数据将被存储到变量$foo中,该变量由name属性指定。
在模板中,可以通过$smarty.capture.foo访问{capture}内容,‘foo’是传递给name属性的值。如果没有提供name属性,函数默认将使用 “default” 作为参数,例如$smarty.capture.default。
{capture}可以嵌套。
7 {config_load}
8 {debug} 调试
9 {extends} 继承
10 {for} 循环
{for}、{forelse}标签用来创建一个简单循环,支持以下不同的格式:
{for $var=$start to $end}步长为1的简单循环;
{for $var=$start to $end step $step}其它步长循环。
当循环无迭代时执行{forelse}。

{for $foo=1 to 3}
<ul>
	<li>{$foo}</li>
</ul>
{/for}

11 {foreach},{foreachelse}遍历
foreach语法不能接受任何属性名,这是Smarty3新增的语法,但Smarty2.x中的{foreach from=$myarray key=”mykey” item=”myitem”}语法仍受支持。

{foreach}用来遍历数据数组,{foreach}与{section}循环相比更简单、语法更干净,也可以用来遍历关联数组。
{foreach $arrayvar as $itemvar}
{foreach $arrayvar as $keyvar=>$itemvar}

//
{foreach $myColors as $color}
<ul>
	<li>{$color}</li>
</ul>
{/foreach}

// Smarty 3 新语法$value@key
<ul>
<ul>{foreach $myPeople as $value}
	<li>{$value@key}: {$value}</li>
</ul>
</ul>
{/foreach}

@index
包含当前数组的下标,开始时为0。

{foreach $items as $i} {if $i@index eq 3} //$i@index就是一个整体,比如整数1,2 {* put empty table row *}{/if}{/foreach}

nbsp;
{$i.label}

@iteration
iteration包含当前循环的迭代,总是以1开始,这点与index不同。每迭代一次值自动加1。

@first

@last

@show 这个奇葩
show属性用在检测{foreach}循环是否无数据显示,show是个布尔值(true or false)。

@total
total包含{foreach}循环的总数(整数),可以用在{forach}里面或后面。

12 {function} 函数
13 {if}{elseif}{else} 条件
14 {include} 包含
15 {include_php}
16 {insert}插入
假设你在页面上端使用一个带有广告条位置的模板,广告条可以包含任何HTML、图象、FLASH等混合信息。因此这里不能使用一个静态链接,同时我们也不希望该广告条被缓存。这就需要在{insert}标签指
17 {ldelim},{rdelim}
18 {literal}
19 {nocache}禁止缓存
20 {php}
21 {section},{sectionelse}遍历数组
http://www.php100.com/manual/smarty3/language.function.section.html
22 {while}循环

自定义函数———————————————————–

MySQL 分表测试 与 分表模型

例子:

<?php
$server="localhost";
$user="root";
$pwd="";
$db="test";
$port=3306;
$sock='/var/lib/mysql/mysql.sock';
$charset='utf8';

function mysqlidb($server,$user,$pwd,$db,$charset,$port,$sock){
	$link = false;

	$connectionRetry = 10;

	while (!isset($link) || ($link == FALSE && $connectionRetry !=0) ){
		$link = mysqli_connect($server,$user,$pwd,$db,$port,$sock);
		$connectionRetry--;
	}

	if($link) {
		if (@mysqli_select_db($link, $db)) {
			if ((trim($charset) != '') && version_compare(@mysqli_get_server_info(), '4.1.0', '>=')) {
				@mysqli_query($link, "SET NAMES '" . trim($charset) . "'");
				if (function_exists('mysqli_set_charset')) {
					@mysqli_set_charset($link, trim($charset));
				} else {
					@mysqli_query($link, "SET CHARACTER_SET_CLIENT = '" . trim($charset) . "'");
					@mysqli_query($link, "SET CHARACTER_SET_RESULTS = '" . trim($charset) . "'");
				}
			}
		}
	}
	return $link;
}

$db = mysqlidb($server,$user,$pwd,$db,$charset,$port,$sock);

function getTableNum($id) {
	$cap = 200;
	return (int)($id / $cap);
}

function hasTable($table = '') {
	global $db;
	if(empty($table)) {
		return FALSE;
	}
	
	$r = mysqli_query($db,"SELECT table_name FROM information_schema.TABLES WHERE table_name ='$table';");
	$row = mysqli_fetch_assoc($r);
	
	if(!empty($row)) {
		return TRUE;
	}	
	
	return FALSE;
}

function createTable($table = '') {
	global $db;
	
	if(empty($table)) {
		return FALSE;
	}
	
	if(!hasTable($table)) {
		$dd = mysqli_query($db,'
			CREATE TABLE `'.$table.'` (
			`id`  int(11) NOT NULL ,
			`body`  text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL ,
			PRIMARY KEY (`id`)
			)
			ENGINE=InnoDB
			DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
			ROW_FORMAT=COMPACT
			;
		');
		if((int)$dd < 1) {
			$find = FALSE;
		}		
	}
	return TRUE;
}

/**/
for($i = 1; $i < 1000; $i++) {
	//echo $i . " - ". getTableNum($i)."\n";
	$num = getTableNum($i);
	$table = "t_".$num;
	
	if(!hasTable($table)) {
		createTable($table);
	}
	
	$sql = "INSERT INTO `".$table."` (`id`,`body`) values(".$i.",'123456789')";
	//echo $sql;
	$r = mysqli_query($db,$sql);
}


//for($i = 800; $i < 900; $i++) {
	$i = mt_rand(1, 1000);

	$num = getTableNum($i);
	$table = "t_".$num;
	
	if(hasTable($table)) {
		$sql = "SELECT * FROM `".$table."` WHERE id='".$i."' ";
		$r = mysqli_query($db,$sql);
		
		$row = mysqli_fetch_assoc($r);
		echo "From Table(".$table."):" . $row['id']."\n";
		
	} else {
		echo $table."表不存在,记录也就不存在。";
	}
//}

以下试着用一个具体模型来说明:
mysql-table-model

关联用户的订单表是一对多关系,根据订单关联用户这个维度,使用用户的ID,把订单放入各个分表中存放。比如:

function getTableNum($id) {
	$cap = 200;
	return (int)($id / $cap);
}

比如用户的ID从1-200,放入订单表_1,用户的ID从201-400,放入订单表_2等,这样的实现方式最简单高效,做一个除法运算就可以定位到分表号。

情况一:
知道订单ID,要到订单分表获取信息,那么首先要到订单主表去获取这个ID的订单,然后取回用户ID号,然后用这个用户ID号定位到分表,然后到分表中根据订单的ID定位到订单。这个过程说明,分表中需要保持订单ID。这个表现为主表与分表之间的一对一关系。

情况二:
知道用户ID,要定位用户所有的订单,可以根据用户ID定位到分表号,然后到分表中获取所有具有这个用户ID的订单。这个过程说明,分表中需要保存用户ID。这个表现为分表跟用户表之间的一对多关系。

情况三:
要获取一段时间内的所有订单。这个必须是查询订单主表,如果需要到保存在订单详情中的信息,需要循环结果集,分别到订单分表中获取。这个过程说明,主表需要保存尽可能多的元数据,这些元数据也要保存到分表中,分表中保存的是大数据。

这个是典型的一对多模型进行水平扩展的方案,可以满足一定数据存储的需求。不过缺点也明显,主表虽然只保存元数据,容量可能不会太大,但是记录的数量可能很大,比如千万级别的订单。如果一天10w,一个月就是300w,一年就是3600w,不用三年就到达亿级。

所以订单主表还需要进一步进行处理,可以按照时间对其进行归档处理,比如按照年来进行归档,当要查询某个时间段的订单时,可以定位到年归档表查询(如果时间跨越了一年,需要分别进行查询,然后合并结果)。

另外,用户表数量也可能非常大,那么可以继续对用户表进行水平扩展,不过这个扩展跟上面的根据用户ID对应订单进行水平扩展的方式是不一样的。用户表只保存元数据,根据ID定位到分表号(还是一样的函数):

function getTableNum($id) {
	$cap = 200;
	return (int)($id / $cap);
}

一般应该使用一样的分表算法。

以上的两个分表方式,实际是差不多相同,只不过订单的分表是间接根据用户来来分的,而用户表的分表则直接根据用户ID进行拆分,间接分表这种方式是比较常见的,它应该满足一对多的关系模型,在遇到其它需要分表的情况,对照这个模型思考一下就可以。

以上的分表方案可以说是最简单的了,可以看到,由于引入了分表,所有相关的程序也会变得复杂一些。经常可能碰到的概念是分库,一般策略应该是垂直分库(根据业务模型拆分,类似多个子系统,子系统实现水平分表),水平分库(每个库都一样的结构)看起来会比较复杂,需要解决全局ID、事务等问题,当然目前方案是有的….

Cygwin – Windows下运行Linux软件

Cygwin可以说是一个伟大的软件,通过一个DLL(cygwin1.dll)建立了Linux与Windows之间的系统调用和API之间的转换,使得Linux下的绝大多数软件能向Windows迁移。Cygwin和其它的虚拟机软件不一样,虚拟机软件会独占系统资源(Cygwin进行系统调用和API转换)。

Gitlab 安装 与 基本使用

地址:https://about.gitlab.com/downloads/

#CentOS 6.x 7.x下,YUM安装:

curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash
yum install gitlab-ce

这里实际是安装/etc/yum.repos.d/gitlab_gitlab-ce.repo,这里引用的连接比较缓慢。可以参考https://mirror.tuna.tsinghua.edu.cn/help/gitlab-ce/:

[gitlab-ce]
name=gitlab-ce
baseurl=http://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7
repo_gpgcheck=0
gpgcheck=0
enabled=1
gpgkey=https://packages.gitlab.com/gpg.key

然后运行:

yum makecache
yum install gitlab-ce

也可以直接下载RPM包进行安装:

#CentOS 6.x
curl -LJO https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/6/gitlab-ce-XXX.rpm/download
rpm -i gitlab-ce-XXX.rpm

#CentOS 7.x
curl -LJO https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/7/gitlab-ce-XXX.rpm/download
rpm -i gitlab-ce-XXX.rpm

完成安装后运行:

#产生配置
gitlab-ctl reconfigure

#启动
gitlab-ctl start

首次登录并修改密码。gitlab-ctl reconfigure会产生一个Nginx配置,默认监听80端口。直接IP地址即可访问。

———————————————————————————————————–
Giblab全局配置文件:/etc/gitlab//etc/gitlab/gitlab.rb,修改后需要运行gitlab-ctl reconfigure重新产生配置文件。

Gitlab使用Nginx作为HTTP服务器,如果本地已经安装了Nginx,通常80端口已经被占用,这个使用需要修改Gitlab使用的Nginx的实例的HOST配置不要占用80端口,修改/etc/gitlab//etc/gitlab/gitlab.rb,指定external_url ‘http://gitlab.vfeelit:8000’,然后gitlab-ctl reconfigure后就会产生新的配置:

vi /var/opt/gitlab/nginx/conf/gitlab-http.conf
#自动生成的HOST配置
server {
  listen *:8000;
  server_name gitlab.ifeeline;
  server_tokens off; ## Don't show the nginx version number, a security best practice
  root /opt/gitlab/embedded/service/gitlab-rails/public;
}

另外一个常见的是发送邮件配置,以下是QQ企业邮箱为例子:

vi /etc/gitlab/gitlab.rb

gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.exmail.qq.com"
gitlab_rails['smtp_port'] = 25
gitlab_rails['smtp_user_name'] = "gitlab@vfeelit.com"
gitlab_rails['smtp_password'] = "xxx"
gitlab_rails['smtp_domain'] = "smtp.qq.com"
gitlab_rails['smtp_authentication'] = :plain
gitlab_rails['smtp_enable_starttls_auto'] = true

# gitlab_rails['smtp_tls'] = false
# gitlab_rails['smtp_openssl_verify_mode'] = 'peer'
# gitlab_rails['smtp_ca_path'] = "/etc/ssl/certs"
# gitlab_rails['smtp_ca_file'] = "/etc/ssl/certs/ca-certificates.crt"

gitlab_rails['gitlab_email_from'] = "gitlab@vfeelit.com"
user['gitlab_user_email'] = 'gitlab@vfeelit.com'

注意最后两个配置,这个是邮件发送商的限制(以登陆账户的邮件帐号发邮件)。修改完成后运行gitlab-ctl reconfigure,去到/var/opt/gitlab/gitlab-rails/etc/smtp_settings.rb:

/var/opt/gitlab/gitlab-rails/etc/smtp_settings.rb

if Rails.env.production?
  Gitlab::Application.config.action_mailer.delivery_method = :smtp

  ActionMailer::Base.smtp_settings = {
    authentication: :plain,
    address: "smtp.exmail.qq.com",
    port: 25,
    user_name: "gitlab@vfeelit.com",
    password: "xxx",
    domain: "smtp.qq.com",
    enable_starttls_auto: true,



    ca_file: "/opt/gitlab/embedded/ssl/certs/cacert.pem",
  }
end

这个文件是gitlab-ctl reconfigure后自动生成的,所以一般不需要直接编辑它。

CHECK:

tcp        0      0 127.0.0.1:8080              0.0.0.0:*                   LISTEN      3878/unicorn master 
tcp        0      0 0.0.0.0:80                  0.0.0.0:*                   LISTEN      3845/nginx          
tcp        0      0 127.0.0.1:18198             0.0.0.0:*                   LISTEN      3859/redis-server 1 
udp        0      0 127.0.0.1:39151             127.0.0.1:39151             ESTABLISHED 3851/postgres

默认至少安装了nginx,redis。 安装位置:/opt/gitlab,进入/opt/gitlab/bin:

gitlab-ci-rails  gitlab-ci-rake  gitlab-ctl  gitlab-rails  gitlab-rake

需要知道gitlab-ctl这个工具:

gitlab-ctl reconfigure
gitlab-ctl show-config
gitlab-ctl uninstall
gitlab-ctl restart
gitlab-ctl start
gitlab-ctl stop

比如整体启动 或 停止,就需要它。

gitlab-ctl stop
ok: down: logrotate: 0s, normally up
ok: down: nginx: 1s, normally up
ok: down: postgresql: 0s, normally up
ok: down: redis: 1s, normally up
ok: down: sidekiq: 0s, normally up
ok: down: unicorn: 0s, normally up

安装完成后,首次登录会提示修改密码,密码修改了之后就会进入一个Dashboard界面:
Gitlab Dashboard

如果使用过Github,那么对Gitlab应该不陌生,Gitlab跟Github很多地方都非常类似。用如下图说明大体概念:
Gitlab 结构图
用户可以建立一个或多个组(Namespace),在一个组中可以建立多个项目(Project)。每个组和项目都有一个Members的概念,组的Members意味着可以操作组下的所有项目(根据Group Access决定),项目的Members可以操作具体的项目(根据Group Access决定)。注意:组的创建者也是组的Member,并且它的Group Access是Owner(拥有者);项目的创建者也是项目的Member,并且它的Project Access是Owner(拥有者)。项目在添加成员时,Project Access不可以指定是Owner,说明项目的拥有者只能是创建者,但是组在添加成员时可以指定它的Group Access为Owner,说明组可以有多个拥有者。

默认gitlab安装好之后有一个root用户,可以经过它直接创建用户(也可以用户自己去申请),在创建用户时(右上角的Admin Area,只有用户时管理员时才显示):
Gitlab添加用户
Can create group设置用户是否可以创建组,如果不勾选,此用户将无法创建组。注:默认用户创建的项目如果没有指定组,默认就是一个和用户名一样的组中,意思就是不存在没有组(Namespace)的项目。

如果勾选Admin,那么这个用户就成了管理员,那么这个用户登录后右上角就会有进入Admin Area的按钮,看起来,它跟root具备最高权限。

为了可以推送项目,还需要正确设置RSA秘钥,具体怎么生产秘钥,这里忽略,进入Profile(右上角),点击SSH Keys:
Gitlab SSH Key设置
只有把公钥粘贴上去后才能通过SSH协议进行推拉项目。

项目建立好之后,会有提示:
Gitlab链接
这个是项目的发布地址。接下来是一些初始化脚本和如果推送的命令:

git config --global user.name "ifeeline"
git config --global user.email "ifeeline@qq.com"

#初始化一个项目,然后推送
mkdir tt
cd tt
git init
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin git@gitlab.vfeelit.com:test/tt.git
git push -u origin master

#推送一个已经存在的项目
cd existing_git_repo
git remote add origin git@gitlab.vfeelit.com:test/tt.git
git push -u origin master

——————————————————————————————-
备份: 默认以时间戳命名备份的包,存放目录为/var/opt/gitlab/backups

gitlab-rake gitlab:backup:create

——————————————————————————————-
迁移:
迁移就是还原备份,为了避免潜在的问题,一般在新机器上按照了Gitlab后,然后把旧机器的Gitlab做版本升级,以保持新旧机器上的Gitlab版本一致。然后把备份传递到新机器:

# 旧机器,推送备份到新机器
scp /var/opt/gitlab/backups/1481857595_gitlab_backup.tar root@xx.xx.xx.xx:/var/opt/gitlab/backups

# 新机器
cd /var/opt/gitlab/backups 
chown git:git 1481857595_gitlab_backup.tar

#停止相关数据连接服务
gitlab-ctl stop unicorn
gitlab-ctl stop sidekiq

# 从1481857595还原备份
gitlab-rake gitlab:backup:restore BACKUP=1481857595

# 启动
gitlab-ctl start

由于修改了IP地址,如果出现WARNING: POSSIBLE DNS SPOOFING DETECTED! 或和WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED之类的警告,去.ssh下,编辑known_hosts,把原来IP对应的公钥那行记录删除。或者直接删除known_hosts文件。

永久链接:http://blog.ifeeline.com/1628.html