workerman+yii实现快速开发交互

示例:

http://24.igd.cc/workerman/page?caihaibin=ok

外部使用demo

http://24.igd.cc/workerman_remote/page?caihaibin=ok

http://gxu.igd.cn/workerman_remote/page?caihaibin=ok

(端口只有一个, 多个项目使用同一个workerman,通过特殊机制,实现将workerman请求转发到yii,并共享两边的sessiono数据,支持wss)

需要明确一个关键点,workerman本身是一个独立的运行容器,类似 apache+php的概念,独立运行.

http://24.igd.cc/workerman_remote/page?caihaibin=ok

因为 workerman 是一个独立的脚本容器,所以这类代码

lib ( ‘workerman/Gateway.php’ );
\GatewayClient\Gateway::joinGroup ( State ( ‘client_id’ ), WebSocketTools::KEY_PRE . ‘test’ );

其实比较像调用workerman的接口,实现相关功能.需要理解这块,所以两者有一定的时间差,这块初试化时,必须认login推送返回的消息为准,做为完全建立两者通讯的标志.如果没有,页面应当进行刷新,再次进行连接.有时websocket open是成功的,但是login消息也会没有推送回来,表示之前的网络通讯出了问题.当做不成功.

 

workerman 游戏的两种模式

第一种:

一种频率不高的,几秒甚至十几秒点一次的,如猜拳,24点小游戏.直接使用post调用yii动作实现游戏逻辑.

第二种:

频率非常高的,类似数钱,一秒会有数次请求的,全部走workerman session,不经过yii session 和数据库. 前台屏幕数据显示,可通过gatewayclient获取workerman所有的会话数据,再根据数据进行排名显示. 当比赛结束时,再将数据插入数据库.

//===============发送workerman 内部参数,用于直接调用workerman动作===============
// 自定义一个key 和 value ,一秒内多次推送也不影响性能
ws.send(JSON.stringify({model:’__workerman’,action:’set_value’,value_key:’score’,value_value:Math.random(0)}));
//===============发送workerman 内部参数,用于直接调用workerman动作

//gateway 获取session数据,不同项目,分组前缀必须不同,防止冲突

Gateway::getAllClientSessions ( WebSocketTools::KEY_PRE . ‘test’ )

广度workerman时序图

 

写给公司的yii1到yii2的转化

