<commit_before><?php
namespace yoka;
/**
 * 实体对象基类
 * @author <EMAIL>
 *
 * 【注意】 使用实体类的约定：
 * 1， 实体对应数据表，主键必须是自增id.否则此基类方法无效 * [可通过pkey自定义主键名称]
 * 2， 子类中update_time 为自动更新timestamp字段，更新时忽略由系统进行更新
 * 3， 子类中定义静态方法 refresh() 用于刷新子类中定制的缓冲
 * 4， 支持SQL兼容及数据库直接操作，但不推荐使用
 * 5， 默认数据为已addslashes处理，否则应添加addslashes标记
 * 6， add/save/update/del 操作失败时返回false， 使用串列写法时请注意！
 * 7， update 默认禁止批量更新。使用$cretia['__FORCE__'] == true可执行批量更新。
 * 8， 手写SQL时不要直接使用表名，使用"%_table_%"。
 * 9， 如必须使用表名（如JOIN操作），需在SQL后加上：";#@USE_TABLE_NAME" 标记
 * 10，【重要】请留意默认缓冲的配置(default)。
 * 
 * [自定义主键]
 * 1， 支持非id作为主键。在子类中定义：static $pkey 。
 * 2， 原有id为主键的无需任何调整，完全兼容
 *
 * [2020.04.10 更新]
 * 1， 增加cache控制
 * 2， 更新_slime方法，增加type自动格式化
 * 
 * [v3 更新]
 * 1， 更新_slim方法，增加type/filter
 * 2， 支持非默认数据库连接
 *
 * [v2 更新]
 * 1， 不确定id是否存在的，请使用 geteById() 方法
 * 2， update 更新非本体数据时，需使用 __FORCE__ 参数
 * 3， 高性能开发时请优先使用带缓冲的方法：
 * 			getEntityById()		默认缓冲
 * 			getInstance(true)	参数设定缓冲
 * 			fetchAllCached()
 * 4， 默认fetchAll仅获取1000条数据。获取数据较多时，可用fetchAllRaw提高性能(注意返回值不是id作为主键)。
 * 5， 获取大量数据时，应使用 query(xql, true) 方法获取迭代对象进行操作
 * 6,  批量更新较多数据时，应使用 stopAutoRefresh() ... restartAutoRefresh() 屏蔽缓存操作提高效率
 * 7， 增加了字段自动过滤，需在子类中定义 $filter_fields 。注意：仅对 add/save/replace/update 有效
 * 8， 增加snapshop 和 slim 方法。子类中使用 $default_slim 定义默认slim字段
 * 9， 新增_slim方法取代slim。子类中使用 $define_slim 定义字段含义。（请结合db_info工具使用）
 *
 */
class BaseModel{
	public $db;
	public $entity;
	public static $_EnableBuffer = true;		//防止大数据量处理时内存不足
	public static $_BaseModel_Buffer;			//用于内存缓冲
	public static $_DefaultCacheTime = 3600; 	//默认fetchAllCache缓冲时间，秒
	public static $cacheName = 'default';		//缺省缓冲名称
	public static $cacheFlag = true;			//默认使用缓冲
	public $_stopAutoRefresh = false;			//禁止自动更新，用于批量操作。
	private $ismaster = true; 					//默认使用主库
	
	// 用于自动过滤，在子类设置 $filter_fields 值（需要过滤的字段）
	protected $filter_str = [" ","'","\r","\n","\t",'"', '(', ')', '（', '）', ',', '，', '“', '”', '<', '>'];
	protected $filter_fields = [];				//待过滤字段
	protected $filter_all = false;				//如果true，过滤所有字段
	
	// 字段类型定义 （用于_slim及自动处理）
	const TYPE_NONE = 0;	//未定义
	const TYPE_INT = 1;		//整数
	const TYPE_STRING = 2;	//字符串
	const TYPE_FLOAT = 3;	//浮点数
	const TYPE_JSON = 4;	//JSON格式字符串
	const TYPE_DATE = 5;	//日期
	const TYPE_DATETIME = 6;	//日期时间
	const TYPE_TIMESTAMP = 7;	//时间戳整数
	//const TYPE_LIST = 8;  //逗号分隔的
	static public function _type($type, $val){
		switch($type){
			case self::TYPE_INT:
			case self::TYPE_TIMESTAMP;
			$re = (int)$val;
			break;
			case self::TYPE_FLOAT:
				$re = (float)$val;
				break;
			case self::TYPE_STRING:
			case self::TYPE_JSON:
				$re = (string)$val;
				break;
			case self::TYPE_DATE:
				if(! $val) $re = '0000-00-00';
				else $re = date('Y-m-d', strtotime($val));
				break;
			case self::TYPE_DATETIME:
				if(! $val) $re = '0000-00-00 00:00:00';
				else $re = date('Y-m-d H:i:s', strtotime($val));
				break;
			case self::TYPE_NONE:
			default:
				$re = $val;
				break;
		}
		return $re;
	}
	
