PHP的笔记

下载安装php集成环境的网站

https://www.phpenv.cn/

https://www.phpenv.cn/download.html

记录一下写TP项目的笔记

config/view.php
···

return [
// 模板引擎类型使用Think
‘type’ => ‘Think’,
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
‘auto_rule’ => 1,
// 模板目录名
‘view_dir_name’ => ‘view’,
// 模板后缀
‘view_suffix’ => ‘php’, // 这个我们吧他从html改成php
// 模板文件名分隔符
‘view_depr’ => DIRECTORY_SEPARATOR,
// 模板引擎普通标签开始标记
‘tpl_begin’ => ‘{’,
// 模板引擎普通标签结束标记
‘tpl_end’ => ‘}’,
// 标签库标签开始标记
‘taglib_begin’ => ‘{’,
// 标签库标签结束标记
‘taglib_end’ => ‘}’,
];

···
config/middleware.php
···

// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
\think\middleware\SessionInit::class //要启动session的话解除注释
];

···

config/app.php
···
return [
// 应用地址
‘app_host’ => env(‘app.host’, ‘’),
// 应用的命名空间
‘app_namespace’ => ‘’,
// 是否启用路由
‘with_route’ => true,
// 默认应用,这个设置我们自己的默认应用
‘default_app’ => ‘bews’,
// 默认时区
‘default_timezone’ => ‘Asia/Shanghai’,

// 应用映射(自动多应用模式有效)
'app_map'          => [],
// 域名绑定(自动多应用模式有效)
'domain_bind'      => [],
// 禁止URL访问的应用列表(自动多应用模式有效)
'deny_app_list'    => [],

// 异常页面的模板文件
'exception_tmpl'   => app()->getThinkPath() . 'tpl/think_exception.tpl',
//  dispatch_success_tmpl 和这个dispatch_error_tmpl 是我们自己定义的
'dispatch_success_tmpl' => app()->getRootPath() . 'app/tpl/think_exception.tpl',
'dispatch_error_tmpl'  => app()->getRootPath() . 'app/tpl/think_exception.tpl',

// 错误显示信息,非调试模式有效
'error_message'    => '页面错误!请稍后再试~',
// 显示错误信息
'show_error_msg'   => true,

];

···

php的数据类型
<?php
//数据类型
$age=30;     //int float
$price = 25.66;   //

//字符串
$username='admin';

//布尔类型
$isDel = true;


//复合类型 :数组,对象

//php的数组里支持任何类型
$arr=[30,99.66,'admin',true];


$obj =new Class(123456)
{
 private $salary;

 public function __construct($salary){
    $this->salary=$salary;
 }

 //访问器
 public function __get($name)
 {
    return $this->salary;
 }

};

echo gettype($obj);
echo '工资是:',$obj->salary;


//   特殊类型:null ,资源
$x;
if(is_null($x)){
    echo "Null";
}

?>



//   特殊类型:null ,资源
$x;
if(is_null($x)){
    echo "Null";
}

这里会报这个错: 
Warning: Undefined variable $x in D:\H5program\phpdemo1\lession2\demo1.php on line 41
NullPHP Warning:  Undefined variable $x in D:\H5program\phpdemo1\lession2\demo1.php on line 41



//   特殊类型:null ,资源
$x=null;
if(is_null($x)){
    echo "Null";
}

这个才会打印 Null , 判断出是否为空

用unset 干掉$x

$x=null;
unset($x)
if(is_null($x)){
    echo "Null";
}

这里也会报这个错: 
Warning: Undefined variable $x in D:\H5program\phpdemo1\lession2\demo1.php on line 41
NullPHP Warning:  Undefined variable $x in D:\H5program\phpdemo1\lession2\demo1.php on line 41

<?php
  $f = fopen('readme.txt','r');
  echo gettype($f)  这个会打印 resource ,说明它是资源类型
?>

另一种数据类型:回调类型 callback

php 可以用字符串表示函数,也可以传递函数,所以可以用任何方式来引用或者传递函数,当成值 / 数据类型。

接受回调为参数的函数方法有很多,array filter

<?php

function hello(string $name):string
{
    return 'Hello ' .$name;
}
//常规调用
echo hello('猪老师'),'<br>';

//回调的方式来调用,第一个参数是被调用函数的名字,后面的参数被调用函数的参数
echo  call_user_func('hello','王老师')



?>

<?php
// 函数作为对象方法
class Demo1
{
   // 这里必须是static
    public static function hello(string $name):string
    {
        return 'Hello world' .$name;
    }
}
//['Demo1','hello']  第一个参数是对象的类名,第二个参数是方法名字
echo  call_user_func(['Demo1','hello'],'马老师');
?>

流程控制
<?php
// * 1. 单分支
if ($age >= 18) {
    echo '恭喜, 已成年,可以观看<br>';
}
?>

<?php
// * 2. 双分支
if ($age >= 18)
    echo '恭喜, 已成年,可以观看<br>';
else
    // 默认分支
    echo '未成年, 请在家长陪同下观看<br>';
?>

<?php
// * 3. 多分支
$age = 44;
if ($age >= 18 && $age < 30)
    echo "{$age}岁, 彩礼, 能按揭吗? <br>";
else if ($age >= 30 && $age < 45)
    echo "{$age}岁, 应该成个家了 <br>";
else if ($age >= 45)
    echo "{$age}岁, 房贷快还完了 <br>";
// 未成年, < 18, 默认分支
else
    echo "{$age}岁, 放学了, 我送你回家 <br>";
?>

<?php
// * 4. 多分支的语法糖:switch
$age = 15;
switch (true) {
    case $age >= 18 && $age < 30:
        echo "{$age}岁, 彩礼, 能按揭吗? <br>";
        break;
    case $age >= 30 && $age < 45:
        echo "{$age}岁, 应该成个家了 <br>";
        break;
    case $age >= 45:
        echo "{$age}岁, 房贷快还完了 <br>";
        break;
    default:
        echo "{$age}岁, 放学了, 我送你回家 <br>";
}
?>

循环

<?php
$colors=['red','green','blue'];
echo $colors[1]; // 打印 green;
echo '<br>';
echo "colors的长度是:",count($colors);
echo '<br>';

// 1. 初始化循环变量,这里用索引当循环变量, $i=0;指向第一个数组元素

$list  = '<ul style="border:1px solid;background: lightcyan">';
$i = 0;
while ($i < count($colors)) {
    $list .= "<li>{$colors[$i]}</li>";
    // 更新条件 
    $i = $i + 1;
}

$list .= '</ul>';

echo $list;

// while 还有一个双胞胎, do-while 与上面的区别在于条件判断的时机不同
$list  = '<ul style="border:1px solid;background: lightgreen">';

$i = 0;
do {
    $list .= "<li>{$colors[$i]}</li>";
    // 更新条件 
    $i = $i + 1;
} while ($i > count($colors));

$list .= '</ul>';

echo $list;

// for 可看成 while 的语法糖
$list  = '<ul style="border:1px solid;background: violet">';
for ($i = 0; $i < count($colors); $i++) {
    $list .= "<li>{$colors[$i]}</li>";
}
$list .= '</ul>';
echo $list;

// 中断或跳过某次循环
// break; continue
$list  = '<ul style="border:1px solid;background: pink">';
for ($i = 0; $i < count($colors); $i++) {
    // 只输出前二个
    // if ($i > 1) break;
    // 跳过第2个,只输出第1个和第3个
    if ($i === 1) continue;
    $list .= "<li>{$colors[$i]}</li>";
}
$list .= '</ul>';
echo $list;


?>

<?php
 $a ='hello';
 $b = a .'world';
 echo $b;
?>
数组的声明与遍历

1 索引数组:键名是从 0 递增的整数

<?php
$colors =['red','green','blue']; // 完全形态是这样的 $colors =[0=>'red',1=>'green',2=>'blue'];
//索引数组,键名是从 0 递增的整数
printf('<pre>%s</pre>',print_r($colors,true));

?>

这个打印出来是这样的 :
Array
(
    [0] => red
    [1] => green
    [2] => blue
)


  1. 关联数组: 键名是字符串

    <?php
    KaTeX parse error: Expected 'EOF', got '&' at position 15: user = ['id' =&̲gt; 5, 'name' =…user, true));
    echo $user[‘name’];
    ?>

  • 二者关系
    索引数组实际上关联数组的一个子集,只不过用了数字型的字符串

一个小例子

<?php
$users = [
    0 => ['id' => 5, 'name' => '猪老师', 'gender' => 0, 'age' => 18],
    1 => ['id' => 6, 'name' => '牛老师', 'gender' => 1, 'age' => 30],
    2 => ['id' => 5, 'name' => '狗老师', 'gender' => 0, 'age' => 35],
];
// foreach
// foreach (数组  as 键名=>值) {...} , 键名是可选的 
$table = '<table border="1" width="400" cellspacing="0" cellpadding="3" align="center">';
$table .= '<caption>用户信息表</caption>';
$table .= '<thead bgcolor="#ccc"><tr><th>id</th><th>用户名</th><th>性别</th><th>年龄</th></tr></thead>';
$table .= '<tbody align="center">';

// 遍历这个二维数组
foreach ($users as  $user) {
    // $user 还是一个数组
    print_r($user);

    $table .= '<tr>';
    $table .= '<td>' . $user['id'] . '</td>';
    $table .= '<td>' . $user['name'] . '</td>';
    // 1->true, 0->false 
    $table .= '<td>' . ($user['gender'] ? '女' : '男') . '</td>';
    $table .= '<td>' . $user['age'] . '</td>';

    $table .= '</tr>';
}
$table .= '</tbody></table>';

echo $table;
?>

两个模板语法的例子:

