├── README.md ├── composer.json ├── demo ├── composer.json └── demo.php └── src └── Env.php /README.md: -------------------------------------------------------------------------------- 1 | # php-client-adapter 2 | 3 | 此代码仅为高可用架构群分享使用. 4 | 5 | 因此, 仅摘取了适配器的核心逻辑, 及其中的Feature应用场景代码示例. 6 | 7 | demo使用方法: 8 | ``` 9 | cd demo 10 | composer update 11 | php demo.php 12 | ``` 13 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goosman/php-client-adapter", 3 | "description": "Adapter for client env", 4 | "version": "0.0.1-dev", 5 | "type": "library", 6 | "autoload": { 7 | "psr-4": { 8 | "ClientAdapter\\": "src/" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /demo/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goosman/php-client-adapter-demo", 3 | "description": "demo for client adapter", 4 | "version": "0.0.1-dev", 5 | "type": "project", 6 | "require": { 7 | "goosman/php-client-adapter": "*" 8 | }, 9 | "repositories": [ 10 | { 11 | "packagist": false 12 | }, 13 | { 14 | "type": "vcs", 15 | "url": "git@github.com:goosman-lei/php-client-adapter.git" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /demo/demo.php: -------------------------------------------------------------------------------- 1 | $envDesc) { 16 | if (\ClientAdapter\Env::checkEnv($envDesc)) { 17 | self::$features[$featureName] = TRUE; 18 | } 19 | } 20 | } 21 | 22 | public static function isEnable($featureName) { 23 | return array_key_exists($featureName, self::$features) && self::$features[$featureName] === TRUE; 24 | } 25 | } 26 | 27 | 28 | ########################## OK. 下面是应用中使用Feature的应用场景 29 | 30 | echo "app start\n\n"; 31 | 32 | echo "prePare Env\n\n"; 33 | $appEnv = new \AppEnv(); 34 | $appEnv->sessUid = rand(0, 10000); 35 | $appEnv->clientIp = long2ip(rand(0, pow(2, 32))); 36 | $appEnv->latitude = rand(0, 1000000) / 1000; 37 | $appEnv->longitude = rand(0, 1000000) / 1000; 38 | $appEnv->osName = rand(0, 1) ? 'ios' : 'android'; 39 | $appEnv->appVersion = rand(0, 100) / 10; 40 | 41 | \ClientAdapter\Env::setCurrEnv($appEnv); 42 | 43 | echo "Features init\n\n"; 44 | $featuresConfig = array( 45 | 'support-hail' => array( # ios 5.2及以上版本; android 5.3及以上版本. 支持打招呼功能 46 | array( 47 | 'osName eq ios', 48 | 'appVersion v>= 5.2', 49 | ), 50 | array( 51 | 'osName eq android', 52 | 'appVersion v>= 5.3', 53 | ), 54 | ), 55 | 'support-emotion' => array( # ios 5.3及以上版本, 10%小流量用户开启表情功能 56 | 'osName eq ios', 57 | 'appVersion v>= 5.3', 58 | 'sessUid <=% 100:10', 59 | ), 60 | ); 61 | Feature::init($featuresConfig); 62 | 63 | echo "Business code is\n"; 64 | if (Feature::isEnable('support-hail')) { 65 | echo "\tHere is hail code\n"; 66 | } 67 | if (Feature::isEnable('support-emotion')) { 68 | echo "\tHere is emotion code\n"; 69 | } 70 | 71 | echo "\nAppEnv is:\n"; 72 | echo json_encode((array)$appEnv, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . chr(10); 73 | -------------------------------------------------------------------------------- /src/Env.php: -------------------------------------------------------------------------------- 1 | $getter(); 25 | } 26 | 27 | /** 28 | * checkEnv 29 | * 检查当前客户端环境, 是否和$envDesc描述的环境match 30 | * @param mixed $envDesc 31 | * @access private 32 | * @return void 33 | */ 34 | public static function checkEnv($envDesc) { 35 | $envDesc = (array)$envDesc; 36 | //支持两层array结构,如果是两层array,每个array之间使用逻辑或,每个array自身使用逻辑与 37 | if(!is_array($envDesc[0])){ 38 | $envDesc = array($envDesc); 39 | } 40 | foreach ($envDesc as $descs) { 41 | $ret = false; 42 | $current = true; 43 | foreach ($descs as $desc) { 44 | if (!preg_match(';^(?P\w+)\s+(?P\S+)(?:\s+(?P.*))?$;', $desc, $match)) { 45 | continue; 46 | } 47 | // 目前的实现, 仅支持所有条件逻辑与 48 | $arg = array_key_exists('arg', $match) ? $match['arg'] : ''; 49 | $envName = $match['env']; 50 | $envValue = self::$env->$envName; 51 | $operator = $match['operator']; 52 | switch ($operator) { 53 | case 'match': 54 | if (!preg_match(strval($arg), strval($envValue))) { 55 | $current = false; 56 | } 57 | break; 58 | case 'eq' : 59 | if (strval($envValue) !=trim($arg)) { 60 | $current = false; 61 | } 62 | break; 63 | case 'neq' : 64 | if (strval($envValue) ==trim($arg)) { 65 | $current = false; 66 | } 67 | break; 68 | case '>=' : 69 | if (floatval($envValue) ' : 84 | if (floatval($envValue) <=floatval($arg)) { 85 | $current = false; 86 | } 87 | break; 88 | case '<' : 89 | if (floatval($envValue) >=floatval($arg)) { 90 | $current = false; 91 | } 92 | case 'v>=' : 93 | if (self::versionDiff($envValue, $arg) <0) { 94 | $current = false; 95 | } 96 | break; 97 | case 'v<=' : 98 | if (self::versionDiff($envValue, $arg) >0) { 99 | $current = false; 100 | } 101 | break; 102 | case 'v=' : 103 | if (self::versionDiff($envValue, $arg) !==0) { 104 | $current = false; 105 | } 106 | break; 107 | case 'v>' : 108 | if (self::versionDiff($envValue, $arg) <=0) { 109 | $current = false; 110 | } 111 | break; 112 | case 'v<' : 113 | if (self::versionDiff($envValue, $arg) >=0) { 114 | $current = false; 115 | } 116 | break; 117 | case '<=%': # deviceId <=% 100:10 表示设备id经过运算后的对100取模小于等于10为真 118 | case '>=%': # deviceId >=% 100:10 表示设备id经过运算后的对100取模大于等于10为真 119 | case '>%': # deviceId >% 100:10 表示设备id经过运算后的对100取模大于10为真 120 | case '<%': # deviceId <% 100:10 表示设备id经过运算后的对100取模小于10为真 121 | if(!self::_judgeDuration($envValue, $operator, $arg)){ 122 | $current = false; 123 | } 124 | break; 125 | case '~': #不区分大小写 126 | if (empty($envValue) || strpos(strtolower($envValue), strtolower($arg)) === false) { 127 | $current = false; 128 | } 129 | break; 130 | case '!~':#不区分大小写 131 | if (empty($envValue) || strpos(strtolower($envValue), strtolower($arg)) != false) { 132 | $current = false; 133 | } 134 | break; 135 | case 'in':#in操作, 后面的数据以英文逗号分开: 切记中文逗号不算分隔符号 136 | $arg = $arg ? trim($arg) : ''; 137 | $elems = explode(",", $arg); 138 | $elems = array_filter(array_map('trim', $elems)); 139 | if(empty($envValue) || !in_array(trim(strval($envValue)), $elems, TRUE)){ 140 | $current = false; 141 | } 142 | break; 143 | case 'notin':#in操作, 后面的数据以英文逗号分开: 切记中文逗号不算分隔符号 144 | $arg = $arg ? trim($arg) : ''; 145 | $elems = explode(",", $arg); 146 | $elems = array_filter(array_map('trim', $elems)); 147 | if(in_array(trim(strval($envValue)), $elems, TRUE)){ 148 | $current = false; 149 | } 150 | break; 151 | default : 152 | trigger_error('undefined operator[' .$operator .'] for client env adapter', E_USER_ERROR); 153 | break; 154 | } 155 | if(!$current){ 156 | break; 157 | } 158 | } 159 | if($current){//找到一个符合的分支 160 | return TRUE; 161 | } 162 | 163 | } 164 | return $ret; 165 | } 166 | 167 | private static function _judgeDuration($envValue, $operator, $arg){ 168 | $ret = true; 169 | if(empty($envValue)){ 170 | $ret = false; 171 | } 172 | if($ret){ 173 | list($total, $limit) = explode(':', $arg); 174 | $total = intval($total); 175 | $limit = intval($limit); 176 | $res = 0; 177 | if (is_numeric($envValue)){ 178 | $res = intval($envValue); 179 | } else { 180 | $res = crc32($envValue); 181 | } 182 | if(!$res){ 183 | $ret = false; 184 | } 185 | $tmpRes = $res % $total; 186 | if ($operator == '>=%' && $tmpRes < $limit){ 187 | $ret = false; 188 | } else if ($operator == '<=%' && $tmpRes > $limit){ 189 | $ret = false; 190 | } else if ($operator == '<%' && $tmpRes >= $limit){ 191 | $ret = false; 192 | } else if ($operator == '>%' && $tmpRes <= $limit){ 193 | $ret = false; 194 | } 195 | } 196 | return $ret; 197 | } 198 | 199 | 200 | private static function versionDiff($v1, $v2) { 201 | 202 | $i1 = explode('.', $v1); 203 | $i2 = explode('.', $v2); 204 | $l1 = count($i1); 205 | $l2 = count($i2); 206 | 207 | $len = max($l1, $l2); 208 | for ($i = 0; $i < $len; $i++) { 209 | $p1 = isset($i1[$i]) ? intval($i1[$i]) : 0; 210 | $p2 = isset($i2[$i]) ? intval($i2[$i]) : 0; 211 | 212 | if ($p1 > $p2) { 213 | return 1; 214 | } elseif ($p1 < $p2) { 215 | return -1; 216 | } 217 | } 218 | return 0; 219 | } 220 | 221 | } --------------------------------------------------------------------------------