/** * 通过IP获取地址 * @param string $ip * @return string */ function get_address($ip = '') { $ipadd = get_url("https://api.map.baidu.com/location/ip?ak=" . "这里去百度申请ak" . "&ip=" . $ip); $data = json_decode($ipadd); return $data; }
/** * 是否为手机端 * @return bool */ function is_Mobile() { // 如果有HTTP_X_WAP_PROFILE则一定是移动设备 if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) { return true; } // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息 if (isset($_SERVER['HTTP_VIA'])) { // 找不到为flase,否则为true return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false; } // 脑残法,判断手机发送的客户端标志,兼容性有待提高。其中'MicroMessenger'是电脑微信 if (isset($_SERVER['HTTP_USER_AGENT'])) { $clientkeywords = array('nokia', 'sony', 'ericsson', 'mot', 'samsung', 'htc', 'sgh', 'lg', 'sharp', 'sie-', 'philips', 'panasonic', 'alcatel', 'lenovo', 'iphone', 'ipod', 'blackberry', 'meizu', 'android', 'netfront', 'symbian', 'ucweb', 'windowsce', 'palm', 'operamini', 'operamobi', 'openwave', 'nexusone', 'cldc', 'midp', 'wap', 'mobile', 'MicroMessenger'); // 从HTTP_USER_AGENT中查找手机浏览器的关键字 if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) { return true; } } // 协议法,因为有可能不准确,放到最后判断 if (isset ($_SERVER['HTTP_ACCEPT'])) { // 如果只支持wml并且不支持html那一定是移动设备 // 如果支持wml和html但是wml在html之前则是移动设备 if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) { return true; } } return false; }
/** * 提取富文本中的纯文字 * @param $string 富文本内容 * @return string */ function StringExtractionText($string) { // 把一些预定义的 HTML 实体转换为字符 // 预定义字符是指:,&等有特殊含义(,用于链接签,&用于转义),不能直接使用 $html_string = htmlspecialchars_decode($string); // 将空格去除 $content = str_replace(" ", "", $html_string); // 去除字符串中的 HTML 标签 $contents = strip_tags($content); return $contents; }
/** * 判断当前是否是微信浏览器 * 一般放在common中 * 返回1是,0否 */ function isWeixin() { if (strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false) { return 1; } return 0; }
/** * 输入天数的时间戳,返回该时间戳的发布时间在多久之前 * @param int $time 时间戳 * @return string 返回的时间 */ function formatTime($time) { $way = time() - $time; $r = ''; if($way < 60) { $r = '刚刚'; }else if($way >= 60 && $way =3600 && $way =86400 && $way =2592000 && $way <15552000) { $r = floor($way/2592000).'个月前'; }else { $r = date("Y-m-d H:i:s",$time); } return $r; }
/** * 无限级分类数据处理(数据库数据) * @param $data 数据库查询数据 * @param string $id 主键 * @param string $pid 上级 * @param string $child 子级名 * @param int $max_pid 最高级pid * @return array|mixed 返回处理好的数组 */ function disposeInfiniteData($data, $id = 'id', $pid = 'pid', $child = "child", $max_pid = 0) { $newData = []; //数据库中信息预处理 foreach ($data as $v) { $newData[$v[$id]] = $v; } $return_arr = []; foreach ($newData as $k => $v) { if ($v['pid'] == $max_pid) { $return_arr[] =& $newData[$k]; } else { $newData[$v[$pid]][$child][] =& $newData[$k]; } } return $return_arr; }
浏览器的同源策略(Same origin policy),它是一种约定,也是浏览器最核心最基本的安全功能。所谓同源指的是:协议(是指http和https)、域名(domain)、端口(port)相同。
在php后端api入口文件中加入下面的代码即可解决问题:
header("Access-Control-Allow-Origin:*"); header("Access-Control-Allow-Methods:GET, POST, OPTIONS, DELETE"); header("Access-Control-Allow-Headers:DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type, Accept-Language, Origin, Accept-Encoding");
CORS是一个W3C标准,全称是“跨域资源共享”(Cross-origin resource sharing),它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了只能同源调用的限制。
Access-Control-Allow-Origin指的是允许发起跨域的域名。
在浏览器跨域请求中,请求头(request headers)会出现Origin字段(有个奇怪现象,谷歌游览器在非跨域情况下,也会发送origin字段),这个字段会与Access-Control-Allow-Origin字段的内容进项匹配,如果发现符合Access-Control-Allow-Origin字段的要求才会放行通过,否则会出现CORS错误。
但实际上Access-Control-Allow-Origin有很多的设置方式,“*”是最粗暴简单的,但也是最不安全的,它代表着所有请求都允许通过。如果为了服务器安全考虑不建议这么设置,最好还是使用具体的域名地址。而且如果是“*”的话,浏览器将不会发送cookies,即使你的xhr设置了withCredentials,只有在指定域名下才允许发送。
Access-Control-Allow-Methods指的是请求的方式。
Access-Control-Allow-Headers指的是请求的自定义请求头字段。
跨域时浏览器会向服务器发送预检请求,询问是否支持跨域的自定义header字段,这时候就需要设置的Access-Control-Allow-Headers字段进行应答。
connect('127.0.0.1', 6379, 30); //设置连接密码 $redis->auth('junyi'); //获取出售的数量,默认为空 $kuchun = $redis->get('kucun'); //秒杀数量 $total = 100; if ($kuchun < $total) { //监控售出数量是否变动,一旦中途变动就会打断redis事务 $redis->watch('kucun'); //开启事务 $redis->multi(); //设置售出数量+1 $redis->set("kucun", $kuchun + 1); //执行事务 $result = $redis->exec(); if ($result) { //剩余数量 $number = $total - ($kuchun + 1); //$openid 用户id $openid = $number; $redis->hset("list", "user_" . $openid, $kuchun); //获取抢购成功的用户 $data = $redis->hgetall('list'); var_dump($data); var_dump($number); } else { var_dump('手气很差哦,再试一下!'); } } else { var_dump('已经被抢光了'); } }
connect('127.0.0.1', 6379); for ($i = 1; $i rPush("goods_list", $i); } } //秒杀 function kill() { //假设这是是用户的uid $uuid = md5(uniqid('user') . time()); //创建连接redis对象 $redis = new \Redis(); //连接到服务器127.0.0.1,端口号6379,默认连接时间300,密码为空 $redis->connect('127.0.0.1', 6379); //监控列表中的值是否变动 $redis->watch("goods_list"); //开启事务 $redis->multi(); //从左边开始删除一个元素,并把删除的值赋给$goodsId if ($goodsId = $redis->lPop("goods_list")) { //秒杀成功,将幸运用户存在集合中 $redis->hSet("buy_order", $uuid, $goodsId); //执行事务 $redis->exec(); } else { //秒杀失败,将失败用户计数,默认从0开始+1 $redis->incr("fail_user_num"); } echo "SUCCESS"; }
使用phpinfo();查看PHP的版本:
去下面的两个网站下载对应版本的压缩包并解压(注意:必须下载 nts 版本)
https://windows.php.net/downloads/pecl/releases/igbinary/
https://windows.php.net/downloads/pecl/releases/redis/
复制两个文件中的如下四个文件到php环境中的
ext文件夹中(F:\phpstudy\PHPTutorial\php\php-7.0.12-nts\ext)
打开Apache的配置文件 php.ini,复制下面的两行代码到php.ini 文件中,并重启环境
extension=php_igbinary.dll extension=php_redis.dll
重新使用 phpinfo() 函数 查看php相关信息,出现下图才是安装成功,如果失败请查看下载的对应压缩包的版本是否正确
去下面的网站下载对应的压缩包并解压:https://github.com/MicrosoftArchive/redis/releases/
直接解压,并且cmd到解压目录下,运行文件夹中的redis-server.exe,出现下图即为安装成功:
要想在PHP中使用redis这个窗口是不能关的,否则redis将无法使用。当然如果一直开着会很麻烦,所以我们设置一下开机自启,让他在系统中一直启动着。用cmd打开解压目录,运行以下代码:
redis-server --service-install redis.windows-service.conf --loglevel verbose
设置开机自动启动,打开cmd窗口并输入:services.msc,找到redis 服务点击启动即可
如果命令失败是找不到redis服务的
1067错误:
原因1:可能是因为他需要在logs目录下生成日志文件,而执行命令时权限不够没有生成,所以只需要手动创建一个logs目录即可。
原因2:肯能是因为redis的启动窗口未关闭造成的
json_encode 所有字符串数据的编码必须是 UTF-8,不然返回false
ini_set('session.cookie_lifetime',0);
把安装目录下的composer.phar移动到项目根目录下:
1、找到composer位置的命令:which composer
2、把安装目录下的composer.phar移动到项目根目录下
3、在项目下正常运行安装命令即可
javascript的sessionStorage,在微信关闭时会自动失效,默认设置。这里的关闭不是指彻底关闭微信,而是只要离开页面就会失效!
设置:
sessionStorage.setItem('inviteuid', inviteuid);
获取:
sessionStorage.getItem('inviteuid')
判断是否设置了:
sessionStorage.getItem("inviteuid") != null
if ($a) { } else switch($a) { }
if ($a) { } else do { } while (!$a);
//例如判断数组是否为空后做操作 if (count($array)) { for ($i = 0; $i < count($array); $i++) { } } else { //数组为空的逻辑 } //可以写成 if (count($array) == 0) { //数组为空的逻辑 } else for ($i = 0; $i < count($array); $i++) { }
$a = true; if ($a) { echo “true”; } else label: { echo “false”; } //转化后写法 $a = true; if ($a) { echo "true"; } else { label: ; //单独的一条语句 } echo "false"; //所以运行结果是:truefalse //label不是一个常量可以是任意字符串
unserialize(): Error at offset 0 of 13465 bytes
类似于上面这种报错大多数是因为缓存引起的,建议先彻底清掉缓存试一下,再找其他原因!
var_dump(1...9);//输出10.9
原因:
首先明确一点,var_dump()把1...9识别为了浮点数。
1...9会被依次识别为:1(浮点数1),然后是.(字符串连接符号),然后是.9(浮点数0.9)
function sum(...$numbers) { $acc = 0; foreach ($numbers as $n) { $acc += $n; } return $acc; } echo sum(1, 2, 3, 4);//输出10
function add($a, $b) { return $a + $b; } echo add(...[1, 2]);//输出3 $a = [1, 2]; echo add(...$a);//输出3
PHP 从5.6+开始在用户自定义函数中支持可变数量的参数列表
if (print("1\n") && print("2\n") && print("3\n") && print("4\n")) { ; }//输出:4 111
实际上等同于:
if (print ("1\n" && print ("2\n" && print ("3\n" && print "4\n")))) { ; }
解析:
print并不是一个函数,所以并不要求一定要有小括号(所以即使你写了小括号,括号也会在语法分析阶段被忽略),即:第一个代码块在PHP看来就是第二个代码块的样子。
所以就是:
最先输出4, 然后输出 "3\n" && print的结果1 , 然后输出 "2\n" && 1的结果1, 最后是 "1\n" && 1的结果1。
多数情况直接删除与入口文件同级的user.ini
在Linux系统时,路径中的斜杠必须用(/)而不能用反斜杠(\)否则将识别不到文件。
首先说一下self的限制,使用 self:: 或者 __CLASS__ 对当前类的静态引用,取决于定义当前方法所在的类。
<?php class A { public static function who() { echo __CLASS__; } public static function test() { self::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test();//A
以上案例本意是想输出:B,却因为self限制而失败。
后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用 test() 时引用的类是 B 而不是 A。最终决定不引入新的关键字,而是使用已经预留的 static 关键字。
<?php class A { public static function who() { echo __CLASS__; } public static function test() { static::who(); // 后期静态绑定从这里开始 } } class B extends A { public static function who() { echo __CLASS__; } } B::test();//B
在非静态环境下,所调用的类即为该对象实例所属的类。由于 $this-> 会在同一作用范围内尝试调用私有方法,而 static:: 则可能给出不同结果。另一个区别是 static:: 只能用于静态属性。
foo(); static::foo(); } } class B extends A { /* foo() will be copied to B, hence its scope will still be A and * the call be successful */ } class C extends A { private function foo() { /* original method is replaced; the scope of the new one is C */ } } $b = new B(); $b->test(); $c = new C(); $c->test(); //fails
输出结果:
success! success! success! Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9
后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止。另一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。
<?php class A { public static function foo() { static::who(); } public static function who() { echo __CLASS__."\n"; } } class B extends A { public static function test() { A::foo(); parent::foo(); self::foo(); } public static function who() { echo __CLASS__."\n"; } } class C extends B { public static function who() { echo __CLASS__."\n"; } } C::test(); //结果: A C C