<?php
// 用二维数组来模拟数据表查询结果集
$stus = [
    ['id' => 1, 'name' => '刘备', 'course' => 'js', 'score' => 83],
    ['id' => 2, 'name' => '关羽', 'course' => 'php', 'score' => 75],
    ['id' => 3, 'name' => '张飞', 'course' => 'js', 'score' => 52],
    ['id' => 4, 'name' => '孙权', 'course' => 'php', 'score' => 88],
    ['id' => 5, 'name' => '周瑜', 'course' => 'js', 'score' => 65],
    ['id' => 6, 'name' => '孔明', 'course' => 'php', 'score' => 53],
    ['id' => 7, 'name' => '赵云', 'course' => 'js', 'score' => 63],
    ['id' => 8, 'name' => '马超', 'course' => 'js', 'score' => 77],
    ['id' => 9, 'name' => '姜维', 'course' => 'php', 'score' => 93],
    ['id' => 10, 'name' => '黄忠', 'course' => 'js', 'score' => 81],
]
?>

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>php与html原生混编</title>
    <style>
        table {
            border-collapse: collapse;
            width: 360px;
            text-align: center;
        }

        table th,
        table td {
            border: 1px solid #000;
            padding: 5px;
        }

        table caption {
            font-size: 1.3em;
        }

        table thead {
            background-color: lightcyan;
        }

        .active {
            color: red;
        }
    </style>
</head>

<body>

    <table>
        <caption>学生成绩表</caption>
        <thead>
            <tr>
                <th>ID</th>
                <th>姓名</th>
                <th>课程</th>
                <th>成绩</th>
            </tr>
        </thead>

        <tbody>
            <!-- 这里显示的用户数据 -->
            <?php
            foreach ($stus as $stu) {
                // echo "<tr>";
                // echo "<td>{$stu['id']}</td>";
                // echo "<td>{$stu['name']}</td>";
                // echo "<td>{$stu['course']}</td>";
                // echo "<td>{$stu['score']}</td>";
                // echo "</tr>";

                // heredoc, 写模板, 可以解析内部变量
                // echo <<< STU
                //     <tr>
                //         <td>{$stu['id']}</td>
                //         <td>{$stu['name']}</td>
                //         <td>{$stu['course']}</td>
                //         <td>{$stu['score']}</td>
                //     </tr>
                // STU;

                // 只查php
                if ($stu['course'] === 'php') {
                    echo <<< STU
                    <tr>
                        <td>{$stu['id']}</td>
                        <td>{$stu['name']}</td>
                        <td class="active">{$stu['course']}</td>
                        <td>{$stu['score']}</td>
                    </tr>
                STU;
                }
            }
            ?>
        </tbody>
    </table>

</body>
</html>
<?php
// 用二维数组来模拟数据表查询结果集
$stus = [
    ['id' => 1, 'name' => '刘备', 'course' => 'js', 'score' => 83],
    ['id' => 2, 'name' => '关羽', 'course' => 'php', 'score' => 75],
    ['id' => 3, 'name' => '张飞', 'course' => 'js', 'score' => 52],
    ['id' => 4, 'name' => '孙权', 'course' => 'php', 'score' => 88],
    ['id' => 5, 'name' => '周瑜', 'course' => 'js', 'score' => 65],
    ['id' => 6, 'name' => '孔明', 'course' => 'php', 'score' => 53],
    ['id' => 7, 'name' => '赵云', 'course' => 'js', 'score' => 63],
    ['id' => 8, 'name' => '马超', 'course' => 'js', 'score' => 77],
    ['id' => 9, 'name' => '姜维', 'course' => 'php', 'score' => 93],
    ['id' => 10, 'name' => '黄忠', 'course' => 'js', 'score' => 81],
]
?>

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>php流程控制的模板语法/替代语法</title>
    <style>
        table {
            border-collapse: collapse;
            width: 360px;
            text-align: center;
        }

        table th,
        table td {
            border: 1px solid #000;
            padding: 5px;
        }

        table caption {
            font-size: 1.3em;
        }

        table thead {
            background-color: lightcyan;
        }

        .active {
            color: red;
        }
    </style>
</head>

<body>

    <table>
        <caption>学生成绩表</caption>
        <thead>
            <tr>
                <th>ID</th>
                <th>姓名</th>
                <th>课程</th>
                <th>成绩</th>
            </tr>
        </thead>

        <tbody>
            <!-- php模板语法的目标: html与php代码分离 -->
            <?php foreach ($stus as $stu) : ?>
                <!-- "{" => 冒号加php结束标记 -->
                <!-- 当前已离开了php环境,处于html中  -->
                <!-- 使用短标签进行简化: 只打印一个变量  -->
                <!-- <tr>
                    <td><?php echo $stu['id'] ?></td>
                    <td><?php echo $stu['name'] ?></td>
                    <td><?= $stu['course'] ?></td>
                    <td><?= $stu['score'] ?></td>
                </tr> -->

                <!-- 只输出成绩大于70分  -->
                <!-- <?php if ($stu['score'] > 70) : ?>
                    <tr>
                        <td><?php echo $stu['id'] ?></td>
                        <td><?php echo $stu['name'] ?></td>
                        <td><?= $stu['course'] ?></td>
                        <td class="active"><?= $stu['score'] ?></td>
                    </tr>
                <?php endif ?> -->

                <!-- 输出全部,并将不及格成绩描红 -->
                <tr>
                    <td><?php echo $stu['id'] ?></td>
                    <td><?php echo $stu['name'] ?></td>
                    <td><?= $stu['course'] ?></td>
                    <?php $active = $stu['score'] < 60 ? "active" : '' ?>
                    <td class=<?= $active ?>><?= $stu['score'] ?></td>
                </tr>

                <!-- 动态设置样式的方法  -->

            <?php endforeach ?>

        </tbody>
    </table>

</body>

</html>

作用域

php 只有函数作用域,没有块作用域,函数之外都是全局作用域

<?php
// ! 作用域

// * php只有函数作用域,没有块作用, 函数之外全是全局作用域
if (true) {
    $a = 10;
}
注意,这里$a 是可以拿到的,因为这个  if(true){} 不是函数
echo $a, '<br>';
?>

函数中不能访问到外部的变量

<?php
$name = '猪老师';

// $hello = function (): string {
// 函数中不能访问到外部的变量,这和js不一样  ,你是拿不到这个$name
// return 'Hello , ' . $name;
// };


?>

如何使用外部变量呢?
1使用 global 关键字 (不推荐)

<?php
$name = '猪老师';

$hello = function():string {
    //使用关键字 global引用一下外部变量
    global $name;
    retrun 'Hello , ' . $name;
}

?>

2 使用use关键字 (推荐)

<?php
$name = '猪老师';

$hello = function() use($name):string {
    retrun 'Hello , ' . $name;
}

?>

3 第三种方式,使用 $GLOBALS 这个系统的全局变量

<?php
$name = '猪老师';

$hello = function() use($name):string {
     // $GLOBALS['name']
    retrun 'Hello , ' . $GLOBALS['name'];
}

?>


常量

常量的值不能更新

    1. 默认全局有效
    1. define, 可以用在if中, 但不能用在class中
    1. const, 编译阶段处理, 速度最快, 必须写到作用域的顶部,适合声明类常量,不能用在if中
    <?php // 方式1. 用函数声明 // 函数 define('USER_NAME', 'admin');

    // 方式2: 用const
    const EMAIL = ‘498668472@qq.com’

    function hello(): string
    {
    return sprintf(‘Hello , %s, (%s)’, USER_NAME, EMAIL);
    }

    echo hello() . ‘


    ’;

    ?>

define(‘’, ‘中文网站’); 这里定义常量的时候 填了个空串,是合法的。

要获取的时候用一个函数来获取,很奇怪

<?php
define('', 'php中文网');
// ''变量名, 是否合法?  合法的
// 使用 一个函数来获取
echo constant('') . '<hr>';
?>

预定义常量 就是php的系统里的常量,有很多,可以查手册来看有哪些,这个是不用声明的

<?php
echo 'PHP版本: ' . PHP_VERSION . '<br>';
echo '操作系统: ' . PHP_OS . '<hr>';
?>

魔术常量
总是有一个特定的值,但是用户不能更新,由系统来维护

<?php
echo '当前行号 : ' . __LINE__ . '<br>';
echo '当前文件 : ' . __FILE__ . '<br>';
echo '当前文件路径 : ' . __DIR__ . '<br>';
?>
字符串

1 纯文本的使用方式

<?php
$domain = 'www.php.cn';
echo $domain . '<hr>';
?>
  1. 纯文本的语法糖: nowdoc

写法,先写三个 “<” ,后面再跟一个标识符,标识符可以任意写,这里我们用 ‘TEXT’ 来标识,结束的时候也必须用这个标识符。

<?php
$str = <<< 'TEXT'
    <header>
        <nav>
            <a href="">index</a>
            <a href="">video</a>
            <a href="">article</a>
        </nav>
    </header>
TEXT;
echo $str . '<hr>';
?>
  1. 模板: 双引号

    <?php $domain = 'www.php.cn'; $site = "PHP中文网 ($domain)"; echo $site . '
    '; ?>
  2. 模板语法糖: heredoc ,这里不需要双引号,可以插入任意变量

    <?php $tpl = <<< PHPCN
    • PHP中文网
    • $domain
    PHPCN; echo $tpl;

    ?>

字符串常用函数

1 explode 函数,字符串转数组,必须有个特殊字符来供切割

<?php

$str = 'mysql:dbname=phpedu;root;root';
$arr = explode(';', $str);
printf('<pre>%s</pre>', print_r($arr, true));
?>

2 split

<?php

$str = 'abcdefg';
$arr = str_split($str);
print_r($arr);
?>

3 join 函数把数组转成字符串

<?php
$colors = ['red', 'green', 'blue'];
echo join('', $colors) . '<br>';
?>

<?php
$colors = ['red', 'green', 'blue'];
echo join(',', $colors) . '<br>';
?>