只针对模块形式代码进行yii1到yii2的转化,config中需要手动进行补充配置.
<?PHP
header ( 'Content-Type: text/html; charset=utf-8' );
include 'FileTools.php';
include 'lib.php';
// 需要转换的模块
$path = 'D:/PHPnow-1.5.6/htdocs/igddata/yii2sys/app/modules/member/';
// $path = 'D:/PHPnow-1.5.6/htdocs/igddata/yii2sys/app/';
// 根路径,用于识别命名空间
$root_path = 'D:/PHPnow-1.5.6/htdocs/igddata/yii2sys/';
$root_path = preg_replace ( '/\//', '\\', $root_path );
$preg_file = array ();
// Yii::$app->user->setState('_id',$identity->_id); 修改 成 Yii::$app->session->set ( '_id', $identity->getId () );
// ar getList findAll 需要针对性处理.
// CHTML
// <?
$preg_contents = array (
		'/Yii\:\:app\s*?\(\)/' => 'Yii::$app',
		// $app->params->membermodules => 变成数组 ['membermodules']
		'/Yii\:\:\$app\-\>params\-\>(\w+)/' => 'Yii::$app->params[\'\\1\']',
		// 直接调用\Yii跳过use
		'/(?<!\\\\)Yii/' => '\Yii',
		// CHTML 全部调整成 \yii\helpers\Html
		'/(?<!\\\\)CHTML/ims' => '\yii\helpers\Html',
		// 所有短标签自动补充成<?php
		'/\<\?(\s)/' => '<?php\\1',
		// post请求判断切换成yii2形式判断
		'/isPostRequest/' => 'isPost',
		'/getIsPostRequest\s*\(\s*\)/' => 'isPost',
		// user->setState 在新版本中已经被取消
		'/user\-\>setState/' => 'session\-\>set',
		// 统一当前登录用户获取用户id
		'/\>\_id/' => '>getId ()',
		// 所有原参数获取,调整为默认获取
		'/getParam\s*\(\s*?([\'\"\w]+)[^\)]*?\)/ims' => '$_REQUEST[\\1]',
		// '/render\s*\(/' => 'renderPartial(',
		// 渲染页面动作需要进行返回
		'/\n(\s*)\$this\-\>render/' => '
\\1return $this->render',
		// 统一当前登录用户获取用户id
		'/user\-\>\_id/' => 'user->getId()',
		// getAttributes()不能带false参数, 不然会导致出错
		'/getAttributes\s*\(\s*false\s*\)/' => 'getAttributes()',
		// ;;去除无效代码,但是for(;;)需要绕过
		'/(?<!\()\;\s*\;/' => ';',
		// 补充表前缀
		'/\{\{(?=\w)/' => '{{%',
		'/findByPk/i' => 'findOne',
		// 所有数组修改成新形式
		'/(?<!\w)array\s*\(([^\(\)]*?)\)/ms' => '[\\1]',
		'/jquery\-easyui\-1\.2\.5/ims' => 'jquery-easyui-1.3.1',
		'/public\s*function\s*tableName/ims' => 'public static function tableName',
		'/getAction\s*\(\)/' => 'action',
		// load为默认函数,不可用该函数名,全部调整为loadModel
		'/([\s\>])load\s*\(/' => '\\1loadModel(',
		// 修改http异常,并直接引用
		'/CHttpException/' => '\yii\web\HttpException',
		// 修改ar继承类
		'/extends CActiveRecord/' => 'extends \yii\db\ActiveRecord',
		// 修改注释,用于引入框架时,实现代码提示
		'/CActiveRecord/' => 'ActiveRecord',
		'/action\-\>getId\s*\(\)/' => 'action->id',
		'/getController\s*\(\)/' => 'controller',
		'/controller\-\>getId\s*\(\)/' => 'controller->id',
		'/getModule\s*\(\)/' => 'module',
		'/module\-\>getId\s*\(\)/' => 'module->id',
		// array 引用
		'/require\_once\s*\(\s*[\'\"]array\.php[\'\"]\s*\)\;/ims' => 'lib(\'array.php\');',
		'/require\_once\s*\(\s*[\'\"]array2\.php[\'\"]\s*\)\;/ims' => 'lib(\'array2.php\');',
		// 去除WebControls引用
		'/require\_once\s*\(\s*[\'\"]WebControls\.php[\'\"]\s*\)\;/' => '',
		// 直接调用WebControls
		'/new WebControls/' => 'new \ext\WebControls',
		// 去除MysqlTools引用
		'/require\_once\s*\([\'\"]MysqlTools\.php[\'\"]\)\;/' => '',
		'/lib\s*\(\s*[\'\"]MysqlTools\.php[\'\"]\s*\)\;/' => '',
		// 直接调用MysqlTools
		'/(?<!\\\\)MysqlTools/' => '\ext\MysqlTools',
		// GCheckData直接转化
		'/(?<!\\\\)GCheckData/' => '\ext\GCheckData',
		// 替换控制器集成类
		'/extends\s*CController/' => 'extends \yii\web\Controller',
		// beforeAction只能为公开函数
		'/protected\s*function\s*beforeAction/ims' => 'public function beforeAction',
		// 判断到有beforeAction的,需要进行父函数调用,否则会导致过滤器失效
		'/beforeAction/' => function ($path, $content) {
			// 有使用before,但是没有进行父函数调用,需要补充父类beforeAction调用
			if (! preg_match ( '/parent\:\:beforeAction/', $content )) {
				$content = preg_replace ( '/beforeAction[^\{]+?\{/ims', 'beforeAction($action) {
		// 需要调用父函数,否则不会触发过滤器
		if (!parent::beforeAction ( $action )) {
			return false;
		}', $content );
			}
			return $content;
		},
		// 整个替换module.php内容,不然配置文件需要手动修改.
		'/CWebModule/' => function ($path, $content) {
			$namespace = getNamespaceByPath ( $path );
			$content = <<<EOF
<?php
namespace {$namespace};
class Module extends \yii\base\Module {
	public function init() {
		parent::init ();
	}
}
EOF;
			return $content;
		},
		// ar特殊处理,补充命名空间
		// 需要先进行命名空间的添加,用于控制器,页面添加对象时,直接识别命名空间,并添加
		'/\\\\yii\\\\db\\\\ActiveRecord/' => function ($path, $content) {
			$namespace = getNamespaceByPath ( $path );
			// \s和(?!)正向零宽断言会导致误判,需要后面补充一个标志
			if (! preg_match ( '/^\<\?php\s*namespace/is', $content )) {
				$content = preg_replace ( '/^\<\?php\s*/ims', "<?php\nnamespace {$namespace};\n", $content );
			}
			// ArticleX 的model有特殊实现,不进行替换.
			if (! preg_match ( '/ArticleX/', $path ) && preg_match ( '/function\s*model\s*\(/ims', $content )) {
				$content = preg_replace ( '/function\s*model\s*\([^\}]+?\}/ims', 'function model($className = __CLASS__) {
		return new self ();
	}', $content );
			}
			if (preg_match ( '/function\s*getList\s*\(\s*\$criteria\s*\=\s*null/ims', $content )) {
				$content = preg_replace ( '/function\s*getList\s*\(\s*\$criteria\s*\=\s*null\s*/ims', 'function getList($afmodel = null', $content );
				$content = preg_replace ( '/(function\s*getList\s*\(\s*\$afmodel\s*\=\s*null\s*[^\;]+?)\$this\-\>findAll\s*\(\s*\$criteria\s*\)\s*\;/ims', '\\1$afmodel->all();', $content );
			}
			if (preg_match ( '/function\s*getCount\s*\(\s*\$criteria\s*\=\s*null/ims', $content )) {
				$content = preg_replace ( '/function\s*getCount\s*\(\s*\$criteria\s*\=\s*null\s*/ims', 'function getCount($afmodel = null', $content );
				$content = preg_replace ( '/(function\s*getCount\s*\(\s*\$afmodel\s*\=\s*null\s*[^\;]+?)\$this\-\>count\s*\(\s*\$criteria\s*\)\s*\;/ims', '\\1$afmodel->count();', $content );
			}
			return $content;
		},
		// 控制器特殊处理,补充命名空间,修改过滤器
		'/\\\\yii\\\\web\\\\Controller/' => function ($path, $content) {
			$namespace = getNamespaceByPath ( $path );
			$models_namespace = getNamespaceByPath ( dirname ( $path ) ) . '\\models';
			
			// 修改过滤器
			// 其它特殊模块,照样修改
			if (preg_match ( '/public\s*function\s*filters\(\s*\)\s*\{[^}]+?\}/ims', $content )) {
				$string = <<<EOF
public function behaviors() {
		return [ 
				'access' => [ 
						'class' => \\ext\\RbacFilter::className () 
				] 
		];
	}				
EOF;
				$content = preg_replace ( '/public\s*function\s*filters\(\s*\)\s*\{[^}]+?\}/ims', $string, $content );
			}
			// 先获取ar列表,并添加到use中,再添加命名空间
			// 获取所有ar,并标记到use中和页面中
			$models = getModelsByControllerPath ( $path );
			$use_models = array ();
			foreach ( $models as $model ) {
				$model_ns = $models_namespace . '\\' . $model;
				// 判断是否有该类同时判断是否已添加命名空间
				if (preg_match ( "/\W{$model}\W/ms", $content ) && ! stristr ( $content, $model_ns )) {
					$content = preg_replace ( '/^\<\?php\s*/ims', "<?php\nuse \\{$model_ns};\n", $content );
					$use_models [] = $model;
				}
			}
			// 控制器补充命名空间使用
			// \s和(?!)正向零宽断言会导致误判,需要后面补充一个标志
			// 命名空间前面不能直接加反斜杠,use 前可以
			if (! preg_match ( '/^\<\?php\s*namespace/ims', $content )) {
				$content = preg_replace ( '/^\<\?php\s*/ims', "<?php\nnamespace {$namespace};\n", $content );
			}
			// 页面补充命名空间使用
			$views = getViewsByControllerPath ( $path );
			foreach ( $views as $view ) {
				$page_content = file_get_contents ( $view );
				// 去除头部和尾部空白部分
				$page_content = trim ( $page_content );
				// 如果没有php头,就补充php头
				if (preg_match ( '/^(?!\<\?php\s*)/i', $page_content )) {
					$page_content = preg_replace ( '/^(?!\<\?php\s*)/i', '<?php

?>', $page_content );
				}
				$use_models = array ();
				foreach ( $models as $model ) {
					$model_ns = $models_namespace . '\\' . $model;
					// 判断是否有该类同时判断是否已添加命名空间
					if (preg_match ( "/\W{$model}\W/ms", $page_content ) && ! stristr ( $page_content, $model_ns )) {
						$page_content = preg_replace ( '/^\<\?php\s*/is', "<?php\nuse \\{$model_ns};\n", $page_content );
					}
				}
				file_put_contents ( $view, $page_content );
			}
			return $content;
		},
		// $criteria =new CDbCriteria 及分页逻辑实现自动转化
		'/\$criteria\s*\=\s*new\s*CDbCriteria.+?(\$this|\$model|\w+|\w+\:\:model\s*\(\s*\))\-\>(findAll|find|getList)\s*\([^\;]*?\)\;/ims' => function ($path, $content, $preg) {
			while ( preg_match ( $preg, $content, $matchs ) ) {
				$subcontent = $matchs [0];
				$object = $matchs [1];
				// 如果是xxx::model()和$this ,需要对(:,(,),$)补充斜杠,进行转义处理,用于正则表达式
				if (preg_match ( '/([\:\(\)\$])/', $object )) {
					$preg_object = preg_replace ( '/([\:\(\)\$])/', '\\\\\\1', $object );
				}
				// 调试识别结果
				// gddebug ( $preg_object, $object, $subcontent, $matchs );
				// exit ();
				// 原CDbCriteria编程ActiveQuery形式,直接通过find返回该对象,取代.
				$subcontent = preg_replace ( '/\$criteria\s*\=\s*new\s*CDbCriteria\s*(\(\s*\))?\;/ims', "\$afmodel=(\$afmodel?\$afmodel:{$object}->find());", $subcontent );
				// CDbCriteria condition 转化,全部通过andWhere代替,重叠部分,会自动补充and处理
				$subcontent = preg_replace ( '/\$criteria\s*\-\>condition\s*\.?\s*\=\s*([^\;]+?)\;/ims', "\$afmodel->andWhere(\\1);", $subcontent );
				// CDbCriteria addCondition 转化,全部通过andWhere代替,重叠部分,会自动补充and处理
				$subcontent = preg_replace ( '/\$criteria\s*\-\>addCondition\s*\(([^\;]+?)\)\;/ims', "\$afmodel->andWhere(\\1);", $subcontent );
				// CDbCriteria order 转化
				$subcontent = preg_replace ( '/\$criteria\s*\-\>order\s*\.?\s*\=\s*([^\;]+?)\;/ims', "\$afmodel->orderBy(\\1);", $subcontent );
				// CDbCriteria select 转化
				$subcontent = preg_replace ( '/\$criteria\s*\-\>select\s*\.?\s*\=\s*([^\;]+?)\;/ims', "\$afmodel->select(\\1);", $subcontent );
				// ActiveRecord count 转化
				$subcontent = preg_replace ( '/' . $preg_object . '?\s*\-\>count\s*\([^\;]*?\)\;/ims', "\$afmodel->count();", $subcontent );
				
				// 如果出现特殊自定函数,修改传入参数
				$subcontent = preg_replace ( '/\-\>getCount\s*\(\s*\$criteria\s*\)/', '->getCount($afmodel)', $subcontent );
				
				// CPagination分页对象转化
				$subcontent = preg_replace ( '/new\s*CPagination\s*\(([^\)]+?)\)/ims', 'new \yii\data\Pagination ( [ 
				"totalCount" => \\1
		] )', $subcontent );
				// 分页大小转化
				$subcontent = preg_replace ( '/\$pages\s*\-\>\s*pageSize\s*\=([^\;]+)\;/ims', "\$pages->setPageSize ( \\1 );", $subcontent );
				// $pages->applyLimit($criteria) 没用,替换成page变量设置及分页变量带入ActiveQuery处理
				$subcontent = preg_replace ( '/\$pages\s*\-\>\s*applyLimit([^\;]+)\;/ims', "\$pages->setPage ( \$page-1 );
			\$afmodel->offset ( \$pages->offset )->limit ( \$pages->limit );", $subcontent );
				// ActiveRecord::findAll转化成 ActiveQuery::all()
				$subcontent = preg_replace ( '/' . $preg_object . '\s*\-\>(findAll|find\s*\(\s*\)\-\>)\s*\([^\;]*?\)\;/ims', "\$afmodel->all();", $subcontent );
				// $model->getList进行特殊处理
				$subcontent = preg_replace ( '/' . $preg_object . '\s*\-\>getList\s*\([^\;]*?\)\;/ims', "{$object}->getList(\$afmodel);", $subcontent );
				// 所有andWhere 内多余的and进行剔除
				$subcontent = preg_replace ( '/andWhere\s*\(\s*(\'|\")\s*and/ims', 'andWhere(\\1', $subcontent );
				// 调试转化结果
				// gddebug ( $subcontent );
				// exit ();
				$content = str_replace ( $matchs [0], $subcontent, $content );
			}
			return $content;
		},
		'/(url\(\'\w+\'\)\?\>)\/(\w+)\//ims' => '\\1&\\2=',
		// 原find(where)调整成find()->where(where)->one()
		'/\-\>find\s*\(([^\)]+)\)/ims' => '->find ()->where (\\1)->one ()',
		// 原findAll(where)调整成find()->where(where)->all()
		'/\-\>findAll\s*\(([^\)]+)\)/ims' => '->find ()->where (\\1)->all ()',
		// count 替换成 find()->where()->count();
		'/\-\>count\s*\(([^\)]+)\)/ims' => '->find ()->where (\\1)->count ()',
		// deleteByPk转化成findOne()->delete();
		'/\-\>deleteByPk\s*\(([^\)]+)\)/ims' => '->findOne(\\1)->delete ()',
		// 页面url自动转化成全小写
		'/(?:\W)url\s*\(\s*\'[^\']+\'/ims' => function ($path, $content) {
			$content = preg_replace_callback ( '/url\s*\(\s*\'[^\']+\'/ims', '_strtolower', $content );
			return $content;
		},
		// 控制器,动作,只首字母大写,后半部分全部小写
		'/function\s*action(\w)\s*\(\s*\)/ims' => function ($path, $content) {
			$content = preg_replace_callback ( '/function\s*action(\w+)\s*\(\s*\)/ims', '_ucfirst', $content );
			return $content;
		} 
);
function _strtolower($matches) {
	return strtolower ( $matches [0] );
}
function _ucfirst($matches) {
	$action = strtolower ( $matches [1] );
	// actions 为过滤器配置,不进行过滤处理
	if ($action === 's') {
		return 'function actions()';
	}
	return 'function action' . ucfirst ( $action ) . '()';
}
function getModelsByControllerPath($path) {
	$models_dir = dirname ( dirname ( $path ) ) . '/models';
	$list = FileTools::getCurList ( $models_dir );
	if (! $list)
		return array ();
	$items = array ();
	foreach ( $list as $item ) {
		$filename = basename ( $item );
		$classname = str_replace ( '.php', '', $filename );
		$items [$classname] = $classname;
	}
	return $items;
}
function getViewsByControllerPath($path) {
	$ctlName = basename ( str_replace ( 'Controller.php', '', $path ) );
	$ctlName = strtolower ( $ctlName );
	$models_dir = dirname ( dirname ( $path ) ) . '/views/' . $ctlName;
	$list = FileTools::getCurList ( $models_dir );
	if (! $list)
		return array ();
	$items = array ();
	foreach ( $list as $item ) {
		$filename = basename ( $item );
		$classname = str_replace ( '.php', '', $filename );
		$items [$classname] = $item;
	}
	return $items;
}
function getNamespaceByPath($path) {
	global $root_path;
	$path = dirname ( $path );
	$path = preg_replace ( '/\//', '\\', $path );
	return str_replace ( $root_path, '', $path );
}
// sql 中,pow需要手动检查,替换成 power ,pi 需要手动检查,替换成 acos(-1)

$preg_file = array (
		'/^\w+Module\.php$/' => 'Module.php' 
);
FileTools::replacenames ( $path, $preg_file );
FileTools::replaceContents ( $path, $preg_contents, array (
		'/\.php$/i' => false,
		'/yiic/i' => true 
) );

//直接根据列表文件进行替换规则处理


 

模块自动转化工具

/yii2sys/_cover_tools/replacefile_to_yii2.php
通过配置指定目录,脚本可实现yii1绝大部分自动转换到yii2形式
主要区别
yii2补充命名空间
工具已实现页面和控制器,自动引入models相关类,model类和控制器自动追加命名空间
全局类引入,非常用类型,直接以\yii\xxx 或\ext\xxx形式带入,减少不必要引入代码
rbac
基本和yii1相同,但是一些数据字段名做了调整(如:adminid调整为admin_id,很多类似字段都补充了下划线)
ar的查询方式
通过find的形式返回ActiveQuery对象,通过对该对象进行操作实现查询
原 CDbCriteria 对象被取消,工具已实现常用查询类的自动转化,关联查询仍然需要进行手动补充调整.
关联查询,通过配置查询对象,实现分页,主要特殊在主表和子表重命名,具体看项目案例.
分页对象做了调整,默认$_GET[‘page’]不做为默认分页值,需要手动设置
系统结构调整
/system 原模块被整合到/app/modules/里
框架结构
/yii2framework
使用yii2高级模板,已做结构的调整同时项目yii2sys也做了调整,使现有结构形式按原来的部署方式
(注:有需要可自行下载原高级模板进行了解)
编辑器使用
为实现快速开发,所有有ar返回的函数,函数注释可以通过补充@return ActiveRecord 实现代码提示(需要在inlucde_path中引入框架做为附属项目)
主要修改点整理:
url() yii2fix module 默认会到根当成模块
导致顶根判断异常,所以需要跳过
自定义state
yii2已移除state,官方说法是避免混乱.导致无法同域名多应用session共享冲突
md5版本
可以实现端口共享session
域名+目录+程序id版本
实现多项目时,自动区分session数据
\Wechat 类第三方重写,可能类很多,编写时,统一全部认根域名.实现快速重写
HeadersAlreadySentException 异常
// ini_set ( ‘output_buffering’, 0 );设置无效,还是需要通过修改配置.不然超出会抛
__CLASS__ 会带入命名空间路径
php 7.2 下 upload 上传兼容 media data missing hint 异常
兼容方式为 curl_file_create 形式
Yii2 已 去除任意参数 路径化处理.
需要路径化的参数,只能通过配置实现. 前台除了控制器经过重写外,其它参数全部都以 ? 形式拼接
\yii\helpers\Html::DropDownList 不再自动代入id,需要通过htmloptions配置进去
CHtml 调整成 Html
CWenhaoPager.php 重写成 LinkPager 形式
反射类调用,需要写全命名空间
计划任务
表前缀 {{%table_name}} 多了个 “%”
入口环境 测试环境(保留) 开发环境 ,生产环境 切换
将/system 移动到 app/modules/system 做为 app 下的模块
Yii::$app->createUrl 换成 Yii::$app->urlManager->createUrl
CDbCriteria
$models = (new \app\modules\system\models\Menu ())->find ()->where ( “parentId=0 and state=1” )->orderBy ( ‘ordernum asc,id asc’ )->all ();
ar 修改成yii2形式,需要实现接口继承+覆盖几个内置函数
$model->getAttributes ( false ); false 去掉 ,默认动作只识别null
Yii::$app->request->isPostRequest 替换成 Yii::$app->request->post ()
Yii::$app->controller->getAction ()->getId () Yii::$app->controller->action->id
Yii::$app->controller->getId () Yii::$app->controller->id
Yii::$app->controller->getModule () Yii::$app->controller->module->id
controllers 控制器 render 类动作,需要返回. 变成 return $this->render…
enableCsrfValidation 全局.暂时csrf校验关闭,默认开启.动作在action之前
后台基本版 igd_system_adminauthassignment 表 userid 调整为 user_id (框架写死字段)
beforeAction 不能直接返回true,需要调用父函数,否则会导致过滤器无法触发
数据库权限判断 app ()->authManager->checkAccess ( $user_id, $permission ) 参数顺序变化
表重命名
AdminLog::model ()->find ()->alias ( ‘t’ );
表链接
AdminLog::model ()->find ()->innerJoinWith ( [
// 关联表,重命名
‘admin as a’
] )
http 异常抛出调整
throw new CHttpException ( 404, ‘The ‘ . __CLASS__ . ‘ does not exist.’ );
throw new \yii\web\HttpException ( 404, ‘The ‘ . __CLASS__ . ‘ does not exist.’ );
动态表限制实在太多.后续有需要,需要通过ArticleX->fixTableName进行修正
原find(where)调整成find()->where(where)->one()
原findAll(where)调整成find()->where(where)->all()

解决 yii oracle date 类型 默认值 bug

主要问题出在yii对这种类型的读取和保存都有异常,会丢失时间.

解决方式

一、设置当前会话的日期格式,在beforeSave里调用

 alter session set nls_date_format = 'yyyy-mm-dd hh24:mi:ss'

二、ar保存时,使用

			if(isset($this->create_time)) 
				$this->create_time=new CDbExpression('create_time');

误区

因为时间格式默认不同于 Y-m-d H:i:s 所以bindValue时,oci当成了字符串。。。哥绕了一圈去修改框架,血的教训啊。。我擦。。。

当格式统一后,bindValue 就保持有效了。可以不使用方式二那种蹩脚的处理方式

修复Yii ar 在win环境下 oci_pconnect 访问慢问题

同样的代码,在linux 环境下的 apache访问速度就正常,一样是链接外部数据库。

在开发环境中win(php5.2)链接测试机linux数据库访问里,速度会慢。还是用oci_pconnect的情况下.

折腾过php的不同版本 5.4 ts nts 这些版本。速度依旧烂。

测试机内部apache访问时,速度不慢。觉得是出在win上。

这块无解。尝试分析各sql访问上的速度问题.

log 补充 trace 打印出sql访问日志,单条进行数据分析

				'log' => array (
						'class' => 'CLogRouter',
						'routes' => array (
								array (
										'class' => 'CFileLogRoute',
										'levels' => 'trace, error, warning'
								)
						) 
				),

 

发现ar 在获取结构时,一次要花费约1s左右。

其它访问速度正常。

ar开启缓存。第一次访问时较慢,后续访问均明显变快,和原来mysql环境下差不多。

'db' => array (
              'class'=>'ext.oci8Pdo.OciDbConnection',
				'connectionString' => 'oci:dbname=//xxx/xx;charset=UTF8;',
				// 开启表结构缓存(schema caching)提高性能
				'schemaCachingDuration'=>3600,
		),

 

yii kindeditor sessionid丢失问题

主要原因是kindeditor 中使用了swfupload ,而ff调用时,不会使用浏览器当前的cookie。

chrome也尝试过,通过切换不同的flash,会导致该情况发生。

如果flash上传不法传递cookie,则通过post该参数,并替换成原来的session即可.

http://kindeditor.net/docs/option.html#extrafileuploadparams

KindEditor.ready(function(K) {
        K.create('#id', {
                extraFileUploadParams : {
                        GDSESSION_ID : 'XXXXXXXXXXX'
                }
        });
});

yii 端根据session_id切换session代码

/**
 * 直接识别文件类型,修改上传文件扩展,防止缩略变形
 */
class EditorController extends CController {

	public function init() {
		// 切换session 修复部分不支持 flash cookie的浏览器,暂时发现ff
		if (isset ( $_REQUEST ['GDSESSION_ID'] ) && $_REQUEST ['GDSESSION_ID'] != Yii::app ()->session->getSessionID ()) {
			Yii::app ()->session->destroy ();
			Yii::app ()->session->setSessionID ( $_REQUEST ['GDSESSION_ID'] );
			// Yii::app ()->session->open ();
		}
	}
....

 

 

YII mysql 迁移 oracle 小写处理

因为框架内联绝大多数代码是未进行引号包裹的。

oracle 纯sql的情况下,默认当做大写,影响很大。只能统一数据库和字段,表名大写转移。

Ar 兼容小写处理
编写 CActiveRecordO 集成 CActiveRecord

修改以下函数进行小写兼容

$model->field

public function __get($name) {
		$upname = strtoupper ( $name );
		if (isset ( $this->_attributes [$upname] )) {
			return $this->_attributes [$upname];
		} elseif (isset ( $this->getMetaData ()->columns [$upname] ))
			return null;
		elseif (isset ( $this->_related [$name] ))
			return $this->_related [$name];
		elseif (isset ( $this->getMetaData ()->relations [$name] ))
			return $this->getRelated ( $name );
		else
			return parent::__get ( $name );
	}

 

$model->field=val;

public function __set($name, $value) {
		$upname = strtoupper ( $name );
		if (($this->setAttribute ( $name, $value ) === false && $this->setAttribute ( $upname, $value ) === false)) {
			if (isset ( $this->getMetaData ()->relations [$name] ))
				$this->_related [$name] = $value;
			else
				parent::__set ( $name, $value );
		}
		$name = $upname;
	}

 

empty($model->field) //判断

public function __isset($name) {
		$upname = strtoupper ( $name );
		if (isset ( $this->_attributes [$upname] ))
			return true;
		elseif (isset ( $this->getMetaData ()->columns [$upname] ))
			return false;
		elseif (isset ( $this->_related [$name] ))
			return true;
		elseif (isset ( $this->getMetaData ()->relations [$name] ))
			return $this->getRelated ( $name ) !== null;
		else
			return parent::__isset ( $name );
	}

默认 getAttributes setAttributes 抱成别的函数进行重写,因为内部调用太多地方,不可直接重写该函数,另外编写函数getAttributesWithO 之类的即可

	public function getAttributesWithO($names = true) {
		$attributes = $this->getAttributes ( $names );
		$attributes = array_change_key_case ( $attributes, CASE_LOWER );
		return $attributes;
	}
	public function setAttributesWithO($attributes = array(), $safeOnly = true, $hasClob = false) {
		$attributes = array_change_key_case ( $attributes, CASE_UPPER );
		if ($hasClob && $this->getMetaData ()->columns && is_array ( $this->getMetaData ()->columns )) {
			foreach ( $this->getMetaData ()->columns as $key => $column ) {
				if (stristr ( $column->dbType, "CLOB" ) && empty ( $attributes [$key] )) {
					$attributes [$key] = '';
				}
			}
		}
		$this->setAttributes ( $attributes, $safeOnly );
	}
	public function getClobByKey($key) {
		if (! empty ( $this->$key ) && is_object ( $this->$key ) && get_class ( $this->$key ) == 'OCI-Lob') {
			$this->$key = $this->$key->read ( 50000 );
		}
	}

pdo 对clob各种奇葩问题。需要调整pdo为oci8相关处理

http://www.yiiframework.com/extension/oci8pdo/

	public function afterFind() {
		$this->getClobByKey ( 'description' );
		return true;
	}

内部组件需要进行特殊处理。

主要出现在需要调用数据库实现相关功能的组件,如:CDbAuthManager,CDbHttpSession

重写相关即可

主要为大小写冲突问题。

public function hasItemChild($itemName,$childName)
	{
		return $this->db->createCommand()
			->select('parent')
			->from($this->itemChildTable)
			->where('parent=:parent AND child=:child', array(
				':parent'=>$itemName,
				':child'=>$childName))
			->queryScalar() !== false;
	}

select (‘parent’) 会返回 select “parent” 导致大小写异常,where parent =又是小写处理,纠结吧。。。

所以只能将被小写了的地方进行大写转换处理。

将被大写返回的数组,需要进行小写转换下。

		$row=array_change_key_case ( $row, CASE_LOWER );

oci 修复个setFetchModel的小异常问题,兼容有点问题,不过只在权限判断中出现,其它暂时未发现

http://www.php.net/manual/en/pdostatement.setfetchmode.php

public function setFetchMode($mode, $colClassOrObj = null, array $ctorArgs = array()) {
		// yii 内部调用
// 		parent::setFetchMode($mode,$colClassOrObj,$ctorArgs);
// 		return true;
		if ($mode == PDO::FETCH_COLUMN||$mode==PDO::FETCH_ASSOC) {
			$this->_fetchMode = $mode;
			return true;
		}
		// 52: $this->_statement->setFetchMode(PDO::FETCH_ASSOC);
		if ($colClassOrObj !== null || ! empty ( $ctorArgs )) {
			throw new PDOException ( 'Second and third parameters are not implemented for Oci8PDO_Statement::setFetchMode()' );
			// see http://www.php.net/manual/en/pdostatement.setfetchmode.php
		}
		$this->_fetchMode = $mode;
		return true;
	}

 

 

 

YII MYSQL 转移至 Oracle

1.字段大小写有所不同,特别注意,mysql不区分在小写,oracle区分大小,如果使用小写需要用“”进行引起。如yii ar 自定义t,在相关使用时,需要用“t”进行表示。

		$criteria->order .= '"t".ID desc';

		$models = AdminLog::model ()->with ( 'admin' )->findAll ( $criteria );

2.逻辑上,分组排序不同于mysql,需要用

		$criteria->select = "ICON,ROW_NUMBER () OVER (
		PARTITION BY ICON
		ORDER BY
			ordernum asc
	)";
		// $criteria->group = "ICON";
		$models = Menu::model ()->findAll ( $criteria );

3.主键自增也有所不同,凡原来涉及insert  null操作相关地方,需要用OracleTools::getNewId()获取id进行替换,现在使用通用序列,后需特殊字段,需要进行特殊处理

	protected function beforeSave() {
		$this->ID = OracleTools::getNewId ();
		if ($this->hasEventHandler ( 'onBeforeSave' )) {
			$event = new CModelEvent ( $this );
			$this->onBeforeSave ( $event );
			return $event->isValid;
		} else
			return true;
	}

 

<?php
/**
 * 
 * 现在简单实现,可以处理成,根据对应的表,生成对应的序列进行生成,主要同步自增id
 *
 */
class OracleTools {
	const TABLE_COMMOON = "seq_common";
	/**
	 * 通过序列获取最新的id
	 */
	public static function getNewId($tablename = "seq_common") {
		$id = sql_fetch ( "SELECT seq_common.nextVal newid FROM dual", 'newid' );
		return $id;
	}
}

 

yii 跨mysql & oracle 实现同表数据同步解决方案

只限数据,不涉及其它。

oracle 数据库ar使用

参考:http://blog.martoo.cn/?p=459

oracle 编码注意事项

编码需要在connectionString 后指定 .charset

直接配置charset 无效..

		'odb'=>array(
			'class'=>'CDbConnection',
            'connectionString'=>'oci:dbname=//xxxxxxx/xxx;charset=utf8;',
            'username'=>'xxxx',
            'password'=>'xxxx',
         	'charset'=>'utf8',
            'tablePrefix'=>'xxx_'
		),

 

需要明白一个点。任何业务流程操作后,最终只在数据这块同步。

所以yii 原ar的修改只需要在save 动作之后,同步数据。

即afterSave 事件处理.

demo:

	
	public function afterSave(){
		if($this->hasEventHandler('onAfterSave'))
			$this->onAfterSave(new CEvent($this));
		$omodel= OUser::model()->findByPk($this->getPrimaryKey());
		if(!$omodel){
			$omodel=new OUser();
		}else{
		}
                //因为大小写问题。这里将key $item=array_change_key_case($this->getAttributes(false), CASE_UPPER); 进行该处理
		$omodel->setAttributes($this->getAttributes(false),false);
		$omodel->save();
	}

 

补充yii 相关版本插入异常问题:

QQ Photo20140318091839

 

转发请注明出处http://blog.martoo.cn
如有漏缺,请联系我 QQ 243008827

AR 使用其它数据库进行操作

config.php里加多配置

'db2'=>array(
			'class'=>'CDbConnection',
		),

AR中覆盖方法,设置其它的数据库

特别需要注意 $odb这个独立的处理,否则在连续操作的过层中,会有数据库链接变量冲突的相关问题。

	public  static $odb=null;
        public function getDbConnection()
	{
		if(self::$odb!==null)
			return self::$odb;
		else
		{
			self::$odb=Yii::app()->getComponent('db2');
			if(self::$odb instanceof CDbConnection)
				return self::$odb;
			else
				throw new CDbException(Yii::t('yii','Active Record requires a "db" CDbConnection application component.'));
		}
	}

转发请注明出处http://blog.martoo.cn
如有漏缺,请联系我 QQ 243008827

YII 异常之伪装和捕获

公司之前做的一个基于YII的系统让别人去检测,结果各种有的没的挑毛病。

因为这个原因,考虑到系统的健壮性,需要对这些企图通过各种暴力破解的鸟蛋进行隔离。

实现流程思考:

为了不让小黑察觉,又不影响用户。考虑伪装系统原来的提示方式。然后如果限制的时间内,达到某个值。封ip(当然是n小时什么的不能访问啦)。

再研究系统默认异常处理机制。因为一个合格的框架这个最基本的处理浏览肯定是有滴。虽然平时有看源代码,但整体把握肯定是没有绝对到位的。不会不要紧。google下。

yii error handle 果然一堆。

配置如下:

'components'=>array(
		'errorHandler' => array(
            'errorAction' => 'errormsg/error', 
         ),)

伪装提示页面(这个简单研究下yii的源代码,猜到在哪处理):

Y(^_^)Y 直接复制yii的东西来用,一点都不用改:

framework1.1.10viewszh_cn 下的就是国际化的提示页面。咋就简单提取。

Controller操作:

function actionError() {
		$error = Yii::app ()->errorHandler->error;
		if ($error) {
			$error=Errorinfo::model()->record($error);
			if($error['code']=='500'){
				die("系统繁忙中!");
			}
			$this->renderPartial( 'error'.$error['code'], array (
					'data' => $error
			) );
		} else {
			js_alert ( "error page", "", url ( 'site/index' ) );
		}

	}

强化升级:

1. 考虑文件的读取,如 图片,脚本,样式文件等这些,会造成异常的值偏高。所以要根据相关的后缀进行过滤。当然,为了防止别人是在暴力查询控制器的函数。所以最后限制下目录前缀。

转发请注明出处http://blog.martoo.cn
如有漏缺,请联系我 QQ 243008827