增 append（最后） insert(位置，内容)删 clear pop(2) remove("内容") del lis[2]改 list[0]= "ddd"查 index("") count list[]
最新版本的phpoffice组件最低版本要求PHP8.0，所以需要安装旧版本的，这里测试挑选了一个1.23的版本可以使用，在项目根目录composer运行安装组件：composer require phpoffice/phpspreadsheet:1.23应用案例：首先定义一个导出表格的函数#[NoReturn] function exportExcel($expTitle, $expCellName, $expTableData, $setWidth = []): void { $spreadsheet = new Spreadsheet(); $spreadsheet->getActiveSheet()->setTitle($expTitle . date('Ymd')); $cellNum = count($expCellName); $len = count($expTableData); // 设置表头字段 foreach ($expCellName as $k => $v) { $spreadsheet->getActiveSheet()->setCellValueByColumnAndRow($k + 1, 1, $v); } // 添加数据 foreach ($expTableData as $k => $v) { for ($i = 0; $i < $cellNum; $i++) { $spreadsheet->getActiveSheet()->setCellValueByColumnAndRow($i + 1, $k + 2, $expTableData[$k][$expCellName[$i]]); } } // // 添加所有边框/居中 // $styleArrayBody = [ // 'borders' => [ // 'allBorders' => [ // 'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN, // 'color' => ['argb' => '333333'], // ], // ], // 'alignment' => [ // 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER, // ], // ]; // $total_rows = $len + 1; // 表格总行数要加上表头一栏 // $spreadsheet->getActiveSheet()->getStyle('A1:' . end($setWidth) . $total_rows)->applyFromArray($styleArrayBody); // // 设置列宽 // if ($setWidth) { // foreach ($setWidth as $k => $v) { // $spreadsheet->getActiveSheet()->getColumnDimension($v)->setAutoSize(true); // 列宽自定义 // } // } else { // $spreadsheet->getActiveSheet()->getDefaultColumnDimension()->setWidth(14); // } $filename = $expTitle . date('Ymd') . '.xlsx'; header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="' . $filename . '"'); header('Cache-Control: max-age=0'); $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx'); $writer->save('php://output'); die(); }其次处理数据// 导出数据 $list = Db::table('user')->select(); $columns = [ 'id' => 'ID', 'username' => '用户名', 'age' => '年龄', 'gender' => '性别', ]; $columns_fields = array_keys($columns); // 导出数据到csv文件 $xlsData = []; // 导出数据到excel文件 foreach ($list as $key => $value) { foreach ($columns_fields as $k => $v) { if (isset($value[$v])) { $xlsData[$key][$columns[$v]] = $value[$v]; } } } // 编辑表格的title，返回的数据格式是：$xlsCell = ['id', 'username', 'age'] $length = count($xlsData); $xlsCell = []; foreach ($xlsData[$length - 1] as $k => $v) { $xlsCell[] = $k; } // 你的表格一共有多少字段信息需要导出，设置对应的excel坐标，动态判断，从A到Z $cellNum = count($xlsCell); $setWidth = []; for ($i = 0; $i < $cellNum; $i++) { $setWidth[] = chr(ord('A') + $i); } // 调用自定义方法 exportExcel('用户表', $xlsCell, $xlsData, $setWidth);
PHP根据经纬度计算两点距离与方圆范围方法
经常遇到根据用户当前位置来计算出与用户之间的距离，或者给用户展示方圆一公里的店铺商家之类的功能。所以在此记录一下方法。/** * 求两个已知经纬度之间的距离,单位为米 * * @param lng1 $ ,lng2 经度 * @param lat1 $ ,lat2 纬度 * @return float 距离，单位米 */ function getdistance($lng1, $lat1, $lng2, $lat2) { // 将角度转为狐度 $radLat1 = deg2rad($lat1); //deg2rad()函数将角度转换为弧度 $radLat2 = deg2rad($lat2); $radLng1 = deg2rad($lng1); $radLng2 = deg2rad($lng2); $a = $radLat1 - $radLat2; $b = $radLng1 - $radLng2; $s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))) * 6378.137 * 1000; return $s; }根据经纬度获取指定方圆距离的四个点/** * 获取指定方圆距离的四个点 * param lng float 经度 * param lat float 纬度 * param distance float 该点所在圆的半径，该圆与此正方形内切，默认值为单位米 * return array 正方形的四个点的经纬度坐标 */ function getSquarePoint($lng, $lat,$distance) { $PI = 3.14159265; $longitude = $lng; $latitude = $lat; $degree = (24901*1609)/360.0; $raidusMile = $distance; $dpmLat = 1/$degree; $radiusLat = $dpmLat*$raidusMile; $minLat = $latitude - $radiusLat; //拿到最小纬度 $maxLat = $latitude + $radiusLat; //拿到最大纬度 $mpdLng = $degree*cos($latitude * ($PI/180)); $dpmLng = 1 / $mpdLng; $radiusLng = $dpmLng*$raidusMile; $minLng = $longitude - $radiusLng; //拿到最小经度 $maxLng = $longitude + $radiusLng; //拿到最大经度 $range = array( 'minLat' => $minLat, 'maxLat' => $maxLat, 'minLon' => $minLng, 'maxLon' => $maxLng ); return $range; }
php redis 集合有序和无序的使用方法和应用场景
无序集合（Set）的使用方法和应用场景：使用方法：添加元素到集合：$redis->sAdd('myset', 'element1'); $redis->sAdd('myset', 'element2');获取集合中的所有元素：$elements = $redis->sMembers('myset');检查元素是否存在于集合中：$exists = $redis->sIsMember('myset', 'element1');从集合中移除元素：$redis->sRem('myset', 'element1');应用场景：用户标签：记录用户的兴趣、爱好等，方便进行个性化推荐或群体分析。好友列表：用于表示用户的好友关系，便于进行社交功能的实现。记录在线用户：用于快速查找当前在线的用户。去重：用于去重操作，保证数据的唯一性。有序集合（Sorted Set）的使用方法和应用场景：使用方法：添加元素到有序集合并指定分数： $redis->zAdd('mysortedset', 1, 'element1'); $redis->zAdd('mysortedset', 2, 'element2');获取有序集合中的成员数量：$size = $redis->zCard('mysortedset');获取成员的分数：$score = $redis->zScore('mysortedset', 'element1');获取指定范围内的成员：$members = $redis->zRange('mysortedset', 0, -1);应用场景：排行榜：记录用户积分、贡献值等，并根据分数进行排序。按分数范围查询：查询指定范围内的成员，如时间范围、价格范围等。记录点击量：用于统计网站的热门内容或广告的点击量。实时排名：用于实时显示排名变化，如股票排行、比赛排名等。总之，无序集合和有序集合各有自己适用的场景。无序集合适合简单的去重、集合运算等场景，而有序集合适合需要排序和范围查询的场景，如排行榜、实时统计等。
在 Yii2 中，components 是用于配置和管理各种应用程序组件的关键部分。组件是 Yii2 框架中的基本构建块，可以是数据库连接、缓存、日志处理、邮件发送等。通过在配置文件中定义 components，我们可以在整个应用程序中轻松访问和使用这些组件。底层原理：组件的配置：在 Yii2 中，组件配置是以数组的形式定义在应用程序的配置文件中，通常是 config/web.php 或 config/console.php。配置文件可以定义多个组件，每个组件都有一个唯一的名称，用于在应用程序的其他部分引用和访问该组件。组件的实例化和注册：当应用程序启动时，Yii2 框架会根据配置文件中的组件定义，实例化并注册这些组件。这意味着当你访问应用程序的某个组件时，Yii2 会返回该组件的实例，并且这个实例将在整个应用程序的生命周期内保持不变，以确保组件的单例模式。组件的访问：要在 Yii2 应用程序中访问组件，可以使用 Yii::$app->componentName 的方式，其中 componentName 是在配置文件中定义的组件名称。通过这种方式，你可以在任何地方访问和使用组件的功能，而不需要重复实例化或管理组件的生命周期。组件的依赖注入：Yii2 框架还支持组件之间的依赖注入。这意味着你可以在定义一个组件时，通过 depends 属性指定该组件依赖于其他组件。这样，当实例化一个组件时，Yii2 会自动解析和注入该组件所依赖的其他组件，使组件之间的耦合度降低，代码更加灵活和可维护。示例：以下是一个简单的组件配置示例，演示了如何定义一个数据库连接组件和一个缓存组件：return [ 'components' => [ 'db' => [ // 数据库连接组件 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=localhost;dbname=mydatabase', 'username' => 'db_user', 'password' => 'db_password', 'charset' => 'utf8', ], 'cache' => [ // 缓存组件 'class' => 'yii\caching\FileCache', ], // 其他组件的配置... ], // 应用程序的其他配置... ];在上述示例中，我们定义了两个组件：db 是一个数据库连接组件，使用了 yii\db\Connection 类来表示数据库连接；cache 是一个缓存组件，使用了 yii\caching\FileCache 类来表示文件缓存。通过在代码中使用 Yii::$app->db 和 Yii::$app->cache，我们可以在整个应用程序中访问和使用这两个组件的功能。总结：components 是 Yii2 框架中用于配置和管理应用程序组件的关键部分，底层原理是通过配置文件中的数组定义来实例化和注册这些组件，并通过依赖注入实现组件之间的耦合。
查看已安装的swoole的版本php --ri swoole查看phpinfo信息php -r "phpinfo();" |grep swoole查看命令列表php bin/hyperf.php启动hyperf服务php bin/hyperf.php startphp bin/hyperf.php server:watch生成控制器php bin/hyperf.php gen:Controller UserControllerphp bin/hyperf.php gen:Controller --namespace='App\Application\Admin\Controller' UserController生成一个自定义命令php bin/hyperf.php gen:command FooCommand生成Modelphp bin/hyperf.php gen:model User生成监听器php bin/hyperf.php gen:listener RegisterListener生成表单验证器php bin/hyperf.php gen:request UserForm生成配置文件php bin/hyperf.php vendor:publish hyperf/view
1、对查询进行优化，应尽量避免全表扫描，首先应考虑在where及order by涉及的列上建立索引。2、应尽量避免在where子句中对字段进行null值判断，创建表时NULL是默认值，但大多数时候应该使用NOT NULL，或者使用一个特殊的值，如0，-1作为默认值。3、应尽量避免在where子句中使用!=或<>操作符，MySQL只有对以下操作符才使用索引：<，<=，=，>，>=，BETWEEN，IN，以及某些时候的LIKE。4、应尽量避免在where子句中使用or来连接条件，否则将导致引擎放弃使用索引而进行全表扫描，可以使用UNION合并查询：select id from t where num=10 union all select id from t where num=20。5、in和not in也要慎用，否则会导致全表扫描，对于连续的数值，能用between就不要用in了：Select id from t where num between 1 and 3。6、下面的查询也将导致全表扫描：select id from t where name like‘%abc%’或者select id from t where name like‘%abc’若要提高效率，可以考虑全文检索。而select id from t where name like‘abc%’才用到索引。7、如果在where子句中使用参数，也会导致全表扫描。8、应尽量避免在where子句中对字段进行表达式操作，应尽量避免在where子句中对字段进行函数操作。9、很多时候用exists代替in是一个好的选择：select num from a where num in(select num from b)。用下面的语句替换：select num from a where exists(select 1 from b where num=a.num)。10、索引固然可以提高相应的select的效率，但同时也降低了insert及update的效率，因为insert或update时有可能会重建索引，所以怎样建索引需要慎重考虑，视具体情况而定。一个表的索引数最好不要超过6个，若太多则应考虑一些不常使用到的列上建的索引是否有必要。11、应尽可能的避免更新clustered索引数据列， 因为clustered索引数据列的顺序就是表记录的物理存储顺序，一旦该列值改变将导致整个表记录的顺序的调整，会耗费相当大的资源。若应用系统需要频繁更新clustered索引数据列，那么需要考虑是否应将该索引建为clustered索引。12、尽量使用数字型字段，若只含数值信息的字段尽量不要设计为字符型，这会降低查询和连接的性能，并会增加存储开销。13、尽可能的使用varchar/nvarchar代替char/nchar，因为首先变长字段存储空间小，可以节省存储空间，其次对于查询来说，在一个相对较小的字段内搜索效率显然要高些。14、最好不要使用”“返回所有：select from t ，用具体的字段列表代替“*”，不要返回用不到的任何字段。15、尽量避免向客户端返回大数据量，若数据量过大，应该考虑相应需求是否合理。16、使用表的别名(Alias)：当在SQL语句中连接多个表时，请使用表的别名并把别名前缀于每个Column上。这样一来，就可以减少解析的时间并减少那些由Column歧义引起的语法错误。17、使用“临时表”暂存中间结果 ：简化SQL语句的重要方法就是采用临时表暂存中间结果，但是临时表的好处远远不止这些，将临时结果暂存在临时表，后面的查询就在tempdb中了，这可以避免程序中多次扫描主表，也大大减少了程序执行中“共享锁”阻塞“更新锁”，减少了阻塞，提高了并发性能。18、一些SQL查询语句应加上nolock，读、写是会相互阻塞的，为了提高并发性能，对于一些查询，可以加上nolock，这样读的时候可以允许写，但缺点是可能读到未提交的脏数据。使用nolock有3条原则：查询的结果用于“插、删、改”的不能加nolock；查询的表属于频繁发生页分裂的，慎用nolock ；使用临时表一样可以保存“数据前影”，起到类似Oracle的undo表空间的功能，能采用临时表提高并发性能的，不要用nolock。19、常见的简化规则如下：不要有超过5个以上的表连接（JOIN），考虑使用临时表或表变量存放中间结果。少用子查询，视图嵌套不要过深，一般视图嵌套不要超过2个为宜。20、将需要查询的结果预先计算好放在表中，查询的时候再Select。这在SQL7.0以前是最重要的手段，例如医院的住院费计算。21、用OR的字句可以分解成多个查询，并且通过UNION 连接多个查询。他们的速度只同是否使用索引有关，如果查询需要用到联合索引，用UNION all执行的效率更高。多个OR的字句没有用到索引，改写成UNION的形式再试图与索引匹配。一个关键的问题是否用到索引。22、在IN后面值的列表中，将出现最频繁的值放在最前面，出现得最少的放在最后面，减少判断的次数。23、尽量将数据的处理工作放在服务器上，减少网络的开销，如使用存储过程。存储过程是编译好、优化过、并且被组织到一个执行规划里、且存储在数据库中的SQL语句，是控制流语言的集合，速度当然快。反复执行的动态SQL，可以使用临时存储过程，该过程（临时表）被放在Tempdb中。24、当服务器的内存够多时，配制线程数量 = 最大连接数+5，这样能发挥最大的效率；否则使用 配制线程数量<最大连接数启用SQL SERVER的线程池来解决，如果还是数量 = 最大连接数+5，严重的损害服务器的性能。25、查询的关联同写的顺序 ：select a.personMemberID, * from chineseresume a,personmember b where personMemberID = b.referenceid and a.personMemberID = ‘JCNPRH39681’ （A = B ,B = ‘号码’） select a.personMemberID, * from chineseresume a,personmember b where a.personMemberID = b.referenceid and a.personMemberID = ‘JCNPRH39681’ and b.referenceid = ‘JCNPRH39681’ （A = B ,B = ‘号码’， A = ‘号码’） select a.personMemberID, * from chineseresume a,personmember b where b.referenceid = ‘JCNPRH39681’ and a.personMemberID = ‘JCNPRH39681’ （B = ‘号码’， A = ‘号码’） 26、尽量使用exists代替select count(1)来判断是否存在记录，count函数只有在统计表中所有行数时使用，而且count(1)比count(*)更有效率。27、尽量使用“>=”，不要使用“>”。28、索引的使用规范：索引的创建要与应用结合考虑，建议大的OLTP表不要超过6个索引；尽可能的使用索引字段作为查询条件，尤其是聚簇索引，必要时可以通过index index_name来强制指定索引；避免对大表查询时进行table scan，必要时考虑新建索引；在使用索引字段作为条件时，如果该索引是联合索引，那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引，否则该索引将不会被使用；要注意索引的维护，周期性重建索引，重新编译存储过程。 29、下列SQL条件语句中的列都建有恰当的索引，但执行速度却非常慢：SELECT * FROM record WHERE substrINg(card_no,1,4)=’5378’ (13秒) SELECT * FROM record WHERE amount/30< 1000 （11秒） SELECT * FROM record WHERE convert(char(10),date,112)=’19991201’ （10秒） 分析： WHERE子句中对列的任何操作结果都是在SQL运行时逐列计算得到的，因此它不得不进行表搜索，而没有使用该列上面的索引。如果这些结果在查询编译时就能得到，那么就可以被SQL优化器优化，使用索引，避免表搜索，因此将SQL重写成下面这样：SELECT * FROM record WHERE card_no like ‘5378%’ （< 1秒） SELECT * FROM record WHERE amount< 1000*30 （< 1秒） SELECT * FROM record WHERE date= ‘1999/12/01’ （< 1秒）30、当有一批处理的插入或更新时，用批量插入或批量更新，绝不会一条条记录的去更新。31、在所有的存储过程中，能够用SQL语句的，我绝不会用循环去实现。例如：列出上个月的每一天，我会用connect by去递归查询一下，绝不会去用循环从上个月第一天到最后一天。32、选择最有效率的表名顺序（只在基于规则的优化器中有效）： Oracle的解析器按照从右到左的顺序处理FROM子句中的表名，FROM子句中写在最后的表（基础表 driving table）将被最先处理，在FROM子句中包含多个表的情况下，你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询，那就需要选择交叉表（intersection table）作为基础表，交叉表是指那个被其他表所引用的表。33、提高GROUP BY语句的效率，可以通过将不需要的记录在GROUP BY之前过滤掉。下面两个查询返回相同结果，但第二个明显就快了许多。 低效：SELECT JOB , AVG(SAL) FROM EMP GROUP BY JOB HAVING JOB =’PRESIDENT’ OR JOB =’MANAGER’ 高效:SELECT JOB , AVG(SAL) FROM EMP WHERE JOB =’PRESIDENT’ OR JOB =’MANAGER’ GROUP BY JOB34、SQL语句用大写，因为Oracle总是先解析SQL语句，把小写的字母转换成大写的再执行。35、别名的使用，别名是大型数据库的应用技巧，就是表名、列名在查询中以一个字母为别名，查询速度要比建连接表快1.5倍。36、避免死锁，在你的存储过程和触发器中访问同一个表时总是以相同的顺序；事务应经可能地缩短，在一个事务中应尽可能减少涉及到的数据量；永远不要在事务中等待用户输入。37、避免使用临时表，除非却有需要，否则应尽量避免使用临时表，相反，可以使用表变量代替；大多数时候(99%)，表变量驻扎在内存中，因此速度比临时表更快，临时表驻扎在TempDb数据库中，因此临时表上的操作需要跨数据库通信，速度自然慢。38、最好不要使用触发器：触发一个触发器，执行一个触发器事件本身就是一个耗费资源的过程；如果能够使用约束实现的，尽量不要使用触发器；不要为不同的触发事件(Insert，Update和Delete)使用相同的触发器；不要在触发器中使用事务型代码。39、索引创建规则： 表的主键、外键必须有索引； 数据量超过300的表应该有索引； 经常与其他表进行连接的表，在连接字段上应该建立索引； 经常出现在Where子句中的字段，特别是大表的字段，应该建立索引； 索引应该建在选择性高的字段上； 索引应该建在小字段上，对于大的文本字段甚至超长字段，不要建索引； 复合索引的建立需要进行仔细分析，尽量考虑用单字段索引代替； 正确选择复合索引中的主列字段，一般是选择性较好的字段； 复合索引的几个字段是否经常同时以AND方式出现在Where子句中？单字段查询是否极少甚至没有？如果是，则可以建立复合索引；否则考虑单字段索引； 如果复合索引中包含的字段经常单独出现在Where子句中，则分解为多个单字段索引； 如果复合索引所包含的字段超过3个，那么仔细考虑其必要性，考虑减少复合的字段； 如果既有单字段索引，又有这几个字段上的复合索引，一般可以删除复合索引； 频繁进行数据操作的表，不要建立太多的索引； 删除无用的索引，避免对执行计划造成负面影响； 表上建立的每个索引都会增加存储开销，索引对于插入、删除、更新操作也会增加处理上的开销。另外，过多的复合索引，在有单字段索引的情况下，一般都是没有存在价值的；相反，还会降低数据增加删除时的性能，特别是对频繁更新的表来说，负面影响更大。 尽量不要对数据库中某个含有大量重复的值的字段建立索引。40、MySQL查询优化总结：使用慢查询日志去发现慢查询，使用执行计划去判断查询是否正常运行，总是去测试你的查询看看是否他们运行在最佳状态下。久而久之性能总会变化，避免在整个表上使用count(*)，它可能锁住整张表，使查询保持一致以便后续相似的查询可以使用查询缓存，在适当的情形下使用GROUP BY而不是DISTINCT，在WHERE、GROUP BY和ORDER BY子句中使用有索引的列，保持索引简单，不在多个索引中包含同一个列。有时候MySQL会使用错误的索引，对于这种情况使用USE INDEX，检查使用SQL_MODE=STRICT的问题，对于记录数小于5的索引字段，在UNION的时候使用LIMIT不是是用OR。 为了避免在更新前SELECT，使用INSERT ON DUPLICATE KEY或者INSERT IGNORE，不要用UPDATE去实现，不要使用MAX，使用索引字段和ORDER BY子句，LIMIT M，N实际上可以减缓查询在某些情况下，有节制地使用，在WHERE子句中使用UNION代替子查询，在重新启动的MySQL，记得来温暖你的数据库，以确保数据在内存和查询速度快，考虑持久连接，而不是多个连接，以减少开销。基准查询，包括使用服务器上的负载，有时一个简单的查询可以影响其他查询，当负载增加在服务器上，使用SHOW PROCESSLIST查看慢的和有问题的查询，在开发环境中产生的镜像数据中测试的所有可疑的查询。41、MySQL备份过程：从二级复制服务器上进行备份；在进行备份期间停止复制，以避免在数据依赖和外键约束上出现不一致；彻底停止MySQL，从数据库文件进行备份；如果使用MySQL dump进行备份，请同时备份二进制日志文件 – 确保复制没有中断；不要信任LVM快照，这很可能产生数据不一致，将来会给你带来麻烦；为了更容易进行单表恢复，以表为单位导出数据——如果数据是与其他表隔离的。 当使用mysqldump时请使用–opt；在备份之前检查和优化表；为了更快的进行导入，在导入时临时禁用外键约束。；为了更快的进行导入，在导入时临时禁用唯一性检测；在每一次备份后计算数据库，表以及索引的尺寸，以便更够监控数据尺寸的增长；通过自动调度脚本监控复制实例的错误和延迟；定期执行备份。42、查询缓冲并不自动处理空格，因此，在写SQL语句时，应尽量减少空格的使用，尤其是在SQL首和尾的空格（因为查询缓冲并不自动截取首尾空格）。43、member用mid做标准进行分表方便查询么？一般的业务需求中基本上都是以username为查询依据，正常应当是username做hash取模来分表。而分表的话MySQL的partition功能就是干这个的，对代码是透明的；在代码层面去实现貌似是不合理的。44、我们应该为数据库里的每张表都设置一个ID做为其主键，而且最好的是一个INT型的（推荐使用UNSIGNED），并设置上自动增加的AUTO_INCREMENT标志。45、在所有的存储过程和触发器的开始处设置SET NOCOUNT ON，在结束时设置SET NOCOUNT OFF。无需在执行存储过程和触发器的每个语句后向客户端发送DONE_IN_PROC消息。46、MySQL查询可以启用高速查询缓存。这是提高数据库性能的有效MySQL优化方法之一。当同一个查询被执行多次时，从缓存中提取数据和直接从数据库中返回数据快很多。47、EXPLAIN SELECT查询用来跟踪查看效果：使用EXPLAIN关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。EXPLAIN的查询结果还会告诉你你的索引主键被如何利用的，你的数据表是如何被搜索和排序的。48、当只要一行数据时使用LIMIT 1 ：当你查询表的有些时候，你已经知道结果只会有一条结果，但因为你可能需要去fetch游标，或是你也许会去检查返回的记录数。在这种情况下，加上LIMIT 1可以增加性能。这样一来，MySQL数据库引擎会在找到一条数据后停止搜索，而不是继续往后查少下一条符合记录的数据。49、选择表合适存储引擎： myisam：应用时以读和插入操作为主，只有少量的更新和删除，并且对事务的完整性，并发性要求不是很高的。 InnoDB：事务处理，以及并发条件下要求数据的一致性。除了插入和查询外，包括很多的更新和删除。（InnoDB有效地降低删除和更新导致的锁定）。对于支持事务的InnoDB类型的表来说，影响速度的主要原因是AUTOCOMMIT默认设置是打开的，而且程序没有显式调用BEGIN 开始事务，导致每插入一条都自动提交，严重影响了速度。可以在执行SQL前调用begin，多条SQL形成一个事物（即使autocommit打开也可以），将大大提高性能。50、优化表的数据类型，选择合适的数据类型： 原则：更小通常更好，简单就好，所有字段都得有默认值，尽量避免null。 例如：数据库表设计时候更小的占磁盘空间尽可能使用更小的整数类型。(mediumint就比int更合适) 比如时间字段：datetime和timestamp，datetime占用8个字节，而timestamp占用4个字节，只用了一半，而timestamp表示的范围是1970—2037适合做更新时间 MySQL可以很好的支持大数据量的存取，但是一般说来，数据库中的表越小，在它上面执行的查询也就会越快。 因此，在创建表的时候，为了获得更好的性能，我们可以将表中字段的宽度设得尽可能小。例如：在定义邮政编码这个字段时，如果将其设置为CHAR(255)，显然给数据库增加了不必要的空间。甚至使用VARCHAR这种类型也是多余的，因为CHAR(6)就可以很好的完成任务了。同样的，如果可以的话，我们应该使用MEDIUMINT而不是BIGIN来定义整型字段，应该尽量把字段设置为NOT NULL，这样在将来执行查询的时候，数据库不用去比较NULL值。 对于某些文本字段，例如“省份”或者“性别”，我们可以将它们定义为ENUM类型。因为在MySQL中，ENUM类型被当作数值型数据来处理，而数值型数据被处理起来的速度要比文本类型快得多。这样，我们又可以提高数据库的性能。51、字符串数据类型：char，varchar，text选择区别。52、任何对列的操作都将导致表扫描，它包括数据库函数、计算表达式等等，查询时要尽可能将操作移至等号右边。
第一章 前言我们现实生活中的协议是指相互遵守的规定，单方面违背，协议不成立；而在互联网交互的过程中，也存在这许多协议，例如FTP、HTTP、STMP、TCP/IP等。而HTTP协议则是web服务器和web客户端达成的一种可靠的数据传输协议，通过HTTP可以从遍布全世界的Web服务器上将JPEG图片，HTML页面，文本文件，MPEG电影，WAV音频文件和其他资源信息块迅速、便捷、可靠地搬移到人们桌面上的Web浏览器上去。它能够确保数据在传输的过程当中不会损坏或者产生混乱。这样，对用户来说是个好事，同样对internet应用的开发人员来说也是一件好事。因为我们在开发过程中也不需要担心自己的页面和数据会在传输过程中发生破坏和畸变了。第二章 WEB客户端和服务器Web内容都是存储在Web服务器上的。Web服务器所使用的是HTTP协议，因此经常会被称为HTTP服务器。这些HTTP服务器存储了因特网中的数据，如果HTTP客户端发出请求的话，它们会提供数据。客户端向服务器发送HTTP请求，服务器会在HTTP响应中回送所请求的数据。那么一次请求和响应的过程中发生了什么？HTTP属于无状态链接协议，即是限制每次连接只处理一个请求。服务器处理完客户的请求，并收到客户的应答后，即断开连接。采用这种方式可以节省传输时间和服务器资源。当然也有有状态链接，那就是我们常用的QQ。H5新增的特性中也包含Websocket帮助我们构建长链接。第三章 资源和媒体类型web服务器是web资源的宿主，而web资源就是我们常见的web内容的源头，最简单的web资源就是我们服务器中的静态文件：文本文件，HTML文档，JPEG图片文件，mp4文件等等。当然web资源也可以是动态生成的，类似搜索引擎生成的页面，qq空间的动态等，总之，所有类型的内容来源都是资源。因特网上有数千种不同类型的数据类型，HTTP在传输的过程中为每个传输的数据都打上了名为MIME类型的数据类型标签，描述并标记多媒体内容。web浏览器请求一个网站的时候往往会发布多个HTTP请求，比如我们在浏览一个具有丰富图片的的web页面的时候，浏览器会执行一次HTTP请求来获取描述页面布局的HTML，然后发布另外的请求来获取每个嵌入式的图片，这些图片甚至可能位于不同的服务器上。因此，一个web页面通常不是单个资源，而是一组资源的集合。web服务器会为所有的HTTP对象数据附加一个MIME类型，当浏览器从服务器中取回一个对象的时候，会查看相关的MIME类型。看看它是否知道应该如何处理这个对象。对象的类型写在响应的content-type头中；同样，请求的时候浏览器也会告知服务器请求数据类型。常见的MIME类型：HTML 格式的文本文档由 text/html 类型来标记。普通的 ASCII 文本文档由 text/plain 类型来标记。JPEG 格式的图片为 image/jpeg 类型。GIF 格式的图片为 image/gif 类型。以application开头的媒体格式类型：application/xhtml+xml ：XHTML格式application/xml ： XML数据格式application/atom+xml ：Atom XML聚合格式application/json ： JSON数据格式application/octet-stream ： 二进制流数据（如常见的文件下载）application/x-www-form-urlencoded ： 中默认的encType，form表单数据被编码为key/value格式发送到服务器（表单默认的提交数据的格式）multipart/form-data ： 需要在表单中进行文件上传使用的数据格式。当然还有很多类型，我们就不一一列举了。MIME 参考手册：http://www.w3school.com.cn/media/media_mimeref.asp第四章 URL统一资源定位符（Uniform Resource Locator）是资源标识符最常见的形式。URL描述了一台特定服务器上某资源的特定位置。它们可以明确说明如何从一个精确、固定的位置获取资源。互联网上的每个文件都有一个唯一的URL，它包含的信息指出文件的位置以及浏览器应该怎么处理它。大部分 URL 都遵循一种标准格式， 这种格式包含三个部分。URL 的第一部分被称为方案（scheme）， 说明了访问资源所使用的协议类型。 这部分通常就是 HTTP 协议（http://）。第二部分给出了服务器的因特网地址（比如，http://www.itheima.com）。其余部分指定了 Web 服务器上的某个资源（比如， /static/image/common/zixuelogo.png）。URL可以更为细致的进行划分。 foo://username:password@xxx.xx.xx:8080/over/there/index.html?type=answer&name=yanshiba#nose \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/ | | | | | | | | | userinfo host port | | query fragment | \________________________________/\_____________|____|/ \__/ \__/ scheme | | | | | | name authority | | | | | path | | interpretable as keys | | \_______________________________________________|____|/ \____/ \_____/ | | | | | hierarchical part | | interpretable as values | | interpretable as filename | | | interpretable as extension第五章 方法HTTP支持几种不同请求和命令，这些命令被称为HTTP方法，每条HTTP请求报文都包含一个方法。 这个方法会告诉服务器要执行什么动作（获取一个Web页面、发送一段信息、删除一个文件等）；我们在学习form表单的时候也知道method可以写作post和get。这就是HTTP请求的方法。请求方法如下：请求方法描述GET获取一个URL指定的资源，即资源实体POST向服务器提交数据PUT向服务器提交数据DELETE请求源服务器删除Request-URI标志的资源HEAD获取一个指定资源的头信息OPTIONS获取服务器支持的方法我们随后学习的node和ajax中都会用到HTTP请求具体的方法实现。第六章 状态码每条HTTP响应报文返回时都会携带一个状态码。状态码是一个三位数字的代码，告知客户端请求是否成功， 或者是否需要采取其他动作。状态码分成如下几个系列：状态码定义说明1XX请求被接受一般只在实验环境下使用2XX成功操作成功的收到，理解和接收3XX重定向为了完成请求，进行进一步措施4XX客户端错误请求的语法有错误或者不能完全被满足5XX服务器错误服务器无法完成明显有效的请求常见的HTTP状态码：HTTP状态码描述200OK。文档正确返回301/2永久/临时重定向304not modified 未修改（原有的缓存可以继续使用）404请求的网页不存在503服务器暂时不可用500服务器内部错误第七章 报文HTTP报文是由一行一行的简单字符串组成的。HTTP报文都是纯文本，不是二进制代码，所以人们可以很方便地对其进行读写。从Web客户端发往Web服务器的HTTP报文称为请求报文（request message）。从服务器发往客户端的报文称为响应报文（response message）。HTTP 报文包括以下三个部分:起始行报文的第一行就是起始行， 在请求报文中用来说明要做些什么， 在响应报文中说明出现了什么情况。请求行部分由 请求方法（GET，POST等），请求路径，协议版本组成。响应行部分由 协议版本，状态码，状态文字组成。首部字段起始行后面有零个或多个首部字段。 每个首部字段都包含一个名字和一个值， 为了便于解析， 两者之间用冒号来分隔。 首部以一个空行结束。主体空行之后就是可选的报文主体了， 其中包含了所有类型的数据。 请求主体中包括了要发送给 Web 服务器的数据； 响应主体中装载了要返回给客户端的数据。 起始行和首部都是文本形式且都是结构化的， 而主体则不同， 主体中可以包含任意的二进制数据（比如图片、视频、音轨、软件程序）。 当然， 主体中也可以包含文本。HTTP报文常见的内容：http://www.cnblogs.com/Joans/p/3956490.html\使用火狐浏览器打开一个网页，找到其中一个网络请求查看报文：
1.1 目标掌握Smarty模板技术的实际运用；掌握Smarty模板技术的基础配置；掌握常用的smarty内置函数应用；掌握模板变量的使用以及常见的保留变量的使用；了解smarty配置文件的使用规范；掌握Smarty中内置函数：if分支、foreach和section循环的使用掌握Smarty在类中引入实现子类便捷使用的方式1.4 Smarty简介1.4.1 Smarty的引入1、为了分工合作，模板页面中最好不要出现PHP的代码。2、需要将表现和内容相分离1.2.2 Smarty介绍1.5 自定义Smarty1.3.1 演化一:（smarty生成混编文件）在模板中不能出现PHP定界符，标准写法如下：1、html代码 {$title} 2、PHP代码<?php $title='锄禾'; require './1-demo.html';运行结果不能解析的原因是：PHP不能识别 { 和 }解决：将大括号替换成PHP的定界符代码实现<?php $title='锄禾'; $str=file_get_contents('./index.html'); $str=str_replace('{','<?php echo ',$str); //替换左大括号 $str=str_replace('}',';?>',$str); //替换右大括号 file_put_contents('./index.html.php', $str); //写入混编文件 require './index.html.php'; //包含混编文件运行1.3.2 演化二：（smarty封装）由于每个页面都要替换定界符，所以需要将替换定界符的代码封装起来由于封装在类中，所有访问的方法需要通过面向对象的方式来访问1、创建Smarty.class.php<?php class Smarty{ private $tpl_var=array(); //赋值 public function assign($k,$v){ $this->tpl_var[$k]=$v; } /* *作用：编译模板 *@param $tpl string 模板的路径 */ public function compile($tpl){ $com_file=$tpl.'.php'; //混编文件地址 $str=file_get_contents($tpl); $str=str_replace('{$','<?php echo $this->tpl_var[\'',$str); //替换左大括号 $str=str_replace('}','\'];?>',$str); //替换右大括号 file_put_contents($com_file, $str); //写入混编文件 require $com_file; //包含混编文件 } }2、在index.php<?php require './Smarty.class.php'; $smarty=new Smarty(); $smarty->assign('title','锄禾'); $smarty->compile('./index.html');小结：1、需要将外部的变量赋值到对象的内部2、要通过面向对象的方式访问1.3.3 演化三：（有条件的生成混编文件）混编文件存在并且是最新的就直接包含，否则就重新生成模板文件修改时间<混编文件修改时间 => 混编文件是最新的Smarty类中的代码编译代码如下<?php class Smarty{ private $tpl_var=array(); //赋值 public function assign($k,$v){ $this->tpl_var[$k]=$v; } /* *作用：编译模板 *@param $tpl string 模板的路径 */ public function compile($tpl){ $com_file=$tpl.'.php'; //混编文件地址 //文件存在，并且模板文件修改时间<混编文件修改时间 if(file_exists($com_file) && filemtime($tpl)<filemtime($com_file)) require $com_file; else{ $str=file_get_contents($tpl); $str=str_replace('{$','<?php echo $this->tpl_var[\'',$str); //替换左大括号 $str=str_replace('}','\'];?>',$str); //替换右大括号 file_put_contents($com_file, $str); //写入混编文件 require $com_file; //包含混编文件 } } }小结：生成混编文件的条件1、混编不存在2、模板修改了， 模板文件修改时间>混编文件修改时间，说明模板修改过了。1.3.4 演化四：文件分类存放模板文件：view混编文件：viewcSmarty文件：smarty.class.phpSmarty.class.php代码如下：<?php class Smarty{ public $template_dir='./templates/'; //默认模板目录 public $templatec_dir='./templates_c/'; //默认混编目录 private $tpl_var=array(); //赋值 public function assign($k,$v){ $this->tpl_var[$k]=$v; } /* *作用：编译模板 *@param $tpl string 模板的名字 */ public function compile($tpl){ $tpl_file=$this->template_dir.$tpl; //拼接模板地址 $com_file=$this->templatec_dir.$tpl.'.php'; //混编文件地址 //文件存在，并且模板文件修改时间<混编文件修改时间 if(file_exists($com_file) && filemtime($tpl_file)<filemtime($com_file)) require $com_file; else{ $str=file_get_contents($tpl_file); $str=str_replace('{$','<?php echo $this->tpl_var[\'',$str); //替换左大括号 $str=str_replace('}','\'];?>',$str); //替换右大括号 file_put_contents($com_file, $str); //写入混编文件 require $com_file; //包含混编文件 } } }index.php代码如下<?php require './Smarty/Smarty.class.php'; $smarty=new Smarty(); $smarty->template_dir='./view/'; //更改模板目录 $smarty->templatec_dir='./viewc/'; //更改混编目录 $smarty->assign('title','锄禾'); $smarty->compile('index.html');1.3.5 演化五：封装编译方法编译的方法是smarty的核心方法，核心方法一般是不可以直接调用，需要进行二次封装smarty.class.php<?php class Smarty{ ... public function display($tpl){ require $this->compile($tpl); } /* *作用：编译模板 *@param $tpl string 模板的名字 */ private function compile($tpl){ .. //文件存在，并且模板文件修改时间<混编文件修改时间 if(file_exists($com_file) && filemtime($tpl_file)<filemtime($com_file)) return $com_file; //返回混编地址 else{ $str=file_get_contents($tpl_file); $str=str_replace('{$','<?php echo $this->tpl_var[\'',$str); //替换左大括号 $str=str_replace('}','\'];?>',$str); //替换右大括号 file_put_contents($com_file, $str); //写入混编文件 return $com_file; //返回混编地址 } } }index.php<?php ... $smarty->assign('title','锄禾'); $smarty->display('index.html'); //传递文件名1.6 官方Smarty介绍1.6.1 smarty目录结构到www.smarty.net网站下载最新的smarty版本解压libs目录结构需要掌握的smarty的属性和方法public $left_delimiter = "{"; //左界定 public $right_delimiter = "}"; //右界定 protected $template_dir = array('./templates/'); //默认模板目录 protected $compile_dir = './templates_c/'; //默认混编目录 protected $config_dir = array('./configs/'); //默认配置目录 protected $cache_dir = './cache/'; //默认缓存目录 public function setTemplateDir(){} //设置模板文件夹 public function setConfigDir(){} //设置配置文件夹 public function setCompileDir(){} //设置混编文件夹 public function setCacheDir(){} //设置缓存文件夹练习：以下关于Smarty配置描述正确的是（ABCD） A： 使用left_delimiter属性可以修改Smarty左定界符； B： 使用right_delimiter属性可以修改Smarty右定界符； C： 使用setTemplateDir()方法可以重新指定默认模板工作目录； D： 使用setCompileDir()方法可以重新指定默认编译文件工作目录。 1.6.2 smarty简单的操作1、将libs目录拷贝到站点下，改名为smarty2、创建模板目录templates3、创建混编目录templates_c4、在站点下创建1-demo.php<?php require './Smarty/Smarty.class.php'; $smarty=new Smarty(); $smarty->assign('title','锄禾'); $smarty->left_delimiter='{{'; //更改左界定 $smarty->right_delimiter='}}'; //更改右界定 $smarty->setTemplateDir('./view/'); //设置模板目录 $smarty->setCompileDir('./viewc/'); //设置混编目录 $smarty->display('1-demo.html');在templates下创建demo1.html {{$title}} 1.6.3 注释语法：{ }注意：smarty注释在源码中看不见。思考：已知smarty的定界符是{ 和 }，那么它的注释是什么?答：{ }1.2 变量smarty中变量有3中，普通变量、配置变量、保留变量1、普通变量普通变量就是我们自己定义的变量方法一：在PHP中定义$smarty->assign('name','tom');方法二：可以在模板定义语法：{assign var='变量名' value='值'} 例如：{assign var='sex' value='男'}简化写法：{$sex='男'}例题：php代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->assign('name','tom'); //给变量赋值 $smarty->display('1-demo.html');HTML代码 姓名：{$name} {assign var='age' value=20} 年龄：{$age} {$add='北京'} 地址：{$add} 运行结果2、保留变量Smarty中有一个特殊的保留变量（内置变量），类似于PHP中的所有的超全局变量、常量、时间等信息表达式描述{$smarty.get.name}获取get提交的name的值{$smarty.post.name}获取post提交的name的值{$smarty.request.name}获取get和post提交的name的值{$smarty.cookies.name}获取cookie中的name的值{$smarty.session.name}获取session中的name的值{$smarty.const.name}获取常量name{$smarty.server.DOCUMENT_ROOT}获取服务器的虚拟目录地址{$smarty.config.name}获取配置文件中的值{$smarty.now}时间戳{$smarty.ldelim}获取左界定{$smarty.rdelim}获取右界定例题PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); define('name', '常量name'); setcookie('name','cookie的值'); $_SESSION['name']='session的值'; $smarty->display('1-demo.html');HTML代码 get提交：{$smarty.get.name} post提交：{$smarty.post.name} request提交：{$smarty.request.name} 常量：{$smarty.const.name} cookie的值：{$smarty.cookies.name} session：{$smarty.session.name} 时间戳：{$smarty.now} 版本号：{$smarty.version} 根目录：{$smarty.server.DOCUMENT_ROOT} 左界定：{$smarty.ldelim} 右界定：{$smarty.rdelim} 运行结果3、配置变量从配置文件中获取变量值，配置文件默认的文件夹是configs1、在站点下创建配置文件夹configs2、在configs目录下创建smarty.conf文件color='#FF0000'; size='15px';3、PHP页面<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->display('1-demo.html');4、HTML页面{config_load file='smarty.conf'} 小结：1、要使用配置文件中的值，首先必须引入配置文件，通过{config_load}标签引入2、获取配置文件中的值的方法有两种​ 第一：{#变量名#}​ 第二：{$smarty.config.变量名}多学一招：配置文件中的节在配置文件中，‘[ ]’表示配置文件的段落例题：配置文件color=#FF0000 size=30px [spring] # 配置文件中的段落 color=#009900; size=20px; [winter] color=#000000; size=5px;注意：1、全局的一定要写在节的前面2、配置文件中[]表示节3、配置文件中的注释是 #HTML页面{config_load file='smarty.conf' section='winter'} -- 通过section引入配置文件中的段落 1.3 运算符smary中的运算符是PHP是一样的。除此以外，smarty还支持如下的运算符。运算符描述eqequal 相等neqnot equal 不等于gtgreater than 大于ltless than 小于lteless than or equal 小于等于gtegreat than or equal 大于等于is even是偶数is odd是奇数is not even不是偶数is not odd不是奇数not非mod求模取余div by被整除is [not] div by能否被某数整除，例如：{if $smarty.get.age is div by 3}...{/if}is [not] even by商的结果是否为偶数is [not] odd by商的结果是否为奇数1.4 判断语法：{if 条件} {elseif 条件} {else} {/if}例题：php代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->display('1-demo.html');html代码 {if is_numeric($smarty.get.score)} {#判断是否是数字#} {if $smarty.get.score gte 90} A {elseif $smarty.get.score gte 80} B {else} C {/if} {else} 不是数字 {/if} {if $smarty.get.score is even} 是偶数 {elseif $smarty.get.score is odd} 是奇数 {/if} 运行结果小结：在判断中是可以使用PHP的函数的1.5 数组smarty中访问数组的方式有两种数组[下标] 数组.下标PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $stu=array('tom','berry'); //索引数组 $emp=array('name'=>'rose','sex'=>'女'); //关联数组 $goods=array( array('name'=>'手机','price'=>22), array('name'=>'钢笔','price'=>10) ); $smarty->assign('stu',$stu); $smarty->assign('emp',$emp); $smarty->assign('goods',$goods); $smarty->display('2-demo.html');HTML代码 学生：{$stu[0]}-{$stu.1} 雇员：{$emp['name']}-{$emp.sex} 商品： {$goods[0]['name']} {$goods[0].price} {$goods.1['name']} {$goods.1.price} 运行结果1.6 循环smarty中支持的循环有：{for}、{while}、{foreach}、{section}。对于开发来说用的最多就是{foreach}循环1.6.1 for语法：{for 初始值 to 结束值 [step 步长]} {/for} 默认步长是1例题 {for $i=1 to 5} {$i}：锄禾日当午 {/for} {for $i=1 to 5 step 2} {$i}：锄禾日当午 {/for} 运行结果1.6.2 while语法{while 条件} {/while}例题（输出5句）： {$i=1} {while $i<=5} {$i++}：锄禾日当午 {/while} 1.6.3 foreach既能遍历关联数组也能遍历索引数组语法：{foreach 数组 as $k=>$v} {foreachelse} 没有数组输出 {/foreach}foreach的属性@index:从0开始的索引 @iteration：从1开始的编号 @first：是否是第一个元素 @last：是否是最后一个元素PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->assign('stu',array('first'=>'tom','second'=>'berry','third'=>'ketty','forth'=>'rose')); $smarty->display('3-demo.html');html代码 是否是第一个元素 索引 编号 键 值 是否是最后一个元素 {foreach $stu as $k=>$v} {$v@first} {$v@index} {$v@iteration} {$k} {$v} {$v@last} {foreachelse} 没有输出 {/foreach} 运行结果1.6.4 sectionsection不支持关联数组，只能遍历索引数组语法：{section name=自定义名字 loop=数组} {/section}例题：php<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->assign('stu',array('tom','berry')); $smarty->display('4-demo.html');html代码 是否是第一个元素 索引 编号 值 是否是最后一个元素 {section name=s loop=$stu} {$smarty.section.s.first} {$smarty.section.s.index} {$smarty.section.s.iteration} {$stu[s]} {$smarty.section.s.last} {sectionelse} 没有输出 {/section} 1.7 函数函数有两种，自定义函数和内置函数smarty的内置函数就是封装的PHP的关键字1.8 变量修饰器1.8.1 变量修饰器变量修饰器的本质就是PHP函数，用来转换数据php代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->display('5-demo.html');html代码 转成大写：{'abc'|upper} 转成小写：{'ABC'|lower} 默认值：{$add|default:'地址不详'} 去除标签：{'你好吗'|strip_tags} 实体转换：{'你好吗'|escape} 日期：{$smarty.now|date_format:'%Y-%m-%d %H:%M:%S'} 多个管道连续使用：{'boy'|strip_tags|upper} 运行结果注意：1、将PHP的关键字或函数封装成标签称为函数，将PHP关键字封装成smarty关键字称为修饰器。内部的本质都是、PHP函数或PHP关键字。2、|称为管道运算符，将前面的参数传递后后面的修饰器使用1.8.2 自定义变量修饰器变量修饰器存放在plugins目录中规则：文件的命名规则：modifier.变量修饰器名称.php文件内方法命名规则：smarty_modifier_变量修饰器名称(形参...){}例题1、在plugins目录中创建modifier.cal.php页面<?php function smarty_modifier_cal($num1,$num2,$num3){ return $num1+$num2+$num3; }2、在模板中调用{10|cal:20:30} 10作为第一个参数传递 参数之间用冒号分隔1.9 避免Smarty解析smarty的定界符和css、js中的大括号产生冲突的时候，css、js中的大括号不要被smarty解析方法一：更换定界符方法二：左大括号后面添加空白字符方法三：{literal} {/literal}smarty不解析{literal} {/literal}中的内容 1.10 缓存缓存：页面缓存、空间缓存、数据缓存。smarty中的缓存就是页面缓存smarty的缓存是页面缓存。1.10. 1 开启缓存$smarty->caching=true|1; //开启缓存1.10.2 缓存的更新方法一：删除缓存，系统会重新生成新的缓存文件方法二：更新了模板文件，配置文件，缓存自动更新方法三：过了缓存的生命周期，默认是3600秒方法四：强制更新PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->caching=true; //开启缓存 if(date('H')>=9) $smarty->force_cache=true; //强制更新缓存 $smarty->display('6-demo.html');1.10.3 缓存的生命周期$smarty->cache_lifetime=-1 | 0 | N -1:永远不过期 0:立即过期 N:有效期是N秒，默认是3600秒PHP代码$smarty->cache_lifetime=3; //缓存的生命周期1.10.4 局部不缓存局部不缓存有两种方法1、变量不缓存 {$变量名 nocache} 2、整个块不缓存 {nocache} {/nocache}代码不缓存：{$smarty.now nocache} 不缓存：{nocache} {$smarty.now} {/nocache}1.10.5 缓存分页通过$smarty->display(模板,识别id)。通过识别id来缓存分页、集合PHP页面<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->caching=1; $smarty->display('7-demo.html',$_GET['pageno']);html页面 这是第{$smarty.get.pageno}页 运行结果1.10.6 缓存集合每个组合都会产生缓存PHP代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); $smarty->caching=1; $color=$_GET['color']; $size=$_GET['size']; $smarty->display('7-demo.html',"$color|$size");HTML代码 颜色：{$smarty.get.color} 大小：{$smarty.get.size} 运行结果1.10.7 清除缓存$smarty->clearCache(模板,[识别id]) $smarty->clearAllCache(); //清除所有缓存代码<?php require './Smarty/smarty.class.php'; $smarty=new Smarty(); //$smarty->clearCache('7-demo.html',1); //$smarty->clearCache('7-demo.html','red|10'); //$smarty->clearCache('7-demo.html'); $smarty->clearAllCache(); //清除所有缓存1.11 将smarty集成到项目中1、将smarty拷贝到Lib目录下2、实现smarty类的自动加载 private static function initAutoLoad(){ spl_autoload_register(function($class_name){ //Smarty类存储不规则，所以将类名和地址做一个映射 $map=array( 'Smarty' => LIB_PATH.'Smarty'.DS.'Smarty.class.php' ); ... elseif(isset($map[$class_name])) $path=$map[$class_name]; else //控制器 $path=__URL__.$class_name.'.class.php'; if(file_exists($path) && is_file($path)) require $path; }); }3、创建混编目录，并且定义混编目录地址private static function initRoutes(){ ... define('__VIEW__',VIEW_PATH.$p.DS); //当前视图的目录地址 define('__VIEWC__', APP_PATH.'Viewc'.DS.$p.DS); //混编目录 }4、由于前后台都要启动模板，所以应该在基础控制器中实例化smarty<?php //基础控制器 namespace Core; class Controller{ protected $smarty; use \Traits\Jump; public function __construct() { $this->initSession(); $this->initSmarty(); } //初始化session private function initSession(){ new \Lib\Session(); } //初始化Smarty private function initSmarty(){ $this->smarty=new \Smarty(); $this->smarty->setTemplateDir(__VIEW__); //设置模板目录 $this->smarty->setCompileDir(__VIEWC__); //设置混编目录 } }5、在控制器中使用smartyclass ProductsController extends BaseController{ //获取商品列表 public function listAction() { //实例化模型 $model=new \Model\ProductsModel(); $list=$model->select(); //加载视图 //require __VIEW__.'products_list.html'; $this->smarty->assign('list',$list); $this->smarty->display('products_list.html'); }6、在模板中更改如下：{foreach $list as $rows} {$rows['proID']} {$rows['proname']} {$rows['proprice']} 修改 删除 {/foreach}
1.1 目标更好的理解类的封装特性；理解代码根据业务和功能的分类管理思想；理解公共控制器的作用，掌握公共控制器的封装；掌握PHP面向对象中继承的核心应用；1.2 文件上传1.2.1 封装文件上传类1、在Lib目录下创建Upload.class.php<?php namespace Lib; class Upload{ private $path; //上传的路径 private $size; //上传的大小 private $type; //允许上传的类型 private $error; //保存错误信息 public function __construct($path,$size,$type) { $this->path=$path; $this->size=$size; $this->type=$type; } //返回错误信息 public function getError(){ return $this->error; } /* * 文件上传 * @param $files array $_FILES[] * @return bool|string 成功返回文件路径，失败返回false */ public function uploadOne($files){ if($this->checkError($files)){ //没有错误就上传 $foldername=date('Y-m-d'); //文件夹名称 $folderpath= $this->path.$foldername; //文件夹路径 if(!is_dir($folderpath)) mkdir($folderpath); $filename=uniqid('',true).strrchr($files['name'],'.');//文件名 $filepath="$folderpath/$filename"; //文件路径 if(move_uploaded_file($files['tmp_name'],$filepath)) return "{$foldername}/{$filename}"; else{ $this->error='上传失败 '; return false; } } return false; } //验证上传是否有误 private function checkError($files){ //1、验证错误号 if($files['error']!=0){ switch($files['error']) { case 1: $this->error='文件大小超过了php.ini中允许的最大值,最大值是：'.ini_get('upload_max_filesize'); return false; case 2: $this->error='文件大小超过了表单允许的最大值'; return false; case 3: $this->error='只有部分文件上传'; return false; case 4: $this->error='没有文件上传'; return false; case 6: $this->error='找不到临时文件'; return false; case 7: $this->error='文件写入失败'; return false; default: $this->error= '未知错误'; return false; } } //2、验证格式 $info=finfo_open(FILEINFO_MIME_TYPE); $mime=finfo_file($info,$files['tmp_name']); if(!in_array($mime, $this->type)){ $this->error='只能上传'.implode(',', $this->type).'格式'; return false; } //3、验证大小 if($files['size']> $this->size){ $this->error='文件大小不能超过'.number_format($this->size/1024,1).'K'; return false; } //4、验证是否是http上传 if(!is_uploaded_file($files['tmp_name'])){ $this->error='文件不是HTTP POST上传的 '; return false; } return true; } }1.2.2 封装缩略图类在Lib目录下创建Image.class.php<?php namespace Lib; class Image{ /* * 制作缩略图 * @param $src_path 源图的路径 */ public function thumb($src_path,$prefix='small_',$w=200,$h=200){ $dst_img=imagecreatetruecolor($w,$h); //目标图 $src_img=imagecreatefromjpeg($src_path); //源图 $src_w=imagesx($src_img); $src_h=imagesy($src_img); imagecopyresampled($dst_img,$src_img,0,0,0,0,$w,$h,$src_w,$src_h); $filename=basename($src_path); //文件名 $foldername=substr(dirname($src_path),-10); //目录名 $save_path= dirname($src_path).'/'.$prefix.$filename; imagejpeg($dst_img,$save_path); return "{$foldername}/{$prefix}{$filename}"; } }1.2.3 实现文件上传1、register.html ...2、更改注册控制器public function registerAction(){ //第二步：执行注册逻辑 if(!empty($_POST)){ //文件上传 $path=$GLOBALS['config']['app']['path']; $size=$GLOBALS['config']['app']['size']; $type=$GLOBALS['config']['app']['type']; $upload=new \Lib\Upload($path, $size, $type); if($filepath=$upload->uploadOne($_FILES['face'])){ //生成缩略图 $image=new \Lib\Image(); $data['user_face']=$image->thumb($path.$filepath,'s1_'); }else{ $this->error('index.php?p=Admin&c=Login&a=register', $upload->getError()); } //文件上传结束 ...3、配置文件 'app' =>array( 'path' => './Public/Uploads/', 'size' => 1234567, 'type' => ['image/png','image/jpeg','image/gif'],1.3 登录模块1.3.1 记住密码登录成功后，如果需要记录用户名和密码，则将用户名和密码记录在cookie中打开登录页面的时候，获取cookie的值在视图页面（login.html）页面显示cookie的信息<input type="text" class="input" name="username" placeholder="登录账号" value="<?=$name?>" /> ... <input type="password" class="input" name="password" placeholder="登录密码" value="<?=$pwd?>" />运行结果1.3.2 安全退出退出：退出的时候不销毁令牌安全退出：退出的时候销毁了令牌top.html安全退出 _top：表示在最顶端的窗口中打开控制器（LoginController）public function logoutAction(){ session_destroy(); header('location:index.php?p=Admin&c=Login&a=login'); }