查询和替换:

$str = 'php.cn';

"php"
substr(string $string, int $offset, ?int $length): string
echo substr($str, 0, 3) . '<br>';
"cn"
echo substr($str, -2, 2) . '<br>';
echo substr($str, -2) . '<br>';

strstr
$img = 'banner.png';
".png"
echo strstr($img, '.') . '<br>';
"banner"
echo strstr($img, '.', true) . '<br>';
$email = '498668472@qq.com';
echo 'QQ: ' . strstr($email, '@', true) . '<hr>';

strpos
0: 索引, 表示第一个字符就是,找到了
echo strpos('php.cn', 'p') . '<br>';
可以指定查询起点
echo strpos('php.cn', 'p', 1) . '<br>';
echo strpos('php.cn', 'p', 3) ? 'OK' : '没找到' . '<hr>';

str_replace
带有命名空间的完整的类名
$class = '\admin\controllers\User';
类的自动加载器
将这个类名->类的路径上, 然后用require
'\admin\controllers\User' => '/admin/controllers/User.php';

windows: / \,  linux/macos: /
$path = str_replace('\\', '/', $class) . '.php';
DIRECTORY_SEPARATOR: 能动识别操作系统,使用适当的路径分隔符
$path = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
echo $path . '<br>';

违禁词
echo str_replace(['交友', '异性', '带货'], '**', '我用交友软件找了一个会带货的异性女友') . '<hr>';
echo str_replace(['交友', '异性', '带货'], ['JY', 'YX', 'DH'], '我用交友软件找了一个会带货的异性女友') . '<hr>';

删除指定字符

$str = 'php.cn';
echo strlen($str) . '<br>';

$str = '       php.cn  ';
echo strlen($str) . '<br>';
echo strlen(trim($str)) . '<br>';
// trim(string $string, string $characters = " \t\n\r\0\x0B"): string { }
$path = '/0421/';
echo $path, ' => ', trim($path, '/') . '<br>';     删除两边的 '/'
echo $path, ' => ', ltrim($path, '/') . '<br>';  删除左边的 '/'
echo $path, ' => ', rtrim($path, '/') . '<br>';  删除右边的 '/'
$tags = '<h1>Hello world</h1><?php echo "给我一百万, 否则黑了你的服务器" ?>';
echo strip_tags($tags) . '<hr>';

url相关

这个会打印很多东西
<?php
 printf('<pre>%s</pre>', print_r($_SERVER, true));
?>

echo $_SERVER['QUERY_STRING'] . '<br>';
$arr = explode('&', $_SERVER['QUERY_STRING']);
// printf('<pre>%s</pre>', print_r($arr, true));
// queryString -> array
parse_str($_SERVER['QUERY_STRING'], $arr);
// printf('<pre>%s</pre>', print_r($arr, true));

$userArr = ['id' => 1, 'username' => 'admin', 'role' => 'manager'];
// id=1&username=admin&role=manage 
// echo http_build_query($userArr) . '<br>';

// printf('<pre>%s</pre>', print_r($_GET, true));

$url = 'http://php.edu/0421/demo4.php?a=1&b=2&c=3&id=1';

$arr = parse_url($url);
printf('<pre>%s</pre>', print_r($arr, true));

echo $_SERVER['QUERY_STRING'];

echo '<hr>';

echo parse_url($url)['query'];

echo  parse_url($url)['query'] === $_SERVER['QUERY_STRING'] ? '相等' : '不等';


键值对形式数组的遍历

<?php
 //再谈数组遍历
 $stu =['id'=>1,'name'=>'Jack','course'=>'java','score'=>90];
 
 // 拿到数组的键值对
 printf('[%s]=>%s<br>',key($stu),current($stu));// 这个默认会拿到当前数组的第一个值
?>

<?php
 
 $stu =['id'=>1,'name'=>'Jack','course'=>'java','score'=>90];
 printf('[%s]=>%s<br>',key($stu),current($stu));
 next($stu);//后移
 printf('[%s]=>%s<br>',key($stu),current($stu));
 next($stu);//后移
 printf('[%s]=>%s<br>',key($stu),current($stu));
 prev($stu); //前移
 printf('[%s]=>%s<br>',key($stu),current($stu));

?>

打印这个 :
[id]=>1
[name]=>Jack
[course]=>java
[name]=>Jack

上述代码实现自动遍历

 //  自动遍历
 
<?php
 
 $stu =['id'=>1,'name'=>'Jack','course'=>'java','score'=>90];

 if (count($stu) > 0) {
     while (true) {
         printf('[%s]=>%s<br>', key($stu), current($stu));
         if (next($stu)) {
            continue;
         }else{
            break;
         }
     }
 } else {
     echo '空数组';
 }
 ?>

foreach 遍历(快捷遍历)

<?php
$stu = ['id' => 1, 'name' => 'Jack', 'course' => 'php', 'score' => 90];
foreach($stu as $key => $value) {
    printf('[%s]=>%s<br>', $key, $value); 
}

?>

解构遍历

// 索引数组
list($id,  $name) = [10, 'Tony'];
// list 不是函数,因为函数不能放在等号左边, 不能用在“左值” 
printf('$id = %s, $name = %s<br>', $id, $name);

<?php
// 解构通常用来遍历二维或以上的数组
$users = [
    ['id' => 10, 'name' => 'Tony'],
    ['id' => 11, 'name' => 'John'],
    ['id' => 12, 'name' => 'Jerry'],1. 与值相关
    
];
// foreach
foreach ($users as list('id' => $id, 'name' => $name)) {
    printf('$id = %s, $name = %s<br>', $id, $name);
}

?>

数组函数

与值相关

<?php
$arr = [3 => 10, 9 => 20, 0 => 'html', 'id' => 'css', 20 => 20, 30];
printf('<pre>%s</pre>', print_r($arr, true));

?>

打印:  
Array
(
    [3] => 10
    [9] => 20
    [0] => html
    [id] => css
    [20] => 20
    [21] => 30
)  
这个打印的全部的数组

array_values()

<?php
$arr = [3 => 10, 9 => 20, 0 => 'html', 'id' => 'css', 20 => 20, 30];
printf('<pre>%s</pre>', print_r(array_values($arr), true));

?>

打印:
Array
(
    [0] => 10
    [1] => 20
    [2] => html
    [3] => css
    [4] => 20
    [5] => 30
)
这个打印的是数组的值

in_array()

$arr = [3 => 10, 9 => 20, 0 => 'html', 'id' => 'css', 20 => 20, 30];
var_dump(in_array('html', $arr));

这个打印  : bool(true)  , 说明 html 这个值在数组里



array_search()

<?php
$arr = [3 => 10, 9 => 20, 0 => 'html', 'id' => 'css', 20 => 20, 30];

$key = array_search('20', $arr);
echo $key;  这个打印的是9 ,它是20的键

echo  $arr[$key];  这个会根据键值拿到value的值,键值是9,value就是20

?>

array_unique 去重复

<?php
$arr = [3 => 10, 9 => 20, 0 => 'html', 'id' => 'css', 20 => 20, 30];

printf(
    '<pre>%s</pre>',
    print_r(array_unique($arr), true)
);

?>
打印:
Array
(
    [3] => 10
    [9] => 20
    [0] => html
    [id] => css
    [21] => 30
)

少了一个 20=>20 去掉了重复的

统计

$arr = [3 => 10, 9 => 20, 0 => 'html', 'id' => 'css', 20 => 20, 30];

echo count($arr);  这个会打印出数组里有6个元素

这个是求和函数

<?php
function sum (...$args)
{
    return array_sum($args);
}
echo sum(1,2,3,4,5,6)   //打印 21
?>

这个是求乘积的

function mul(...$args)
{
     return array_reduce($args, function ($acc, $cur) {
         return $acc * $cur;
     }, 1);

}

echo mul(2, 3, 4, 5);   //这个打印120
--------------------------------------------------
function mul(...$args)
{
    return array_product($args);
}
echo mul(2, 3, 4, 5);  //这个也打印120 

栈和队列

<?php
// 栈与队: 一个增删元素受限的数组(线性表)
// 1. 栈操作: 
// 仅限在尾部进行增删
$stack = [];
array_push($stack, 10);
// printf('<pre>%s</pre>', print_r($stack, true));
array_push($stack, 20, 30);
// printf('<pre>%s</pre>', print_r($stack, true));
echo array_pop($stack);
echo array_pop($stack);
echo array_pop($stack);
// printf('<pre>%s</pre>', print_r($stack, true));
// 仅限头部增删
array_unshift($stack, 10);
// printf('<pre>%s</pre>', print_r($stack, true));
array_unshift($stack, 30, 20);
// printf('<pre>%s</pre>', print_r($stack, true));
echo array_shift($stack);
echo array_shift($stack);
echo array_shift($stack);

// 2. 队: 尾部添加, 头部删除
$queue = [];
// 入队
array_push($queue, 10, 20, 30);

printf('<pre>%s</pre>', print_r($queue, true));

// 出队
echo array_shift($queue);
echo array_shift($queue);
echo array_shift($queue);
printf('<pre>%s</pre>', print_r($queue, true));

 头部添加, 尾部删除
array_unshift() +  array_pop()

?>

排序相关



<?php
$blue = range(1, 16); 
printf('<pre>%s</pre>', print_r($blue, true));

打乱排序
shuffle($blue);
printf('<pre>%s</pre>', print_r($blue, true));
?>

<?php
// ! 值
$arr = [30, 4, 82, 15, 20, 'abc', 'hello', 2, 46];
printf('原始:<pre>%s</pre>', print_r($arr, true));
sort($arr);
// 确保原关系不变, 原来的键与值的对应不发生变化 
asort($arr);
printf('升序:<pre>%s</pre>', print_r($arr, true));