	/**
	 * 实例化
	 * @param int $id 用户ID
	 */
	function __construct($id = null){
		if(isset(static::$database)){
			//按设定的数据库连接
			$this->db = DB::getInstance(static::$database);
			//设定的数据库只连接主库（防止slave标志混淆），需要时请 db_change_slave
			$this->ismaster = $master;
		}else{
			if(\YsConfig::$SLAVE_DB_FIRST === true) $master = false;
			elseif(is_string(\YsConfig::$SLAVE_DB_FIRST)) $master = \YsConfig::$SLAVE_DB_FIRST;
			else $master = true;
			// 保存当前链接使用的是否是主库
			$this->ismaster = $master;
			$this->db = DB::getInstance('default', $this->ismaster);
		}
		
		// 不推荐ID实例化。建议使用： class::getById($id)
		if($id){
			$table = static::$table;
			// $pkey = static::$pkey?:'id';
			$pkey = isset(static::$pkey)?static::$pkey:'id';
			$re = $this->db->fetchOne("SELECT * FROM `{$table}` WHERE %_creteria_%", array($pkey=>$id));
			if($re){
				$this->entity = $re;
			}else{
				$exception = new \Exception("ysdb_{$table} {$id} 实体不存在");
				throw $exception;
			}
		}
		//注意：不传入id时，创建空实体
	}
	
	/**
	 * 设置从库读取
	 * 注意：
	 * 1，必须在实例化之前执行
	 * 2, 修改后全局影响<commit_msg>fix bug<commit_after><?php
namespace yoka;
/**
 * 实体对象基类
 * @author <EMAIL>
 *
 * 【注意】 使用实体类的约定：
 * 1， 实体对应数据表，主键必须是自增id.否则此基类方法无效 * [可通过pkey自定义主键名称]
 * 2， 子类中update_time 为自动更新timestamp字段，更新时忽略由系统进行更新
 * 3， 子类中定义静态方法 refresh() 用于刷新子类中定制的缓冲
 * 4， 支持SQL兼容及数据库直接操作，但不推荐使用
 * 5， 默认数据为已addslashes处理，否则应添加addslashes标记
 * 6， add/save/update/del 操作失败时返回false， 使用串列写法时请注意！
 * 7， update 默认禁止批量更新。使用$cretia['__FORCE__'] == true可执行批量更新。
 * 8， 手写SQL时不要直接使用表名，使用"%_table_%"。
 * 9， 如必须使用表名（如JOIN操作），需在SQL后加上：";#@USE_TABLE_NAME" 标记
 * 10，【重要】请留意默认缓冲的配置(default)。
 * 
 * [自定义主键]
 * 1， 支持非id作为主键。在子类中定义：static $pkey 。
 * 2， 原有id为主键的无需任何调整，完全兼容
 *
 * [2020.04.10 更新]
 * 1， 增加cache控制
 * 2， 更新_slime方法，增加type自动格式化
 * 
 * [v3 更新]
 * 1， 更新_slim方法，增加type/filter
 * 2， 支持非默认数据库连接
 *
 * [v2 更新]
 * 1， 不确定id是否存在的，请使用 geteById() 方法
 * 2， update 更新非本体数据时，需使用 __FORCE__ 参数
 * 3， 高性能开发时请优先使用带缓冲的方法：
 * 			getEntityById()		默认缓冲
 * 			getInstance(true)	参数设定缓冲
 * 			fetchAllCached()
 * 4， 默认fetchAll仅获取1000条数据。获取数据较多时，可用fetchAllRaw提高性能(注意返回值不是id作为主键)。
 * 5， 获取大量数据时，应使用 query(xql, true) 方法获取迭代对象进行操作
 * 6,  批量更新较多数据时，应使用 stopAutoRefresh() ... restartAutoRefresh() 屏蔽缓存操作提高效率
 * 7， 增加了字段自动过滤，需在子类中定义 $filter_fields 。注意：仅对 add/save/replace/update 有效
 * 8， 增加snapshop 和 slim 方法。子类中使用 $default_slim 定义默认slim字段
 * 9， 新增_slim方法取代slim。子类中使用 $define_slim 定义字段含义。（请结合db_info工具使用）
 *
 */
class BaseModel{
	public $db;
	public $entity;
	public static $_EnableBuffer = true;		//防止大数据量处理时内存不足
	public static $_BaseModel_Buffer;			//用于内存缓冲
	public static $_DefaultCacheTime = 3600; 	//默认fetchAllCache缓冲时间，秒
	public static $cacheName = 'default';		//缺省缓冲名称
	public static $cacheFlag = true;			//默认使用缓冲
	public $_stopAutoRefresh = false;			//禁止自动更新，用于批量操作。
	private $ismaster = true; 					//默认使用主库
	
