目录
echo、print、print_r、var_dump的区别
strlen和mb_strlen的区别
PHP单引号和双引号的区别
GET和POST提交方式的区别
include和require的区别
AJAX的优势是什么?
SESSION与COOKIE的区别?
PHP错误和异常的区别?
PHP中的魔术常量、预定义常量和预定义变量
一、魔术常量
二、魔术方法
三、预定义常量
四、预定义变量
1,1,2,3,5,8,13,21...,求出第n个数
接口类和抽象类的区别
遍历出某文件夹下的文件夹和文件
防止SQL注入的函数
解决多进程读写一个文件的方法
while,外循环是释放不了内循环
测试代码块执行时间
PHP获取真实IP
谈谈数据库的事务?
1、事务四大特性:ACID
2、隔离性的四个等级(重点)
高并发的情况下会主要产生以下情况:
三、乐观锁、悲观锁、行锁、表锁
1、乐观锁
2、悲观锁
主键、外键和索引的区别?
一、三者区别
二、索引的分类
1、从数据结构角度
2、从物理存储角度
3、从逻辑角度
MySQL存储引擎的选择?
堆内存和栈内存的区别?
redis是单线程的么,为什么?
Redis和Memcached的区别?
redis有哪些数据结构?
print、echo:是语言结构,只能输出简单类型的值(int,string),它们在输出数组时提示Notice错误,输出对象时提示Catchable fatal error。而这二者的区别是echo可以可多参,print只单参
print_r、var_dump:是函数,可用于打印数组和对象,就像echo和print_r的升级版,而其二者的区别,print_r只单参,简单打印,var_dump可多参,连数据类型也打印
echo mb_strlen('W.X 你好啊'); // 7 = 4 + 1*3
echo mb_strlen('W.X 你好啊', 'gbk'); // 9 约等于 4 + 1.5*3
echo strlen('W.X 你好啊'); // 13 = 4 + 3*3
/**
* 可以看出:
* mb_strlen默认utf-8计算,中文字符=1字节,若为gbk计算,中文字符=1.5字节
* strlen 把中文字符当作3字节
* mb_strlen是属于MBString扩展的一个函数,而strlen是php核心函数
*/
$one = 1;
$two = 2;
class A{
protected $name;
public function __construct()
{
$this->name = 'X.W.X';
}
public function __toString()// 不使用这魔术方法时,print_r会报错
{
return strval($this->name);
}
}
$a = new A();
print_r('$one"$two"'.PHP_EOL); // 单引号不会把$one和$two解析成变量,注意:为啥单引号中的双引号不解析
print_r("自动解析'$one''{$two}'{$two }{ $two}{ $two }{$a}"); // 双引号自动解析变量$one和$two
/* result:
$one"$two"
自动解析'1''2'2{ 2}{ 2 }X.W.X
*/
/* 可以推出:
1.双引号解释变量,单引号不解释变量
2.单引号比双引号效率高,所以尽量使用单引号
3.双引号可放单引号,单引号也可放双引号,但嵌套之后的引号不再具备之前的功能
*/
都是导入文件,都有返回值。
有无_once的区别?
<>php
class Test1
{
public function __construct()
{
echo __CLASS__.PHP_EOL;
}
}
return [1,2,3];
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
bool(true)
*/
但require(需要)当文件不存在或者无法打开的时候,会报E_ERROR,停止脚本
include(包含)当文件不存在或者无法打开的时候,也会E_WARNING,但不会停止脚本
页面局部刷新,减轻服务器压力
其它语言一般只剩下异常,无错误和异常之分,php有错误和异常之分
错误:不会被catch捕捉到,不过php提供产生错误之后的钩子:set_error_handler($err_type),需要注意的是这个函数还是捕捉不到 E_ERROR
、 E_PARSE
、 E_CORE_ERROR
、 E_CORE_WARNING
、 E_COMPILE_ERROR
、 E_COMPILE_WARNING
等级别的
错误,但可使用三种方法
方法一:@mysql_connect(...) or die("Database Connect Error")。
方法二:编辑php.ini ,查找"display_errors =" ,将“=”后面的值改为"off。
方法三:在php脚本前加error_reporting(0),屏蔽所有错误提示。
去屏蔽E_ERROR等级别的错误,但需要注意的是若使用了set_error_handler函数代理错误会使@魔法糖失效
异常:可手动抛出异常,若抛出不主动catch,则会fatal错误,停止脚本,若抛出主动catch,则是异常,可通过set_exception_handler代理所有异常
二者语法:
mixed set_error_handler ( callable $error_handler
[, int $error_types
= E_ALL | E_STRICT ] )
bool error_handler ( int $errno , string $errstr [, string $errfile [, int $errline ]] )
第一个参数 errno,包含了错误的级别,是一个 integer。
第二个参数 errstr,包含了错误的信息,是一个 string。
第三个参数是可选的,errfile, 包含了发生错误的文件名,是一个 string。
第四个参数是一个可选项, errline, 包含了错误发生的行号,是一个 integer。
callable set_exception_handler (callable $exception_handler
)
";
echo $e->getMessage();
}
set_exception_handler("exception_handler");
if(!function_exists("write")){
//抛出自定义IO异常 若无exception_handler代理异常,会报fatal错,停止脚本
throw new IoException("方法write()不存在");
}
特点:它们的值会随着它们在代码中的位置的改变而改变。这些特殊的常量不区分大小写
__LINE__ :返回文件中的当前行号。也可写成__line__。
__FILE__:返回当前文件的绝对路径(包含文件名)。
__DIR__:返回当前文件的绝对路径(不包含文件名),等价于 dirname(__FILE__)。
__FUNCTION__:返回当前函数(或方法)的名称。
__CLASS__:返回当前的类名(包括该类的作用区域或命名空间)。
__TRAIT__:返回当前的trait名称(包括该trait的作用区域或命名空间)。
__METHOD__:返回当前的方法名(包括类名)。
__NAMESPACE__:返回当前文件的命名空间的名称。
点击此链接:魔术方法
特点:已经在PHP的内核中就定义好了的常量。区分大小写。
PHP_VERSION:返回PHP的版本。
PHP_OS:返回执行PHP解释器的操作系统名称。
PHP_EOL:系统换行符,Windows是(\r\n),Linux是(\n),MAC是(\r)
特点:都是数组类型,是对于全部脚本而言的(全部脚本都能使用的环境变量)
$GLOBALS:global全局变量,是一个包含了所有全局变量的组合数组,全局变量的名称就是该组合数组的键。
$_GET:HTTP GET 变量,通过 URL 参数传递给当前脚本的变量的数组。
$_POST:HTTP POST 变量,通过 HTTP POST 方式传递给当前脚本的变量的数组。
$_COOKIE:HTTP Cookies 变量,通过 HTTP Cookies 方式传递给当前脚本的变量的数组。
$_SESSION:session 变量,当前脚本可用的 SESSION 变量的数组。
$_REQUEST:HTTP Request 变量,默认情况下包含了 $_GET,$_POST 和 $_COOKIE 的数组。
$_FILES:HTTP 文件上传变量,通过 HTTP POST 方式上传到当前脚本的项目的数组。
$_SERVER:服务器信息变量,包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等信息的数组。这个数组中的项目由 Web 服务器创建。
$_ENV:环境变量,通过环境方式传递给当前脚本的变量的数组。
$php_errormsg:前一个错误信息,$php_errormsg 变量包含由 PHP 生成的最新错误信息。这个变量只在错误发生的作用域内可用,并且要求 track_errors 配置项是开启的(默认是关闭的)。
$HTTP_RAW_POST_DATA:包含 POST 提交的原始数据。
$http_response_header:HTTP 响应头,$http_response_header 数组与 get_headers() 函数类似。当使用HTTP包装器时,$http_response_header 将会被 HTTP 响应头信息填充。
$argc:传递给脚本的参数数目,包含当运行于命令行下时传递给当前脚本的参数的数目。脚本的文件名总是作为参数传递给当前脚本,因此 $argc 的最小值为 1,这个变量仅在 register_argc_argv 打开时可用。
$argv:传递给脚本的参数数组,包含当运行于命令行下时传递给当前脚本的参数的数组。第一个参数总是当前脚本的文件名,因此 $argv[0] 就是脚本文件名,这个变量仅在 register_argc_argv 打开时可用。
特别注意:
php 4.2.0 以及后续版本中,php 指令 register_globals 的默认值为 off。这是 php 的一个主要变化。
让 register_globals 的值为 off 将影响到预定义变量集在全局范围内的有效性。例如,为了得到 DOCUMENT_ROOT 的值,
将必须使用 $_SERVER['DOCUMENT_ROOT'] 代替 $DOCUMENT_ROOT,又如,
使用 $_GET['id'] 来代替 $id 从中获取 id 值,亦或使用 $_ENV['HOME'] 来代替 $HOME 获取环境变量 HOME 的值。
// 1,1,2,3,5,8,13,21...,求出第n个数
// 采用 for控制循环
/**
* @param int $n 输入循环次数
*
* @return int 返回值
*
*/
function _add($n){
$pre = 0;
$next = 1;
$tmp = null;
for($i=0; $i<$n; $i++){
$tmp = $pre + $next;
$pre = $next;
$next = $tmp;
}
return $pre;
}
// 采用 递归控制循环
/**
* @param int $pre 输入前一个数
* @param int $next 输入后一个数
* @param int $n 输入循环次数
*
* @return int 返回值
*
*/
function rec($pre, $next, $n){
// 使用递归,必须有退出条件,不然就死循环
if($n<=0){
return $pre;
}
$n--;
return rec($next, $pre+$next, $n);
}
echo _add(7); // 13
echo rec(0,1,7); // 13
// 可以看出
// for循环和递归循环的区别
// for循环: 使用临时值$tmp,来记录每一次和的值
// 递归循环: 使用两个输入参数,来记录每一次和的值
二者差异:
// 接口类使用interface class_name定义,而抽象类使用abstract class class_name定义
// 接口类使用implements继承,可以多继承interface类,而抽象类是使用extends继承,只能单继承抽象类
// 接口类默认都是abstract,纯粹是接口,且不能自己实现方法,而抽象类都不一定
// 使用了四个函数
bool is_dir(string $dir_name)
resource||false opendir(string $dirname) // resource代表文件句柄
string||false readdir(resource $dir_handler)
bool is_file(string $file_name)
// 实现方法
$dir = 'D:\Temp';
function getAll($dir){
$allFile = [];
if (is_dir($dir)) {
$dir_h = opendir($dir);
if ($dir_h) {
// 万一返回 目录名为 '0',所以采用 !==
while (false !== ($row = readdir($dir_h))){
if ($row == '.' || $row == '..'){
continue;
}
if (is_file($dir . '/' . $row)){
$allFile[] = $row;
}
elseif (is_dir($dir . '/' . $row)){
$allFile[$row] = getAll($dir . '/' . $row);
}
}
closedir($dir_h);
}
}
return $allFile;
}
var_dump(getAll($dir));
// 注意:本扩展自 PHP 5.5.0 起已废弃,并在自 PHP 7.0.0 开始被移除。应使用 MySQLi 或 PDO_MySQL 扩展来替换之
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier = NULL ] )
// mysqli扩展的函数
string mysqli_real_escape_string ( string $unescaped_string [, resource $link_identifier = NULL ] )
// 使用了fopen、flock、fwrite、fclose四个函数
function putFile($file,$mode="w"){
$file = fopen($file,$mode);
if(flock($file,LOCK_EX)){
fwrite($file,'write a word');
flock($file,LOCK_UN);
}else{
echo "无法访问";
}
fclose($file);
}
$s_ = 0;
$e_ = 2;
while ($s_ < $e_){
while (true){
$s_++;
echo '死循环';
}
}
string|float microtime ([ bool $get_as_float ] ); //返回当前 Unix 时间戳的微秒数
string number_format ( float $number , int $decimals = 0 , string $dec_point = "." , string $thousands_sep = "," ); //通过千位分组来格式化数字
mixed call_user_func_array ( callback $funcname , array $param_arr ); //动态调用函数
Class Debug
{
// 记录时间点
protected static $info;
/**
* @param string $name 标记时间名
* @param int $overide 是否重写标记时间名
*/
public static function remark($name='start', $overide=0){
if ($overide){
static::$info[$name] = microtime(true);
}
else{
if (!isset(static::$info[$name])){
static::$info[$name] = microtime(true);
}
else {
echo '您输入的标记时间名已经存在';
}
}
}
/**
* @param string $start_name 起始标记时间名
* @param string $end_name 结束标记时间名
* @param int $dec 数字格式化,保留两个小数点
*
* @return int|string 返回时间差
*/
public static function getRangeTime($start_name, $end_name, $dec = 2){
if (!isset(static::$info[$start_name])){
echo '输入的起始时间名不存在';
return -1;
}
elseif (!isset(static::$info[$end_name]))
{
echo '自动帮您以当前时间戳作为您的结束时间名';
static::$info[$end_name]=microtime(true);
}
return number_format(static::$info[$end_name]-static::$info[$start_name], $dec);
}
/**
* 计算某函数使用时间
* @param callback(string|array) $function [空间名+类名+]函数名
* @param array $arr 传参数组
*/
public static function countClassTime($function, $arr){
Debug::remark('start',1);
call_user_func_array($function, $arr);
Debug::remark('end',1);
echo Debug::getRangeTime('start', 'end').PHP_EOL;
}
}
/**
* 获取客户端IP地址
* @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
* @param boolean $adv 是否进行高级模式获取(有可能被伪装)
* @return mixed
*/
public function ip($type = 0, $adv = true)
{
$type = $type ? 1 : 0;
static $ip = null; // 为什么ip变量要使用static?防止一个请求多次执行$httpAgentIp以下代码
if (null !== $ip) {
return $ip[$type];
}
// 是否使用自己配置的代理ip
$httpAgentIp = Config::get('http_agent_ip');
if ($httpAgentIp && isset($_SERVER[$httpAgentIp])) {
$ip = $_SERVER[$httpAgentIp];
} elseif ($adv) {
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown', $arr);
if (false !== $pos) {
unset($arr[$pos]);
}
$ip = trim(current($arr));
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
// 为什么使用$_SERVER['REMOTE_ADDR'],而不使用getenv('REMOTE_ADDR')
// getenv在一些web服务器下是不支持,例如iis服务器
$ip = $_SERVER['REMOTE_ADDR'];
}
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
// IP地址合法验证
$long = sprintf("%u", ip2long($ip));
$ip = $long ? [$ip, $long] : ['0.0.0.0', 0];
return $ip[$type];
}
事务,为了高并发导致数据不一致而生,
SELECT @@TRANSACTION_ISOLATION; // 查看事务隔离级别
SELECT @@AUTOCOMMIT; // 查看语句是否被自动提交
原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)
①、脏读(未提交读):又称无效数据的读出,一个事务读取到了另一个事务未提交的数据操作结果。若依据脏数据update、insert、delete,这是相当危险的,因为很可能所有的操作都被回滚。
假设在秒杀活动中,某一商品的库存数量剩100?
事务A 事务B
select查询 库存=100
update更新 库存=99
select查询 库存=100
事务回滚库存=100
事务结束库存=100
②、更新丢失:两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。
第一类丢失更新 (通过设置事务隔离级别为 Repeatable Read可以防止)
时间 |
取款事务A |
转账事务B |
T1 |
开始事务 |
|
T2 |
|
开始事务 |
T3 |
查询账户余额为1000元 |
|
T4 |
|
查询账户余额为1000元 |
T5 |
|
汇入100元把余额改为1100元 |
T6 |
|
提交事务 |
T7 |
取出100元把余额改为900元 |
|
T8 |
撤销事务 |
|
T9 |
余额恢复为1000 元(丢失更新) |
|
A事务在撤销时,把1100的数据替换成1000,导致银行把汇入的100抹去,进而导致用户亏了100
第二类丢失更新 (需要应用程序控制,乐观锁)
时间 |
转账事务A |
取款事务B |
T1 |
|
开始事务 |
T2 |
开始事务 |
|
T3 |
|
查询账户余额为1000元 |
T4 |
查询账户余额为1000元 |
|
T5 |
|
取出100元把余额改为900元 |
T6 |
|
提交事务 |
T7 |
汇入100元 |
|
T8 |
提交事务 |
|
T9 |
把余额改为1100 元(丢失更新) |
|
A事务提交在提交时,把900替换成1100,导致银行把取出的100抹去,进而导致银行亏了100
二者的区别:第一类丢失更新是回滚覆盖丢失,第二类丢失更新是提交覆盖丢失
③、不可重复读:一个事务对同一行数据重复读取两次,但是却得到了不同的结果。包括以下情况:
为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
未授权读取
也称为读未提交(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
授权读取
也称为读提交(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
可重复读取(Repeatable Read)<==>mysql的默认隔离级别
禁止不可重复读和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
序列化(Serializable)
提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新提交的时候才会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
实现方式:多增加个字段version,代表当前数据版本,当写(增、删、改)表的时候,必须在原来数据版本上自增1;
悲观锁:顾名思义,就是很悲观,总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
innodb有行锁、表锁,myisam只有表锁
①、行锁
②、表锁
1、B+树索引(O(log(n))):关于B+树索引,可以参考 MySQL索引背后的数据结构及算法原理
2、hash索引:
3、FULLTEXT索引(现在MyISAM和InnoDB引擎都支持了)
4、R-Tree索引(用于对GIS数据类型创建SPATIAL索引)
点击链接:Mysql数据库存储引擎
栈是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义;
堆是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小,也就是new的时候。
是啊!Redis属于内存数据库,进程废内存,多线程不也废内存,这样cpu才不会成为负担
set key1 0 0 8
即永不过期,redis可以通过expire设定,例如:expire name 10
string、hash、list、set等
String
字符串类型是redis最基础的数据结构,首先键是字符串类型,而且其他几种结构都是在字符串类型基础上构建的
字符串类型实际上可以是字符串、数字、二进制(图片、音频),单最大不能超过512M
使用场景:
Hash
在redis中哈希类型是指键本身又是一种键值对结构,如 value = {{field1,value1}...{fieldn,valuen}}
使用场景:
list
列表类型是用来存储多个有序的字符串,列表的每个字符串成为一个元素,一个列表最多可以存储2的32次方减1个元素。在redis中,可以对列表插入(push)和弹出(pop),还可以获取指定范围的元素列表。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色。
使用场景:
lpush+brpop
命令组合就可以实现阻塞队列,生产者客户端是用lpush
从列表左侧插入元素,多个消费者客户端使用brpop
命令阻塞式的抢列表尾部的元素,多个客户端保证了消费的负载均衡的高可用性。使用技巧列表
rpush+rpop = lpush+lpop = Stack(栈) 先进后出
rpush+lpop = lpush+rpop = Queue(队列) 先进先出
lpush+ltrim=Capped Collection(有限集合)
rpush+blpop = lpush+brpop=Message Queue(消息队列)