?>
打印这个:
原始:
Array
(
    [0] => 30
    [1] => 4
    [2] => 82
    [3] => 15
    [4] => 20
    [5] => abc
    [6] => hello
    [7] => 2
    [8] => 46
)
升序:
Array
(
    [0] => 2
    [1] => 4
    [2] => 15
    [3] => 20
    [4] => 30
    [5] => 46
    [6] => 82
    [7] => abc
    [8] => hello
)


降序

<?php
$arr = [30, 4, 82, 15, 20, 'abc', 'hello', 2, 46];
printf('原始:<pre>%s</pre>', print_r($arr, true));

rsort($arr);
//确保原关系不变, 原来的键与值的对应不发生变化 
arsort($arr);
printf('升序:<pre>%s</pre>', print_r($arr, true));
?>
打印:
原始:
Array
(
    [0] => 30
    [1] => 4
    [2] => 82
    [3] => 15
    [4] => 20
    [5] => abc
    [6] => hello
    [7] => 2
    [8] => 46
)
升序:
Array
(
    [0] => hello
    [1] => abc
    [2] => 82
    [3] => 46
    [4] => 30
    [5] => 20
    [6] => 15
    [7] => 4
    [8] => 2
)



查询和替换

array_slice

<?php
  $stu = ['id' => 101, 'name' => '无忌', 'age' => 20, 'course' => 'php', 'grade' => 80];

printf('<pre>%s</pre>',print_r($stu,true));
$res = array_slice($stu,0,2);// 从0 开始取前2个
printf('<pre>%s</pre>',print_r($res,true));
$res = array_slice($stu,-2); //取后2个的值
printf('<pre>%s</pre>',print_r($res,true));

?>

array_splice() 这个删除数组里的元素

<?php

$arr = [10, 28, 9, 33, 56, 21, 82, 47];
// 删除从索引1 开始的2个元素
$res = array_splice($arr, 1, 2); // 删除28 和 9 ,res数组里是28 和9
printf('<pre>%s</pre>', print_r($res, true));

?>

array_splice() 也可以更新指定位置的元素

 第2个位置删除2个,使用新的数据来替换掉它
 
<?php

$arr = [10, 28, 9, 33, 56, 21, 82, 47];
$res = array_splice($arr, 1, 2, ['hello', 'world']);// 这个的$res 是返回的2个被删除的指定位置的元素
printf('<pre>%s</pre>', print_r($res, true));
printf('<pre>%s</pre>', print_r($arr, true));

?>

res是这个
 Array
(
    [0] => 28
    [1] => 9
)

arr被更新了
Array
(
    [0] => 10
    [1] => hello
    [2] => world
    [3] => 33
    [4] => 56
    [5] => 21
    [6] => 82
    [7] => 47
)

数组回调函数

<?php
 array_filter: 仅返回数组中可转为true的元素集合
$arr = [
    150,
    'php',
    true,
    [4, 5, 6],
    (new class
    {
    }),
    [],
    null,
    false,
    '',
    0,
    '0'
];
$res = array_filter($arr, function ($item) {
    if ($item) {
        return is_scalar($item);
    }
});

printf('<pre>%s</pre>', print_r($res, true));

?>


array_map

$arr = ['php', [3, 4, 5], (new class
{
    public $name = '电脑';
    public $price = 8888;
}), 15, 20];

//  array_map的第一个值是回调函数,第二个值是arr

$res = array_map(function ($item) {
    switch (gettype($item)) {
        case 'array':
            $item = join(', ', $item);
            break;
        case 'object':
            $item =  join(', ', get_object_vars($item));
    }
    return $item;
}, $arr);
printf('<pre>%s</pre>', print_r($res, true));

归并

这个会把数组里的元素加起来
$arr = [10, 20, 30, 40, 50];
$res = array_reduce($arr, function ($acc, $cur) {
    echo $acc, ', ', $cur, '<br>';
    return $acc + $cur;
}, 0);

echo $res . '<hr>';

array_walk()

<?php

$user = ['id' => 10, 'name' => 'admin', 'email' => 'admin@php.cn'];
array_walk($user, function ($value, $key, $color) {
    printf('[%s]=><span style="color:%s">%s</span>', $key, $color, $value);
}, 'blue');

?>

打印这个:
[id]=>10[name]=>admin[email]=>admin@php.cn

php操作数据库

https://www.php.net/

启动数据库扩展:

在php.ini 中

extension="D:\PHP\php-8\ext\php_mysqli.dll"
extension="D:\PHP\php-8\ext\php_pdo_mysql.dll"
```



<?php

namespace pdo_edu;

use PDO;

$username = 'root';
$password = 'helloMuseum';
$dsn = 'mysql:host=192.168.56.104;dbname=mydb1;port=3307;charset=utf8';
$db=new PDO($dsn,$username,$password);

var_dump($db);
if($db){
    echo '<h2>连接成功</h2>';
}

```
php 操作数据库的例子

同一级别目录下创建两个文件

config/database.php

<?php

namespace pdo_edu;

$dbConfig=[
    'type'=>'mysql',
    'host'=>'192.168.56.104',
    'dbname'=>'mydb1',
    'port'=>'3307',
    'charset' => 'utf8',
    'username'=>'root',
    'password'=>'helloMuseum'
];

return $dbConfig;

config/connect_test.php

<?php

namespace pdo_edu;
use PDO;

// 同一级别目录下的文件引入,
$dbConfig = require 'database.php';

// $dbConfig = include 'database.php';  这是用include 引入,也是可以的

//print_r($dbConfig);

//将关联数组中的每一个成员,打散成独立的变量
extract($dbConfig);
echo <<< SHOW
$type;
$host;
$dbname;
$port;
$charset;
$username;
$password;
SHOW;

// pdo: 连接三要素

// 1. dsn
// mysql:host=127.0.0.1;dbname=phpedu;port=3306;charset=utf8;
$tpl = '%s:host=%s;dbname=%s;port=%s;charset=%s';
$args = [$type, $host, $dbname, $port, $charset];
$dsn = sprintf($tpl, ...$args);

// 2. 创建数据对象
$db = new PDO($dsn, $username, $password);
// var_dump($db);
if ($db)  echo '<h2>连接成功</h2>';

config/connect.php

<?php
namespace pdo_edu;
use PDO;

$dbConfig = include 'database.php';
extract($dbConfig);
$tpl = '%s:host=%s;dbname=%s;port=%s;charset=%s';
$args = [$type, $host, $dbname, $port, $charset];
$dsn = sprintf($tpl, ...$args);
//echo $dsn;
$db = new PDO($dsn, $username, $password);
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
// var_dump($db);

跟config文件夹同级创建demo2.php

<?php

namespace pdo_edu;

use PDO;

require __DIR__ . '/config/connect.php';
数据库的操作例子

读操作select,写操作insert update,delete

create table staff(
  id int primary key auto_increment,
  name varchar(10) comment '姓名',
  sex int default 1 comment '性别',
  email varchar(30) comment '邮箱'
)



demo3.php

<?php

namespace php_edu;

/**
 * 数据库常用操作
 * 1. 读操作: select
 * 2. 写操作: insert,update,delete
 * 简称: CURD, 增删改查
 */

// 1. 连接数据库
require __DIR__ . '/config/connect.php';

// 2. CURD: INSERT

/**
 * PDO预处理
 * 为什么要用预处理?
 * 1. 防止SQL注入攻击, 2. 数据延迟绑定
 * (编程时只写SQL语句模板,执行SQL时再给占位符绑定真实数据)
 * 预处理过程:
 * 1. 创建SQL语句模板对象: 数据使用占位符表示
 * 2. 执行SQL语句,根据操作类型(写/读),读返回结果集/数组, 写返回受影响的记录数量
 */

// INSERT 插入
// INSERT 表名 SET 字段1=值1, 字段2=值2, .... 
// SQL语句的推荐规范:
// 1. 关键字全大写
// 2. 表名,字段名使用反引号做为定界符
$sql = 'INSERT `staff` SET `name` = ?, `sex` = ?, `email` = ?';


// 1. 创建SQL语句模板对象
$stmt = $db->prepare($sql);
// 2. 执行SQL语句

$stmt->execute(['小龙女', 1, 'xiaolongnv@php.cn']);
$stmt->execute(['洪七公', 0, 'hongqigong@php.cn']);
$stmt->execute(['黄蓉', 0, 'huangrong@php.cn']);
$stmt->execute(['杨过', 0, 'yangguo@php.cn']);
$stmt->execute(['猪老师', 0, 'zhulaoshi@php.cn']);
$stmt->execute(['灭绝', 0, 'miejue@php.cn']);
// 成功
// $stmt->rowCount(): 返回受影响的记录数量
if ($stmt->rowCount() > 0) {
    echo '新增成功, 新增记录的主键ID = ' . $db->lastInsertId();
} else {
    echo '新增失败';
    print_r($stmt->errorInfo());
}


// INSET INTO table (字段列表) VALUES (值列表)


update

<?php

namespace php_edu;


// 1. 连接数据库
require __DIR__ . '/config/connect.php';

// 2. CURD: UPDATE 更新
// UPDATE 表名 SET 字段1=值1 ... WHERE 更新条件
$sql = 'UPDATE `staff` SET `name` = ? WHERE `id` = ?';

$stmt = $db->prepare($sql);


$stmt->execute(['老顽童', 6]);


if ($stmt->rowCount() > 0) {
    echo '更新成功';
} else {
    echo '更新失败';
    print_r($stmt->errorInfo());
}


delete

<?php

namespace php_edu;


// 1. 连接数据库
require __DIR__ . '/config/connect.php';

// 2. CURD: DELETE 删除
// DELETE FROM 表名 SET 字段1=值1 ... WHERE 更新条件
// $sql = 'DELETE FROM `staff`  WHERE `id` = ? ';
// '?' : 匿名占位符
// 'string': 命名占位符
$sql = 'DELETE FROM `staff`  WHERE `id` = :id';