	// 用于自动过滤，在子类设置 $filter_fields 值（需要过滤的字段）
	protected $filter_str = [" ","'","\r","\n","\t",'"', '(', ')', '（', '）', ',', '，', '“', '”', '<', '>'];
	protected $filter_fields = [];				//待过滤字段
	protected $filter_all = false;				//如果true，过滤所有字段
	
	// 字段类型定义 （用于_slim及自动处理）
	const TYPE_NONE = 0;	//未定义
	const TYPE_INT = 1;		//整数
	const TYPE_STRING = 2;	//字符串
	const TYPE_FLOAT = 3;	//浮点数
	const TYPE_JSON = 4;	//JSON格式字符串
	const TYPE_DATE = 5;	//日期
	const TYPE_DATETIME = 6;	//日期时间
	const TYPE_TIMESTAMP = 7;	//时间戳整数
	//const TYPE_LIST = 8;  //逗号分隔的
	static public function _type($type, $val){
		switch($type){
			case self::TYPE_INT:
			case self::TYPE_TIMESTAMP;
			$re = (int)$val;
			break;
			case self::TYPE_FLOAT:
				$re = (float)$val;
				break;
			case self::TYPE_STRING:
			case self::TYPE_JSON:
				$re = (string)$val;
				break;
			case self::TYPE_DATE:
				if(! $val) $re = '0000-00-00';
				else $re = date('Y-m-d', strtotime($val));
				break;
			case self::TYPE_DATETIME:
				if(! $val) $re = '0000-00-00 00:00:00';
				else $re = date('Y-m-d H:i:s', strtotime($val));
				break;
			case self::TYPE_NONE:
			default:
				$re = $val;
				break;
		}
		return $re;
	}
	
	/**
	 * 实例化
	 * @param int $id 用户ID
	 */
	function __construct($id = null){
		if(isset(static::$database)){
			//按设定的数据库连接
			$this->db = DB::getInstance(static::$database);
			//设定的数据库只连接主库（防止slave标志混淆），需要时请 db_change_slave
			$this->ismaster = true;
		}else{
			if(\YsConfig::$SLAVE_DB_FIRST === true) $master = false;
			elseif(is_string(\YsConfig::$SLAVE_DB_FIRST)) $master = \YsConfig::$SLAVE_DB_FIRST;
			else $master = true;
			// 保存当前链接使用的是否是主库
			$this->ismaster = $master;
			$this->db = DB::getInstance('default', $this->ismaster);
		}
		
		// 不推荐ID实例化。建议使用： class::getById($id)
		if($id){
			$table = static::$table;
			// $pkey = static::$pkey?:'id';
			$pkey = isset(static::$pkey)?static::$pkey:'id';
			$re = $this->db->fetchOne("SELECT * FROM `{$table}` WHERE %_creteria_%", array($pkey=>$id));
			if($re){
				$this->entity = $re;
			}else{
				$exception = new \Exception("ysdb_{$table} {$id} 实体不存在");
				throw $exception;
			}
		}
		//注意：不传入id时，创建空实体
	}
	
	/**
	 * 设置从库读取
	 * 注意：
	 * 1，必须在实例化之前执行
	 * 2, 修改后全局影响
{'fraction_non_alphanumeric': 0.1760110873125866, 'fraction_numerical': 0.019402797026584352, 'mean_word_length': 3.816747572815534, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3067977', 'n_tokens_mistral': 4553, 'n_tokens_neox': 4514, 'n_words': 628}