/** * 构造函数 * * @param array $config * * @return Log */ function __construct(array $config = null) { $names = array('EMERG' => self::EMERG, 'ALERT' => self::ALERT, 'CRIT' => self::CRIT, 'ERR' => self::ERR, 'WARN' => self::WARN, 'NOTICE' => self::NOTICE, 'INFO' => self::INFO, 'DEBUG' => self::DEBUG); $arr = isset($config['log_priorities']) ? Q::normalize($config['log_priorities']) : Q::normalize(Q::getIni('log_priorities')); $this->_priorities = array('index' => array(), 'names' => array()); foreach ($arr as $item) { if (isset($names[$item])) { $this->_priorities['index'][$names[$item]] = $item; $this->_priorities['names'][$item] = $names[$item]; } } $dir_tmp = isset($config['log_writer_dir']) ? $config['log_writer_dir'] : Q::getIni('log_writer_dir'); $dir = realpath($dir_tmp); if (empty($dir)) { $dir = realpath(Q::getIni('runtime_cache_dir')); if (empty($dir)) { // LC_MSG: 指定的日志文件保存目录不存在 "%s". throw new QLog_Exception(__('指定的日志文件保存目录不存在 "%s".', $dir_tmp)); } } $filename = isset($config['log_writer_filename']) ? $config['log_writer_filename'] : Q::getIni('log_writer_filename'); $this->_filename = rtrim($dir, '/\\') . DS . $filename; $chunk_size = isset($config['log_cache_chunk_size']) ? intval($config['log_cache_chunk_size']) : intval(Q::getIni('log_cache_chunk_size')); $this->_cache_chunk_size = $chunk_size * 1024; $this->append(__METHOD__, self::DEBUG); }
function UserAclRoles($user_id = '') { $show_box['title'] = '获取用户全部角色'; $return_value = ''; $roles_idname = array(); $roles_id = array(); $sp_roles = Q::ini('appini/sp_role'); // 第一步:直接从中间表获得用户的全部角色ID $user_roles = UsersHaveRoles::find('user_id = ?', intval($user_id))->asArray()->getAll(); //dump($user_roles); // 取出有用的ID,去除deny的ID foreach ($user_roles as $value) { if ($value['is_include']) { $roles_id[] = $value['role_id']; } } //dump ( $roles_id); $roles_arr = Roles::find('role_id in (?)', Q::normalize($roles_id, ","))->asArray()->getAll(); foreach ($roles_arr as $value) { $roles_idname[$value['role_id']] = $value['rolename']; } //dump($roles_idname); if (in_array($sp_roles['REPEAL'], $roles_idname)) { $return_value = array($value['role_id'] => $sp_roles['REPEAL']); return $return_value; } elseif (in_array($sp_roles['FREEZE'], $roles_idname)) { $return_value = array($value['role_id'] => $sp_roles['FREEZE']); return $return_value; } elseif (in_array($sp_roles['UNCHECKED'], $roles_idname)) { $return_value = array($value['role_id'] => $sp_roles['UNCHECKED']); return $return_value; } else { return $roles_idname; } }
/** * @dataProvider normalizeProvider */ function testNormalize($items, $test) { $data = array('key1', 'key2', 'key3'); if ($test) { $this->assertEquals($data, Q::normalize($items, $test)); } else { $this->assertEquals($data, Q::normalize($items)); } }
/** * 允许上传的文件类型 * * @param stirng $types * @return array */ function uploadAllowedTypes($types = null) { if ($types) { $this->_upload_allowed_types = Q::normalize($types); return $this; } else { return $this->_upload_allowed_types; } }
function setTags($tags) { if (is_array($tags) || $tags instanceof Iterator) { $arr = array(); foreach ($tags as $tag) { $arr[] = $tag->label; } $tags = $arr; } return implode(' ', Q::normalize($tags, ' ')); }
static function createForm($id, $action) { $form = new Form_Admin_Links($id, $action); $form->add(QForm::ELEMENT, 'name', array('_ui' => 'textbox', '_label' => '友情链接名称', '_req' => true, '_tips' => '友情链接的网站名'))->add(QForm::ELEMENT, 'url', array('_ui' => 'textbox', '_label' => '网站地址', '_req' => true, '_tips' => '友情链接网站的网址'))->add(QForm::ELEMENT, 'link_id', array('_ui' => 'hidden'))->add(QForm::ELEMENT, 'img', array('_ui' => 'admin_picpreview', '_label' => '已上传图片', 'attr' => array('dir' => 'links')))->add(QForm::ELEMENT, 'ico', array('_ui' => 'upload', '_label' => '上传图标', '_tips' => ''))->add(QForm::ELEMENT, 'order_num', array('_ui' => 'textbox', '_label' => '排序', 'value' => 0)); $form->addValidations(Links::meta()); //上传图片的限制 $types = Q::normalize(Q::ini('appini/teapics/upload_allowed_types')); $size = intval(Q::ini('appini/teapics/upload_allowed_size') * 1024); $dim = Q::ini('appini/teapics/img_pic_width') . 'x' . Q::ini('appini/teapics/img_pic_height'); $form['ico']->_tips = sprintf($form['ico']->_tips, implode('/', $types), $size / 1024, $dim); $form->selectUploadElement('ico')->uploadAllowedSize($size)->uploadAllowedTypes($types)->enableSkipUpload(); //表单验证规则 return $form; }
/** * 从配置文件中创建表单 * @param string $action 表单的url * @param string $configName 配置文件的名字 * @return Form_Tea $form */ protected static function _createFormConfig($action, $configName) { $form = new Form_Admin_Tea('tea_form', $action); $filename = rtrim(dirname(__FILE__), '/\\') . DS . $configName; $form->loadFromConfig(Helper_YAML::load($filename)); $form->addValidations(Tea::meta()); //图片的路径 $form['thumb_filename']->dir = Q::ini('appini/teapics/img_dir'); //上传图片的限制 $types = Q::normalize(Q::ini('appini/teapics/upload_allowed_types')); $size = intval(Q::ini('appini/teapics/upload_allowed_size') * 1024); $dim = Q::ini('appini/teapics/img_pic_width') . 'x' . Q::ini('appini/teapics/img_pic_height'); $form['postfile']->_tips = sprintf($form['postfile']->_tips, implode('/', $types), $size / 1024, $dim); // 茶叶类别 // $teatype = new TeaType(); // $form['type_id']->items=$teatype->list; $form->selectUploadElement('postfile')->uploadAllowedSize($size)->uploadAllowedTypes($types)->enableSkipUpload(); return $form; }
/** * 绑定行为插件 */ function bind() { $props = Q::normalize($this->_settings['assoc_props']); foreach ($props as $prop_name) { if (empty($this->_meta->props[$prop_name]) || !$this->_meta->props[$prop_name]['assoc']) { // LC_MSG: 类 "%s" 的 "%s" 属性不是一个关联. throw new QDB_ActiveRecord_Behavior_Exception(__('类 "%s" 的 "%s" 属性不是一个关联.', $this->_meta->class_name, $prop_name)); } $assoc_type = $this->_meta->props[$prop_name]['assoc']; $suffix = $this->_meta->props[$prop_name]['assoc_class']; $arg = array($prop_name); if ($assoc_type == QDB::HAS_MANY) { $this->_addDynamicMethod("add{$suffix}", array($this, 'addRelated'), $arg); } elseif ($assoc_type == QDB::MANY_TO_MANY) { $this->_addDynamicMethod("bind{$suffix}", array($this, 'bindRelated'), $arg); $this->_addDynamicMethod("unbind{$suffix}", array($this, 'unbindRelated'), $arg); $this->_addDynamicMethod("unbindAll{$suffix}", array($this, 'unbindAllRelated'), $arg); } } }
protected function _checkUniqueness(QDB_ActiveRecord_Abstract $obj, QDB_Cond $more_cond = null, $ignore_id = false) { $check_props = Q::normalize($this->_settings['check_props']); if (empty($check_props)) { return; } $checks = array(); $error = array(); foreach ($check_props as $check) { if ($ignore_id && $check == $obj->idname()) { continue; } if (strpos($check, '+') !== false) { $props = Q::normalize($check, '+'); $cond = array(); foreach ($props as $prop_name) { $cond[$prop_name] = $obj->{$prop_name}; } } else { $cond = array($check => $obj->{$check}); $props = $check; } if (!is_null($more_cond)) { $cond[] = $more_cond; } $test = $this->_meta->find($cond)->count()->query(); if ($test['row_count'] < 1) { continue; } if (isset($this->_settings['error_messages'][$check])) { $error[$check] = array($check => $this->_settings['error_messages'][$check]); } else { $error[$check] = array($check => "{$check} duplicated"); } } if (!empty($error)) { throw new QDB_ActiveRecord_ValidateFailedException($error, $obj); } }
protected function _processMeta($meta, $allows_tags = null) { if ($allows_tags) { $allows_tags = Q::normalize($allows_tags); } $tags = preg_split('/^\\s*@/m', $meta, -1, PREG_SPLIT_NO_EMPTY); $arr = array(); foreach ($tags as $tag) { $segs = preg_split('/\\s+/', trim($tag), 2); $tag_name = $segs[0]; $param = isset($segs[1]) ? trim($segs[1]) : ''; if ($allows_tags && !in_array($tag_name, $allows_tags)) { continue; } $tag_method = '_processTag' . ucfirst($tag_name); if (method_exists($this, $tag_method)) { $this->{$tag_method}($param); } elseif (property_exists($this, $tag_name)) { $this->{$tag_name} = $param; } else { throw new API_Doc_NotSupportedTagException($this, $tag_name); } } }
/** * 获得用户的 ACL 数据 * * ACL 数据一般包含用户 ID 和用户名,通常用于配合 QACL 实现基于角色的访问控制。 * * 用法: * @code php * $data = $member->aclData(); * dump($data); * @endcode * * 要返回的数据由 acl_data_props 设置来指定。不过不管指定了哪些属性,aclData() * 的返回结果中总是为包含名为 id 的键。该键的值是用户对象的 ID。 * * 也可以在获得 ACL 数据时通过 $props 参数指定要返回的属性值: * @code php * $data = $member->aclData('email, addr'); * @endcode * * @param QDB_ActiveRecord_Abstract $member 用户对象 * @param string $props 要返回的属性值 * * @return array 包含指定属性值的数组 */ function aclDataDyn(QDB_ActiveRecord_Abstract $member, $props = null) { if (!$props) { $props = $this->_settings['acl_data_props']; } $props = Q::normalize($props); $data = array(); foreach ($props as $pn) { $data[$pn] = $member[$pn]; } $data['id'] = $member->id(); return $data; }
/** * 对 ACL 整理,返回整理结果 * * @param array $acl 要整理的 ACL * * @return array */ function normalize(array $acl) { $acl = array_change_key_case($acl, CASE_LOWER); $ret = array(); $keys = array('allow', 'deny'); foreach ($keys as $key) { do { if (!isset($acl[$key])) { $values = self::ACL_NULL; break; } $acl[$key] = strtolower($acl[$key]); if ($acl[$key] == self::ACL_EVERYONE || $acl[$key] == self::ACL_HAS_ROLE || $acl[$key] == self::ACL_NO_ROLE || $acl[$key] == self::ACL_NULL) { $values = $acl[$key]; break; } $values = Q::normalize($acl[$key]); if (empty($values)) { $values = self::ACL_NULL; } } while (false); $ret[$key] = $values; } return $ret; }
/** * 将缓存的日志信息写入实际存储,并清空缓存 */ function flush() { if (empty($this->_log)) { return; } // 更新日志记录优先级 $keys = Q::normalize(Q::ini('log_priorities')); $arr = array(); foreach ($keys as $key) { if (!isset($this->_priorities[$key])) { continue; } $arr[$key] = true; } $this->_priorities = $arr; // 确定日志写入目录 $dir = realpath(Q::ini('log_writer_dir')); if ($dir === false || empty($dir)) { $dir = realpath(Q::ini('runtime_cache_dir')); if ($dir === false || empty($dir)) { // LC_MSG: 指定的日志文件保存目录不存在 "%s". if ($this->_destruct) { return; } else { throw new QLog_Exception(__('指定的日志文件保存目录不存在 "%s".', Q::ini('log_writer_dir'))); } } } $filename = Q::ini('log_writer_filename'); $this->_filename = rtrim($dir, '/\\') . DS . $filename; //if(is_file($this->_filename)) //unlink($this->_filename); $chunk_size = intval(Q::ini('log_cache_chunk_size')); if ($chunk_size < 1) { $chunk_size = 64; } $this->_cache_chunk_size = $chunk_size * 1024; $this->_writeable = true; // 写入日志 $string = ''; foreach ($this->_log as $offset => $item) { list($time, $msg, $type) = $item; unset($this->_log[$offset]); // 过滤掉不需要的日志条目 if (!isset($this->_priorities[$type])) { continue; } $string .= date('c', $time) . " {$type}: {$msg}\n"; } if ($string) { $fp = fopen($this->_filename, 'a'); if ($fp && flock($fp, LOCK_EX)) { fwrite($fp, $string); flock($fp, LOCK_UN); fclose($fp); } } //unset($this->_log); $this->_log = array(); $this->_cached_size = 0; }
/** * 删除多个属性 * * @param string|array $props * * @return QForm_Element */ function unsetProps($props) { $props = Q::normalize($props); foreach ($props as $prop) { unset($this->_props[$prop]); } if (in_array('value', $props)) { $this->setValue(null); } return $this; }
/** * 第一步初始化 * * @param string $class */ protected function _init1($class) { // 从指定类获得初步的定义信息 Q::loadClass($class); $this->class_name = $class; $ref = (array) call_user_func(array($class, '__define')); /** * 检查是否是继承 */ if (!empty($ref['inherit'])) { $this->inherit_base_class = $ref['inherit']; /** * 继承类的 __define() 方法只需要指定与父类不同的内容 */ $base_ref = (array) call_user_func(array($this->inherit_base_class, '__define')); $ref = array_merge_recursive($base_ref, $ref); } // 被继承的类 $this->inherit_type_field = !empty($ref['inherit_type_field']) ? $ref['inherit_type_field'] : null; // 设置表数据入口对象 $table_config = !empty($ref['table_config']) ? (array) $ref['table_config'] : array(); if (!empty($ref['table_class'])) { $this->table = $this->_tableByClass($ref['table_class'], $table_config); } else { $this->table = $this->_tableByName($ref['table_name'], $table_config); } $this->table_meta = $this->table->columns(); // 根据字段定义确定字段属性 if (empty($ref['props']) || !is_array($ref['props'])) { $ref['props'] = array(); } foreach ($ref['props'] as $prop_name => $config) { $this->addProp($prop_name, $config); } // 将没有指定的字段也设置为对象属性 foreach ($this->table_meta as $prop_name => $field) { if (isset($this->props2fields[$prop_name])) { continue; } $this->addProp($prop_name, $field); } // 设置其他选项 if (!empty($ref['create_reject'])) { $this->create_reject = array_flip(Q::normalize($ref['create_reject'])); } if (!empty($ref['update_reject'])) { $this->update_reject = array_flip(Q::normalize($ref['update_reject'])); } if (!empty($ref['create_autofill']) && is_array($ref['create_autofill'])) { $this->create_autofill = $ref['create_autofill']; } if (!empty($ref['update_autofill']) && is_array($ref['update_autofill'])) { $this->update_autofill = $ref['update_autofill']; } if (!empty($ref['attr_accessible'])) { $this->attr_accessible = array_flip(Q::normalize($ref['attr_accessible'])); } if (!empty($ref['attr_protected'])) { $this->attr_protected = array_flip(Q::normalize($ref['attr_protected'])); } // 准备验证规则 if (empty($ref['validations']) || !is_array($ref['validations'])) { $ref['validations'] = array(); } $this->validations = $this->_prepareValidationRules($ref['validations']); // 设置对象 ID 属性名 $pk = $this->table->getPK(); $this->idname = array(); foreach ($this->table->getPK() as $pk) { $pn = $this->fields2props[$pk]; $this->idname[$pn] = $pn; } $this->idname_count = count($this->idname); // 绑定行为插件 if (isset($ref['behaviors'])) { $config = isset($ref['behaviors_settings']) ? $ref['behaviors_settings'] : array(); $this->bindBehaviors($ref['behaviors'], $config); } }
function excludes($excludes) { $this->_excludes = Q::normalize($excludes); return $this; }
/** * 获得多个完全限定名 * * @param array|string $names * @param string $as * * @return array */ function qids($names, $as = null) { $arr = array(); $names = Q::normalize($names); foreach ($names as $alias => $name) { if (!is_string($alias)) { $alias = null; } $arr[] = $this->qid($name, $alias, $as); } return $arr; }
/** * 查询,并返回对象或对象集合 * * @return QDB_ActiveRecord_Association_Coll|QDB_ActiveRecord_Abstract */ protected function _queryObjects() { /** * 执行查询,获得一个查询句柄 * * $this->_query_params[self::USED_LINKS] 是查询涉及到的关联(关联别名 => 关联对象) */ $handle = $this->getQueryHandle(); /* @var $handle QDB_Result_Abstract */ $class_name = $this->_meta->class_name; $rowset = array(); $this->_query_params[self::USED_LINKS] = $this->_query_params[self::USED_LINKS]; $no_lazy_query = Q::normalize($this->_query_params[self::NON_LAZY_QUERY]); while ($row = $handle->fetchRow()) { $obj = new $class_name($row, QDB::FIELD, true); foreach ($no_lazy_query as $assoc) { $obj->{$assoc}; } $rowset[] = $obj; } if (empty($rowset)) { // 没有查询到数据时,返回 Null 对象或空集合 if (!$this->_parts[self::LIMIT_QUERY]) { return $this->_meta->newObject(); } else { if ($this->_query_params[self::AS_COLL]) { return new QDB_ActiveRecord_Association_Coll($this->_meta->class_name); } else { return array(); } } } if (!$this->_parts[self::LIMIT_QUERY]) { // 创建一个单独的对象 return reset($rowset); } else { if ($this->_query_params[self::AS_COLL]) { return QDB_ActiveRecord_Association_Coll::createFromArray($rowset, $class_name); } else { return $rowset; } } }
/** * 检查唯一性 * * @param QDB_ActiveRecord_Abstract $obj * @param QDB_Cond $more_cond * @param boolean $ignore_id */ private function _checkUniqueness(QDB_ActiveRecord_Abstract $obj, QDB_Cond $more_cond = null, $ignore_id = false) { $check_props = Q::normalize($this->_settings['check_props']); if (empty($check_props)) { return; } $failed = array(); foreach ($check_props as $check) { // 如果忽略主键字段,而要检查的属性正好是主键字段,则跳过检查 if ($ignore_id && in_array($check, $this->_meta->idname)) { continue; } if (strpos($check, '+') !== false) { $check = Q::normalize($check, '+'); } else { $check = array($check); } $cond = array(); $skip_valid = false; // FIXED! 标注是否跳过验证 foreach ($check as $offset => $prop_name) { if (substr($prop_name, -1, 1) == '?') { $prop_name = substr($prop_name, 0, -1); $value = $obj[$prop_name]; if (strlen($value) == 0) { $skip_valid = true; continue; } $check[$offset] = $prop_name; } else { $value = $obj[$prop_name]; } if (is_object($value)) { $value = $value->id(); if (is_array($value)) { foreach ($value as $p => $v) { $cond[$p] = $v; } } else { $cond[$obj[$prop_name]->idname()] = $value; } } else { $cond[$prop_name] = $value; } } if ($skip_valid) { continue; } if (!is_null($more_cond)) { $cond[] = $more_cond; } if ($this->_meta->find($cond)->getCount() < 1) { continue; } // 验证失败,保存错误消息 $check = implode('+', $check); if (isset($this->_settings['error_messages'][$check])) { $failed[$check] = $this->_settings['error_messages'][$check]; } else { $failed[$check] = "{$check} duplicated"; } } if (!empty($failed)) { throw new QDB_ActiveRecord_ValidateFailedException($failed, $obj); } }
/** * 检查指定角色是否有权限访问特定的控制器和动作 * * @param array $roles * @param string|array $udi * * @return boolean */ function authorizedUDI($roles, $udi) { /** * 将 UDI 封装为一个资源 * 读取控制器的 ACL(访问控制列表) * 通过 QACL 组件进行权限检查 */ $roles = Q::normalize($roles); $udi = QContext::instance()->normalizeUDI($udi); $controller_acl = $this->controllerACL($udi); // 首先检查动作 ACT $acl = Q::singleton('QACL'); $action_name = strtolower($udi[QContext::UDI_ACTION]); if (isset($controller_acl['actions'][$action_name])) { // 如果动作的 ACT 检验通过,则忽略控制器的 ACT return $acl->rolesBasedCheck($roles, $controller_acl['actions'][$action_name]); } if (isset($controller_acl['actions'][QACL::ALL_ACTIONS])) { // 如果为所有动作指定了默认 ACT,则使用该 ACT 进行检查 return $acl->rolesBasedCheck($roles, $controller_acl['actions'][QACL::ALL_ACTIONS]); } // 否则检查是否可以访问指定控制器 return $acl->rolesBasedCheck($roles, $controller_acl); }
/** * 检查上传的文件是否成功上传,并符合检查条件(文件类型、最大尺寸) * * 文件类型以扩展名为准,多个扩展名以 , 分割,例如 “jpg, jpeg, png。”。 * * 用法: * @code * // 检查文件类型和大小 * if ($file->isValid('jpg, jpeg, png', 2048 * 1024)) * { * .... * } * @endcode * * @param string $allowed_types 允许的扩展名 * @param int $max_size 允许的最大上传字节数 * * @return boolean 是否检查通过 */ function isValid($allowed_types = null, $max_size = null) { if (!$this->isSuccessed()) { return false; } if ($allowed_types) { $allowed_types = Q::normalize($allowed_types); foreach ($allowed_types as $offset => $extname) { if ($extname[0] == '.') { $extname = substr($extname, 1); } $allowed_types[$offset] = strtolower($extname); } $allowed_types = array_flip($allowed_types); // when upload filename is chinese, basename() only return extension name, it will be make mistake follow step //$filename = strtolower(basename($this->filename())); $filename = $this->filename(); // jerry2801 $extnames = Q::normalize($filename, '.'); array_shift($extnames); $passed = false; for ($i = count($extnames) - 1; $i >= 0; $i--) { $checking_ext = implode('.', array_slice($extnames, $i)); if (isset($allowed_types[$checking_ext])) { $passed = true; break; } } if (!$passed) { return false; } } if ($max_size > 0 && $this->filesize() > $max_size) { return false; } return true; }
/** * 在指定目录及其子目录中查找文件 * * 用法: * @code php * $files = Helper_FileSys::findFiles($dir, array( * // 只查找扩展名为 .jpg, .jpeg, .png 和 .gif 的文件 * 'extnames' => 'jpg, jpeg, png, gif', * // 排除 .svn 目录和 upload/avatars 目录 * 'excludes' => '.svn, upload/avatars', * // 只查找 2 层子目录 * 'level' => 2, * )); * @endcode * * findFiles() 的 $options 参数支持下列选项: * * - extnames: 字符串或数组,指定查找文件时有效的文件扩展名。 * 如果不指定该参数,则查找所有扩展名的文件。 * * - excludes: 字符串或数组,指定查找文件时要排除的目录或文件。 * "upload/avatars" 表示排除 "$dir/upload/avatars" 目录。 * * - levels: 整数,指定查找的目录深度,默认为 -1。 * 如果为 0 表示不查找子目录。 * * findFiles() 返回一个数组,包含了排序后的文件完整路径。 * * @param string|array $dir 要查找文件的目录 * @param array $options 查找选项 * * @return array 包含有效文件名的数组 */ static function findFiles($dir, $options = array()) { $extnames = !empty($options['extnames']) ? Q::normalize($options['extnames']) : array(); foreach ($extnames as $offset => $extname) { if ($extname[0] == '.') { $extnames[$offset] = substr($extname, 1); } } $excludes = !empty($options['excludes']) ? Q::normalize($options['excludes']) : array(); $level = isset($options['level']) ? intval($options['level']) : -1; $list = self::_findFilesRecursive($dir, '', $extnames, $excludes, $level); sort($list); return $list; }
/** * 指示群组的数据为无效状态 * * @param mixed $error * * @return QForm_Group */ function invalidate($error = null) { if (empty($error)) { return $this; } if ($error instanceof QValidator_ValidateFailedException) { $errors = $error->validate_errors; } elseif (!is_array($error)) { $keys = Q::normalize($error); $errors = array(); foreach ($keys as $key) { $errors[$key] = ''; } } else { $errors = $error; } foreach ($errors as $id => $msg) { if ($this->existsElement($id)) { $this->element($id)->invalidate($msg); } } return $this; }
/** * 清除所有属性或指定的“脏”状态 * * @param string|array $props * * @return QDB_ActiveRecord_Abstract 连贯接口 */ function cleanChanges($props = null) { if ($props) { $props = Q::normalize($props); foreach ($props as $prop) { unset($this->_changed_props[$prop]); } } else { $this->_changed_props = array(); } return $this; }
/** * 构造 url * * 用法: * * @code php * url(UDI, [附加参数数组], [路由名]) * @endcode * * UDI 是统一目的地标识符(Uniform Destination Identifier)的缩写。 * UDI 由控制器、动作、名字空间以及模块名组成,采用如下的格式: * * @code php * ns::controller/action@module * @endcode * * UDI 字符串中,每一个部分都是可选的。 * 如果没有提供控制器和动作名,则使用当前的控制器和默认动作名(index)代替。 * 同样,如果未提供模块名和名字空间,均使用当前值代替。 * * UDI 字符串写法示例: * * @code php * 'controller' * 'controller/action' * '/action' * 'controller@module' * 'controller/action@module' * 'ns::controller' * 'ns::controller/action' * 'ns::controller@module' * 'ns::controller/action@module' * '@module' * 'ns::@module' * @endcode * * 示例: * @code php * url('admin::posts/edit', array('id' => $post->id())); * @endcode * * $params 参数除了采用数组,还可以是以“/”符号分割的字符串: * * @code php * url('posts/index', 'page/3'); * url('users/show', 'id/5/profile/yes'); * @endcode * * 在使用 PATHINFO 和 URL 重写时,可以使用通过制定路由名来强制要求 QeePHP * 采用指定的路由规则来生成 URL。强制指定路由规则可以加快 URL 的生成, * 但在路由规则名称发生变化时,需要修改生成 URL 的代码。 * * $opts 参数用于控制如何生成 URL。可用的选项有: * * - base_uri: 指定 URL 前部要添加的路径(可以包括协议、域名和端口,以及路径) * - script: 指定 URL 前部要使用的脚本名 * - mode: 指定 URL 生成模式,可以是 standard、pathinfo 和 rewrite * * @param string $udi UDI 字符串 * @param array|string $params 附加参数数组 * @param string $route_name 路由名 * @param array $opts 控制如何生成 URL 的选项 * * @return string 生成的 URL 地址 */ function url($udi, $params = null, $route_name = null, array $opts = null) { static $base_uri; if (is_null($base_uri)) { if (strlen(Q::ini('base_uri')) > 0) { $base_uri = Q::ini('base_uri'); } else { $base_uri = '/' . trim($this->baseDir(), '/'); if ($base_uri != '/') { $base_uri .= '/'; } } } $udi = $this->normalizeUDI($udi); if (!is_array($params)) { $arr = Q::normalize($params, '/'); $params = array(); while ($key = array_shift($arr)) { $value = array_shift($arr); $params[$key] = $value; } } $params = array_filter($params, 'strlen'); // 处理 $opts if (is_array($opts)) { $mode = !empty($opts['mode']) ? $opts['mode'] : self::$_url_mode; $script = !empty($opts['script']) ? $opts['script'] : $this->scriptName(); $url = strlen($opts['base_uri']) > 0 ? rtrim($opts['base_uri'], '/') . '/' : $base_uri; } else { $mode = self::$_url_mode; $url = $base_uri; $script = $this->scriptName(); } if (!is_null($this->_router) && $mode != self::URL_MODE_STANDARD) { // 使用路由生成 URL $params = array_merge($params, $udi); $path = $this->_router->url($params, $route_name); if (self::$_url_mode == self::URL_MODE_PATHINFO && $path != '/') { $url .= $this->scriptName(); } else { $url = rtrim($url, '/'); } $url .= $path; } else { foreach (self::$_udi_defaults as $key => $value) { if ($udi[$key] == $value) { unset($udi[$key]); } unset($params[$key]); } $params = array_filter(array_merge($udi, $params), 'strlen'); $url .= $script; if (!empty($params)) { $url .= '?' . http_build_query($params, '', '&'); } } return $url; }
function testFindHasMany() { $tableAuthors = Q::singleton('Table_Authors'); /* @var $tableAuthors Table_Authors */ $tableAuthors->getConn()->startTrans(); // $tableAuthors->disableLinks('books'); $authors = $this->insertAuthors(); $contents = $this->insertContents($authors); $this->insertComments($authors, $contents); $author = $tableAuthors->find($authors['liaoyulei'])->query(); $tableAuthors->getConn()->completeTrans(false); $this->assertTrue(!empty($author['contents']), "!empty(\$author['contents'])"); $this->assertType('array', $author['contents'], "type of \$author['contents'] == array"); $first = reset($author['contents']); $this->assertType('array', $first, "reset(\$author['contents']) == array"); $this->assertTrue(!empty($first['title']), "!empty(reset(\$author['contents']['title']))"); $link_contents = $tableAuthors->getLink('contents'); $on_find_keys = Q::normalize($link_contents->on_find_keys); $this->assertEquals(count($on_find_keys), count($first), "count(\$first) == 1"); if (is_int($link_contents->on_find)) { $this->assertEquals($link_contents->on_find, count($author['contents']), "count(\$author['contents']) == " . $link_contents->on_find); } $first = reset($author['contents']); $next = next($author['contents']); $this->assertTrue($first['content_id'] < $next['content_id'], "\$first['content_id'] < \$next['content_id']"); }
/** * @dataProvider processDataProvider */ function testProcessData($test_failed, $post, $values) { $forms = array(Form_Data::createDirect(), Form_Data::createFromConfig()); $test_failed = Q::normalize($test_failed); foreach ($forms as $form) { $this->assertTrue(isset($form['name']), 'element "name" not exists.'); $this->assertTrue(isset($form['price']), 'element "price" not exists.'); /* @var $form Form_Data */ $failed = null; $ret = $form->validate($post, $failed); $this->assertEquals($ret, $form->isValid()); $this->assertEquals($values, $form->values()); $this->assertEquals($values, $form->value()); $this->assertEquals($post, $form->unfilteredValues()); $this->assertEquals($post, $form->unfilteredValue()); if (empty($test_failed)) { $this->assertTrue($ret); } else { foreach ($failed as $id => $errors) { $this->assertEquals(false, $form[$id]->isValid()); $this->assertEquals($errors, $form[$id]->errorMsg()); } $failed = array_keys($failed); $this->assertEquals($test_failed, $failed); } } }
/** * 构造控件的属性字符串 * * @param array|string $exclude * * @return string */ protected function _printAttrs($exclude = 'id, name, value') { $exclude = Q::normalize($exclude); $exclude = array_flip($exclude); $out = ''; foreach ($this->_attrs as $attr => $value) { if (isset($exclude[$attr])) { continue; } $out .= $attr . '="' . htmlspecialchars($value) . '" '; } return $out; }
/** * 将指定的属性设置为“脏”状态 * * @param string|array $props_name * * @return QDB_ActiveRecord_Abstract * 返回 ActiveRecord 本身,实现连贯接口 */ function willChanged($props_name) { $props_name = Q::normalize($props_name); foreach ($props_name as $prop_name) { if (!isset(self::$_meta[$this->_class_name]->props[$prop_name])) { continue; } $this->_changed_props[$prop_name] = $prop_name; } return $this; }
/** * 添加到内部的数据表->字段名映射数组 * * @param string $table_name * @param array|string|QDB_Expr $cols */ protected function _addCols($table_name, $cols) { $cols = Q::normalize($cols); if (is_null($table_name)) { $table_name = ''; } $m = null; foreach ($cols as $alias => $col) { if (is_string($col)) { // 将包含多个字段的字符串打散 foreach (Q::normalize($col) as $col) { $current_table_name = $table_name; // 检查是不是 "字段名 AS 别名"这样的形式 if (preg_match('/^(.+)\\s+' . self::SQL_AS . '\\s+(.+)$/i', $col, $m)) { $col = $m[1]; $alias = $m[2]; } // 检查字段名是否包含表名称 if (preg_match('/(.+)\\.(.+)/', $col, $m)) { $current_table_name = $m[1]; $col = $m[2]; } if (isset($this->_columns_mapping[$col])) { $col = $this->_columns_mapping[$col]; } $this->_parts[self::COLUMNS][] = array($current_table_name, $col, is_string($alias) ? $alias : null); } } else { $this->_parts[self::COLUMNS][] = array($table_name, $col, is_string($alias) ? $alias : null); } } }