$stmt = $db->prepare($sql);


$stmt->execute([':id' => $_GET['id']]);

// 如果条件来自外部, 例如 url 中 get 参数 
// echo $_GET['id'];


if ($stmt->rowCount() > 0) {
    echo 'id = ' . $_GET['id'] . ' 删除成功';
} else {
    echo '删除失败';
    print_r($stmt->errorInfo());
}

select

<?php

namespace php_edu;

use PDO;

// 1. 连接数据库
require __DIR__ . '/config/connect.php';

// 2. CURD: SELECT 单条查询
// SELECT 字段列表 FROM 表名 WHERE 查询条件
$sql = 'SELECT `id`,`name` FROM `staff` WHERE `id` > :id';

$stmt = $db->prepare($sql);


$stmt->execute(['id' => 10]);

// 单条查询
// $staff = $stmt->fetch(PDO::FETCH_ASSOC);
// printf('<pre>%s</pre>', print_r($staff, true));
// $staff = $stmt->fetch(PDO::FETCH_ASSOC);
// printf('<pre>%s</pre>', print_r($staff, true));
// $staff = $stmt->fetch(PDO::FETCH_ASSOC);
// printf('<pre>%s</pre>', print_r($staff, true));
// $staff = $stmt->fetch(PDO::FETCH_ASSOC);
// var_dump($staff);

// PDO::FETCH_ASSOC: 结果集获取模式,只返回关联部分
while ($staff = $stmt->fetch()) {
    printf('<pre>%s</pre>', print_r($staff, true));
}

 <?php

namespace php_edu;

use PDO;

// 1. 连接数据库
require __DIR__ . '/config/connect.php';

// 2. CURD: SELECT 多条查询
// SELECT 字段列表 FROM 表名 WHERE 查询条件
$sql = 'SELECT `id`,`name` FROM `staff` WHERE `id` > :id';

$stmt = $db->prepare($sql);


$stmt->execute(['id' => 10]);

// fetchAll: 返回全部满足条件的记录集合,二维数组
$staffs = $stmt->fetchAll();

// print_r($staffs);

foreach ($staffs as $staff) {
    printf('<pre>%s</pre>', print_r($staff, true));
}

文件上传

https://www.php.net/manual/zh/index.php

https://www.php.net/manual/zh/reserved.variables.files.php

允许上传文件的表单特征:
1. method=“POST”
2. 文件编码格式enctype=“multipart/form-data”

$_FILES: PHP超全局变量数量, 保存着上传文件的全部信息

  1. $_FILES: 二维数组,每个元素对应一个上传的文件
  2. name: 原始文件名
  3. type: 文件类型, mime类型
  4. tmp_name: 临时目录
  5. error: 错误代码
  6. size: 文件大小(字节表示 byte)

单文件上传:

<!DOCTYPE html>
<html lang="zh-CN">

<?php
// $_FILES: PHP超全局变量数量, 保存着上传文件的全部信息
printf('<pre>%s</pre>', print_r($_FILES, true));


if (isset($_FILES['my_pic'])) {
    $name = $_FILES['my_pic']['name'];
    $tmpName = $_FILES['my_pic']['tmp_name'];
    $error = $_FILES['my_pic']['error'];

    if ($error > 0) {
        $tips = '<span style="color:red">上传失败:</span>';
        switch ($error) {
            case 1:
                $tips .= '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值';
                break;
            case 2:
                $tips .= '文件大小超过了上传表单中MAX_FILE_SIZE最大值';
                break;
            case 3:
                $tips .= '文件只有部分被上传';
                break;
            case 4:
                $tips .= '没有文件被上传';
                break;
            case 6:
                $tips .= '找不到临时目录';
                break;
            case 7:
                $tips .= '文件写入失败,请检查目录权限';
                break;
        }
        echo "<p>$tips</p>";
    } else {
        // 判断用户是不是通过合法的POST方式上传
        if (is_uploaded_file($tmpName)) {
            // 设置允许上传文件类型的白名单
            $allow = ['jpg', 'jpeg', 'png', 'gif'];
            // 获取文件扩展名
            $ext =  pathinfo($name)['extension'];
            if (in_array($ext, $allow)) {
                // 二个条件都满足了
                // 1. post方式上传的 2. 文件类型是合法的

                // 目标目录
                $path = 'uploads/';
                // 自定义目标文件名
                $dest = $path . md5($name) . '.' . $ext;

                // 将文件从临时目录中移动到目标目录中并重命名
                if (move_uploaded_file($tmpName, $dest)) {
                    echo '<p style="color:green">上传成功</p>';
                    // 预览
                    echo "<img src='$dest' width='200' >";
                } else {
                    echo '<p style="color:red">移动失败</p>';
                }
            } else {
                echo '<p style="color:red">文件类型错误</p>';
            }
        } else {
            echo '<p style="color:red">非法方式上传</p>';
        }
    }
}
?>

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传的表单构造,超全局变量$_FILE</title>


</head>

<body>
    <!-- 允许上传文件的表单特征:
    1. method="POST"
    2. enctype="multipart/form-data" -->
    <form action="" method="POST" enctype="multipart/form-data">
        <fieldset>
            <legend>单文件上传</legend>
            <!-- 浏览器中限制上传文件的大小,写到一个隐藏域中,并写到type=file之前  -->
            <input type="hidden" name="MAX_FILE_SIZE" value="300000">
            <input type="file" name="my_pic">
            <button>上传</button>
        </fieldset>
    </form>
</body>

</html>

多文件上传

<!DOCTYPE html>
<html lang="zh-CN">

<?php
// $_FILES: PHP超全局变量数量, 保存着上传文件的全部信息
printf('<pre>%s</pre>', print_r($_FILES, true));

foreach ($_FILES as $file) {
    // $file中保存着每一个文件的信息
    if ($file['error'] === 0) { // error等于0 表示上传成功
        $destFile = 'uploads/' . $file['name'];
        move_uploaded_file($file['tmp_name'], $destFile);
        echo "<img src='$destFile' width='200'>";
    }
}


?>

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多文件上传1</title>


</head>

<body>
    <form action="" method="POST" enctype="multipart/form-data">
        <fieldset>
            <legend>多文件上传:逐个上传</legend>
            <input type="file" name="my_pic1">
            <input type="file" name="my_pic2">
            <input type="file" name="my_pic3">
            <button>上传</button>
        </fieldset>
    </form>
</body>

</html>

<!DOCTYPE html>
<html lang="zh-CN">

<?php
// $_FILES: PHP超全局变量数量, 保存着上传文件的全部信息
printf('<pre>%s</pre>', print_r($_FILES, true));

if (isset($_FILES['my_pic'])) {
    // 这时只需要遍历 $_FILES['my_pic']['error'] 这个数组
    foreach ($_FILES['my_pic']['error'] as $key => $error) {
        if ($error === 0) {//error等于0 表示上传成功
            // 临时文件名
            $tmpName = $_FILES['my_pic']['tmp_name'][$key];
            // 原始文件名
            $name = $_FILES['my_pic']['name'][$key];
            // 目标文件名
            $destFile = 'uploads/' . $name;
            move_uploaded_file($tmpName, $destFile);
            echo "<img src='$destFile' width='200'>";
        }
    }
}




?>

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多文件上传2</title>


</head>

<body>
    <form action="" method="POST" enctype="multipart/form-data">
        <fieldset>
            <legend>多文件上传:逐个上传2</legend>
            <!-- 数组来表示每个要上传的表单域的名称  -->
            <input type="file" name="my_pic[]">
            <input type="file" name="my_pic[]">
            <input type="file" name="my_pic[]">
            <button>上传</button>
        </fieldset>
    </form>
</body>

</html>


批量上传

<!DOCTYPE html>
<html lang="zh-CN">

<?php
// $_FILES: PHP超全局变量数量, 保存着上传文件的全部信息
printf('<pre>%s</pre>', print_r($_FILES, true));

if (isset($_FILES['my_pic'])) {
    // 这时只需要遍历 $_FILES['my_pic']['error'] 这个数组
    foreach ($_FILES['my_pic']['error'] as $key => $error) {
        if ($error === 0) {
            // 临时文件名
            $tmpName = $_FILES['my_pic']['tmp_name'][$key];
            // 原始文件名
            $name = $_FILES['my_pic']['name'][$key];
            // 目标文件名
            $destFile = 'uploads/' . $name;
            move_uploaded_file($tmpName, $destFile);
            echo "<img src='$destFile' width='200'>";
        }
    }
}



?>

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多文件上传2</title>


</head>

<body>
    <form action="" method="POST" enctype="multipart/form-data">
        <fieldset>
            <legend>多文件上传:批量上传</legend>
            <!-- multiple: 允许同时选择多个  -->
            <input type="file" name="my_pic[]" multiple>
            <button>上传</button>
        </fieldset>
    </form>
</body>

</html>


分页

demo5.php

<?php
// 1. 连接数据库
$db = new PDO('mysql:dbname=phpedu', 'root', 'root');

// 2. 当前页, 在GET参数中
// https://www.php.cn/course.html?p=5
// $page = isset($_GET['p']) ? $_GET['p'] : 1;
// null合并
$page = $_GET['p'] ?? 1;
echo "当前页: p= $page <br>";

// 3. 每页显示数量
$num = 5;

// 4. 记录总数
$sql = 'SELECT COUNT(`id`) AS `total` FROM `staff`';
$stmt = $db->prepare($sql);
$stmt->execute();
// 将某列的仠与php变量绑定 , `total` => $total
$stmt->bindColumn('total', $total);
$stmt->fetch(PDO::FETCH_ASSOC);
echo "总记录数量: $total <br>";

// 5. 总页数
// 10.1 => 11 ceil: 向上取整,不丢数据
$pages = ceil($total / $num);
echo "总页数: $pages <br>";

// 6. 偏移量
// offset = (page - 1) * num
$offset = ($page - 1) * $num;
echo "偏移量: $offset <br>";

// 7. 分页数据
// $sql = "SELECT * FROM `staff` LIMIT $num OFFSET $offset";
$sql = "SELECT * FROM `staff` LIMIT $offset, $num";
$stmt = $db->prepare($sql);
$stmt->execute();
$staffs = $stmt->fetchAll(PDO::FETCH_ASSOC);

// 遍历
echo '<hr>';
if (count($staffs) === 0) {
    echo '查询结果为空';
} else {
    foreach ($staffs as $staff) {
        extract($staff);
        printf('$d-%s-%s-%s<br>', $id, $name, $sex, $email);
    }
}
echo '<hr>';


demo6.php

<?php require 'demo5.php' ?>
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>页面展示分页数据</title>
    <style>
        table {
            width: 400px;
            border-collapse: collapse;
            text-align: center;
        }

        table th,
        table td {
            border: 1px solid;
            padding: 5px;
        }

        table thead {
            background-color: lightcyan;
        }

        table caption {
            font-size: larger;
            margin-bottom: 8px;
        }

        p>a {
            text-decoration: none;
            color: #555;
            border: 1px solid;
            padding: 5px 10px;
            margin: 10px 2px;
        }

        .active {
            background-color: seagreen;
            color: white;
            border: 1px solid seagreen;
        }
    </style>
</head>

<body>
    <table>
        <caption>员工信息表</caption>
        <thead>
            <tr>
                <th>ID</th>
                <th>姓名</th>
                <th>性别</th>
                <th>邮箱</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($staffs as $staff) : extract($staff) ?>
                <tr>
                    <td><?= $id ?></td>
                    <td><?= $name ?></td>
                    <td><?= $sex ?></td>
                    <td><?= $email ?></td>
                </tr>
            <?php endforeach ?>
        </tbody>
    </table>
    <p>
        <?php for ($i = 1; $i <= $pages; $i++) : ?>
            <!-- <a href="<?= $_SERVER['PHP_SELF'] . '?p=' . $i ?>" class="<?= ($i == $_GET['p']) ? 'active' : null ?>"><?= $i ?></a> -->

            <?php
            $url = $_SERVER['PHP_SELF'] . '?p=' . $i;
            $active = $i == $_GET['p'] ? 'active' : null;
            ?>
            <a href="<?= $url ?>" class="<?= $active ?>"><?= $i ?></a>

        <?php endfor ?>
    </p>
</body>

</html>
php的会话控制

这个是cookie的用法

    <?php
    // cookie: 在客户端(浏览器)保存用户信息

    // 第一次访问一个php脚本文件
    // 那么这个php可以通过一个函数来给客户端设置cookie
    // 服务器识别用户,是通过用户使用的终端/浏览器来识别

    setcookie('username', 'Peter-Zhu', time() + 60, '/');

    echo $_COOKIE['username'] . '<br>';

    $_COOKIE['username'] = 'admin';

    echo $_COOKIE['username'] . '<br>';

    // $_COOKIE['username'] = null;
    unset($_COOKIE['username']);

    echo $_COOKIE['username'] ?? '没找到' . '<br>';

    // 实际工作中, 为了用户数据的安全, 应该将用户资料保存到服务器上

实际工作中, 为了用户数据的安全, 应该将用户资料保存到服务器上

这才是session的用法

在php.ini 里的

这个参数可以修改session的位置
session.save_path = "/tmp"
改成
session.save_path = "D:/PHP/session/"


<?php

// 发一张好人卡给用户的访问终端
// 好人卡,就是用户使用设备的ID

// 开启一个会话
session_start();

/**
 * 执行二个动作
 * 1. 浏览器: PHPSESSID, 基于cookie
 * 2. 服务器: 创建一个与PHPSESSID同名的会话文件
 */

$_SESSION['email'] = 'admin@php.cn';
$_SESSION['password'] = md5(md5('123456') . 'php.cn888');

// $_SESSION = [];

// 直接将服务器上的会话文件删除,如果不把这个 session_destory()注释掉,你就看不到你的session 了,在文件夹里
session_destroy();


index.php

<?php
session_start();
// 判断用户是否已经登录?
if (isset($_SESSION['user'])) $user = unserialize($_SESSION['user']);
// print_r($user);
?>
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <nav>
        <?php if (isset($user)) : ?>
            <a href="" id="logout">退出</a>

        <?php else : ?>
            <a href="login.php">登录</a>
        <?php endif ?>
    </nav>
    <script>
        document.querySelector('#logout').addEventListener('click', function(event) {
            if (confirm('是否退出?')) {
                // 禁用默认跳转行为
                event.preventDefault();
                // 跳转到处理器
                location.assign('handle.php?action=logout');
            }
        });
    </script>
</body>

</html>


login.php

<!DOCTYPE html>
<html lang="zh-CN">

<?php
session_start();
// 判断用户是否已经登录?
if (isset($_SESSION['user'])) echo '<script>alert("不要重复登录");locatoin.href="index.php"</script>';

?>

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户登录表单</title>
</head>

<body>

    <form action="handle.php?action=login" method="POST">
        <fieldset style="display: inline-block;background:lightcyan">


            <legend>用户登录</legend>
            <p>
                <input type="email" name="email" placeholder="user@email.com" require>
            </p>
            <p>
                <input type="password" name="password" placeholder="不少于6位" require>
            </p>

            <p>
                <button>提交</button>
            </p>

        </fieldset>

        <a href="register.php">如果没有帐号,请先登录</a>
    </form>


</body>

</html>


register.php

<!DOCTYPE html>
<html lang="zh-CN">

<?php
session_start();
// 判断用户是否已经登录?
if (isset($_SESSION['user'])) echo '<script>alert("不要重复登录");locatoin.href="index.php"</script>';

?>

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户注册表单</title>
</head>

<body>

    <form action="handle.php?action=login" method="POST">
        <fieldset style="display: inline-block;background:lightcyan">


            <legend>用户注册</legend>
            <p>
                <input type="email" name="email" placeholder="user@email.com" require>
            </p>
            <p>
                <input type="password" name="password" placeholder="不少于6位" require>
            </p>
            <p>
                <input type="password" name="password" placeholder="二次必须一致" require>
            </p>

            <!-- 二次密码是否一致用JS进行验证就可以了  -->
            <p>
                <button>提交</button>
            </p>

        </fieldset>
    </form>


</body>

</html>


handle.php

<?php
// 开启会话
session_start();
// 根据用户的不同请求,执行不同的操作

// 比如:登录 , 注册, 退出

// 连接数据并获取用户表中的数据
$db = new PDO('mysql:dbname=phpedu', 'root', 'root');
$stmt = $db->prepare('SELECT * FROM `user`');
$stmt->execute();
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

// print_r($users);

$action = $_GET['action'];

switch (strtolower($action)) {
        // 登录
    case 'login':
        //要保证数据是通用POST请求发送的
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            // 先拿到登录数据
            extract($_POST);
            // $email = $_POST['email'];
            // $password = sha1($_POST['password']);

            // $result 是数组 
            $result =  array_filter($users, function ($user) use ($email, $password) {
                return  $user['email'] === $email && $user['password'] === md5($password);
            });





            if (count($result) === 1) {
                // 验证成功,将用户信息写到SESSION
                // print_r(serialize(array_pop($result)));
                // $a = serialize(array_pop($result));
                // print_r(unserialize($a));

                // 将用户信息序列化之后保存到SESSION中
                $_SESSION['user'] = serialize(array_pop($result));
                exit('<script>alert("验证通过");location.href="index.php"</script>');
            } else {
                exit('<script>alert("邮箱或密码错误");location.href="index.php"</script>');
            }
        } else {
            die('请求错误');
        }
        break;

        // 退出
    case 'logout':
        if (isset($_SESSION['user'])) {
            session_destroy();
            exit('<script>alert("退出成功");location.assign("index.php")</script>');
        }

        break;

        // 注册
    case 'register':
        // 1. 获取到新用户的信息
        $name = $_POST['name'];
        $email = $_POST['email'];
        $password = md5($_POST['psw1']);
        $register_time = time();

        // 2. 将新用户添加到表中
        $sql = 'INSERT user SET name=?,email=?,password=?,register_time=?';
        $stmt = $db->prepare($sql);
        $stmt->execute([$name, $email, $password, $register_time]);
        if ($stmt->rowCount() === 1) {
            echo '<script>alert("注册成功");locaton.href="index.php"</script>';
        } else {
            exit('<script>alert("注册失败");locaton.href="index.php"</script>');
        }

        break;
}

php的面向对象

文件包含

方式1 用include     inc目录下建立一个f1.php

inc/f1.php

<?php
$username ='朱元璋';
return $email ='1234@qq.com';


demo1.php

<?php
include 'inc/f1.php';

echo $username  .'<br>';
echo $email  .'<br>';

```
方式2  用 绝对路径
<?php
include __DIR__  . '/inc/f1.php';
echo $username  .'<br>';
echo $email  .'<br>';

```
这里使用了  f1.php 里return 返回的值
<?php
include __DIR__  . '/inc/f1.php';
echo $username  .'<br>';
$email = include __DIR__  . '/inc/f1.php' ;

echo $email  .'<br>';
```



<?php
require __DIR__  . '/inc/f1.php';
echo $username  .'<br>';
$email = require __DIR__  . '/inc/f1.php' ;

echo $email  .'<br>';
```

include 和 require的区别:

include 用到的时候再加载,动态的; require 应该写在顶部,静态;一般用require多一点,需要出错不执行。

<?php

// 类声明
class Goods
{

}
//类的实例化:创建对象的过程
$goods = new Goods;
var_dump($goods);

echo '<hr>';

echo get_class($goods) .'<br>';

// 动态类
$str = 'goods';

$class = ucfirst('goods');

$o = new $class;
var_dump($o);
var_dump($o instanceof Goods);


类的成员: (1) 常规成员:属性,方法。

<?php

/**
 * 类成员
 * (一) 常规成员
 *  1. 属性: 可以被其它方法所共享
 *  2. 方法: 自定义, 魔术方法
 * 
 * 访问限制符:
 *  1. private: 私有,仅限在当前类中使用
 *  2. protected: 继承, 可在本类或子类中使用, 不对外公开
 *  3. public: 公共/公共,默认
 * 
 * (二) 静态成员
 *  1. static, 静态属性, 静态方法
 *  2 . 与类实例无关, 静态成员 不要用$this
 *  3 . 内部用self, 外部用类名
 *  
 */

class User1
{
    // 常规成员
    // 1. 属性
    // 语法: 访问限制符 类成员声明
    // 声明为私有, 实现封装
    private $username;

    // 2. 方法
    // 2.1 自定义方法: 用户自己声明自己调用
    public function getUsername()
    {

        // -> 对象成员访问符
        // 当前实例的引用, $this 与实例绑定的
        return $this->username;
    }

    // 2.2 魔术方法: 由系统来调用,用户不能直接调用
    // 构造方法: 实例化进自动调用
    public function __construct(string $username)
    {
        $this->username = $username;
    }
}


// 实例化
$user1 = new User1('张老师');
echo $user1->getUsername();


echo '<hr>';

class User2
{
    // 静态属性
    private static $username = 'admin';


    // 类常量: 与实例无关,用类访问
    const APP = '商城';

    // 方法
    public static function getUsername()
    {

        // self: 功能与$this类似, 与当前类 User2 绑定
        return self::$username;
    }
}

// 实例访问静态方法是一个兼容bug, 能成功,但不要再用了
// echo (new User2)->getUsername();
// 应该永远用类来访问静态成员
echo User2::getUsername();
echo User2::APP;

<?php

/**
 * 类的扩展/抽象/最终
 * 1. protected: 受保护/可继承
 * 2. extends: 扩展/继承
 * 3. parent: 父类引用
 * 4. abstract: 抽象
 * 5. final: 最终类
 */

// 父类, 基类, 超类
class Person
{
    // protected: 成员可继承,可以在子类中使用
    protected $name;
    // private: 私有, 仅限当前类, 子类,外部都不可见
    private $id = 12345;
    // public: 类中,子类, 类外都可见
    public function __construct($name)
    {
        $this->name = $name;
    }

    // getInfo::protected
    // 比protected再严格的是 private, 比它更宽松的是: public
    protected function getInfo()
    {
        return $this->name;
    }
}

// 学生类
// extends: Stu这个类,扩展了Person类的功能 
class Stu extends Person
{
    // 1. 属性扩展
    private $lesson;
    private $score;

    // 2. 方法扩展/重写
    public function __construct($name, $lesson, $score)
    {
        // 引用了父类的构造方法
        // parent: 父类引用 Person
        parent::__construct($name);
        $this->lesson = $lesson;
        $this->score = $score;
    }

    public function getInfo()
    {
        // $this->name 
        // return $this->name . "同学, ($this->lesson : $this->score 分)";
        return parent::getInfo() . "同学, ($this->lesson : $this->score 分)";
    }
}

$stu = new Stu('小狗', 'PHP', 88);
echo $stu->getInfo();

echo '<hr>';
$person = new Person('小猪');
// var_dump只打印属性
var_dump($person);
echo '<hr>';

// 如果不想让用户直接使用父类,而必须通过继承/扩展的子类来间接使用
// 将父类声明为一个抽象类
abstract class Demo1
{
}

// (new Demo1);

class Demo2 extends Demo1
{
}
echo 'Demo2的父类是: ' . get_parent_class(new Demo2);

echo '<hr>';

abstract class Demo3
{
    // hello 方法已经被实现了
    // protected function hello()
    // {
    //     // ...
    // }

    // 抽象方法: 只有方法名,参数列表,没有具体实现(大括号)
    abstract protected function hello($name);
}

class Demo4 extends Demo3
{
    // 工作类Demo4中必须实现父类中的抽象成员
    public function hello($name)
    {
        return 'Hello , ' . $name;
    }
}

echo call_user_func([new Demo4, 'hello'], '牛老师');

echo '<hr>';

// 如果一个类不用扩展,直接当成工作类/直接干活的,直接new
// 为了防止被继承, 可声明为最终类
// final class Demo5
// {
// }

// class Demo6 extends Demo5
// {
// }

<?php
// 接口: 大号的抽象类
// 接口的所有成员,必须是抽象
// interface: 声明接口
// implements: 实现接口
interface iUser
{
    // 1. 类常量
    const NATION = 'CHINA';

    // 2. 必须是抽象,必须是public
    public function m1();
    public function m2();

    // 官方手册: 不建议声明抽象构造方法
}

// 接口不能直接用, 要用一个类来实现它

// 1. 用普通类来实现一个接口, 必须将接口中的所有抽象方法全部实现
class Demo1 implements iUser
{
    public function m1()
    {
    }
    public function m2()
    {
    }
}

// 2. 用抽象类来实现一个接口, 允许有不实现的抽象成员
abstract class Demo2 implements iUser
{
    public function m1()
    {
    }
}

// php默认只支持单继承
// class A extends B, C , D 

// 可通过接口,间接实现多继承
interface A
{
}
interface B
{
}
interface C
{
}

class Test implements A, B, C
{
}

// 查看当前类实现的所有接口
$arr = class_implements('Test');

printf('<pre>%s</pre>', print_r($arr, true));


属性和方法的重载命、名空间和自动加载
<?php

class User
{

    //属性
    private $data=[
        'age' =>20
    ];

//查询拦截器
    public function __get($name)
    {
        // $name  :属性名
        if(array_key_exists($name,$this->data))
        {
            return $this->data[$name];
        }
        return "属性 {$name} 不存在";
    }

    //更新拦截器
    public function __set($name,$value)
    {
        // 1 有没有这个属性
        if(array_key_exists($name,$this->data))
        {
            if($name==='age')
            {
                if($value>=18 && $value <=59)
                {
                    $this->data[$name]=$value;
                }else{
                    echo '年龄必须在18-59之间';
                }

            }else{
                // 以上操作仅对age有效,其它属性直接赋值
                $this->data[$name]=$value;
            }

        }else{
            echo '禁止动态创建属性';
        }

    }


     // 方法拦截器,使用 __call,当访问一个方法的时候默认会调用它
     // 当类里有一个方法拦截器后,调用一个不存在的方法的时候就不会报异常了
     public function __call($name, $args)
     {
         // $name: 方法名, $args: 传给方法的参数
         printf('方法: %s<br>参数:<pre>%s</pre>', $name, print_r($args, true));
     }
 
     // 静态方法拦截器 使用 __callStatic,当访问一个静态方法的时候默认会调用它
     public static function __callStatic($name, $args)
     {
         // $name: 方法名, $args: 传给方法的参数
         printf('方法: %s<br>参数:<pre>%s</pre>', $name, print_r($args, true));
     }

}


$user = new User;

// 这个会打印属性name 不存在
echo $user->name . '<br>';

/**
 * 为一个属性赋值的时候,必须要搞清楚2件事
 * 1. 有没有这个属性?
 * 2. 这个值是否合法?
 */
$user->age = 80;
echo '       打印值age :'. $user->age . '<br>';


echo '<hr color=red>';

// 常规方法/非静态方法/用实例调用
$user->hello('猪老师', 'php.cn');


// 静态方法
User::world(100, 200);

方法重载小案例

<?php
// 方法重载的小案例
// Db::table('think_user')->where('id', 1)->find();

// 1. 查询类
class Query
{
    public function table($table)
    {
        // 返回当前类实例,方便后面的链式调用
        return $this;
    }
    public function where($where)
    {
        return $this;
    }
    public function find()
    {
        // 不需要返回 $this,它最一个最终方法,输出查询结果
        // 用数组来模拟数据表的查询结果
        return ['id' => 1, 'name' => '猪老师', 'email' => '498668472@qq.com'];
    }
}

$query = new Query;

// $query->table('think_user');
// $query->where('id', 1);
// $query->find();

$query->table('think_user')->where('id', 1)->find();
// Db::table('think_user')->where('id', 1)->find()

// 2. 入口类: 实现请求转发
class Db
{
    // 静态方法重载/重定向
    public static function __callStatic($name, $args)
    {
        // $query = new Query;
        // return   $query->$name($args);

        // 回调的方式来执行对象方法
        return call_user_func_array([new Query, $name], $args);
    }
}

$res = Db::table('think_user')->where('id', 1)->find();
printf('<pre>%s</pre>', print_r($res, true));
命名空间

文件名相同,但是路径不同,所以不是同一个文件。

一个文件中,只允许声明一个命名空间;
命名空间的命名,应该与成员的路径一致;

inc/f1.php

<?php
 function hello()
 {
     return __FUNCTION__;
 }

const APP = '商城';

 class Demo1
 {
 }

<?php

/**
 * 命名空间: 解决了全局成员的命名冲突
 * 全局成员: 类/接口, 常量 , 函数
 */


require __DIR__ . '/inc/f1.php';

这里会报错的,因为函数默认是全局的
function hello()
{
    return __FUNCTION__;
}

这里会报错的,因为常量默认是全局的
const APP = '社区';
echo APP;

这里会报错,类/接口 是全局成员,不能重复声明
class Demo1
{
}


inc/f2.php

<?php
namespace ns1;
// 如果没有写命名空间,默认在全局
function hello($name)
{
    return 'Hello 1111 ' . $name;
}



demo4.php
<?php
// 命名空间,使用namespace声明,必须写到第一行
 namespace ns1;

require __DIR__ . '/inc/f2.php';
echo hello('猪老师') . '<br>';


demo4.php
<?php
require __DIR__ . '/inc/f2.php';
//命名空间写在了方法前面,指明使用ns1下的hello
echo \ns1\hello('猪老师') . '<br>';

demo5.php

// 一个脚本中,可以创建多个空间
namespace ns1 {
    // 空间成员
    //常量 
    const APP = '商城';
}


// 空间分级管理: 子空间
namespace ns2\ns3 {
    const APP = '问答';
    echo APP . '<br>';
}

namespace ns2 {
    // 空间成员
    //常量 
    const APP = '社区';

    // 2. 非限定名称: 总是从当前空间开始查询
    echo APP . '<br>';
    echo \ns2\APP . '<br>';

    // 在ns2中访问 ns1的APP
    // 一定要通过全局空间/根空间进行访问 
    // 根空间: \
    // 1. 完全限定名称: 从根空间开始查询
    echo \ns1\APP . '<br>';

    // 在ns2空间, 访问子空间  ns2\ns3中的成员
    // 3. 限定名称: ns3\APP
    echo '<span style="color:red">' . ns3\APP . '</span><br>';
}


/**
 * 命名空间类型
 * 1. 完全限定名称: 根空间开始 '\a\b\APP'  "绝对路径"
 * 2. 非限定名称: 从当前空间开始, 'APP'     "当前路径"
 * 3. 限定名称: 子空间, 'ns\APP'          "相对路径"
 */

// 全局空间: 匿名的,不要写空间名, 用"\"来引用
namespace {
    function hello()
    {
        echo 'hello 大家好';
    }

    echo '<span style="color:blue">' . ns1\APP . '</span><br>';
    echo '<span style="color:coral">' . \ns2\ns3\APP . '</span><br>';
}



php/cn/Demo.php

<?php

/**
 * 命名空间
 * 1. 一个文件中, 只允许声明一个命名空间并只写一个类
 * 2. 命名空间的命名,应该与成员的路径一致
 */

// 当前类文件的路径: php/cn/
//  和当前类的命名空间相似
namespace php\cn;

class Demo
{
}

echo Demo::class . '<br>';

php/cn/Demo1.php

<?php

/**
 * 命名空间
 * 1. 一个文件中, 只允许声明一个命名空间并只写一个类
 * 2. 命名空间的命名,应该与成员的路径一致
 */

// 当前类文件的路径: php/cn/
//  和当前类的命名空间相似
namespace php\cn;

class Demo1
{
}

echo Demo1::class . '<br>';;

php/cn/Demo2.php

<?php

/**
 * 命名空间
 * 1. 一个文件中, 只允许声明一个命名空间并只写一个类
 * 2. 命名空间的命名,应该与成员的路径一致
 * 3. 类名,必须与类文件名对应
 */

// 当前类文件的路径: php/cn/
//  和当前类的命名空间相似
namespace php\cn;

class Demo2
{
}

echo Demo2::class . '<br>';

demo8.php


/**
 * 命名空间
 * 1. 一个文件中, 只允许声明一个命名空间并只写一个类
 * 2. 命名空间的命名,应该与成员的路径一致
 * 3. 类名,必须与类文件名对应
 */

<?php

namespace php\cn;

class Demo
{

}

echo Demo::class;

demo8.php

<?php

namespace ns1;

require __DIR__ . '/php/cn/Demo.php';
require __DIR__ . '/php/cn/Demo1.php';
require __DIR__ . '/php/cn/Demo2.php';

如果文件太多那么是不是要写很多require,那么使用自动加载

autoloader.php

<?php

// 类文件自动加载器
// 注册一个类的自动加载器
spl_autoload_register(function ($class) {
    // echo $class;
    // 1. 将命名空间=>映射到一个类文件的绝对路径
    $path = str_replace('\\', DIRECTORY_SEPARATOR, $class);

    // 2. 生成类文件路径 
    $file = __DIR__ . DIRECTORY_SEPARATOR . $path . '.php';

    // 3. 加载这个类文件
    require $file;
});

demo8.php

<?php

namespace ns1;

require __DIR__ . '/autoloader.php';

class Demo2
{
}

//访问Demo2
 new \php\cn\Demo2;
echo Demo2::class . '<br>';
echo \php\cn\Demo2::class;

Composer的使用

Composer:https://www.phpcomposer.com/

Composer 是包依赖管理器

下载php依赖的地址:http://packagist.p2hp.com/

php.ini 修改了xdebug的配置

[xdebug]
zend_extension="D:\PHP\php-8\ext\php_xdebug-3.2.0-8.1-vs16-x86_64.dll"

// 这些参数被我注释掉了
;xdebug.remote_enable = 1
;xdebug.remote_autostart = 1
;xdebug.mode=debug
;xdebug.start_with_request=yes


这个命令可以升级 composer
composer selfupdate

这个命令来查看版本号
composer --version


composer install - 如有 composer.lock 文件,直接安装,否则从 composer.json 安装最新扩展包和依

composer update - 从 composer.json 安装最新扩展包和依赖;

第一步,我们在项目根目录里创建一个配置文件,文件的名字叫composer.json

也可以直接进入空目录,composer init 来创建文件

把依赖的下载地址换成阿里云的
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

把依赖的下载地址换成腾讯云的
composer config -g repos.packagist composer https://mirrors.cloud.tencent.com/composer/

vendor/composer/
    autoload_classmap.php    类用这个操作
    autoload_namespaces.php
    autoload_psr4.php
    autoload_real.php  操作文件用这个
    autoload_static.php
    ClassLoader.php
    installed.json
    instead.php
    InstalledVersions.php
    LICENSE
vendor/
    autoload.php

在composer.json里写

{
    "name": "hiapad/composer-demo",
    "autoload": {
        "psr-4": {
            "Hiapad\\ComposerDemo\\": "src/"
        },
        //这里是我们添加上的 lib 和 src 
        "classmap": ["lib/","src/"]
    },
    "authors": [
        {
            "name": "guiyongbin",
            "email": "739952043@qq.com"
        }
    ],
    "require": {
        "zyan/captcha": "*"
    }
}


然后执行 composer dumpautoload

会在vendor/composer/autoload_classmap.php里生成内容

<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

//这就是生成的内容
return array(
    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
    'ns1\\Test1Class' => $baseDir . '/src/Test1.php',
    'ns2\\Test2Class' => $baseDir . '/src/Test2.php',
    'ns3\\Test1Class' => $baseDir . '/lib/Test1.php',
    'ns4\\Test2Class' => $baseDir . '/lib/Test2.php',
);


demo.php 要使用composer的自动加载器

<?php

namespace demo1;

// 使用composer的自动加载器
require __DIR__ . '/vendor/autoload.php';

echo \ns1\Test1Class::show() .'<br>';
echo \ns2\Test2Class::show() .'<br>';

echo \ns3\Test1Class::show() .'<br>';
echo \ns4\Test2Class::show() .'<br>';


config/database.php 这是一个文件,不是一个类,那么如何加载文件呢

<?php

const DB = [
    'type' => 'mysql',
    'host' => 'localhost'
];


func/helper.php

<?php
// 公共函数库
function D($name)
{
    var_dump($name);
}

在composer.json 里

{
    "name": "hiapad/composer-demo",
    "autoload": {
        "psr-4": {
            "Hiapad\\ComposerDemo\\": "src/"
        },
        "classmap": ["lib/","src/"],
        
        // 这个就是加载文件的
        "files": ["config/database.php", "func/helper.php"]
    },
    "authors": [
        {
            "name": "guiyongbin",
            "email": "739952043@qq.com"
        }
    ],
    "require": {
        "zyan/captcha": "*"
    }
}

执行composer dumpautoload

demo.php

<?php

namespace demo1;


// 使用composer的自动加载器
require __DIR__ . '/vendor/autoload.php';

echo \ns1\Test1Class::show() .'<br>';
echo \ns2\Test2Class::show() .'<br>';
echo \ns3\Test1Class::show() .'<br>';
echo \ns4\Test2Class::show() .'<br>';


echo '<hr color=red>';

echo DB['host'] . '<br>';
//使用D 函数打印一个数组
echo D(['id' => 1, 'usrename' => 'admin']);

PSR-4 类的命名空间与类文件路径进行映射

创建文件夹 admin/controller
创建文件 admin/controller/admin.php

<?php

namespace admin\controller;

class User1
{
    public static function index()
    {
        return '当前类名是: ' . __CLASS__;
    }
}


composer.json

{
    "name": "hiapad/composer-demo",
    "autoload": {
        "psr-4": {
            "Hiapad\\ComposerDemo\\": "src/",
        // 这样把admin 这个命名空间和目录admin 进行映射
        "admin\\": "admin/"
        },
        "classmap": ["lib/","src/"],
        "files": ["config/database.php", "func/helper.php"]
        
 
    },
    "authors": [
        {
            "name": "guiyongbin",
            "email": "739952043@qq.com"
        }
    ],
    "require": {
        "zyan/captcha": "*"
    }
}

demo1.php

<?php

namespace demo1;

// require __DIR__ . '/src/Test1.php';
// require __DIR__ . '/src/Test2.php';
// require __DIR__ . '/lib/Test1.php';
// require __DIR__ . '/lib/Test2.php';

// 使用composer的自动加载器
require __DIR__ . '/vendor/autoload.php';

echo \ns1\Test1Class::show() .'<br>';
echo \ns2\Test2Class::show() .'<br>';

echo \ns3\Test1Class::show() .'<br>';
echo \ns4\Test2Class::show() .'<br>';


echo '<hr color=red>';

echo DB['host'] . '<br>';
//使用D 函数打印一个数组
echo D(['id' => 1, 'usrename' => 'admin']);

// PSR-4: 类的命名空间与类文件路径进行映射

echo \admin\controller\User1::index() . '<br>';