<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Justmd5</title>
  
  <subtitle>独步弈城博客</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://dinghaijun.com/"/>
  <updated>2024-09-28T13:21:33.895Z</updated>
  <id>https://dinghaijun.com/</id>
  
  <author>
    <name>Justmd5</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>PHP和js位移运算的坑</title>
    <link href="https://dinghaijun.com/articles/2021/10/php_and_js_pit_of_bit_operation.html"/>
    <id>https://dinghaijun.com/articles/2021/10/php_and_js_pit_of_bit_operation.html</id>
    <published>2021-10-11T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<ul><li>JavaScript默认32位，32位操作系统int类型的最大值是 2147483647，超过该数值会溢出<a id="more"></a></li><li>php 64位需要使用pack转化</li></ul><p>PHP中pack、unpack的详细用法：</p><ul><li>JavaScript运行结果</li></ul><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://blog.hidove.cn/usr/uploads/2020/03/1455587700.png" alt="php和js位移运算的坑" title="">                </div>                <div class="image-caption">php和js位移运算的坑</div>            </figure><ul><li>PHP运行结果</li></ul><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://blog.hidove.cn/usr/uploads/2020/03/3445052821.png" alt="php和js位移运算的坑" title="">                </div>                <div class="image-caption">php和js位移运算的坑</div>            </figure><p>JavaScript</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> (<span class="params">t</span>) <span class="title">hash33</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> e = <span class="number">0</span>, i = <span class="number">0</span>, n = t . length; i &lt; n; ++i)</span><br><span class="line">        e += (e &lt;&lt; <span class="number">5</span>) + t . charCodeAt(i);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">2147483647</span> &amp; e</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>PHP</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">hash33</span><span class="params">($t)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> ( $e = <span class="number">0</span>, $i = <span class="number">0</span>, $n = strlen($t); $i &lt; $n; ++$i)&#123;</span><br><span class="line">        $tmp = $e &lt;&lt; <span class="number">5</span>;</span><br><span class="line">        $tmp = unpack(<span class="string">"L"</span>,pack(<span class="string">"L"</span>,$tmp))[<span class="number">1</span>];</span><br><span class="line">        $e += $tmp+  uniord(mb_substr($t,$i,<span class="number">1</span>,<span class="string">'utf-8'</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">2147483647</span> &amp; $e;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;ul&gt;
&lt;li&gt;JavaScript默认32位，32位操作系统int类型的最大值是 2147483647，超过该数值会溢出
    
    </summary>
    
    
      <category term="PHP" scheme="https://dinghaijun.com/categories/PHP/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="PHP" scheme="https://dinghaijun.com/tags/PHP/"/>
    
      <category term="Javascript" scheme="https://dinghaijun.com/tags/Javascript/"/>
    
  </entry>
  
  <entry>
    <title>MySQL 规约</title>
    <link href="https://dinghaijun.com/articles/2020/06/mysql_protocol.html"/>
    <id>https://dinghaijun.com/articles/2020/06/mysql_protocol.html</id>
    <published>2020-06-26T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<h1 id="MySQL-规约（转自阿里巴巴-Java-开发手册）"><a href="#MySQL-规约（转自阿里巴巴-Java-开发手册）" class="headerlink" title="MySQL 规约（转自阿里巴巴 Java 开发手册）"></a>MySQL 规约（转自阿里巴巴 Java 开发手册）</h1><h2 id="一、建表规约"><a href="#一、建表规约" class="headerlink" title="一、建表规约"></a>一、建表规约</h2><ol><li>【强制】表达是与否概念的字段，必须使用 is_xxx 的方式命名，数据类型是 unsigned tinyint（ 1 表示是，0 表示否），此规则同样适用于 odps 建表。<br>说明：任何字段如果为非负数，必须是 unsigned。</li><li>【强制】表名、字段名必须使用小写字母或数字；禁止出现数字开头，禁止两个下划线中间只出现数字。数据库字段名的修改代价很大，因为无法进行预发布，所以字段名称需要慎重考虑。<br>正例：getter_admin，task_config，level3_name<br>反例：GetterAdmin，taskConfig，level_3_name</li><li>【强制】表名不使用复数名词。<br>说明：表名应该仅仅表示表里面的实体内容，不应该表示实体数量，对应于 DO 类名也是单数形式，符合表达习惯。</li><li>【强制】禁用保留字，如 desc、range、match、delayed 等，请参考 MySQL 官方保留字。</li><li>【强制】唯一索引名为 uk_字段名；普通索引名则为 idx_字段名。<br>说明：uk_ 即 unique key；idx_ 即 index 的简称。</li><li>【强制】小数类型为 decimal，禁止使用 float 和 double。<br>说明：float 和 double 在存储的时候，存在精度损失的问题，很可能在值的比较时，得到不正确的结果。如果存储的数据范围超过 decimal 的范围，建议将数据拆成整数和小数分开存储。</li><li>【强制】如果存储的字符串长度几乎相等，使用 char 定长字符串类型。</li><li>【强制】varchar 是可变长字符串，不预先分配存储空间，长度不要超过 5000，如果存储长度大于此值，定义字段类型为 text，独立出来一张表，用主键来对应，避免影响其它字段索引效率。</li><li>【强制】表必备三字段：id, gmt_create, gmt_modified。<br>说明：其中 id 必为主键，类型为 unsigned bigint、单表时自增、步长为 1。gmt_create,gmt_modified 的类型均为 date_time 类型。</li><li>【推荐】表的命名最好是加上 “业务名称_表的作用”。<br>正例：tiger_task /tiger_reader/mpp_config</li><li>【推荐】库名与应用名称尽量一致。</li><li>【推荐】如果修改字段含义或对字段表示的状态追加时，需要及时更新字段注释。</li><li>【推荐】字段允许适当冗余，以提高性能，但是必须考虑数据同步的情况。冗余字段应遵循：<br>1）不是频繁修改的字段。<br>2）不是 varchar 超长字段，更不能是 text 字段。<br>正例：商品类目名称使用频率高，字段长度短，名称基本一成不变，可在相关联的表中冗余存储类目名称，避免关联查询。</li><li>【推荐】单表行数超过 500 万行或者单表容量超过 2GB，才推荐进行分库分表。<br>说明：如果预计三年后的数据量根本达不到这个级别，请不要在创建表时就分库分表。</li><li>【参考】合适的字符存储长度，不但节约数据库表空间、节约索引存储，更重要的是提升检索速度。<br>正例：人的年龄用 unsigned tinyint（表示范围 0-255，人的寿命不会超过 255 岁）；海龟就必须是 smallint，但如果是太阳的年龄，就必须是 int；如果是所有恒星的年龄都加起来，那么就必须使用 bigint。</li></ol><h2 id="二、索引规约"><a href="#二、索引规约" class="headerlink" title="二、索引规约"></a>二、索引规约</h2><ol><li>【强制】业务上具有唯一特性的字段，即使是组合字段，也必须建成唯一索引。<br>说明：不要以为唯一索引影响了 insert 速度，这个速度损耗可以忽略，但提高查找速度是明显的；另外，即使在应用层做了非常完善的校验和控制，只要没有唯一索引，根据墨菲定律，必然有脏数据产生。</li><li>【强制】 超过三个表禁止 join。需要 join 的字段，数据类型保持绝对一致；多表关联查询时，保证被关联的字段需要有索引。<br>说明：即使双表 join 也要注意表索引、SQL 性能。</li><li>【强制】在 varchar 字段上建立索引时，必须指定索引长度，没必要对全字段建立索引，根据实际文本区分度决定索引长度。<br>说明：索引的长度与区分度是一对矛盾体，一般对字符串类型数据，长度为 20 的索引，区分度会高达 90% 以上，可以使用 count (distinct left (列名，索引长度))/count (*) 的区分度来确定。</li><li>【强制】页面搜索严禁左模糊或者全模糊，如果需要请走搜索引擎来解决。<br>说明：索引文件具有 B-Tree 的最左前缀匹配特性，如果左边的值未确定，那么无法使用此索引。</li><li>【推荐】如果有 order by 的场景，请注意利用索引的有序性。order by 最后的字段是组合索引的一部分，并且放在索引组合顺序的最后，避免出现 file_sort 的情况，影响查询性能。<br>正例：where a=? and b=? order by c; 索引：a_b_c 反例：索引中有范围查找，那么索引有序性无法利用，如：WHERE a&gt;10 ORDER BY b; 索引 a_b 无法排序。</li><li>【推荐】利用覆盖索引来进行查询操作，来避免回表操作。<br>说明：如果一本书需要知道第 11 章是什么标题，会翻开第 11 章对应的那一页吗？目录浏览一下就好，这个目录就是起到覆盖索引的作用。<br>正例：能够建立索引的种类：主键索引、唯一索引、普通索引，而覆盖索引是一种查询的一种效果，用 explain 的结果，extra 列会出现：using index。</li><li>【推荐】利用延迟关联或者子查询优化超多分页场景。<br>说明：MySQL 并不是跳过 offset 行，而是取 offset+N 行，然后返回放弃前 offset 行，返回 N 行，那当 offset 特别大的时候，效率就非常的低下，要么控制返回的总页数，要么对超过特定阈值的页数进行 SQL 改写。<br>正例：先快速定位需要获取的 id 段，然后再关联：SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20) b where a.id=b.id</li><li>【推荐】SQL 性能优化的目标：至少要达到 range 级别，要求是 ref 级别，如果可以是 consts 最好。<br>说明：<br>1）consts 单表中最多只有一个匹配行（主键或者唯一索引），在优化阶段即可读取到数据。<br>2）ref 指的是使用普通的索引（normal index）。<br>3）range 对索引进行范围检索。<br>反例：explain 表的结果，type=index，索引物理文件全扫᧿，速度非常慢，这个 index 级别比较 range 还低，与全表扫᧿是小巫见大巫。</li><li>【推荐】建组合索引的时候，区分度最高的在最左边。<br>正例：如果 where a=? and b=? ，a 列的几乎接近于唯一值，那么只需要单建 idx_a 索引即可。<br>说明：存在非等号和等号混合判断条件时，在建索引时，请把等号条件的列前置。如：where a&gt;?and b=? 那么即使 a 的区分度更高，也必须把 b 放在索引的最前列。</li><li>【参考】创建索引时避免有如下极端误解：<br>1）误认为一个查询就需要建一个索引。<br>2）误认为索引会消耗空间、严重拖慢更新和新增速度。<br>3）误认为唯一索引一律需要在应用层通过 “先查后插” 方式解决。</li></ol><h2 id="三、SQL-规约"><a href="#三、SQL-规约" class="headerlink" title="三、SQL 规约"></a>三、SQL 规约</h2><ol><li>【强制】不要使用 count (列名) 或 count (常量) 来替代 count (<em>)，count(</em>) 就是 SQL92 定义的标准统计行数的语法，跟数据库无关，跟 NULL 和非 NULL 无关。<br>说明：count (*) 会统计值为 NULL 的行，而 count (列名) 不会统计此列为 NULL 值的行。</li><li>【强制】count (distinct col) 计算该列除 NULL 之外的不重复数量。注意 count (distinctcol1, col2) 如果其中一列全为 NULL，那么即使另一列有不同的值，也返回为 0。</li><li>【强制】当某一列的值全是 NULL 时，count (col) 的返回结果为 0，但 sum (col) 的返回结果为 NULL，因此使用 sum () 时需注意 NPE 问题。<br>正例：可以使用如下方式来避免 sum 的 NPE 问题：SELECT IF (ISNULL (SUM (g)),0,SUM (g)) FROM table;</li><li>【强制】使用 ISNULL () 来判断是否为 NULL 值。注意：NULL 与任何值的直接比较都为 NULL。<br>说明：<br>1） NULL&lt;&gt;NULL 的返回结果是 NULL，而不是 false。<br>2） NULL=NULL 的返回结果是 NULL，而不是 true。<br>3） NULL&lt;&gt;1 的返回结果是 NULL，而不是 true。</li><li>【强制】在代码中写分页查询逻辑时，若 count 为 0 应直接返回，避免执行后面的分页语句。</li><li>【强制】不得使用外键与级联，一切外键概念必须在应用层解决。<br>说明：（概念解释）学生表中的 student_id 是主键，那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id，同时触发成绩表中的 student_id 更新，则为级联更新。外键与级联更新适用于单机低并发，不适合分布式、高并发集群；级联更新是强阻塞，存在数据库更新风暴的风险；外键影响数据库的插入速度。</li><li>【强制】禁止使用存储过程，存储过程难以调试和扩展，更没有移植性。</li><li>【强制】数据订正时，删除和修改记录时，要先 select，避免出现误删除，确认无误才能执行更新语句。</li><li>【推荐】in 操作能避免则避免，若实在避免不了，需要仔细评估 in 后边的集合元素数量，控制在 1000 个之内。</li><li>【参考】如果有全球化需要，所有的字符存储与表示，均以 utf-8 编码，那么字符计数方法注意：<br>说明：<br>SELECT LENGTH (“轻松工作”)； 返回为 12<br>SELECT CHARACTER_LENGTH (“轻松工作”)； 返回为 4<br>如果要使用表情，那么使用 utfmb4 来进行存储，注意它与 utf-8 编码的区别。</li><li>【参考】TRUNCATE TABLE 比 DELETE 速度快，且使用的系统和事务日志资源少，但 TRUNCATE 无事务且不触发 trigger，有可能造成事故，故不建议在开发代码中使用此语句。<br>说明：TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。</li></ol><h2 id="四、ORM-规约"><a href="#四、ORM-规约" class="headerlink" title="四、ORM 规约"></a>四、ORM 规约</h2><ol><li>【强制】在表查询中，一律不要使用 * 作为查询的字段列表，需要哪些字段必须明确写明。<br>说明：1）增加查询分析器解析成本。2）增减字段容易与 resultMap 配置不一致。</li><li>【强制】POJO 类的 boolean 属性不能加 is，而数据库字段必须加 is_，要求在 resultMap 中进行字段与属性之间的映射。<br>说明：参见定义 POJO 类以及数据库字段定义规定，在 sql.xml 增加映射，是必须的。</li><li>【强制】不要用 resultClass 当返回参数，即使所有类属性名与数据库字段一一对应，也需要定义；反过来，每一个表也必然有一个与之对应。<br>说明：配置映射关系，使字段与 DO 类解耦，方便维护。</li><li>【强制】xml 配置中参数注意使用：#{}，#param# 不要使用 ${} 此种方式容易出现 SQL 注入。</li><li>【强制】iBATIS 自带的 queryForList (String statementName,int start,int size) 不推荐使用。<br>说明：其实现方式是在数据库取到 statementName 对应的 SQL 语句的所有记录，再通过 subList 取 start,size 的子集合，线上因为这个原因曾经出现过 OOM。<br>正例：在 sqlmap.xml 中引入 #start#, #size#<br>Map&lt;String, Object&gt; map = new HashMap&lt;String, Object&gt;();<br>map.put(“start”, start);<br>map.put(“size”, size);</li><li>【强制】不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。</li><li>【强制】更新数据表记录时，必须同时更新记录对应的 gmt_modified 字段值为当前时间。</li><li>【推荐】不要写一个大而全的数据更新接口，传入为 POJO 类，不管是不是自己的目标更新字段，都进行 update table set c1=value1,c2=value2,c3=value3; 这是不对的。执行 SQL 时，尽量不要更新无改动的字段，一是易出错；二是效率低；三是 binlog 增加存储。</li><li>【参考】@Transactional 事务不要滥用。事务会影响数据库的 QPS，另外使用事务的地方需要考虑各方面的回滚方案，包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。</li><li>【参考】中的 compareValue 是与属性值对比的常量，一般是数字，表示相等时带上此条件；表示不为空且不为 null 时执行；表示不为 null 值时执行。</li></ol><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>转自阿里巴巴 Java 开发手册，侵删～</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;MySQL-规约（转自阿里巴巴-Java-开发手册）&quot;&gt;&lt;a href=&quot;#MySQL-规约（转自阿里巴巴-Java-开发手册）&quot; class=&quot;headerlink&quot; title=&quot;MySQL 规约（转自阿里巴巴 Java 开发手册）&quot;&gt;&lt;/a&gt;MySQL 规约
      
    
    </summary>
    
    
      <category term="MySQL" scheme="https://dinghaijun.com/categories/MySQL/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="MySQL" scheme="https://dinghaijun.com/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>Charles 使用方法指北</title>
    <link href="https://dinghaijun.com/articles/2019/10/how-to-use-charles.html"/>
    <id>https://dinghaijun.com/articles/2019/10/how-to-use-charles.html</id>
    <published>2019-10-15T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.891Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a><a href="#简介" title="简介"></a>简介</h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/f4/602eca4f9c2eb93c1e9d61c61fdaafdc6f7fd2.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p><a href="http://www.charlesproxy.com/" target="_blank" rel="noopener">Charles</a> 是在 Mac 下常用的网络封包截取工具，在做<br>移动开发时，我们为了调试与服务器端的网络通讯协议，常常需要截取网络封包来分析。</p><p>Charles 通过将自己设置成系统的网络访问代理服务器，使得所有的网络访问请求都通过它来完成，从而实现了网络封包的截取和分析。</p><p>除了在做移动开发中调试端口外，Charles 也可以用于分析第三方应用的通讯协议。配合 Charles 的 SSL 功能，Charles 还可以分析 Https 协议。</p><p>Charles 是收费软件，可以免费试用 30 天。试用期过后，未付费的用户仍然可以继续使用，但是每次使用时间不能超过 30 分钟，并且启动时将会有 10 秒种的延时。因此，该付费方案对广大用户还是相当友好的，即使你长期不付费，也能使用完整的软件功能。只是当你需要长时间进行封包调试时，会因为 Charles 强制关闭而遇到影响。</p><p>Charles 主要的功能包括：</p><ol><li>截取 Http 和 Https 网络封包。</li><li>支持重发网络请求，方便后端调试。</li><li>支持修改网络请求参数。</li><li>支持网络请求的截获并动态修改。</li><li>支持模拟慢速网络。</li></ol><p>Charles 4 新增的主要功能包括：</p><ol><li>支持 Http 2。</li><li>支持 IPv6。</li></ol><h2 id="安装-Charles"><a href="#安装-Charles" class="headerlink" title="安装 Charles"></a><a href="#安装-Charles" title="安装 Charles"></a>安装 Charles</h2><p>去 Charles 的官方网站（<a href="http://www.charlesproxy.com" target="_blank" rel="noopener">http://www.charlesproxy.com</a>）下载最新版的 Charles 安装包，是一个 dmg 后缀的文件。打开后将 Charles 拖到 Application 目录下即完成安装。</p><h2 id="将-Charles-设置成系统代理"><a href="#将-Charles-设置成系统代理" class="headerlink" title="将 Charles 设置成系统代理"></a><a href="#将-Charles-设置成系统代理" title="将 Charles 设置成系统代理"></a>将 Charles 设置成系统代理</h2><p>之前提到，Charles 是通过将自己设置成代理服务器来完成封包截取的，所以使用 Charles 的第一步是将其设置成系统的代理服务器。</p><p>启动 Charles 后，第一次 Charles 会请求你给它设置系统代理的权限。你可以输入登录密码授予 Charles 该权限。你也可以忽略该请求，然后在需要将 Charles 设置成系统代理时，选择菜单中的 “Proxy” -&gt; “Mac OS X Proxy” 来将 Charles 设置成系统代理。如下所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/d8/f3e9dede6d2d97b8cce9211750f2bfa86442fc.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>之后，你就可以看到源源不断的网络请求出现在 Charles 的界面中。</p><p>需要注意的是，Chrome 和 Firefox 浏览器默认并不使用系统的代理服务器设置，而 Charles 是通过将自己设置成代理服务器来完成封包截取的，所以在默认情况下无法截取 Chrome 和 Firefox 浏览器的网络通讯内容。如果你需要截取的话，在 Chrome 中设置成使用系统的代理服务器设置即可，或者直接将代理服务器设置成 <code>127.0.0.1:8888</code> 也可达到相同效果。</p><h2 id="Charles-主界面介绍"><a href="#Charles-主界面介绍" class="headerlink" title="Charles 主界面介绍"></a><a href="#Charles-主界面介绍" title="Charles 主界面介绍"></a>Charles 主界面介绍</h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/32/b5cf245caae30785b22bc9a5a83f4139de7278.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>Charles 主要提供两种查看封包的视图，分别名为 “Structure” 和 “Sequence”。 </p><ol><li>Structure 视图将网络请求按访问的域名分类。</li><li>Sequence 视图将网络请求按访问的时间排序。</li></ol><p>大家可以根据具体的需要在这两种视图之前来回切换。请求多了有些时候会看不过来，Charles 提供了一个简单的 Filter 功能，可以输入关键字来快速筛选出 URL 中带指定关键字的网络请求。</p><p>对于某一个具体的网络请求，你可以查看其详细的请求内容和响应内容。如果请求内容是 POST 的表单，Charles 会自动帮你将表单进行分项显示。如果响应内容是 JSON 格式的，那么 Charles 可以自动帮你将 JSON 内容格式化，方便你查看。如果响应内容是图片，那么 Charles 可以显示出图片的预览。</p><h2 id="过滤网络请求"><a href="#过滤网络请求" class="headerlink" title="过滤网络请求"></a><a href="#过滤网络请求" title="过滤网络请求"></a>过滤网络请求</h2><p>通常情况下，我们需要对网络请求进行过滤，只监控向指定目录服务器上发送的请求。对于这种需求，以下几种办法：</p><p>方法一：在主界面的中部的 Filter 栏中填入需要过滤出来的关键字。例如我们的服务器的地址是：<code>http://yuantiku.com</code> , 那么只需要在 Filter 栏中填入 yuantiku 即可。</p><p>方法二：在 Charles 的菜单栏选择 “Proxy”-&gt;”Recording Settings”，然后选择 Include 栏，选择添加一个项目，然后填入需要监控的协议，主机地址，端口号。这样就可以只截取目标网站的封包了。如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/59/f6627fcffeae8c6bcec6a57929c05d9ca02383.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>通常情况下，我们使用方法一做一些临时性的封包过滤，使用方法二做一些经常性的封包过滤。</p><p>方法三：在想过滤的网络请求上右击，选择 “Focus”，之后在 Filter 一栏勾选上 Focussed 一项，如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/20/b332d17f77daddb26a683d0dca9fd2a8427511.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>这种方式可以临时性的，快速地过滤出一些没有通过关键字的一类网络请求。</p><h2 id="截取-iPhone-上的网络封包"><a href="#截取-iPhone-上的网络封包" class="headerlink" title="截取 iPhone 上的网络封包"></a><a href="#截取-iPhone-上的网络封包" title="截取 iPhone 上的网络封包"></a>截取 iPhone 上的网络封包</h2><p>Charles 通常用来截取本地上的网络封包，但是当我们需要时，我们也可以用来截取其它设备上的网络请求。下面我就以 iPhone 为例，讲解如何进行相应操作。</p><h3 id="Charles-上的设置"><a href="#Charles-上的设置" class="headerlink" title="Charles 上的设置"></a><a href="#Charles-上的设置" title="Charles 上的设置"></a>Charles 上的设置</h3><p>要截取 iPhone 上的网络请求，我们首先需要将 Charles 的代理功能打开。在 Charles 的菜单栏上选择 “Proxy”-&gt;”Proxy Settings”，填入代理端口 8888，并且勾上 “Enable transparent HTTP proxying” 就完成了在 Charles 上的设置。如下图所示:</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/7f/e6e217405c2dcdd299974844a991adfcf05899.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><h3 id="iPhone-上的设置"><a href="#iPhone-上的设置" class="headerlink" title="iPhone 上的设置"></a><a href="#iPhone-上的设置" title="iPhone 上的设置"></a>iPhone 上的设置</h3><p>首先我们需要获取 Charles 运行所在电脑的 IP 地址，Charles 的顶部菜单的 “Help”-&gt;”Local IP Address”，即可在弹出的对话框中看到 IP 地址，如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/a9/df10049afc40abe84c9cdd847e4df618316d07.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>在 iPhone 的 “ 设置 “-&gt;” 无线局域网 “ 中，可以看到当前连接的 wifi 名，通过点击右边的详情键，可以看到当前连接上的 wifi 的详细信息，包括 IP 地址，子网掩码等信息。在其最底部有「HTTP 代理」一项，我们将其切换成手动，然后填上 Charles 运行所在的电脑的 IP，以及端口号 8888，如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/4f/8581dc1907e583972295299eeb7fdc06206921.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>设置好之后，我们打开 iPhone 上的任意需要网络通讯的程序，就可以看到 Charles 弹出 iPhone 请求连接的确认菜单（如下图所示），点击 “Allow” 即可完成设置。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/3c/94140ee901eaeba355503c5511a4171e08b3e7.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><h2 id="截取-Https-通讯信息"><a href="#截取-Https-通讯信息" class="headerlink" title="截取 Https 通讯信息"></a><a href="#截取-Https-通讯信息" title="截取 Https 通讯信息"></a>截取 Https 通讯信息</h2><h3 id="安装证书"><a href="#安装证书" class="headerlink" title="安装证书"></a><a href="#安装证书" title="安装证书"></a>安装证书</h3><p>如果你需要截取分析 Https 协议相关的内容。那么需要安装 Charles 的 CA 证书。具体步骤如下。</p><p>首先我们需要在 Mac 电脑上安装证书。点击 Charles 的顶部菜单，选择 “Help” -&gt; “SSL Proxying” -&gt; “Install Charles Root Certificate”，然后输入系统的帐号密码，即可在 KeyChain 看到添加好的证书。如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/8f/8cd63ff1debec0cf577fc41411f6de6814ca27.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>需要注意的是，即使是安装完证书之后，Charles 默认也并不截取 Https 网络通讯的信息，如果你想对截取某个网站上的所有 Https 网络请求，可以在该请求上右击，选择 SSL proxy，如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/68/1eb4a9f3df77dd4de55d7895a86c83496d55e5.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>这样，对于该 Host 的所有 SSL 请求可以被截取到了。</p><h3 id="截取移动设备中的-Https-通讯信息"><a href="#截取移动设备中的-Https-通讯信息" class="headerlink" title="截取移动设备中的 Https 通讯信息"></a><a href="#截取移动设备中的-Https-通讯信息" title="截取移动设备中的 Https 通讯信息"></a>截取移动设备中的 Https 通讯信息</h3><p>如果我们需要在 iOS 或 Android 机器上截取 Https 协议的通讯内容，还需要在手机上安装相应的证书。点击 Charles 的顶部菜单，选择 “Help” -&gt; “SSL Proxying” -&gt; “Install Charles Root Certificate on a Mobile Device or Remote Browser”，然后就可以看到 Charles 弹出的简单的安装教程。如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/d0/b0f6af142f645073b76a20932945f0c5ba8694.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>按照我们之前说的教程，在设备上设置好 Charles 为代理后，在手机浏览器中访问地址：<a href="http://charlesproxy.com/getssl" target="_blank" rel="noopener">http://charlesproxy.com/getssl</a>，即可打开证书安装的界面，安装完证书后，就可以截取手机上的 Https 通讯内容了。不过同样需要注意，默认情况下 Charles 并不做截取，你还需要在要截取的网络请求上右击，选择 SSL proxy 菜单项。</p><h2 id="模拟慢速网络"><a href="#模拟慢速网络" class="headerlink" title="模拟慢速网络"></a><a href="#模拟慢速网络" title="模拟慢速网络"></a>模拟慢速网络</h2><p>在做移动开发的时候，我们常常需要模拟慢速网络或者高延迟的网络，以测试在移动网络下，应用的表现是否正常。Charles 对此需求提供了很好的支持。</p><p>在 Charles 的菜单上，选择 “Proxy”-&gt;”Throttle Setting” 项，在之后弹出的对话框中，我们可以勾选上 “Enable Throttling”，并且可以设置 Throttle Preset 的类型。如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/72/4f21df9a2e68108ffb81b411fb6a6177f6ef9e.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>如果我们只想模拟指定网站的慢速网络，可以再勾选上图中的 “Only for selected hosts” 项，然后在对话框的下半部分设置中增加指定的 hosts 项即可。</p><h2 id="修改网络请求内容"><a href="#修改网络请求内容" class="headerlink" title="修改网络请求内容"></a><a href="#修改网络请求内容" title="修改网络请求内容"></a>修改网络请求内容</h2><p>有些时候为了调试服务器的接口，我们需要反复尝试不同参数的网络请求。Charles 可以方便地提供网络请求的修改和重发功能。只需要在以往的网络请求上点击右键，选择 “Edit”，即可创建一个可编辑的网络请求。如下所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/61/43c3b5167158e877ffb10753d8c420afbadf9b.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>我们可以修改该请求的任何信息，包括 URL 地址、端口、参数等，之后点击 “Execute” 即可发送该修改后的网络请求（如下图所示）。Charles 支持我们多次修改和发送该请求，这对于我们和服务器端调试接口非常方便，如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/6d/ef55e7ea5c442d2b5f2928286846094cf2a057.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><h2 id="给服务器做压力测试"><a href="#给服务器做压力测试" class="headerlink" title="给服务器做压力测试"></a><a href="#给服务器做压力测试" title="给服务器做压力测试"></a>给服务器做压力测试</h2><p>我们可以使用 Charles 的 Repeat 功能来简单地测试服务器的并发处理能力，方法如下。</p><p>我们在想打压的网络请求上（POST 或 GET 请求均可）右击，然后选择 「Repeat Advanced」菜单项，如下所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/5d/25aecfad06984929aedcd9cbb8fdd76c1edaf8.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>接着我们就可以在弹出的对话框中，选择打压的并发线程数以及打压次数，确定之后，即可开始打压。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/11/083d01257f525c7b0309fa8a1a25309161bc88.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>悄悄说一句，一些写得很弱的投票网站，也可以用这个办法来快速投票。当然，我也拿 Charles 的 Repeat 功能给一些诈骗的钓鱼网站喂了不少垃圾数据，上次不小心还把一个钓鱼网站的数据库打挂了，嗯，请叫我雷锋。</p><h2 id="修改服务器返回内容"><a href="#修改服务器返回内容" class="headerlink" title="修改服务器返回内容"></a><a href="#修改服务器返回内容" title="修改服务器返回内容"></a>修改服务器返回内容</h2><p>有些时候我们想让服务器返回一些指定的内容，方便我们调试一些特殊情况。例如列表页面为空的情况，数据异常的情况，部分耗时的网络请求超时的情况等。如果没有 Charles，要服务器配合构造相应的数据显得会比较麻烦。这个时候，使用 Charles 相关的功能就可以满足我们的需求。</p><p>根据具体的需求，Charles 提供了 Map 功能、 Rewrite 功能以及 Breakpoints 功能，都可以达到修改服务器返回内容的目的。这三者在功能上的差异是：</p><ol><li>Map 功能适合长期地将某一些请求重定向到另一个网络地址或本地文件。</li><li>Rewrite 功能适合对网络请求进行一些正则替换。</li><li>Breakpoints 功能适合做一些临时性的修改。</li></ol><h3 id="Map-功能"><a href="#Map-功能" class="headerlink" title="Map 功能"></a><a href="#Map-功能" title="Map 功能"></a>Map 功能</h3><p>Charles 的 Map 功能分 Map Remote 和 Map Local 两种，顾名思义，Map Remote 是将指定的网络请求重定向到另一个网址请求地址，Map Local 是将指定的网络请求重定向到本地文件。</p><p>在 Charles 的菜单中，选择 “Tools”-&gt;”Map Remote” 或 “Map Local” 即可进入到相应功能的设置页面。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/4e/fe92db120a50c29a1aa592fc7b03c1ba6e29c3.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>对于 Map Remote 功能，我们需要分别填写网络重定向的源地址和目的地址，对于不需要限制的条件，可以留空。下图是一个示例，我将所有 <code>ytk1.yuanku.ws</code>（测试服务器）的请求重定向到了 <code>www.yuantiku.com</code>（线上服务器）。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/7c/50708628cea2fc164725a1b0205a49b294483a.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>对于 Map Local 功能，我们需要填写的重定向的源地址和本地的目标文件。对于有一些复杂的网络请求结果，我们可以先使用 Charles 提供的 “Save Response…” 功能，将请求结果保存到本地（如下图所示），然后稍加修改，成为我们的目标映射文件。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/ef/0f329d55854f6503650d23194f1f65bd1260c6.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>下图是一个示例，我将一个指定的网络请求通过 Map Local 功能映射到了本地的一个经过修改的文件中。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/80/98590d1795a066ef5f3ebea4bf783caf9d3726.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>Map Local 在使用的时候，有一个潜在的问题，就是其返回的 Http Response Header 与正常的请求并不一样。这个时候如果客户端校验了 Http Response Header 中的部分内容，就会使得该功能失效。解决办法是同时使用 Map Local 以下面提到的 Rewrite 功能，将相关的 Http 头 Rewrite 成我们希望的内容。</p><h3 id="Rewrite-功能"><a href="#Rewrite-功能" class="headerlink" title="Rewrite 功能"></a><a href="#Rewrite-功能" title="Rewrite 功能"></a>Rewrite 功能</h3><p>Rewrite 功能功能适合对某一类网络请求进行一些正则替换，以达到修改结果的目的。</p><p>例如，我们的客户端有一个 API 请求是获得用户昵称，而我当前的昵称是 “tangqiaoboy”，如下所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/96/552c0892304e6bdc576f386d36279e0d73646c.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>我们想试着直接修改网络返回值，将 tangqiaoboy 换成成 iosboy。于是我们启用 Rewrite 功能，然后设置如下的规则：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/dc/fa25162d9ba9710f945a1187da7f2d0855e56d.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>完成设置之后，我们就可以从 Charles 中看到，之后的 API 获得的昵称被自动 Rewrite 成了 iosboy，如下图所示：</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/18/120bb394a68df82373163da3c372019b7495ed.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><h3 id="Breakpoints-功能"><a href="#Breakpoints-功能" class="headerlink" title="Breakpoints 功能"></a><a href="#Breakpoints-功能" title="Breakpoints 功能"></a>Breakpoints 功能</h3><p>上面提供的 Rewrite 功能最适合做批量和长期的替换，但是很多时候，我们只是想临时修改一次网络请求结果，这个时候，使用 Rewrite 功能虽然也可以达到目的，但是过于麻烦，对于临时性的修改，我们最好使用 Breakpoints 功能。</p><p>Breakpoints 功能类似我们在 Xcode 中设置的断点一样，当指定的网络请求发生时，Charles 会截获该请求，这个时候，我们可以在 Charles 中临时修改网络请求的返回内容。</p><p>下图是我们临时修改获取用户信息的 API，将用户的昵称进行了更改，修改完成后点击 “Execute” 则可以让网络请求继续进行。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/c5/34219640806d0ae309f1943210ac80109215b8.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p>需要注意的是，使用 Breakpoints 功能将网络请求截获并修改过程中，整个网络请求的计时并不会暂停，所以长时间的暂停可能导致客户端的请求超时。</p><h2 id="反向代理"><a href="#反向代理" class="headerlink" title="反向代理"></a><a href="#反向代理" title="反向代理"></a>反向代理</h2><p>Charles 的反向代理功能允许我们将本地的端口映射到远程的另一个端口上。例如，在下图中，我将本机的 61234 端口映射到了远程（<a href="http://www.yuantiku.com）的80端口上了。这样，当我访问本地的" target="_blank" rel="noopener">www.yuantiku.com）的80端口上了。这样，当我访问本地的</a> 61234 端口时，实际返回的内容会由 <a href="http://www.yuantiku.com" target="_blank" rel="noopener">www.yuantiku.com</a> 的 80 端口提供。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/e1/7de3cc9d7262b6008bf7e43f834405eff6cd27.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><h2 id="设置外部代理，解决与翻墙软件的冲突"><a href="#设置外部代理，解决与翻墙软件的冲突" class="headerlink" title="设置外部代理，解决与翻墙软件的冲突"></a><a href="#设置外部代理，解决与翻墙软件的冲突" title="设置外部代理，解决与翻墙软件的冲突"></a>设置外部代理，解决与翻墙软件的冲突</h2><p>Charles 的原理是把自己设置成系统的代理服务器，但是在中国，由于工作需要，我们常常需要使用 Google 搜索，所以大部分程序员都有自己的翻墙软件，而这些软件的基本原理，也是把自己设置成系统的代理服务器，来做到透明的翻墙。</p><p>为了使得两者能够和平共处，我们可以在 Charles 的 <code>External Proxy Settings</code> 中，设置翻墙的代理端口以及相关信息。同时，我们也要关闭相关翻墙软件的自动设置，使其不主动修改系统代理，避免 Charles 失效。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a><a href="#总结" title="总结"></a>总结</h2><p>通过 Charles 软件，我们可以很方便地在日常开发中，截取和调试网络请求内容，分析封包协议以及模拟慢速网络。用好 Charles 可以极大的方便我们对于带有网络请求的 App 的开发和调试。</p><p>愿本文帮助大家成为 Charles 的专家！</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;简介&quot;&gt;&lt;a href=&quot;#简介&quot; class=&quot;headerlink&quot; title=&quot;简介&quot;&gt;&lt;/a&gt;&lt;a href=&quot;#简介&quot; title=&quot;简介&quot;&gt;&lt;/a&gt;简介&lt;/h2&gt;&lt;figure class=&quot;image-bubble&quot;&gt;
              
      
    
    </summary>
    
    
      <category term="分享" scheme="https://dinghaijun.com/categories/%E5%88%86%E4%BA%AB/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="经验" scheme="https://dinghaijun.com/tags/%E7%BB%8F%E9%AA%8C/"/>
    
      <category term="工具" scheme="https://dinghaijun.com/tags/%E5%B7%A5%E5%85%B7/"/>
    
      <category term="代理" scheme="https://dinghaijun.com/tags/%E4%BB%A3%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>简洁的UDP协议通讯---PHP&amp;&amp;C---Socket</title>
    <link href="https://dinghaijun.com/articles/2019/09/simple_udp_protocol_communication_-_php&amp;&amp;c_-_socket.html"/>
    <id>https://dinghaijun.com/articles/2019/09/simple_udp_protocol_communication_-_php&amp;&amp;c_-_socket.html</id>
    <published>2019-09-19T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<p><strong>UDP传输原理</strong><br>UDP传输不需要连接, 发送端只需要把自己的消息打包好(UDP报文), 然后从电脑上发到因特网即可, 不会有任何的确认帧来反馈给你.</p><a id="more"></a><hr><p><strong>PHP-Server</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="keyword">include_once</span> <span class="string">"actionLoad.php"</span>;</span><br><span class="line"><span class="comment">//include_once "config/socket.php";</span></span><br><span class="line">define(<span class="string">"SERVER"</span>,<span class="string">"udp://127.0.0.1:9998"</span>);</span><br><span class="line"></span><br><span class="line">$socket = stream_socket_server(SERVER, $errno, $errstr, STREAM_SERVER_BIND);</span><br><span class="line"></span><br><span class="line">!$socket ? <span class="keyword">die</span>(<span class="string">"$errstr ($errno)"</span>) : <span class="keyword">null</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">"udp server had started...\nthe port is 9998...\n"</span>;</span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line">    <span class="comment">//接收客户端发来的信息</span></span><br><span class="line">    $request_msg = stream_socket_recvfrom($socket, <span class="number">1024</span> * <span class="number">2</span>, <span class="number">0</span>, $client);</span><br><span class="line">    <span class="comment">//打印客户端的传输信息</span></span><br><span class="line">    <span class="keyword">echo</span> $request_msg.<span class="string">"\n"</span>;</span><br><span class="line">    <span class="comment">/*路由转发 - 业务逻辑路由器*/</span></span><br><span class="line">    coreHandler($socket, $request_msg, $client);</span><br><span class="line">&#125; <span class="keyword">while</span> ($request_msg !== <span class="keyword">false</span>);</span><br></pre></td></tr></table></figure><p>Run Server</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">➜  php server.php</span><br><span class="line">udp server had started...</span><br><span class="line">the port is 9998...</span><br></pre></td></tr></table></figure><hr><p><strong>PHP-Client</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> string $sendMsg</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> string $ip</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> string $port</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> bool|string</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">udpRequest</span><span class="params">($sendMsg = <span class="string">''</span>, $ip = <span class="string">'127.0.0.1'</span>, $port = <span class="string">'9998'</span>)</span> </span>&#123;</span><br><span class="line">    $handle = stream_socket_client(<span class="string">"udp://&#123;$ip&#125;:&#123;$port&#125;"</span>, $errno, $errstr);</span><br><span class="line">    !$handle ? <span class="keyword">die</span>(<span class="string">"ERROR: &#123;$errno&#125; - &#123;$errstr&#125;\n"</span>) : <span class="keyword">null</span>;</span><br><span class="line">    fwrite($handle, $sendMsg . <span class="string">"\n"</span>);</span><br><span class="line">    $result = fread($handle, <span class="number">1024</span>);</span><br><span class="line">    fclose($handle);</span><br><span class="line">    <span class="keyword">return</span> $result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">$result = udpRequest(json_encode(<span class="keyword">array</span>(<span class="string">"code"</span> =&gt; <span class="number">4</span>, <span class="string">"name"</span> =&gt; <span class="string">"alicfeng"</span>)));</span><br><span class="line"><span class="keyword">echo</span> $result;</span><br></pre></td></tr></table></figure><p>Run Client</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜  php client.php</span><br></pre></td></tr></table></figure><hr><p><strong>C-Client</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;arpa/inet.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*UDP服务器地址*/</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOG_SERV_ADDR <span class="meta-string">"127.0.0.1"</span></span></span><br><span class="line"><span class="comment">/*udp服务器端口*/</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOG_SERV_PORT 9998</span></span><br><span class="line"><span class="comment">/*环境模式*/</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOG_ENV_DEV 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 往udp仍数据</span></span><br><span class="line"><span class="comment"> * @param message</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">send_msg_udp</span><span class="params">(<span class="keyword">char</span> *message)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(LOG_ENV_DEV!=<span class="number">1</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">int</span> sockfd;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">servaddr</span>;</span></span><br><span class="line">    sockfd = socket(AF_INET, SOCK_DGRAM, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 初始化udp信息 */</span></span><br><span class="line">    bzero(&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line">    servaddr.sin_family = AF_INET;</span><br><span class="line">    servaddr.sin_port = htons(LOG_SERV_PORT);</span><br><span class="line">    <span class="keyword">if</span> (inet_pton(AF_INET, LOG_SERV_ADDR, &amp;servaddr.sin_addr) &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/* 连接UDP服务器 */</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">connect</span>(sockfd, (struct sockaddr *) &amp;servaddr, <span class="keyword">sizeof</span>(servaddr)) == <span class="number">-1</span>) &#123;</span><br><span class="line">        perror(<span class="string">"connect udp error"</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/*往UDP写数据*/</span></span><br><span class="line">    <span class="built_in">write</span>(sockfd, message, <span class="built_in">strlen</span>(message));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv)</span> </span>&#123;</span><br><span class="line">    send_msg_udp(<span class="string">"alicFeng在扔数据..."</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Run Client</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -o client client.c &amp;&amp; ./client</span><br></pre></td></tr></table></figure><hr><p>文章来源：<br><strong><a href="https://github.com/alicfeng/note.samego.com/blob/master/PHP/%E7%AE%80%E6%B4%81%E7%9A%84UDP%E5%8D%8F%E8%AE%AE%E9%80%9A%E8%AE%AF---PHP%26%26C---socket.md" target="_blank" rel="noopener">价值源于技术，贡献源于分享</a></strong></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;strong&gt;UDP传输原理&lt;/strong&gt;&lt;br&gt;UDP传输不需要连接, 发送端只需要把自己的消息打包好(UDP报文), 然后从电脑上发到因特网即可, 不会有任何的确认帧来反馈给你.&lt;/p&gt;
    
    </summary>
    
    
      <category term="PHP" scheme="https://dinghaijun.com/categories/PHP/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="PHP" scheme="https://dinghaijun.com/tags/PHP/"/>
    
      <category term="TCP" scheme="https://dinghaijun.com/tags/TCP/"/>
    
      <category term="UDP" scheme="https://dinghaijun.com/tags/UDP/"/>
    
  </entry>
  
  <entry>
    <title>Laravel 最佳实践</title>
    <link href="https://dinghaijun.com/articles/2019/08/laravel-best-practices.html"/>
    <id>https://dinghaijun.com/articles/2019/08/laravel-best-practices.html</id>
    <published>2019-08-22T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.891Z</updated>
    
    <content type="html"><![CDATA[<p>这并非laravel官方强制要求的规范，而是我们在日常开发过程中遇到的一些容易忽视的优秀实现方式。</p><h2 id="内容"><a href="#内容" class="headerlink" title="内容"></a>内容</h2><p><span id="contents"></span><br><a href="#single-responsibility-principle">单一职责原则</a></p><p><a href="#fat-models-skinny-controllers">保持控制器的简洁</a></p><p><a href="#validation">使用自定义Request类来进行验证</a></p><p><a href="#business-logic-should-be-in-service-class">业务代码要放到服务层中</a></p><p><a href="#dont-repeat-yourself-dry">DRY原则 不要重复自己</a></p><p><a href="#prefer-to-use-eloquent-over-using-query-builder-and-raw-sql-queries-prefer-collections-over-arrays">使用ORM而不是纯sql语句，使用集合而不是数组</a></p><p><a href="#mass-assignment">集中处理数据</a></p><p><a href="#do-not-execute-queries-in-blade-templates-and-use-eager-loading-n--1-problem">不要在模板中查询，尽量使用惰性加载</a></p><p><a href="#comment-your-code-but-prefer-descriptive-method-and-variable-names-over-comments">注释你的代码，但是更优雅的做法是使用描述性的语言来编写你的代码</a></p><p><a href="#do-not-put-js-and-css-in-blade-templates-and-do-not-put-any-html-in-php-classes">不要把 JS 和 CSS 放到 Blade 模板中，也不要把任何 HTML 代码放到 PHP 代码里</a></p><p><a href="#use-config-and-language-files-constants-instead-of-text-in-the-code">在代码中使用配置、语言包和常量，而不是使用硬编码</a></p><p><a href="#use-standard-laravel-tools-accepted-by-community">使用社区认可的标准Laravel工具</a></p><p><a href="#follow-laravel-naming-conventions">遵循laravel命名约定</a></p><p><a href="#use-shorter-and-more-readable-syntax-where-possible">尽可能使用简短且可读性更好的语法</a></p><p><a href="#use-ioc-container-or-facades-instead-of-new-class">使用IOC容器来创建实例 而不是直接new一个实例</a></p><p><a href="#do-not-get-data-from-the-env-file-directly">避免直接从 <code>.env</code> 文件里获取数据</a></p><p><a href="#store-dates-in-the-standard-format-use-accessors-and-mutators-to-modify-date-format">使用标准格式来存储日期，用访问器和修改器来修改日期格式</a></p><p><a href="#other-good-practices">其他的好建议</a></p><h3 id="单一职责原则"><a href="#单一职责原则" class="headerlink" title="单一职责原则"></a><strong>单一职责原则</strong></h3><p>一个类和一个方法应该只有一个责任。</p><p>例如:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getFullNameAttribute</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (auth()-&gt;user() &amp;&amp; auth()-&gt;user()-&gt;hasRole(<span class="string">'client'</span>) &amp;&amp; auth()-&gt;user()-&gt;isVerified()) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">'Mr. '</span> . <span class="keyword">$this</span>-&gt;first_name . <span class="string">' '</span> . <span class="keyword">$this</span>-&gt;middle_name . <span class="string">' '</span> . <span class="keyword">$this</span>-&gt;last_name;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;first_name[<span class="number">0</span>] . <span class="string">'. '</span> . <span class="keyword">$this</span>-&gt;last_name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getFullNameAttribute</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;isVerifiedClient() ? <span class="keyword">$this</span>-&gt;getFullNameLong() : <span class="keyword">$this</span>-&gt;getFullNameShort();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">isVerifiedClient</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> auth()-&gt;user() &amp;&amp; auth()-&gt;user()-&gt;hasRole(<span class="string">'client'</span>) &amp;&amp; auth()-&gt;user()-&gt;isVerified();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getFullNameLong</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Mr. '</span> . <span class="keyword">$this</span>-&gt;first_name . <span class="string">' '</span> . <span class="keyword">$this</span>-&gt;middle_name . <span class="string">' '</span> . <span class="keyword">$this</span>-&gt;last_name;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getFullNameShort</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;first_name[<span class="number">0</span>] . <span class="string">'. '</span> . <span class="keyword">$this</span>-&gt;last_name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="保持控制器的简洁"><a href="#保持控制器的简洁" class="headerlink" title="保持控制器的简洁"></a><strong>保持控制器的简洁</strong></h3><p>如果您使用的是查询生成器或原始SQL查询，请将所有与数据库相关的逻辑放入Eloquent模型或Repository类中。</p><p>例如:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">index</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $clients = Client::verified()</span><br><span class="line">        -&gt;with([<span class="string">'orders'</span> =&gt; <span class="function"><span class="keyword">function</span> <span class="params">($q)</span> </span>&#123;</span><br><span class="line">            $q-&gt;where(<span class="string">'created_at'</span>, <span class="string">'&gt;'</span>, Carbon::today()-&gt;subWeek());</span><br><span class="line">        &#125;])</span><br><span class="line">        -&gt;get();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> view(<span class="string">'index'</span>, [<span class="string">'clients'</span> =&gt; $clients]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">index</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> view(<span class="string">'index'</span>, [<span class="string">'clients'</span> =&gt; <span class="keyword">$this</span>-&gt;client-&gt;getWithNewOrders()]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Client</span> <span class="keyword">extends</span> <span class="title">Model</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getWithNewOrders</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;verified()</span><br><span class="line">            -&gt;with([<span class="string">'orders'</span> =&gt; <span class="function"><span class="keyword">function</span> <span class="params">($q)</span> </span>&#123;</span><br><span class="line">                $q-&gt;where(<span class="string">'created_at'</span>, <span class="string">'&gt;'</span>, Carbon::today()-&gt;subWeek());</span><br><span class="line">            &#125;])</span><br><span class="line">            -&gt;get();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="使用自定义Request类来进行验证"><a href="#使用自定义Request类来进行验证" class="headerlink" title="使用自定义Request类来进行验证"></a><strong>使用自定义Request类来进行验证</strong></h3><p>把验证规则放到 Request 类中.</p><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">store</span><span class="params">(Request $request)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $request-&gt;validate([</span><br><span class="line">        <span class="string">'title'</span> =&gt; <span class="string">'required|unique:posts|max:255'</span>,</span><br><span class="line">        <span class="string">'body'</span> =&gt; <span class="string">'required'</span>,</span><br><span class="line">        <span class="string">'publish_at'</span> =&gt; <span class="string">'nullable|date'</span>,</span><br><span class="line">    ]);</span><br><span class="line"></span><br><span class="line">    ....</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">store</span><span class="params">(PostRequest $request)</span></span></span><br><span class="line"><span class="function"></span>&#123;    </span><br><span class="line">    ....</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PostRequest</span> <span class="keyword">extends</span> <span class="title">Request</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">rules</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> [</span><br><span class="line">            <span class="string">'title'</span> =&gt; <span class="string">'required|unique:posts|max:255'</span>,</span><br><span class="line">            <span class="string">'body'</span> =&gt; <span class="string">'required'</span>,</span><br><span class="line">            <span class="string">'publish_at'</span> =&gt; <span class="string">'nullable|date'</span>,</span><br><span class="line">        ];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="业务代码要放到服务层中"><a href="#业务代码要放到服务层中" class="headerlink" title="业务代码要放到服务层中"></a><strong>业务代码要放到服务层中</strong></h3><p>控制器必须遵循单一职责原则，因此最好将业务代码从控制器移动到服务层中。</p><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">store</span><span class="params">(Request $request)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> ($request-&gt;hasFile(<span class="string">'image'</span>)) &#123;</span><br><span class="line">        $request-&gt;file(<span class="string">'image'</span>)-&gt;move(public_path(<span class="string">'images'</span>) . <span class="string">'temp'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    ....</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">store</span><span class="params">(Request $request)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">$this</span>-&gt;articleService-&gt;handleUploadedImage($request-&gt;file(<span class="string">'image'</span>));</span><br><span class="line"></span><br><span class="line">    ....</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ArticleService</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">handleUploadedImage</span><span class="params">($image)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!is_null($image)) &#123;</span><br><span class="line">            $image-&gt;move(public_path(<span class="string">'images'</span>) . <span class="string">'temp'</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="DRY原则-不要重复自己"><a href="#DRY原则-不要重复自己" class="headerlink" title="DRY原则 不要重复自己"></a><strong>DRY原则 不要重复自己</strong></h3><p>尽可能重用代码，SRP可以帮助您避免重复造轮子。 此外尽量重复使用Blade模板，使用Eloquent的 scopes 方法来实现代码。</p><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getActive</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;where(<span class="string">'verified'</span>, <span class="number">1</span>)-&gt;whereNotNull(<span class="string">'deleted_at'</span>)-&gt;get();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getArticles</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;whereHas(<span class="string">'user'</span>, <span class="function"><span class="keyword">function</span> <span class="params">($q)</span> </span>&#123;</span><br><span class="line">            $q-&gt;where(<span class="string">'verified'</span>, <span class="number">1</span>)-&gt;whereNotNull(<span class="string">'deleted_at'</span>);</span><br><span class="line">        &#125;)-&gt;get();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">scopeActive</span><span class="params">($q)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> $q-&gt;where(<span class="string">'verified'</span>, <span class="number">1</span>)-&gt;whereNotNull(<span class="string">'deleted_at'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getActive</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;active()-&gt;get();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getArticles</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;whereHas(<span class="string">'user'</span>, <span class="function"><span class="keyword">function</span> <span class="params">($q)</span> </span>&#123;</span><br><span class="line">            $q-&gt;active();</span><br><span class="line">        &#125;)-&gt;get();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="使用ORM而不是纯sql语句，使用集合而不是数组"><a href="#使用ORM而不是纯sql语句，使用集合而不是数组" class="headerlink" title="使用ORM而不是纯sql语句，使用集合而不是数组"></a><strong>使用ORM而不是纯sql语句，使用集合而不是数组</strong></h3><p>使用Eloquent可以帮您编写可读和可维护的代码。 此外Eloquent还有非常优雅的内置工具，如软删除，事件，范围等。</p><p>例子:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> *</span><br><span class="line"><span class="keyword">FROM</span> <span class="string">`articles`</span></span><br><span class="line"><span class="keyword">WHERE</span> <span class="keyword">EXISTS</span> (<span class="keyword">SELECT</span> *</span><br><span class="line">              <span class="keyword">FROM</span> <span class="string">`users`</span></span><br><span class="line">              <span class="keyword">WHERE</span> <span class="string">`articles`</span>.<span class="string">`user_id`</span> = <span class="string">`users`</span>.<span class="string">`id`</span></span><br><span class="line">              <span class="keyword">AND</span> <span class="keyword">EXISTS</span> (<span class="keyword">SELECT</span> *</span><br><span class="line">                          <span class="keyword">FROM</span> <span class="string">`profiles`</span></span><br><span class="line">                          <span class="keyword">WHERE</span> <span class="string">`profiles`</span>.<span class="string">`user_id`</span> = <span class="string">`users`</span>.<span class="string">`id`</span>) </span><br><span class="line">              <span class="keyword">AND</span> <span class="string">`users`</span>.<span class="string">`deleted_at`</span> <span class="keyword">IS</span> <span class="literal">NULL</span>)</span><br><span class="line"><span class="keyword">AND</span> <span class="string">`verified`</span> = <span class="string">'1'</span></span><br><span class="line"><span class="keyword">AND</span> <span class="string">`active`</span> = <span class="string">'1'</span></span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="string">`created_at`</span> <span class="keyword">DESC</span></span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Article::has(<span class="string">'user.profile'</span>)-&gt;verified()-&gt;latest()-&gt;get();</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="集中处理数据"><a href="#集中处理数据" class="headerlink" title="集中处理数据"></a><strong>集中处理数据</strong></h3><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$article = <span class="keyword">new</span> Article;</span><br><span class="line">$article-&gt;title = $request-&gt;title;</span><br><span class="line">$article-&gt;content = $request-&gt;content;</span><br><span class="line">$article-&gt;verified = $request-&gt;verified;</span><br><span class="line"><span class="comment">// Add category to article</span></span><br><span class="line">$article-&gt;category_id = $category-&gt;id;</span><br><span class="line">$article-&gt;save();</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$category-&gt;article()-&gt;create($request-&gt;validated());</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="不要在模板中查询，尽量使用惰性加载"><a href="#不要在模板中查询，尽量使用惰性加载" class="headerlink" title="不要在模板中查询，尽量使用惰性加载"></a><strong>不要在模板中查询，尽量使用惰性加载</strong></h3><p>例子 (对于100个用户，将执行101次DB查询):</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">@<span class="keyword">foreach</span> (User::all() <span class="keyword">as</span> $user)</span><br><span class="line">    &#123;&#123; $user-&gt;profile-&gt;name &#125;&#125;</span><br><span class="line">@<span class="keyword">endforeach</span></span><br></pre></td></tr></table></figure><p>更优的写法 (对于100个用户，使用以下写法只需执行2次DB查询):</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$users = User::with(<span class="string">'profile'</span>)-&gt;get();</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">@<span class="keyword">foreach</span> ($users <span class="keyword">as</span> $user)</span><br><span class="line">    &#123;&#123; $user-&gt;profile-&gt;name &#125;&#125;</span><br><span class="line">@<span class="keyword">endforeach</span></span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="注释你的代码，但是更优雅的做法是使用描述性的语言来编写你的代码"><a href="#注释你的代码，但是更优雅的做法是使用描述性的语言来编写你的代码" class="headerlink" title="注释你的代码，但是更优雅的做法是使用描述性的语言来编写你的代码"></a><strong>注释你的代码，但是更优雅的做法是使用描述性的语言来编写你的代码</strong></h3><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (count((<span class="keyword">array</span>) $builder-&gt;getQuery()-&gt;joins) &gt; <span class="number">0</span>)</span><br></pre></td></tr></table></figure><p>加上注释:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 确定是否有任何连接</span></span><br><span class="line"><span class="keyword">if</span> (count((<span class="keyword">array</span>) $builder-&gt;getQuery()-&gt;joins) &gt; <span class="number">0</span>)</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="keyword">$this</span>-&gt;hasJoins())</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="不要把-JS-和-CSS-放到-Blade-模板中，也不要把任何-HTML-代码放到-PHP-代码里"><a href="#不要把-JS-和-CSS-放到-Blade-模板中，也不要把任何-HTML-代码放到-PHP-代码里" class="headerlink" title="不要把 JS 和 CSS 放到 Blade 模板中，也不要把任何 HTML 代码放到 PHP 代码里"></a><strong>不要把 JS 和 CSS 放到 Blade 模板中，也不要把任何 HTML 代码放到 PHP 代码里</strong></h3><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">let article = `&#123;&#123; json_encode($article) &#125;&#125;`;</span><br></pre></td></tr></table></figure><p>更好的写法:</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;input id=<span class="string">"article"</span> type=<span class="string">"hidden"</span> value=<span class="string">"@json($article)"</span>&gt;</span><br><span class="line"></span><br><span class="line"><span class="keyword">Or</span></span><br><span class="line"></span><br><span class="line">&lt;button class="js-fav-article" data-article="@json($article)"&gt;&#123;&#123; $article-&gt;name &#125;&#125;&lt;button&gt;</span><br></pre></td></tr></table></figure><p>在Javascript文件中加上:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> article = $(<span class="string">'#article'</span>).val();</span><br></pre></td></tr></table></figure><p>当然最好的办法还是使用专业的PHP的JS包传输数据。</p><p><a href="#contents">🔝 返回目录</a></p><h3 id="在代码中使用配置、语言包和常量，而不是使用硬编码"><a href="#在代码中使用配置、语言包和常量，而不是使用硬编码" class="headerlink" title="在代码中使用配置、语言包和常量，而不是使用硬编码"></a><strong>在代码中使用配置、语言包和常量，而不是使用硬编码</strong></h3><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">isNormal</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> $article-&gt;type === <span class="string">'normal'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> back()-&gt;with(<span class="string">'message'</span>, <span class="string">'Your article has been added!'</span>);</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">isNormal</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> $article-&gt;type === Article::TYPE_NORMAL;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> back()-&gt;with(<span class="string">'message'</span>, __(<span class="string">'app.article_added'</span>));</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="使用社区认可的标准Laravel工具"><a href="#使用社区认可的标准Laravel工具" class="headerlink" title="使用社区认可的标准Laravel工具"></a><strong>使用社区认可的标准Laravel工具</strong></h3><p>强力推荐使用内置的Laravel功能和扩展包，而不是使用第三方的扩展包和工具。<br>如果你的项目被其他开发人员接手了，他们将不得不重新学习这些第三方工具的使用教程。<br>此外，当您使用第三方扩展包或工具时，你很难从Laravel社区获得什么帮助。 不要让你的客户为额外的问题付钱。</p><table><thead><tr><th>想要实现的功能</th><th>标准工具</th><th>第三方工具</th></tr></thead><tbody><tr><td>权限</td><td>Policies</td><td>Entrust, Sentinel 或者其他扩展包</td></tr><tr><td>资源编译工具</td><td>Laravel Mix</td><td>Grunt, Gulp, 或者其他第三方包</td></tr><tr><td>开发环境</td><td>Homestead</td><td>Docker</td></tr><tr><td>部署</td><td>Laravel Forge</td><td>Deployer 或者其他解决方案</td></tr><tr><td>自动化测试</td><td>PHPUnit, Mockery</td><td>Phpspec</td></tr><tr><td>页面预览测试</td><td>Laravel Dusk</td><td>Codeception</td></tr><tr><td>DB操纵</td><td>Eloquent</td><td>SQL, Doctrine</td></tr><tr><td>模板</td><td>Blade</td><td>Twig</td></tr><tr><td>数据操纵</td><td>Laravel集合</td><td>数组</td></tr><tr><td>表单验证</td><td>Request classes</td><td>他第三方包,甚至在控制器中做验证</td></tr><tr><td>权限</td><td>Built-in</td><td>他第三方包或者你自己解决</td></tr><tr><td>API身份验证</td><td>Laravel Passport</td><td>第三方的JWT或者 OAuth 扩展包</td></tr><tr><td>创建 API</td><td>Built-in</td><td>Dingo API 或者类似的扩展包</td></tr><tr><td>创建数据库结构</td><td>Migrations</td><td>直接用 DB 语句创建</td></tr><tr><td>本土化</td><td>Built-in</td><td>第三方包</td></tr><tr><td>实时消息队列</td><td>Laravel Echo, Pusher</td><td>使用第三方包或者直接使用WebSockets</td></tr><tr><td>创建测试数据</td><td>Seeder classes, Model Factories, Faker</td><td>手动创建测试数据</td></tr><tr><td>任务调度</td><td>Laravel Task Scheduler</td><td>脚本和第三方包</td></tr><tr><td>数据库</td><td>MySQL, PostgreSQL, SQLite, SQL Server</td><td>MongoDB</td></tr></tbody></table><p><a href="#contents">🔝 返回目录</a></p><h3 id="遵循laravel命名约定"><a href="#遵循laravel命名约定" class="headerlink" title="遵循laravel命名约定"></a><strong>遵循laravel命名约定</strong></h3><p>来源 <a href="http://www.php-fig.org/psr/psr-2/" target="_blank" rel="noopener">PSR standards</a>.</p><p>另外，遵循Laravel社区认可的命名约定：</p><table><thead><tr><th>对象</th><th>规则</th><th>更优的写法</th><th>应避免的写法</th></tr></thead><tbody><tr><td>控制器</td><td>单数</td><td>ArticleController</td><td><del>ArticlesController</del></td></tr><tr><td>路由</td><td>复数</td><td>articles/1</td><td><del>article/1</del></td></tr><tr><td>路由命名</td><td>带点符号的蛇形命名</td><td>users.show_active</td><td><del>users.show-active, show-active-users</del></td></tr><tr><td>模型</td><td>单数</td><td>User</td><td><del>Users</del></td></tr><tr><td>hasOne或belongsTo关系</td><td>单数</td><td>articleComment</td><td><del>articleComments, article_comment</del></td></tr><tr><td>所有其他关系</td><td>复数</td><td>articleComments</td><td><del>articleComment, article_comments</del></td></tr><tr><td>表单</td><td>复数</td><td>article_comments</td><td><del>article_comment, articleComments</del></td></tr><tr><td>透视表</td><td>按字母顺序排列模型</td><td>article_user</td><td><del>user_article, articles_users</del></td></tr><tr><td>数据表字段</td><td>使用蛇形并且不要带表名</td><td>meta_title</td><td><del>MetaTitle; article_meta_title</del></td></tr><tr><td>模型参数</td><td>蛇形命名</td><td>$model-&gt;created_at</td><td><del>$model-&gt;createdAt</del></td></tr><tr><td>外键</td><td>带有_id后缀的单数模型名称</td><td>article_id</td><td><del>ArticleId, id_article, articles_id</del></td></tr><tr><td>主键</td><td>-</td><td>id</td><td><del>custom_id</del></td></tr><tr><td>迁移</td><td>-</td><td>2017_01_01_000000_create_articles_table</td><td><del>2017_01_01_000000_articles</del></td></tr><tr><td>方法</td><td>驼峰命名</td><td>getAll</td><td><del>get_all</del></td></tr><tr><td>资源控制器</td><td><a href="https://laravel.com/docs/master/controllers#resource-controllers" target="_blank" rel="noopener">table</a></td><td>store</td><td><del>saveArticle</del></td></tr><tr><td>测试类</td><td>驼峰命名</td><td>testGuestCannotSeeArticle</td><td><del>test_guest_cannot_see_article</del></td></tr><tr><td>变量</td><td>驼峰命名</td><td>$articlesWithAuthor</td><td><del>$articles_with_author</del></td></tr><tr><td>集合</td><td>描述性的, 复数的</td><td>$activeUsers = User::active()-&gt;get()</td><td><del>$active, $data</del></td></tr><tr><td>对象</td><td>描述性的, 单数的</td><td>$activeUser = User::active()-&gt;first()</td><td><del>$users, $obj</del></td></tr><tr><td>配置和语言文件索引</td><td>蛇形命名</td><td>articles_enabled</td><td><del>ArticlesEnabled; articles-enabled</del></td></tr><tr><td>视图</td><td>短横线命名</td><td>show-filtered.blade.php</td><td><del>showFiltered.blade.php, show_filtered.blade.php</del></td></tr><tr><td>配置</td><td>蛇形命名</td><td>google_calendar.php</td><td><del>googleCalendar.php, google-calendar.php</del></td></tr><tr><td>内容 (interface)</td><td>形容词或名词</td><td>Authenticatable</td><td><del>AuthenticationInterface, IAuthentication</del></td></tr><tr><td>Trait</td><td>使用形容词</td><td>Notifiable</td><td><del>NotificationTrait</del></td></tr></tbody></table><p><a href="#contents">🔝 返回目录</a></p><h3 id="尽可能使用简短且可读性更好的语法"><a href="#尽可能使用简短且可读性更好的语法" class="headerlink" title="尽可能使用简短且可读性更好的语法"></a><strong>尽可能使用简短且可读性更好的语法</strong></h3><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$request-&gt;session()-&gt;get(<span class="string">'cart'</span>);</span><br><span class="line">$request-&gt;input(<span class="string">'name'</span>);</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">session(<span class="string">'cart'</span>);</span><br><span class="line">$request-&gt;name;</span><br></pre></td></tr></table></figure><p>更多示例:</p><table><thead><tr><th>常规写法</th><th>更优雅的写法</th></tr></thead><tbody><tr><td><code>Session::get(&#39;cart&#39;)</code></td><td><code>session(&#39;cart&#39;)</code></td></tr><tr><td><code>$request-&gt;session()-&gt;get(&#39;cart&#39;)</code></td><td><code>session(&#39;cart&#39;)</code></td></tr><tr><td><code>Session::put(&#39;cart&#39;, $data)</code></td><td><code>session([&#39;cart&#39; =&gt; $data])</code></td></tr><tr><td><code>$request-&gt;input(&#39;name&#39;), Request::get(&#39;name&#39;)</code></td><td><code>$request-&gt;name, request(&#39;name&#39;)</code></td></tr><tr><td><code>return Redirect::back()</code></td><td><code>return back()</code></td></tr><tr><td><code>is_null($object-&gt;relation) ? null : $object-&gt;relation-&gt;id</code></td><td><code>optional($object-&gt;relation)-&gt;id</code></td></tr><tr><td><code>return view(&#39;index&#39;)-&gt;with(&#39;title&#39;, $title)-&gt;with(&#39;client&#39;, $client)</code></td><td><code>return view(&#39;index&#39;, compact(&#39;title&#39;, &#39;client&#39;))</code></td></tr><tr><td><code>$request-&gt;has(&#39;value&#39;) ? $request-&gt;value : &#39;default&#39;;</code></td><td><code>$request-&gt;get(&#39;value&#39;, &#39;default&#39;)</code></td></tr><tr><td><code>Carbon::now(), Carbon::today()</code></td><td><code>now(), today()</code></td></tr><tr><td><code>App::make(&#39;Class&#39;)</code></td><td><code>app(&#39;Class&#39;)</code></td></tr><tr><td><code>-&gt;where(&#39;column&#39;, &#39;=&#39;, 1)</code></td><td><code>-&gt;where(&#39;column&#39;, 1)</code></td></tr><tr><td><code>-&gt;orderBy(&#39;created_at&#39;, &#39;desc&#39;)</code></td><td><code>-&gt;latest()</code></td></tr><tr><td><code>-&gt;orderBy(&#39;age&#39;, &#39;desc&#39;)</code></td><td><code>-&gt;latest(&#39;age&#39;)</code></td></tr><tr><td><code>-&gt;orderBy(&#39;created_at&#39;, &#39;asc&#39;)</code></td><td><code>-&gt;oldest()</code></td></tr><tr><td><code>-&gt;select(&#39;id&#39;, &#39;name&#39;)-&gt;get()</code></td><td><code>-&gt;get([&#39;id&#39;, &#39;name&#39;])</code></td></tr><tr><td><code>-&gt;first()-&gt;name</code></td><td><code>-&gt;value(&#39;name&#39;)</code></td></tr></tbody></table><p><a href="#contents">🔝 返回目录</a></p><h3 id="使用IOC容器来创建实例-而不是直接new一个实例"><a href="#使用IOC容器来创建实例-而不是直接new一个实例" class="headerlink" title="使用IOC容器来创建实例 而不是直接new一个实例"></a><strong>使用IOC容器来创建实例 而不是直接new一个实例</strong></h3><p>创建新的类会让类之间的更加耦合，使得测试越发复杂。请改用IoC容器或注入来实现。</p><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$user = <span class="keyword">new</span> User;</span><br><span class="line">$user-&gt;create($request-&gt;validated());</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">(User $user)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">$this</span>-&gt;user = $user;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">....</span><br><span class="line"></span><br><span class="line"><span class="keyword">$this</span>-&gt;user-&gt;create($request-&gt;validated());</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="避免直接从-env-文件里获取数据"><a href="#避免直接从-env-文件里获取数据" class="headerlink" title="避免直接从 .env 文件里获取数据"></a><strong>避免直接从 <code>.env</code> 文件里获取数据</strong></h3><p>将数据传递给配置文件，然后使用<code>config（）</code>帮助函数来调用数据</p><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$apiKey = env(<span class="string">'API_KEY'</span>);</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// config/api.php</span></span><br><span class="line"><span class="string">'key'</span> =&gt; env(<span class="string">'API_KEY'</span>),</span><br><span class="line"></span><br><span class="line"><span class="comment">// Use the data</span></span><br><span class="line">$apiKey = config(<span class="string">'api.key'</span>);</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="使用标准格式来存储日期，用访问器和修改器来修改日期格式"><a href="#使用标准格式来存储日期，用访问器和修改器来修改日期格式" class="headerlink" title="使用标准格式来存储日期，用访问器和修改器来修改日期格式"></a><strong>使用标准格式来存储日期，用访问器和修改器来修改日期格式</strong></h3><p>例子:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#123; Carbon::createFromFormat(<span class="string">'Y-d-m H-i'</span>, $object-&gt;ordered_at)-&gt;toDateString() &#125;&#125;</span><br><span class="line">&#123;&#123; Carbon::createFromFormat(<span class="string">'Y-d-m H-i'</span>, $object-&gt;ordered_at)-&gt;format(<span class="string">'m-d'</span>) &#125;&#125;</span><br></pre></td></tr></table></figure><p>更优的写法:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Model</span></span><br><span class="line"><span class="keyword">protected</span> $dates = [<span class="string">'ordered_at'</span>, <span class="string">'created_at'</span>, <span class="string">'updated_at'</span>];</span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getSomeDateAttribute</span><span class="params">($date)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> $date-&gt;format(<span class="string">'m-d'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// View</span></span><br><span class="line">&#123;&#123; $object-&gt;ordered_at-&gt;toDateString() &#125;&#125;</span><br><span class="line">&#123;&#123; $object-&gt;ordered_at-&gt;some_date &#125;&#125;</span><br></pre></td></tr></table></figure><p><a href="#contents">🔝 返回目录</a></p><h3 id="其他的一些好建议"><a href="#其他的一些好建议" class="headerlink" title="其他的一些好建议"></a><strong>其他的一些好建议</strong></h3><p>永远不要在路由文件中放任何的逻辑代码。</p><p>尽量不要在Blade模板中写原始 PHP 代码。</p><p><a href="#contents">🔝 返回目录</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;这并非laravel官方强制要求的规范，而是我们在日常开发过程中遇到的一些容易忽视的优秀实现方式。&lt;/p&gt;
&lt;h2 id=&quot;内容&quot;&gt;&lt;a href=&quot;#内容&quot; class=&quot;headerlink&quot; title=&quot;内容&quot;&gt;&lt;/a&gt;内容&lt;/h2&gt;&lt;p&gt;&lt;span id=&quot;con
      
    
    </summary>
    
    
      <category term="PHP" scheme="https://dinghaijun.com/categories/PHP/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="Laravel" scheme="https://dinghaijun.com/tags/Laravel/"/>
    
  </entry>
  
  <entry>
    <title>Canvas 绘制文本自动换行</title>
    <link href="https://dinghaijun.com/articles/2018/11/canvas-text-auto-wrap.html"/>
    <id>https://dinghaijun.com/articles/2018/11/canvas-text-auto-wrap.html</id>
    <published>2018-11-25T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.891Z</updated>
    
    <content type="html"><![CDATA[<h1 id="canvas-绘制文本自动换行"><a href="#canvas-绘制文本自动换行" class="headerlink" title="canvas 绘制文本自动换行"></a>canvas 绘制文本自动换行</h1><a id="more"></a><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">CanvasRenderingContext2D.prototype.wrapText = <span class="function"><span class="keyword">function</span> (<span class="params">text, x, y, maxWidth, lineHeight</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> text != <span class="string">'string'</span> || <span class="keyword">typeof</span> x != <span class="string">'number'</span> || <span class="keyword">typeof</span> y != <span class="string">'number'</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">var</span> context = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">var</span> canvas = context.canvas;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> maxWidth == <span class="string">'undefined'</span>) &#123;</span><br><span class="line">        maxWidth = (canvas &amp;&amp; canvas.width) || <span class="number">300</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> lineHeight == <span class="string">'undefined'</span>) &#123;</span><br><span class="line">        lineHeight = (canvas &amp;&amp; <span class="built_in">parseInt</span>(<span class="built_in">window</span>.getComputedStyle(canvas).lineHeight)) || <span class="built_in">parseInt</span>(<span class="built_in">window</span>.getComputedStyle(<span class="built_in">document</span>.body).lineHeight);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 字符分隔为数组</span></span><br><span class="line">    <span class="keyword">var</span> arrText = text.split(<span class="string">''</span>);</span><br><span class="line">    <span class="keyword">var</span> line = <span class="string">''</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> n = <span class="number">0</span>; n &lt; arrText.length; n++) &#123;</span><br><span class="line">        <span class="keyword">var</span> testLine = line + arrText[n];</span><br><span class="line">        <span class="keyword">var</span> metrics = context.measureText(testLine);</span><br><span class="line">        <span class="keyword">var</span> testWidth = metrics.width;</span><br><span class="line">        <span class="keyword">if</span> (testWidth &gt; maxWidth &amp;&amp; n &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            context.fillText(line, x, y);</span><br><span class="line">            line = arrText[n];</span><br><span class="line">            y += lineHeight;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            line = testLine;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    context.fillText(line, x, y);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><ul><li><a href="https://www.zhangxinxu.com/wordpress/2018/02/canvas-text-break-line-letter-spacing-vertical/" target="_blank" rel="noopener">canvas 文本绘制自动换行、字间距、竖排等实现</a></li><li><a href="http://www.zhangxinxu.com/study/201802/canvas-wrapText.html" target="_blank" rel="noopener">example:自动换行扩展 API wrapText 演示 demo</a></li></ul><ol><li>在链接页面还带着一些特效</li></ol><blockquote><p>借助 SVG 直接把 CSS 效果绘制上去</p></blockquote><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> canvas = <span class="built_in">document</span>.querySelector(<span class="string">'canvas'</span>);</span><br><span class="line"><span class="keyword">var</span> context = canvas.getContext(<span class="string">'2d'</span>);</span><br><span class="line">context.font = <span class="string">'16px sans-serif'</span>;</span><br><span class="line"><span class="keyword">var</span> width = canvas.width;</span><br><span class="line"><span class="keyword">var</span> height = canvas.height;</span><br><span class="line"><span class="keyword">var</span> tempImg = <span class="keyword">new</span> Image();</span><br><span class="line">tempImg.width = width;</span><br><span class="line">tempImg.height = height;</span><br><span class="line">tempImg.onload = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 把img绘制在canvas画布上</span></span><br><span class="line">    context.drawImage(<span class="keyword">this</span>, <span class="number">0</span>, <span class="number">0</span>, width, height);</span><br><span class="line">&#125;;</span><br><span class="line">tempImg.src = <span class="string">'data:image/svg+xml;charset=utf-8,&lt;svg xmlns="http://www.w3.org/2000/svg"&gt;&lt;foreignObject width="'</span>+ width +<span class="string">'" height="'</span>+ height +<span class="string">'"&gt;&lt;body xmlns="http://www.w3.org/1999/xhtml" style="margin:0;font:'</span>+ context.font +<span class="string">';"&gt;我是一段需要换行的文字啦啦啦&lt;/body&gt;&lt;/foreignObject&gt;&lt;/svg&gt;'</span>;</span><br></pre></td></tr></table></figure><p><a href="http://www.zhangxinxu.com/study/201802/canvas-wraptext-foreignobject.html" target="_blank" rel="noopener">canvas借助SVG foreignObject实现文本自动换行demo</a></p><ol start="2"><li><p>canvas 支持字符间距</p><blockquote><p>使用 css 的 letter-spacing 属性</p></blockquote></li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> canvas = <span class="built_in">document</span>.querySelector(<span class="string">'canvas'</span>);</span><br><span class="line"><span class="keyword">var</span> context = canvas.getContext(<span class="string">'2d'</span>);</span><br><span class="line"><span class="keyword">var</span> range = <span class="built_in">document</span>.querySelector(<span class="string">'input[type=range]'</span>);</span><br><span class="line"><span class="comment">// 绘制方法</span></span><br><span class="line"><span class="keyword">var</span> draw = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 清除之前的绘制</span></span><br><span class="line">    context.clearRect(<span class="number">0</span>, <span class="number">0</span>, canvas.width, canvas.height);</span><br><span class="line">    <span class="comment">// 字符间距设置</span></span><br><span class="line">    canvas.style.letterSpacing = range.value + <span class="string">'px'</span>;</span><br><span class="line">    <span class="comment">// 并绘制文本，font属性值设置一定要在这里</span></span><br><span class="line">    context.font = <span class="string">'32px sans-serif'</span>;</span><br><span class="line">    context.fillText(<span class="string">'我是一段文本'</span>, <span class="number">0</span>, <span class="number">50</span>);</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 改变字符间距后重绘</span></span><br><span class="line">range.addEventListener(<span class="string">'change'</span>, draw);</span><br><span class="line"><span class="comment">// 一进来根据默认值绘制</span></span><br><span class="line">draw();</span><br></pre></td></tr></table></figure><p>使用 canvas 计算逐字绘制</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">CanvasRenderingContext2D.prototype.letterSpacingText = <span class="function"><span class="keyword">function</span> (<span class="params">text, x, y, letterSpacing</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> context = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">var</span> canvas = context.canvas;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (!letterSpacing &amp;&amp; canvas) &#123;</span><br><span class="line">        letterSpacing = <span class="built_in">parseFloat</span>(<span class="built_in">window</span>.getComputedStyle(canvas).letterSpacing);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!letterSpacing) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.fillText(text, x, y);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">var</span> arrText = text.split(<span class="string">''</span>);</span><br><span class="line">    <span class="keyword">var</span> align = context.textAlign || <span class="string">'left'</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 这里仅考虑水平排列</span></span><br><span class="line">    <span class="keyword">var</span> originWidth = context.measureText(text).width;</span><br><span class="line">    <span class="comment">// 应用letterSpacing占据宽度</span></span><br><span class="line">    <span class="keyword">var</span> actualWidth = originWidth + letterSpacing * (arrText.length - <span class="number">1</span>);</span><br><span class="line">    <span class="comment">// 根据水平对齐方式确定第一个字符的坐标</span></span><br><span class="line">    <span class="keyword">if</span> (align == <span class="string">'center'</span>) &#123;</span><br><span class="line">        x = x - actualWidth / <span class="number">2</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (align == <span class="string">'right'</span>) &#123;</span><br><span class="line">        x = x - actualWidth;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 临时修改为文本左对齐</span></span><br><span class="line">    context.textAlign = <span class="string">'left'</span>;</span><br><span class="line">    <span class="comment">// 开始逐字绘制</span></span><br><span class="line">    arrText.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">letter</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> letterWidth = context.measureText(letter).width;</span><br><span class="line">        context.fillText(letter, x, y);</span><br><span class="line">        <span class="comment">// 确定下一个字符的横坐标</span></span><br><span class="line">        x = x + letterWidth + letterSpacing;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 对齐方式还原</span></span><br><span class="line">    context.textAlign = align;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>3.canvas 支持竖直排列</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">CanvasRenderingContext2D.prototype.fillTextVertical = <span class="function"><span class="keyword">function</span> (<span class="params">text, x, y</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> context = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">var</span> canvas = context.canvas;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">var</span> arrText = text.split(<span class="string">''</span>);</span><br><span class="line">    <span class="keyword">var</span> arrWidth = arrText.map(<span class="function"><span class="keyword">function</span> (<span class="params">letter</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> context.measureText(letter).width;</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">var</span> align = context.textAlign;</span><br><span class="line">    <span class="keyword">var</span> baseline = context.textBaseline;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (align == <span class="string">'left'</span>) &#123;</span><br><span class="line">        x = x + <span class="built_in">Math</span>.max.apply(<span class="literal">null</span>, arrWidth) / <span class="number">2</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (align == <span class="string">'right'</span>) &#123;</span><br><span class="line">        x = x - <span class="built_in">Math</span>.max.apply(<span class="literal">null</span>, arrWidth) / <span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (baseline == <span class="string">'bottom'</span> || baseline == <span class="string">'alphabetic'</span> || baseline == <span class="string">'ideographic'</span>) &#123;</span><br><span class="line">        y = y - arrWidth[<span class="number">0</span>] / <span class="number">2</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (baseline == <span class="string">'top'</span> || baseline == <span class="string">'hanging'</span>) &#123;</span><br><span class="line">        y = y + arrWidth[<span class="number">0</span>] / <span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    context.textAlign = <span class="string">'center'</span>;</span><br><span class="line">    context.textBaseline = <span class="string">'middle'</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 开始逐字绘制</span></span><br><span class="line">    arrText.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">letter, index</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 确定下一个字符的纵坐标位置</span></span><br><span class="line">        <span class="keyword">var</span> letterWidth = arrWidth[index];</span><br><span class="line">        <span class="comment">// 是否需要旋转判断</span></span><br><span class="line">        <span class="keyword">var</span> code = letter.charCodeAt(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">if</span> (code &lt;= <span class="number">256</span>) &#123;</span><br><span class="line">            context.translate(x, y);</span><br><span class="line">            <span class="comment">// 英文字符，旋转90°</span></span><br><span class="line">            context.rotate(<span class="number">90</span> * <span class="built_in">Math</span>.PI / <span class="number">180</span>);</span><br><span class="line">            context.translate(-x, -y);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (index &gt; <span class="number">0</span> &amp;&amp; text.charCodeAt(index - <span class="number">1</span>) &lt; <span class="number">256</span>) &#123;</span><br><span class="line">            <span class="comment">// y修正</span></span><br><span class="line">            y = y + arrWidth[index - <span class="number">1</span>] / <span class="number">2</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        context.fillText(letter, x, y);</span><br><span class="line">        <span class="comment">// 旋转坐标系还原成初始态</span></span><br><span class="line">        context.setTransform(<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        <span class="comment">// 确定下一个字符的纵坐标位置</span></span><br><span class="line">        <span class="keyword">var</span> letterWidth = arrWidth[index];</span><br><span class="line">        y = y + letterWidth;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 水平垂直对齐方式还原</span></span><br><span class="line">    context.textAlign = align;</span><br><span class="line">    context.textBaseline = baseline;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;canvas-绘制文本自动换行&quot;&gt;&lt;a href=&quot;#canvas-绘制文本自动换行&quot; class=&quot;headerlink&quot; title=&quot;canvas 绘制文本自动换行&quot;&gt;&lt;/a&gt;canvas 绘制文本自动换行&lt;/h1&gt;
    
    </summary>
    
    
      <category term="前端" scheme="https://dinghaijun.com/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="canvas" scheme="https://dinghaijun.com/tags/canvas/"/>
    
  </entry>
  
  <entry>
    <title>Github Pages Https</title>
    <link href="https://dinghaijun.com/articles/2018/11/github_pages_https.html"/>
    <id>https://dinghaijun.com/articles/2018/11/github_pages_https.html</id>
    <published>2018-11-04T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.891Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p> 看本文章的前提是你使用过github的pages服务，如果没有，请参考 <a href="https://jekyllcn.com/docs/github-pages/" target="_blank" rel="noopener">GitHub Pages</a>和自定义github pages 域名为<a href="https://help.github.com/articles/using-a-custom-domain-with-github-pages/" target="_blank" rel="noopener">自己的域名</a>，然后再来查看本文。如果一切就绪，那请接着往下看。</p><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><ul><li>阿里云域名一个&amp;VIP域名解析功能</li><li>清楚https的原理并了解letsencrypt，如果不清楚请自行补充知识。参考<a href="https://segmentfault.com/a/1190000011675421" target="_blank" rel="noopener"><br>HTTPS详解</a>&amp;<a href="https://letsencrypt.org/" target="_blank" rel="noopener">letsencrypt官网</a></li></ul><h2 id="正题"><a href="#正题" class="headerlink" title="正题"></a>正题</h2><p>当我们具备以上条件后我们开始正题。</p><ul><li>检查github pages 自定义域名设置正确。例如我的github pages的域名为 justmd5.github.io ，首先这个是能正常访问的。</li><li>其次如果以上没问题，那检查<a href="https://github.com/yourname/yourname.github.io/settings的设置是否正确" target="_blank" rel="noopener">https://github.com/yourname/yourname.github.io/settings的设置是否正确</a><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/f8/ce4e78edb65f15c143227e53a52e7edf770022.png" alt="设置" title="">                </div>                <div class="image-caption">设置</div>            </figure><br>确保设置成功自定义域名<figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/bf/3c11e1780083f6a1f7d3c95c6e5281b5321ed4.jpg" alt="自定义域名" title="">                </div>                <div class="image-caption">自定义域名</div>            </figure></li><li>现在我们来操作阿里云，操作之前我们使用一条命令，查看下当前justmd5.github.io的解析情况。如图所示：<figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/04/a97799ddae5db362a3de1cf72035a473629c86.jpg" alt="dig结果" title="">                </div>                <div class="image-caption">dig结果</div>            </figure>,此处的结果请记录下来，后边会用到。</li><li>登陆阿里云的域名解析，删除之前自定义域名的cname（如果之前你自定义过域名，应该懂我说的是什么）。</li><li>设置 A记录，加入刚才dig查询的结果，<strong>记住：有几条，设置几条A记录</strong>，如：我的自定义域名设置的是：dinghaijun.com，dig查justmd5.github.io 出来的是四条解析记录，那就添加四条A记录指向dig对应的四个IP地址。</li><li>接下来的事情就是<strong>重点了</strong>，使用VIP版（只有VIP版才支持 CAA）的域名解析，设置一条<a href="https://help.aliyun.com/document_detail/65537.html" target="_blank" rel="noopener">CAA</a>解析，内容为：<blockquote><p>0 issue “letsencrypt.org”<br>* 设置成功CAA后的解析情况如图：</p></blockquote><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/d8/4377af6839c204d712f90065fb12de51f562d5.jpg" alt="解析记录" title="">                </div>                <div class="image-caption">解析记录</div>            </figure></li></ul><p>到此，阿里云的工作就完成了。这时候你去使用https协议访问你的自定义域名，你猜会怎么样？作为技术人猜什么猜，访问下嘛！不好，浏览器拦截，告诉你证书不对。这时候想知道为什么嘛？我反正是在这里被坑了的。?给你几个提示<a href="https://help.github.com/articles/troubleshooting-custom-domains/#https-errors" target="_blank" rel="noopener">Troubleshooting custom domains</a>看完再朝下看解决方案。</p><ul><li>如果你看到我给你的提示网址，里边有一段描述，设置自定义域名https的CAA解析后，记得删除一次自定义域名设置，确保自定义域名栏为空，保存。然后再设置一次自定义域名。这样操作的目的是触发github服务器的自定义域名查找和https证书签名生成。</li><li>做完上边一步，你发现设置中https那个勾还是灰色的，是不是有点沮丧?‍♂ 。先别沮丧，灰色选择框的文字，你仔细看了么。对了，就是等待域名证书的生成，当生成完成后，你会发现灰色选择框变为可选了（这个过程有点长，官方说24h，通常过一夜应该就好了。）看一下最终成功吧～！<figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/71/cbe4857043e2d3404e4994f46c990269a43d32.jpg" alt="解析记录" title="">                </div>                <div class="image-caption">解析记录</div>            </figure><br>现在去使用https访问下你的<a href="https://dinghaijun.com/">博客</a>，是不是瞬间感觉高大上（装B）了?！</li></ul><blockquote><p> 附其他github pages对应<a href="https://help.github.com/articles/custom-domain-redirects-for-github-pages-sites/" target="_blank" rel="noopener">自定义域名对应关系</a></p></blockquote><blockquote><p> <a href="https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=xi2us2hj&utm_source=xi2us2hj" target="_blank" rel="noopener">购买阿里云产品</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt; 看本文章的前提是你使用过github的pages服务，如果没有，请参考 &lt;a href=&quot;https://jekyllcn.com/doc
      
    
    </summary>
    
    
      <category term="分享" scheme="https://dinghaijun.com/categories/%E5%88%86%E4%BA%AB/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="https" scheme="https://dinghaijun.com/tags/https/"/>
    
      <category term="github" scheme="https://dinghaijun.com/tags/github/"/>
    
  </entry>
  
  <entry>
    <title>如何使用 Laravel Collections 编写神级代码</title>
    <link href="https://dinghaijun.com/articles/2018/07/how_to_use_laravel_collections.html"/>
    <id>https://dinghaijun.com/articles/2018/07/how_to_use_laravel_collections.html</id>
    <published>2018-07-04T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.891Z</updated>
    
    <content type="html"><![CDATA[<h1 id="如何使用-Laravel-Collections-类编写神级代码"><a href="#如何使用-Laravel-Collections-类编写神级代码" class="headerlink" title="如何使用 Laravel Collections 类编写神级代码"></a>如何使用 Laravel Collections 类编写神级代码</h1><pre><code>Laravel 提供了一些超赞的组件，在我看来，它是目前所有 Web 框架中提供组件支持最好的一个。它不仅提供了开箱即用的视图（views）、身份认证（authentication）、会话（sessions）、缓存（caching）、Eloquent、队列（queues）、数据校验（data validation）等组件。甚至还提供了开发工具（Valet 和 Homestead）。</code></pre><p>但是，这个框架功能中最强大的一个特性常常被萌新们视而不见 - Collection（集合） 类。在这篇文章，我们将探寻如何使用集合提升编码效率、代码的易读行，及编写出更精简的编码。</p><h3 id="预览"><a href="#预览" class="headerlink" title="预览"></a>预览</h3><hr><p>最初接触到使用集合的场景来自于研发人员使用 Eloquent 执行数据库查询，并从返回数据中使用 foreach 语句遍历获取模型集合。</p><p>不过，初学者可能并没有注意到，集合提供了超过 90 个以上的方法来操作底层数据。更妙的是几乎所有的方法都支持链式操作，能够让你的代码读起来就像一篇散文一样。这样使得你的代码更易阅读，无论是你还是其他使用者都是如此。</p><p>还没有进入正题？好吧，让我们回顾一个简单的代码片段，来看看我们如何使用集合编写粗、快、猛的代码吧。</p><h3 id="代码示例"><a href="#代码示例" class="headerlink" title="代码示例"></a>代码示例</h3><p>让我们构建一个真实的世界。假设我们查询某些 API 接口并获取到如下以数组保存的结果集：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// API 请求返回的结果</span></span><br><span class="line">$data = [</span><br><span class="line">    [<span class="string">'first_name'</span> =&gt; <span class="string">'John'</span>, <span class="string">'last_name'</span> =&gt; <span class="string">'Doe'</span>, <span class="string">'age'</span> =&gt; <span class="string">'twenties'</span>],</span><br><span class="line">    [<span class="string">'first_name'</span> =&gt; <span class="string">'Fred'</span>, <span class="string">'last_name'</span> =&gt; <span class="string">'Ali'</span>, <span class="string">'age'</span> =&gt; <span class="string">'thirties'</span>],</span><br><span class="line">    [<span class="string">'first_name'</span> =&gt; <span class="string">'Alex'</span>, <span class="string">'last_name'</span> =&gt; <span class="string">'Cho'</span>, <span class="string">'age'</span> =&gt; <span class="string">'thirties'</span>],</span><br><span class="line">];</span><br></pre></td></tr></table></figure><p>我们看到数组包含名字（first name）、姓氏（last name） 和年龄（age）范围。现在，我们假设从记录中获取一名 年龄（age） 为 30 岁（thirties） 的用户，然后依据 姓氏（last name） 进行 排序（sort）。最后，我们还希望返回的结果为 一个字符串（single string），这样每个用户独占 一行（new line）。最后，我们还希望返回的结果为</p><p>这个需求看起来不难实现，现在让我们看看使用 PHP 如何实现这一功能：</p><p>// 依据姓氏排序</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">usort($data, <span class="function"><span class="keyword">function</span> <span class="params">($item1, $item2)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> $item1[<span class="string">'last_name'</span>] &lt;=&gt; $item2[<span class="string">'last_name'</span>];</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 依据年龄范围分组</span></span><br><span class="line">$new_data = [];</span><br><span class="line"></span><br><span class="line"><span class="keyword">foreach</span> ($data <span class="keyword">as</span> $key =&gt; $item) &#123;</span><br><span class="line">    $new_data[$item[<span class="string">'age'</span>]][$key] = $item;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ksort($new_data, SORT_NUMERIC);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从年龄为 30 岁组里获取用户全名</span></span><br><span class="line">$result = array_map(<span class="function"><span class="keyword">function</span><span class="params">($item)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> $item[<span class="string">'first_name'</span>].<span class="string">' '</span>.$item[<span class="string">'last_name'</span>];</span><br><span class="line">&#125;, $new_data[<span class="string">'thirties'</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将数组转换为字符串并以行分隔符分隔</span></span><br><span class="line">$final = implode(<span class="string">"\n"</span>, $result);</span><br></pre></td></tr></table></figure><p>// 译注：原文是 $final = implode($results, “\n”); implode函数接收两种顺序的参数，为了保持与文档一致所以我这边做了调整。<br>我们的实现代码超过 20 行，并且很不优雅。移除掉注释及换行相关代码，这段代码会变得难以阅读。再者，我们还需要借助临时变量以及 PHP 中内置的不友好的 sort 方法。</p><p>现在，让我们看下借助 Collection 类实现起来是多么简单吧：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">collection($data)-&gt;where(<span class="string">'age'</span>, <span class="string">'thirties'</span>)</span><br><span class="line">                 -&gt;sortBy(<span class="string">'last_name'</span>)</span><br><span class="line">                 -&gt;map(<span class="function"><span class="keyword">function</span><span class="params">($item)</span></span>&#123;</span><br><span class="line">                    <span class="keyword">return</span> $item[<span class="string">'first_name'</span>].<span class="string">' '</span>.$item[<span class="string">'last_name'</span>];</span><br><span class="line">                 &#125;)</span><br><span class="line">                 -&gt;implode(<span class="string">"\n"</span>);</span><br></pre></td></tr></table></figure><p>哇哦！我们的代码从 20 行变成了 6 行。现在的代码不仅顺畅不少，并且在方法实现时无需借助注释告诉我们它们在处理什么问题。</p><p>不过，还存在一个问题阻止我们的代码不如完美阶段… 就是用于比较 first name 和 last name 的 map 方法。坦白说，这真的不是什么大问题，但是它为我们探索 macro（宏） 概念提供了动力。</p><h3 id="扩展集合（Extending-Collections）"><a href="#扩展集合（Extending-Collections）" class="headerlink" title="扩展集合（Extending Collections）"></a>扩展集合（Extending Collections）</h3><p>Collection 类，同其它 Laravel 组件一样，支持宏（macroable），就是说你可以给它添加方法随后使用。</p><blockquote><p>提示： 如果你希望新方法随处可用，你应该将它们添加到服务提供中。我喜欢创建一个 MacroServiceProvider 实先这个功能，对于你来说随你喜欢就好。</p></blockquote><p>让我们添加一个方法它会连接由数组提供的任意数量的字段并返回字符串结果：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Collection::macro(<span class="string">'toConcatenatedString'</span>, <span class="function"><span class="keyword">function</span> <span class="params">($fields = [], $separator = <span class="string">' '</span>)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;map(<span class="function"><span class="keyword">function</span><span class="params">($item)</span> <span class="title">use</span> <span class="params">($fields, $separator)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> implode($separator, array_map(<span class="function"><span class="keyword">function</span> <span class="params">($el)</span> <span class="title">use</span> <span class="params">($item)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> $item[$el];</span><br><span class="line">            &#125;, $fields)</span><br><span class="line">        );</span><br><span class="line">    &#125;)-&gt;implode(<span class="string">"\n"</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>添加完这个方法后，我们的代码基本上就完美了：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">collect($data)-&gt;where(<span class="string">'age'</span>, <span class="string">'thirties'</span>)</span><br><span class="line">              -&gt;sortBy(<span class="string">'last_name'</span>)</span><br><span class="line">              -&gt;toConcatenatedString([<span class="string">'first_name'</span>, <span class="string">'last_name'</span>]);</span><br></pre></td></tr></table></figure><p>我们的代码从混乱的 20 多行精简到了 3 行，代码干净整洁功能清晰任何人都可以立马理解。</p><h3 id="又一个示例"><a href="#又一个示例" class="headerlink" title="又一个示例"></a>又一个示例</h3><p>现在让我们看下第二个示例，假设我们一个用户列表，我们需要基于角色（role）过滤出来，然后进一步如果他们的注册时间为 5 年或以上且 last name 以字母 A-M 开始的仅获取第一个用户。</p><p>数据类似如下：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// API 请求返回的结果</span></span><br><span class="line">$users = [</span><br><span class="line">    [<span class="string">'name'</span> =&gt; <span class="string">'John Doe'</span>, <span class="string">'role'</span> =&gt; <span class="string">'vip'</span>, <span class="string">'years'</span> =&gt; <span class="number">7</span>],</span><br><span class="line">    [<span class="string">'name'</span> =&gt; <span class="string">'Fred Ali'</span>, <span class="string">'role'</span> =&gt; <span class="string">'vip'</span>, <span class="string">'years'</span> =&gt; <span class="number">3</span>],</span><br><span class="line">    [<span class="string">'name'</span> =&gt; <span class="string">'Alex Cho'</span>, <span class="string">'role'</span> =&gt; <span class="string">'user'</span>, <span class="string">'years'</span> =&gt; <span class="number">9</span>],</span><br><span class="line">];</span><br></pre></td></tr></table></figure><p>如果我们使用的是 PHP 实现，我们的代码看下来如下：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$subset = [];</span><br><span class="line"><span class="keyword">foreach</span> ($users <span class="keyword">as</span> $user) &#123;</span><br><span class="line">    <span class="keyword">if</span> ($user[<span class="string">'role'</span>] === <span class="string">'vip'</span> &amp;&amp; $user[<span class="string">'years'</span>] &gt;= <span class="number">5</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (preg_match(<span class="string">'/\s[A-Z]/'</span>, $user[<span class="string">'name'</span>])) &#123;</span><br><span class="line">            $subset[] = $user;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> reset($subset)</span><br></pre></td></tr></table></figure><p>注意： 你可以将第二个 if 语句移至第一个里面，但是我个人喜欢在单个 if 语句中使用不超过两个条件语句，因为我认为超过 2 个条件语句回事代码难以阅读。</p><p>这段代码不至于太糟糕，但是我们依然需要使用临时变量，我们还需要使用 reset 函数将指针重置到第一个用户。我们的代码还有四层缩进，这使得代码解析变得更有挑战性。</p><p>相反，我们来看看集合是如何处理这个问题的：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">collect($users)-&gt;where(<span class="string">'role'</span>, <span class="string">'vip'</span>)</span><br><span class="line">              -&gt;map(<span class="function"><span class="keyword">function</span><span class="params">($user)</span> </span>&#123;</span><br><span class="line">                  <span class="keyword">return</span> preg_match(<span class="string">'/\s[A-Z]/'</span>, $user[<span class="string">'name'</span>]);</span><br><span class="line">              &#125;)</span><br><span class="line">              -&gt;firstWhere(<span class="string">'years'</span>, <span class="string">'&gt;='</span>, <span class="string">'5'</span>);</span><br></pre></td></tr></table></figure><p>我们将代码简化到了之前的一般左右，每一步过滤处理清晰明了，并且我们不需要引入临时变量。</p><p>遗憾的是目前集合还不支持正则匹配，所以我们使用 map 方法，不过我们可以为这个功能创建一个宏：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Collection::macro(<span class="string">'whereRegex'</span>, <span class="function"><span class="keyword">function</span><span class="params">($expression, $field)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;map(<span class="function"><span class="keyword">function</span> <span class="params">($item)</span> <span class="title">use</span> <span class="params">($expression, $field)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> preg_match($expression, $item[$field]);</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>得益于宏方法，我们的代码现在看起来如下：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">collect($users) -&gt; where(<span class="string">'role'</span>, <span class="string">'vip'</span>)</span><br><span class="line">                -&gt; whereRegex(<span class="string">'/\s[A-Z]/'</span>, <span class="string">'name'</span>)</span><br><span class="line">                -&gt; firstWhere(<span class="string">'years'</span>, <span class="string">'&gt;='</span>, <span class="number">5</span>);</span><br></pre></td></tr></table></figure><p>注意： 为了简单起见，我们的红仅仅适用于数组集合。如果你计划让它们可以在 Eloquent 集合上使用，你需要在此场景下做相应的代码处理才行。</p><h3 id="不同的视角"><a href="#不同的视角" class="headerlink" title="不同的视角"></a>不同的视角</h3><p>我们可以继续列出无数的示例，但仍然无法涵盖所有可用的集合方法，并且这从来都不是本文的真正目的。</p><p>需要注意的是，通过使用 Collection 类，您不仅可以获得一个方法库来简化编程工作，还可以选择一种从根本上改善代码的方法。</p><p>你会情不自禁的将你的代码结构从代码块重构简化成一行，同时减少代码的缩进，临时变量的使用和技巧性方法，另外你还可以使用链式编程方法，这让你的代码更加便于阅读和解析，此外最重要的是减少了编码工作！</p><p>查看官方文档获取更多这个迷人的类库的使用细节：<a href="https://laravel-china.org/docs/laravel/collections" target="_blank" rel="noopener">https://laravel-china.org/docs/laravel/collections</a></p><blockquote><p>提示： 你还可以获取这个 Collection 类独立安装包，在使用非 laravel 项目是会非常有帮助。感谢 Tighten Co 团队做出的努力 <a href="https://github.com/tightenco/collect。" target="_blank" rel="noopener">https://github.com/tightenco/collect。</a></p></blockquote><p>感谢阅读，快乐编码！</p><h3 id="原文"><a href="#原文" class="headerlink" title="原文"></a>原文</h3><p><a href="https://medium.com/@mattkingshott/how-laravel-collections-lead-to-zen-code-966c1fa7b03d" target="_blank" rel="noopener">How Laravel Collections lead to Zen Code</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;如何使用-Laravel-Collections-类编写神级代码&quot;&gt;&lt;a href=&quot;#如何使用-Laravel-Collections-类编写神级代码&quot; class=&quot;headerlink&quot; title=&quot;如何使用 Laravel Collections 类编写
      
    
    </summary>
    
    
      <category term="PHP" scheme="https://dinghaijun.com/categories/PHP/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="Laravel" scheme="https://dinghaijun.com/tags/Laravel/"/>
    
  </entry>
  
  <entry>
    <title>如何优雅快速接入腾讯的AI开放平台</title>
    <link href="https://dinghaijun.com/articles/2018/06/tencent_ai_sdk.html"/>
    <id>https://dinghaijun.com/articles/2018/06/tencent_ai_sdk.html</id>
    <published>2018-06-25T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<h3 id="前些时候因为公司业务需要，调研AI相关的云服务，最终选定了腾讯的AI云平台，别问为什么选它，因为免费（穷）😂。但是它官方没有提供现在化-易用的PHP开发SDK，作为一个web-开发者，只能自己动手丰衣足食了，结果就产生了如下的轮子："><a href="#前些时候因为公司业务需要，调研AI相关的云服务，最终选定了腾讯的AI云平台，别问为什么选它，因为免费（穷）😂。但是它官方没有提供现在化-易用的PHP开发SDK，作为一个web-开发者，只能自己动手丰衣足食了，结果就产生了如下的轮子：" class="headerlink" title="前些时候因为公司业务需要，调研AI相关的云服务，最终选定了腾讯的AI云平台，别问为什么选它，因为免费（穷）😂。但是它官方没有提供现在化/易用的PHP开发SDK，作为一个web 开发者，只能自己动手丰衣足食了，结果就产生了如下的轮子："></a>前些时候因为公司业务需要，调研AI相关的云服务，最终选定了腾讯的AI云平台，别问为什么选它，因为免费（穷）😂。但是它官方没有提供现在化/易用的PHP开发SDK，作为一个web 开发者，只能自己动手丰衣足食了，结果就产生了如下的轮子：</h3><p>项目地址：<a href="https://github.com/justmd5/tencent-ai" target="_blank" rel="noopener">https://github.com/justmd5/tencent-ai</a></p><h1 align="center">腾讯AI开放平台 SDK</h1><p align="center">Tencent AI open platform sdk</p><p align="center"><a href="https://styleci.io/repos/138467318" target="_blank" rel="noopener"><img src="https://styleci.io/repos/138467318/shield?branch=master" alt="styleci"></a><a href="https://packagist.org/packages/justmd5/tencent-ai" target="_blank" rel="noopener"><img src="https://img.shields.io/packagist/php-v/justmd5/tencent-ai.svg" alt="PHP from Packagist"></a><a href="https://packagist.org/packages/justmd5/tencent-ai" target="_blank" rel="noopener"><img src="https://poser.pugx.org/justmd5/tencent-ai/v/stable.svg" alt="Latest Stable Version"></a><a href="https://packagist.org/packages/justmd5/tencent-ai" target="_blank" rel="noopener"><img src="https://img.shields.io/github/stars/justmd5/tencent-ai.svg?style=social&label=Stars" alt="GitHub stars"></a><a href="https://packagist.org/packages/justmd5/tencent-ai" target="_blank" rel="noopener"><img src="https://poser.pugx.org/justmd5/tencent-ai/v/unstable.svg" alt="Latest Unstable Version"></a><a href="https://packagist.org/packages/justmd5/tencent-ai" target="_blank" rel="noopener"><img src="https://img.shields.io/github/license/justmd5/tencent-ai.svg" alt="License"></a></p><h3 id="Requirement"><a href="#Requirement" class="headerlink" title="Requirement"></a>Requirement</h3><ol><li>PHP &gt;= 7.0</li><li><strong><a href="https://getcomposer.org/" target="_blank" rel="noopener">Composer</a></strong></li><li>ext-curl 拓展</li><li>ext-json 拓展</li></ol><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p><code>composer require justmd5/tencent-ai</code></p><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">$config  = [</span><br><span class="line">    <span class="string">'appKey'</span>    =&gt; <span class="string">'1106944xxx'</span>,</span><br><span class="line">    <span class="string">'appSecret'</span> =&gt; <span class="string">'dsgnbnWnX8Yxxxxxx'</span>,</span><br><span class="line">    <span class="string">'debug'</span>     =&gt; <span class="number">0</span>,<span class="comment">//1 show debug info </span></span><br><span class="line">];</span><br><span class="line">$AI      = <span class="keyword">new</span> \Justmd5\TencentAi\Ai($config);</span><br></pre></td></tr></table></figure><h3 id="接口调用示例"><a href="#接口调用示例" class="headerlink" title="接口调用示例"></a>接口调用示例</h3><blockquote><p><a href="https://ai.qq.com/doc/nlpchat.shtml" target="_blank" rel="noopener">智能闲聊</a> url: <a href="https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat" target="_blank" rel="noopener">https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat</a><br>请求示例如下：</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$params &#x3D; [</span><br><span class="line">&#39;question&#39;&#x3D;&gt;&#39;腾讯人工智能&#39;,</span><br><span class="line">&#39;session&#39;&#x3D;&gt;123,</span><br><span class="line">];</span><br><span class="line">try &#123;</span><br><span class="line">    dd($AI-&gt;nlp-&gt;request(&#39;textchat&#39;, $params));</span><br><span class="line">&#125; catch (\Justmd5\TencentAi\Exception\NotFoundException $e) &#123;</span><br><span class="line">    dd($e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="文档"><a href="#文档" class="headerlink" title="文档"></a>文档</h3><p><a href="https://ai.qq.com" target="_blank" rel="noopener">Tencent AI</a>  · <a href="https://ai.qq.com/doc/index.shtml" target="_blank" rel="noopener">Official Documents</a></p><h3 id="帮助"><a href="#帮助" class="headerlink" title="帮助"></a>帮助</h3><p>qq群</p><p align="center"><img width="200" src="https://img.vim-cn.com/4a/864a7f14e3ecc78171e2c11386d7554b78d6ca.jpg"></p><h3 id="Todo"><a href="#Todo" class="headerlink" title="Todo"></a>Todo</h3><ul><li><input disabled="" type="checkbox"> parameter verify</li></ul><h3 id="感谢"><a href="#感谢" class="headerlink" title="感谢"></a>感谢</h3><ul><li>thanks to <a href="https://github.com/Hanson/foundation-sdk" target="_blank" rel="noopener">hanson/foundation-sdk</a></li></ul><h3 id="源码列表"><a href="#源码列表" class="headerlink" title="源码列表"></a>源码列表</h3><table><thead><tr><th>SDK 联系人 QQ</th><th>语言</th><th>实现接口</th><th>源代码&amp;SDK 地址</th></tr></thead><tbody><tr><td>910139966</td><td>PHP</td><td>ALL</td><td><a href="https://github.com/justmd5/tencent-ai" target="_blank" rel="noopener">https://github.com/justmd5/tencent-ai</a></td></tr><tr><td>783021975</td><td>JAVA</td><td>ALL</td><td><a href="https://gitee.com/xshuai/taip" target="_blank" rel="noopener">https://gitee.com/xshuai/taip</a></td></tr><tr><td>1361653339</td><td>Golang</td><td>ALL</td><td><a href="https://github.com/shiguanghuxian/txai" target="_blank" rel="noopener">https://github.com/shiguanghuxian/txai</a></td></tr><tr><td>1280827369</td><td>NodeJS</td><td><div>非全部接口实现</div></td><td><a href="https://github.com/w89612b/qqai-api-sdk" target="_blank" rel="noopener">https://github.com/w89612b/qqai-api-sdk</a></td></tr><tr><td>1109527533</td><td>Python</td><td><div>非全部接口实现(完善中)</div></td><td><a href="https://gitee.com/french-home/TencentAISDK" target="_blank" rel="noopener">https://gitee.com/french-home/TencentAISDK</a></td></tr><tr><td>1928881525</td><td>.NET(C#)</td><td><div>OCR接口实现人脸模块接口实现</div></td><td><a href="https://gitee.com/ch_1928881525/Tentcent.Ai" target="_blank" rel="noopener">https://gitee.com/ch_1928881525/Tentcent.Ai</a></td></tr></tbody></table><h2 id="License"><a href="#License" class="headerlink" title="License"></a>License</h2><p>MIT</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h3 id=&quot;前些时候因为公司业务需要，调研AI相关的云服务，最终选定了腾讯的AI云平台，别问为什么选它，因为免费（穷）😂。但是它官方没有提供现在化-易用的PHP开发SDK，作为一个web-开发者，只能自己动手丰衣足食了，结果就产生了如下的轮子：&quot;&gt;&lt;a href=&quot;#前些时
      
    
    </summary>
    
    
      <category term="PHP" scheme="https://dinghaijun.com/categories/PHP/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="SDK" scheme="https://dinghaijun.com/tags/SDK/"/>
    
      <category term="AI" scheme="https://dinghaijun.com/tags/AI/"/>
    
  </entry>
  
  <entry>
    <title>推到重来的讲究</title>
    <link href="https://dinghaijun.com/articles/2017/07/make_a_new_start.html"/>
    <id>https://dinghaijun.com/articles/2017/07/make_a_new_start.html</id>
    <published>2017-07-02T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<p>上个月，有个以前的同事问我：“你在的时候，为什么不把原来的系统都重做了，我们明明有实力啊”。<br>我说：“我们也做了很多事情嘛，系统稳定性、安全性、增加冗余、理清各模块职责、API通讯机制的建立、内部分层的整理。”<br>他说：“对，但我还是想知道，你为什么不把系统重做了呢？”<br>于是我问：“我离职之后，后来似乎多投了不少人重做系统？结果怎么样呢？”</p><p>他说：“结果，结果就是做业务要同时操作三四套系统……”<br>就我所见，把原有系统“推倒重来”的喜好不只程序员有，使用者更有。拿我几年前的那份工作来说，刚入职老大们就来跟我讨论系统重做的打算：需要多少人，多少钱，多长时间，能把原有系统推翻重来。毕竟大家每天都忍受切肤之痛：速度慢、经常出错、不安全、客户抱怨、架构糟糕…… 所以都想拿出“敢叫日月换新天”的劲头，来个干脆的彻底解决。</p><p>这种心情可以理解，但在我任内“重做系统”一直没有被提上日程，整个技术团队所做的都是“改良”的工作，内容就像我上面说的：系统稳定性、安全性、增加冗余、理清各模块职责、API通讯机制的建立、内部分层的整理。这个选择我有充分把握，而且在我看来，如果断然“推倒重来”，我未必能比继任者做得更好，甚至可能更糟糕，因为“推倒重来”绝不是那么简单的事情。</p><p>众所周知，软件开发的难点之一就是控制复杂度。但是在不同的领域，复杂度有不同的表现。</p><p>对于纯互联网业务，或者IT基础架构来说，其复杂度在于软件本身，架构的制定、类库的选择、编码的质量等等。<br>对于其它IT系统——尤其是公司迅速成长，业务不断复杂化的IT系统——而言，其复杂度并不在于软件本身，安全、性能、负载的问题都套用现成的IT解决方案，真正的复杂度来自系统承载的业务本身。<br>比如最简单的：系统里有哪些单据，各种单据承载什么信息，用在什么场景，这些单据是怎样流转的，各种单据存在怎样的约束关系，出现异常情况应当如何处理才能保证业务数据的一致性……这些问题没有准确而稳定的答案，IT再怎样努力也是白搭。</p><p>对于已经能在线下规范运行的业务，或者是有经典解决方案的工作（比如财务、仓库管理），这些知识都是现成的，可以直接拿来用。但对于新兴领域、新兴业务来说，往往不存在“经典解决方案”。加上很多公司成长速度飞快，一开始并没有构筑好的IT基础（其实是业务架构基础）。</p><p>典型的情况就是：业务概念混乱不清，业务逻辑层也是杂乱无章，很多系统里干脆把数据库当作业务逻辑层（这可不是说笑，因为数据库无法推脱责任了）。结果，混乱的业务逻辑依附于糟糕的IT系统，乱上加乱最终成了一锅粥。</p><p>对IT来说，已有业务的问题层出不穷，每次出问题都需要花费大量精力，寻找蛛丝马迹来“破案”；对业务来说，新增业务往往会影响到原有业务，但谁也不知道会不会影响，会如何影响。系统日渐庞大的另一面是内部日趋无序，复杂度和维护成本飞速增长，远远超过可控范围。</p><p>吊诡的是，许多人的解决办法不是针对问题的根本原因，评估业务复杂度、整理业务逻辑、整理业务关系，反而认为“推倒重来”、新做一套系统就能解决。持这种观点的人，通常对系统与业务的关系也有误解。</p><p>对希望“推倒重来”的人来说，系统和业务的关系，有点像车辆对人员：一辆车我开了一段时间觉得不好，就想换一辆车来开，这是很自然的。但是在信息化深入工作各个角落的今天，系统和业务的关系远不是“车辆对人员”那么疏远，而更像“心脏起搏器对人”，或者“人造骨骼与肌肉”的关系，已经如胶似漆缠在了一起，系统对业务的支持越多越广（暂时不论质量），双方纠缠得也就越紧密。更换心脏起搏器或者人造骨骼的难度，远远比换车的难度要大，所以需要慎重考虑，不能单纯因为心脏起搏器“不那么好”就轻率决定更换。对系统来说，也是如此。<br>如果要对基础不好的遗留系统做脱胎换骨的改造，我有几点经验可以参考：</p><h3 id="第一：一定要有非常优秀的业务人员和开发人员。"><a href="#第一：一定要有非常优秀的业务人员和开发人员。" class="headerlink" title="第一：一定要有非常优秀的业务人员和开发人员。"></a>第一：一定要有非常优秀的业务人员和开发人员。</h3><p>对业务人员来说，不但要熟悉自己手头的操作，还必须明白操作背后的逻辑，并且需要超越本职工作，能从全局角度来思考自己的业务（有时甚至要让自己操作更复杂，来提高系统安全性等收益），这样才能真正把握住业务的复杂度。对开发人员来说，要能够完整理解领域知识，同时必须有高超的编程能力来应对遗留代码，敢于出手而不是畏缩不前，谨慎出手而不是贸然行动——如果原有系统开发人员的技术能力可以打30分，全新开发系统的技术要求是60分，那么要成功改造遗留系统的技术人员，往往需要有80以上的分数才能胜任。</p><h3 id="第二：“推倒重来”往往不如“逐步改良”。"><a href="#第二：“推倒重来”往往不如“逐步改良”。" class="headerlink" title="第二：“推倒重来”往往不如“逐步改良”。"></a>第二：“推倒重来”往往不如“逐步改良”。</h3><p>所谓“逐步改良”，指的是大家先通过讨论确认未来系统的设计蓝图，然后需要开发用于过渡的接口层。于是，新开发的模块一定要严格按照新的规范开发（这也就是我说的“理清各模块职责、API通讯机制的建立、内部分层的整理”），同时通过过渡的接口层与原有系统对接，原有的模块则在理清业务逻辑的情况下，按需切出合适的接口，逐部分在测试通过的情况下进行迁移。最终新的系统是像拼图一样慢慢拼出来到最后一天才成型的，而不是平底盖楼造起来的。在这个过程中，最关键的是找到合适的切入点，搭建出合适的接口或者接口层。这些工作就像盖房子的脚手架，哪怕之后不会用到，中途也不能省略，还必须仔细对待。当然，这是一个考验人的工作——我曾经遇到过数据库事务里跨库连表的查询，这个糟糕的设计严重阻碍了单数据库实例拆分成多实例的进展，回想起来真是如噩梦一般。</p><p>如果你对改造遗留系统有自己的见解，或者在这个过程中有什么有意思的经历，欢迎留言给我。</p><p>最后推荐一本有意思的书。其实不管是软件开发还是社会变革，对于不喜欢的现状，大家往往喜欢来个“干脆”、“彻底”的解决方案，但真正成功的往往不是这些方案。在第二次世界大战结束时，世界上到底发生了哪些事情，遇到了哪些问题，又是怎样重建社会秩序的呢？广西师大《理想国》丛书第9册《零年：1945现代世界诞生的时刻》，用翔实的文笔全面记录了“终战”之后的情景，许多画面相信会让读者大吃一惊——很多时候“文明”堪称被打回原形，“零年”这个名字可谓名副其实。</p><p>Posted by Yurii under <a href="http://www.luanxiang.org/blog/archives/2176.html" target="_blank" rel="noopener">Yurii谈开发</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;上个月，有个以前的同事问我：“你在的时候，为什么不把原来的系统都重做了，我们明明有实力啊”。&lt;br&gt;我说：“我们也做了很多事情嘛，系统稳定性、安全性、增加冗余、理清各模块职责、API通讯机制的建立、内部分层的整理。”&lt;br&gt;他说：“对，但我还是想知道，你为什么不把系统重做了
      
    
    </summary>
    
    
      <category term="心得" scheme="https://dinghaijun.com/categories/%E5%BF%83%E5%BE%97/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="职场" scheme="https://dinghaijun.com/tags/%E8%81%8C%E5%9C%BA/"/>
    
  </entry>
  
  <entry>
    <title>Composer 的结构</title>
    <link href="https://dinghaijun.com/articles/2016/11/structure_of_composer.html"/>
    <id>https://dinghaijun.com/articles/2016/11/structure_of_composer.html</id>
    <published>2016-11-14T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Composer-的结构"><a href="#Composer-的结构" class="headerlink" title="Composer 的结构"></a>Composer 的结构</h1><h2 id="Root-Package（根目录包）"><a href="#Root-Package（根目录包）" class="headerlink" title="Root Package（根目录包）"></a>Root Package（根目录包）</h2><p>根目录包就是在你的项目的根目录由 composer.json 定义的包。主要就是由 composer.json 来定义你的项目的依赖。<br>某些字段只能在根目录包的中使用，比如 config 字段，只有根目录包能定义自己的配置。依赖包中的 config 字段是被忽略的。所以 config 字段是 root-only 的。<br>如果你克隆了其中一个依赖包并在上面工作，那么这个包就是根目录包。composer.json 还是一样的，但上下文不同。<br><em>（注：一个包是不是根目录包，取决于上下文。）</em></p><h2 id="composer-json-中的各个属性（字段）"><a href="#composer-json-中的各个属性（字段）" class="headerlink" title="composer.json 中的各个属性（字段）"></a>composer.json 中的各个属性（字段）</h2><ul><li><em>name</em>。包的名字。由供应方（vendor）名和项目名组成，用 / 分隔。==在发布包的时候需要填==。</li><li><em>description</em> 对包的一个简短描述，通常是一行的长度。==在发布包的时候需要填==。</li><li><em>version</em> 包的版本。格式必须是 X.Y.Z，选择性后缀：-dev、-alphaN、-betaN、-RCN。</li><li><em>type</em>包的类型，默认为 library。==包类型用于定制安装逻辑==。如果你的包的安装需要一些特殊的逻辑，你可以定义一个定制的类型。它可以是一个 symfony-bundle 的类型，或者 wordpress-plugin，或者 typo3-module。这些类型将被特定的项目所用，它们将提供安装器来安装这些类型的包。</li></ul><h3 id="Composer-支持-3-种类型："><a href="#Composer-支持-3-种类型：" class="headerlink" title="Composer 支持 3 种类型："></a>Composer 支持 3 种类型：</h3><ul><li>library：默认值。它将复制文件到 vendor 目录。</li><li>project：它表示这是个项目，而不是库。比如像 Symfony 标准版这种应用。</li><li>metapackage：一个含有依赖的空包，能触发安装，但不包含文件，不会向文件系统写任何东西。</li><li>composer-install：为其他的定制类型的包提供安装器的包。</li><li>keywords 一个与包相关的关键词数组。用于包的搜索和过滤。可选。</li></ul><h3 id="homepage"><a href="#homepage" class="headerlink" title="homepage"></a>homepage</h3><ul><li>项目的网站 URL。</li><li>可选。</li></ul><h3 id="time"><a href="#time" class="headerlink" title="time"></a>time</h3><ul><li>版本发布时间。</li><li>必须是 YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS 格式。</li><li>可选。</li></ul><h3 id="license"><a href="#license" class="headerlink" title="license"></a>license</h3><ul><li>包的许可证。</li><li>可以是字符串或字符串数组。</li><li>可选，但强烈建议加上。</li></ul><h3 id="authors包的作者。是个对象数组。每个-author-对象有这些属性："><a href="#authors包的作者。是个对象数组。每个-author-对象有这些属性：" class="headerlink" title="authors包的作者。是个对象数组。每个 author 对象有这些属性："></a>authors包的作者。是个对象数组。每个 author 对象有这些属性：</h3><ul><li>name：作者名字 </li><li>email：作者邮箱 </li><li>homepage：作者网站 URL </li><li>role：作者在项目中的角色（如：developer 或 translator）</li></ul><h3 id="support各种关于该项目如何获取支持的信息。包含这些属性："><a href="#support各种关于该项目如何获取支持的信息。包含这些属性：" class="headerlink" title="support各种关于该项目如何获取支持的信息。包含这些属性："></a>support各种关于该项目如何获取支持的信息。包含这些属性：</h3><ul><li>email：获取支持的邮箱 </li><li>issues：问题跟踪的 URL </li><li>forum：论坛的 URL </li><li>wiki：Wiki 的 URL </li><li>irc：IRC 的频道 </li><li>source：查看或下载源码的 URL</li><li>可选。</li></ul><h3 id="Package-links"><a href="#Package-links" class="headerlink" title="Package links"></a>Package links</h3><p>依赖包的映射表，由包名映射版本约束。如：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"require"</span>: &#123;</span><br><span class="line">        <span class="attr">"monolog/monolog"</span>: <span class="string">"1.0.*"</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="require列出包所依赖的包。"><a href="#require列出包所依赖的包。" class="headerlink" title="require列出包所依赖的包。"></a>require列出包所依赖的包。</h3><p>除非这些依赖已经存在，否则这个包不会被安装。</p><h3 id="require-dev（root-only）列出开发这个包（或跑测试等等）所依赖的包。"><a href="#require-dev（root-only）列出开发这个包（或跑测试等等）所依赖的包。" class="headerlink" title="require-dev（root-only）列出开发这个包（或跑测试等等）所依赖的包。"></a>require-dev（root-only）列出开发这个包（或跑测试等等）所依赖的包。</h3><p>在使用 install 命令时，只有带上 “–dev” 参数才能安装 dev 包。在使用 update 命令时，带上 “–no-dev” 则不更新。</p><h3 id="conflict列出包会和哪些包发生冲突。"><a href="#conflict列出包会和哪些包发生冲突。" class="headerlink" title="conflict列出包会和哪些包发生冲突。"></a>conflict列出包会和哪些包发生冲突。</h3><p>它们将不被允许和你的包一起安装。如果约束了版本，则只会针对特定的版本。</p><h3 id="replace列出哪些包要被这个包替代。"><a href="#replace列出哪些包要被这个包替代。" class="headerlink" title="replace列出哪些包要被这个包替代。"></a>replace列出哪些包要被这个包替代。</h3><h3 id="provide这个包所推荐的包列表。"><a href="#provide这个包所推荐的包列表。" class="headerlink" title="provide这个包所推荐的包列表。"></a>provide这个包所推荐的包列表。</h3><p>这个对公共接口最有用，一个包可以依赖一个虚拟的 logger 包，而实现 logger 接口的库可以放到 provide 字段中。</p><h3 id="suggest建议一些能让这个包工作的更好或得到增强的包列表。"><a href="#suggest建议一些能让这个包工作的更好或得到增强的包列表。" class="headerlink" title="suggest建议一些能让这个包工作的更好或得到增强的包列表。"></a>suggest建议一些能让这个包工作的更好或得到增强的包列表。</h3><p>这些信息只在包安装完成时给出，暗示用户可以添加更多包，虽然不是必须要安装的。<br>格式是，包名映射文字说明，如：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"suggest"</span>: &#123;</span><br><span class="line">        <span class="attr">"monolog/monolog"</span>: <span class="string">"Allows more advanced logging of the application flow"</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="autoload"><a href="#autoload" class="headerlink" title="autoload"></a>autoload</h3><p>提供给 PHP autoloader 的自动加载映射。<br>目前支持的有：PSR-0 自动加载规范，classmap 生成器，还有 files。<br>PSR-0 是比较推荐的，因为它的优秀的扩展性（在添加新的类的适合，不需要重新生成自动加载器）。<br>PSR-0<br>在 psr-0 键名下，定义一个命名空间到路径的映射表，相对于包的根目录。注意，这也同样支持 PEAR-style 的没有命名空间的风格。<br>请注意命名空间的声明得以 \ 结尾，确保自动加载器正确响应。<br>PSR-0 的引用可以在安装或更新时生成的文件中查看：<br>vendor/composer/autoload_namespaces.php<br>例子：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"autoload"</span>: &#123;</span><br><span class="line">        <span class="attr">"psr-0"</span>: &#123;</span><br><span class="line">            <span class="attr">"Monolog\\"</span>: <span class="string">"src/"</span>,</span><br><span class="line">            <span class="attr">"Vendor\\Namespace\\"</span>: <span class="string">"src/"</span>,</span><br><span class="line">            <span class="attr">"Vendor_Namespace_"</span>: <span class="string">"src/"</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果你需要在多个目录里查找同一个前缀的命名空间，你可以用数组，如：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"autoload"</span>: &#123;</span><br><span class="line">        <span class="attr">"psr-0"</span>: &#123; <span class="attr">"Monolog\\"</span>: [<span class="string">"src/"</span>, <span class="string">"lib/"</span>] &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>PSR-0 风格并不局限于加载命名空间的声明的东西，也可以用于类这个层级。当库中只有一个在全局命名空间中的类时，这种方式就能用上。比如你有个 PHP 源文件放在项目的根目录，你可以这样声明：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"autoload"</span>: &#123;</span><br><span class="line">        <span class="attr">"psr-0"</span>: &#123; <span class="attr">"UniqueGlobalClass"</span>: <span class="string">""</span> &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果你有个目录下全是用命名空间组织的，你可以用空前缀：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"autoload"</span>: &#123;</span><br><span class="line">        <span class="attr">"psr-0"</span>: &#123; <span class="attr">""</span>: <span class="string">"src/"</span> &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>（2）Classmap</p><p>classmap 的引用可以在安装或更新时生成的文件中查看：<br>vendor/composer/autoload_classmap.php<br>类映射表是通过扫描指定的目录或文件下的所有的 .php 和 .inc 文件生成的。<br>你可以给任何不支持 PSR-0 的库用 classmap 生成器实现自动加载。配置上只要指定类所在的目录或文件即可：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"> &#123;</span><br><span class="line">    <span class="attr">"autoload"</span>: &#123;</span><br><span class="line">        <span class="attr">"classmap"</span>: [<span class="string">"src/"</span>, <span class="string">"lib/"</span>, <span class="string">"Something.php"</span>]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>（3）files</p><p>如果你确定需要在任何请求中都加载某些文件，你可以使用 files 自动加载机制。对于那些包中有些 PHP 函数但不能自动加载时特别有用。例如：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"autoload"</span>: &#123;</span><br><span class="line">        <span class="attr">"files"</span>: [<span class="string">"src/MyLibrary/functions.php"</span>]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="include-path"><a href="#include-path" class="headerlink" title="include-path"></a>include-path</h3><p>（将被弃用，它的功能由 autoload 代替。其实就是设置 include_path，可选）</p><h3 id="target-dir"><a href="#target-dir" class="headerlink" title="target-dir"></a>target-dir</h3><p>指定安装目标路径。</p><p>如果包的根目录是在命名空间下，自动加载就不正确了，所以才有 target-dir 来解决这个问题。</p><p>Symfony 就是个例子。它由很多组件包组成。Yaml 组件是在</p><p>Symfony\Component\Yaml<br>  命名空间下的，它的根目录是 Yaml 目录。要让自动加载正常工作，我们要确保它不是安装在<br>vendor/symfony/yaml<br> ，而是在<br>vendor/symfony/yaml/Symfony/Component/Yaml<br> ，这样自动加载器才能从 vendor/symfony/yaml 加载它。<br>所以要定义 target-dir 如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"autoload"</span>: &#123;</span><br><span class="line">        <span class="attr">"psr-0"</span>: &#123; <span class="attr">"Symfony\\Component\\Yaml\\"</span>: <span class="string">""</span> &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">"target-dir"</span>: <span class="string">"Symfony/Component/Yaml"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="minimum-stability（root-only）"><a href="#minimum-stability（root-only）" class="headerlink" title="minimum-stability（root-only）"></a>minimum-stability（root-only）</h3><p>定义根据稳定性如何过滤包。默认是 stable，如果你信赖一个 dev 包，你需要指明。</p><h3 id="prefer-stable（root-only）"><a href="#prefer-stable（root-only）" class="headerlink" title="prefer-stable（root-only）"></a>prefer-stable（root-only）</h3><p>如果开启，Composer 会在稳定包和不稳定包中选择前者。</p><h3 id="repositories（root-only）"><a href="#repositories（root-only）" class="headerlink" title="repositories（root-only）"></a>repositories（root-only）</h3><p>定制包的仓库地址。</p><p>默认的，Composer 只使用 Packagist 仓库。通过指定仓库地址，你可以从任何地方获取包。</p><p>仓库不能递归。你只能将它们添加到主的 composer.json 中。所依赖包中 composer.json 文件中的仓库定义是被忽略的。</p><p>支持的仓库的类型有：</p><ul><li>composer<br>composer 仓库通过网络提供 packages.json 文件，它包含一个 composer.json 对象的列表，还有额外的 dist 或 source 信息。packages.json 文件通过 PHP 流加载。</li><li>vcs<br>版本控制系统仓库，如：git、svn、hg。</li><li>pear<br>通过它，你可以导入任何 pear 仓库到你的项目中。</li><li>package<br>如果你依赖一个不支持 composer 的项目，你可以定义一个 package 类型的仓库，然后将 composer.json 对象直接写入。</li></ul><p>完整的例子：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"repositories"</span>: [</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="attr">"type"</span>: <span class="string">"composer"</span>,</span><br><span class="line">            <span class="attr">"url"</span>: <span class="string">"http://packages.example.com"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="attr">"type"</span>: <span class="string">"composer"</span>,</span><br><span class="line">            <span class="attr">"url"</span>: <span class="string">"https://packages.example.com"</span>,</span><br><span class="line">            <span class="attr">"options"</span>: &#123;</span><br><span class="line">                <span class="attr">"ssl"</span>: &#123;</span><br><span class="line">                    <span class="attr">"verify_peer"</span>: <span class="string">"true"</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="attr">"type"</span>: <span class="string">"vcs"</span>,</span><br><span class="line">            <span class="attr">"url"</span>: <span class="string">"https://github.com/Seldaek/monolog"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="attr">"type"</span>: <span class="string">"pear"</span>,</span><br><span class="line">            <span class="attr">"url"</span>: <span class="string">"http://pear2.php.net"</span></span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="attr">"type"</span>: <span class="string">"package"</span>,</span><br><span class="line">            <span class="attr">"package"</span>: &#123;</span><br><span class="line">                <span class="attr">"name"</span>: <span class="string">"smarty/smarty"</span>,</span><br><span class="line">                <span class="attr">"version"</span>: <span class="string">"3.1.7"</span>,</span><br><span class="line">                <span class="attr">"dist"</span>: &#123;</span><br><span class="line">                    <span class="attr">"url"</span>: <span class="string">"http://www.smarty.net/files/Smarty-3.1.7.zip"</span>,</span><br><span class="line">                    <span class="attr">"type"</span>: <span class="string">"zip"</span></span><br><span class="line">                &#125;,</span><br><span class="line">                <span class="attr">"source"</span>: &#123;</span><br><span class="line">                    <span class="attr">"url"</span>: <span class="string">"http://smarty-php.googlecode.com/svn/"</span>,</span><br><span class="line">                    <span class="attr">"type"</span>: <span class="string">"svn"</span>,</span><br><span class="line">                    <span class="attr">"reference"</span>: <span class="string">"tags/Smarty_3_1_7/distribution/"</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="config（root-only）"><a href="#config（root-only）" class="headerlink" title="config（root-only）"></a>config（root-only）</h3><h4 id="针对项目的一些配置："><a href="#针对项目的一些配置：" class="headerlink" title="针对项目的一些配置："></a>针对项目的一些配置：</h4><blockquote><p>process-timeout：默认 300 秒，Composer 进程执行超时时间；<br>use-include-path：默认 false，如果是 true，Composer 自动加载器也会到 PHP 的 include_path 中查找；<br>preferred-install：默认 auto，设置 Composer 安装方式；<br>github-protocols：默认 [“git”, “https”]，设置与 github 通信协议；<br>github-oauth：设置 oauth；<br>vendor-dir：默认 vendor，你可以换成别的；<br>bin-dir：默认 vendor/bin，如果项目有二进制文件，会链接到这；<br>cache-dir：默认 $home/cache，存放 Composer 运行时产生的缓存；<br>cache-files-dir：默认 $cache-dir/files，存放包的 zip 文件；<br>cache-repo-dir：默认 $cache-dir/repo，存放仓库元数据；<br>cache-vcs-dir：默认 $cache-dir/vcs，存放 vcs 克隆；<br>cache-files-ttl：默认六个月，缓存的过期时间；<br>cache-files-maxsize：默认 300M；<br>notify-no-install：默认 true，从仓库安装包会有个通知，可以关掉；<br>discard-changes：默认false，如何处理脏的更新；</p></blockquote><ul><li>scripts（root-only）Composer 允许你在安装进程中安装钩子脚本，钩子是基于事件的；</li><li>extra供 scripts 消费的额外数据；</li><li>bin指定哪些文件必须被当做二进制文件处理的；</li><li>archive<br>设置创建包时的选项，exclude 属性可以设置排除哪些目录，例如：</li></ul><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"archive"</span>: &#123;</span><br><span class="line">        <span class="attr">"exclude"</span>: [<span class="string">"/foo/bar"</span>, <span class="string">"baz"</span>, <span class="string">"/*.test"</span>, <span class="string">"!/foo/bar/baz"</span>]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这篇看下来，再看 Symfony 的标准版中的 composer.json，应该了然了吧。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Composer-的结构&quot;&gt;&lt;a href=&quot;#Composer-的结构&quot; class=&quot;headerlink&quot; title=&quot;Composer 的结构&quot;&gt;&lt;/a&gt;Composer 的结构&lt;/h1&gt;&lt;h2 id=&quot;Root-Package（根目录包）&quot;&gt;&lt;a hr
      
    
    </summary>
    
    
      <category term="PHP" scheme="https://dinghaijun.com/categories/PHP/"/>
    
    
      <category term="composer" scheme="https://dinghaijun.com/tags/composer/"/>
    
  </entry>
  
  <entry>
    <title>语义化版本2.0.0</title>
    <link href="https://dinghaijun.com/articles/2016/10/semantic_version.html"/>
    <id>https://dinghaijun.com/articles/2016/10/semantic_version.html</id>
    <published>2016-10-30T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<h1 id="摘要"><a href="#摘要" class="headerlink" title="摘要"></a>摘要</h1><p>版本格式：主版本号.次版本号.修订号，版本号递增规则如下：</p><ul><li>主版本号：当你做了不兼容的 API 修改，</li><li>次版本号：当你做了向下兼容的功能性新增，</li><li>修订号：当你做了向下兼容的问题修正。</li><li>先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面，作为延伸。</li></ul><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>在软件管理的领域里存在着被称作“依赖地狱”的死亡之谷，系统规模越大，加入的套件越多，你就越有可能在未来的某一天发现自己已深陷绝望之中。</p><p>在依赖高的系统中发布新版本套件可能很快会成为恶梦。如果依赖关系过高，可能面临版本控制被锁死的风险（必须对每一个相依套件改版才能完成某次升级）。而如果依赖关系过于松散，又将无法避免版本的混乱（假设兼容于未来的多个版本已超出了合理数量）。当你专案的进展因为版本相依被锁死或版本混乱变得不够简便和可靠，就意味着你正处于依赖地狱之中。</p><p>作为这个问题的解决方案之一，我提议用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据（但不局限于）已经被各种封闭、开放源码软件所广泛使用的惯例所设计。为了让这套理论运作，你必须先有定义好的公共 API 。这可以透过文件定义或代码强制要求来实现。无论如何，这套 API 的清楚明了是十分重要的。一旦你定义了公共 API，你就可以透过修改相应的版本号来向大家说明你的修改。考虑使用这样的版本号格式：XYZ （主版本号.次版本号.修订号）修复问题但不影响API 时，递增修订号；API 保持向下兼容的新增及修改时，递增次版本号；进行不向下兼容的修改时，递增主版本号。</p><p>我称这套系统为“语义化的版本控制”，在这套约定下，版本号及其更新方式包含了相邻版本间的底层代码和修改内容的信息。</p><h1 id="语义化版本控制规范（SemVer）"><a href="#语义化版本控制规范（SemVer）" class="headerlink" title="语义化版本控制规范（SemVer）"></a>语义化版本控制规范（SemVer）</h1><ul><li><p>以下关键词 MUST、MUST NOT、REQUIRED、SHALL、SHALL NOT、SHOULD、SHOULD NOT、 RECOMMENDED、MAY、OPTIONAL 依照 RFC 2119 的叙述解读。（译注：为了保持语句顺畅， 以下文件遇到的关键词将依照整句语义进行翻译，在此先不进行个别翻译。）</p></li><li><p>使用语义化版本控制的软件“必须 MUST ”定义公共 API。该 API 可以在代码中被定义或出现于严谨的文件内。无论何种形式都应该力求精确且完整。</p></li><li><p>标准的版本号“必须 MUST ”采用 XYZ 的格式，其中 X、Y 和 Z 为非负的整数，且“禁止 MUST NOT”在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素“必须 MUST ”以数值来递增。例如：1.9.1 -&gt; 1.10.0 -&gt; 1.11.0。</p></li><li><p>标记版本号的软件发行后，“禁止 MUST NOT ”改变该版本软件的内容。任何修改都“必须 MUST ”以新版本发行。</p></li><li><p>主版本号为零（0.y.z）的软件处于开发初始阶段，一切都可能随时被改变。这样的公共 API 不应该被视为稳定版。</p></li><li><p>1.0.0 的版本号用于界定公共 API 的形成。这一版本之后所有的版本号更新都基于公共 API 及其修改内容。</p></li><li><p>修订号 Z（x.y.Z | x &gt; 0）“必须 MUST ”在只做了向下兼容的修正时才递增。这里的修正指的是针对不正确结果而进行的内部修改。</p></li><li><p>次版本号 Y（x.Y.z | x &gt; 0）“必须 MUST ”在有向下兼容的新功能出现时递增。在任何公共 API 的功能被标记为弃用时也“必须 MUST ”递增。也“可以 MAY ”在内部程序有大量新功能或改进被加入时递增，其中“可以 MAY ”包括修订级别的改变。每当次版本号递增时，修订号“必须 MUST ”归零。</p></li><li><p>主版本号 X（X.y.z | X &gt; 0）“必须 MUST ”在有任何不兼容的修改被加入公共 API 时递增。其中“可以 MAY ”包括次版本号及修订级别的改变。每当主版本号递增时，次版本号和修订号“必须 MUST ”归零。</p></li><li><p>先行版本号“可以 MAY ”被标注在修订版之后，先加上一个连接号再加上一连串以句点分隔的标识符号来修饰。标识符号“必须 MUST ”由 ASCII 码的英数字和连接号 [0-9A-Za-z-] 组成，且“禁止 MUST NOT ”留白。数字型的标识符号“禁止 MUST NOT ”在前方补零。先行版的优先级低于相关联的标准版本。被标上先行版本号则表示这个版本并非稳定而且可能无法达到兼容的需求。范例：1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。</p></li><li><p>版本编译信息“可以 MAY ”被标注在修订版或先行版本号之后，先加上一个加号再加上一连串以句点分隔的标识符号来修饰。标识符号“必须 MUST ”由 ASCII 的英数字和连接号 [0-9A-Za-z-] 组成，且“禁止 MUST NOT ”留白。当判断版本的优先层级时，版本编译信息“可 SHOULD ”被忽略。因此当两个版本只有在版本编译信息有差别时，属于相同的优先层级。范例：1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。</p></li><li><p>版本的优先层级指的是不同版本在排序时如何比较。判断优先层级时，“必须 MUST ”把版本依序拆分为主版本号、次版本号、修订号及先行版本号后进行比较（版本编译信息不在这份比较的列表中）。由左到右依序比较每个标识符号，第一个差异值用来决定优先层级：主版本号、次版本号及修订号以数值比较，例如：1.0.0 &lt; 2.0.0 &lt; 2.1.0 &lt; 2.1.1。当主版本号、次版本号及修订号都相同时，改以优先层级比较低的先行版本号决定。例如：1.0.0-alpha &lt; 1.0.0。有相同主版本号、次版本号及修订号的两个先行版本号，其优先层级“必须 MUST ”透过由左到右的每个被句点分隔的标识符号来比较，直到找到一个差异值后决定：只有数字的标识符号以数值高低比较，有字母或连接号时则逐字以 ASCII 的排序来比较。数字的标识符号比非数字的标识符号优先层级低。若开头的标识符号都相同时，栏位比较多的先行版本号优先层级比较高。范例：1.0.0-alpha &lt; 1.0.0-alpha.1 &lt; 1.0.0-alpha.beta &lt; 1.0.0-beta &lt; 1.0.0-beta.2 &lt; 1.0.0-beta.11 &lt; 1.0.0- rc.1 &lt; 1.0.0。</p></li></ul><h1 id="为什么要使用语义化的版本控制？"><a href="#为什么要使用语义化的版本控制？" class="headerlink" title="为什么要使用语义化的版本控制？"></a>为什么要使用语义化的版本控制？</h1><p>这并不是一个新的或者革命性的想法。实际上，你可能已经在做一些近似的事情了。问题在于只是“近似”还不够。如果没有某个正式的规范可循，版本号对于依赖的管理并无实质意义。将上述的想法命名并给予清楚的定义，让你对软件使用者传达意向变得容易。一旦这些意向变得清楚，弹性（但又不会太弹性）的依赖规范就能达成。</p><p>举个简单的例子就可以展示语义化的版本控制如何让依赖地狱成为过去。假设有个名为“救火车”的函式库，它需要另一个名为“梯子”并已经有使用语义化版本控制的套件。当救火车创建时，梯子的版本号为 3.1.0。因为救火车使用了一些版本 3.1.0 所新增的功能， 你可以放心地指定相依于梯子的版本号大等于 3.1.0 但小于 4.0.0。这样，当梯子版本 3.1.1 和 3.2.0 发布时，你可以将直接它们纳入你的套件管理系统，因为它们能与原有相依的软件兼容。</p><p>作为一位负责任的开发者，你理当确保每次套件升级的运作与版本号的表述一致。现实世界是复杂的，我们除了提高警觉外能做的不多。你所能做的就是让语义化的版本控制为你提供一个健全的方式来发行以及升级套件，而无需推出新的相依套件，节省你的时间及烦恼。</p><p>如果你对此认同，希望立即开始使用语义化版本控制，你只需声明你的函式库正在使用它并遵循这些规则就可以了。请在你的 README 文件中保留此页连结，让别人也知道这些规则并从中受益。</p><h1 id="FAQ"><a href="#FAQ" class="headerlink" title="FAQ"></a>FAQ</h1><p>在 0.y.z 初始开发阶段，我该如何进行版本控制？</p><p>最简单的做法是以 0.1.0 作为你的初始化开发版本，并在后续的每次发行时递增次版本号。</p><p>如何判断发布 1.0.0 版本的时机？</p><p>当你的软件被用于正式环境，它应该已经达到了 1.0.0 版。如果你已经有个稳定的 API 被使用者依赖，也会是 1.0.0 版。如果你很担心向下兼容的问题，也应该算是 1.0.0 版了。</p><p>这不会阻碍快速开发和迭代吗？</p><p>主版本号为零的时候就是为了做快速开发。如果你每天都在改变 API，那么你应该仍在主版本号为零的阶段（0.y.z），或是正在下个主版本的独立开发分支中。</p><p>对于公共 API，若即使是最小但不向下兼容的改变都需要产生新的主版本号，岂不是很快就达到 42.0.0 版？</p><p>这是开发的责任感和前瞻性的问题。不兼容的改变不应该轻易被加入到有许多依赖代码的软件中。升级所付出的代价可能是巨大的。要递增主版本号来发行不兼容的改版，意味着你必须为这些改变所带来的影响深思熟虑，并且评估所涉及的成本及效益比。</p><p>为整个公共 API 写文件太费事了！</p><p>为供他人使用的软件编写适当的文件，是你作为一名专业开发者应尽的职责。保持专案高效一个非常重要的部份是掌控软件的复杂度，如果没有人知道如何使用你的软件或不知道哪些函数的调用是可靠的，要掌控复杂度会是困难的。长远来看，使用语义化版本控制以及对于公共 API 有良好规范的坚持，可以让每个人及每件事都运行顺畅。</p><p>万一不小心把一个不兼容的改版当成了次版本号发行了该怎么办？</p><p>一旦发现自己破坏了语义化版本控制的规范，就要修正这个问题，并发行一个新的次版本号来更正这个问题并且恢复向下兼容。即使是这种情况，也不能去修改已发行的版本。可以的话，将有问题的版本号记录到文件中，告诉使用者问题所在，让他们能够意识到这是有问题的版本。</p><p>如果我更新了自己的依赖但没有改变公共 API 该怎么办？</p><p>由于没有影响到公共 API，这可以被认定是兼容的。若某个软件和你的套件有共同依赖，则它会有自己的依赖规范，作者也会告知可能的冲突。要判断改版是属于修订等级或是次版等级，是依据你更新的依赖关系是为了修复问题或是加入新功能。对于后者，我经常会预期伴随着更多的代码，这显然会是一个次版本号级别的递增。</p><p>如果我变更了公共 API 但无意中未遵循版本号的改动怎么办呢？（意即在修订等级的发布中，误将重大且不兼容的改变加到代码之中）</p><p>自行做最佳的判断。如果你有庞大的使用者群在依照公共 API 的意图而变更行为后会大受影响，那么最好做一次主版本的发布，即使严格来说这个修复仅是修订等级的发布。记住， 语义化的版本控制就是透过版本号的改变来传达意义。若这些改变对你的使用者是重要的，那就透过版本号来向他们说明。</p><p>我该如何处理即将弃用的功能？</p><p>弃用现存的功能是软件开发中的家常便饭，也通常是向前发展所必须的。当你弃用部份公共 API 时，你应该做两件事：（1）更新你的文件让使用者知道这个改变，（2）在适当的时机将弃用的功能透过新的次版本号发布。在新的主版本完全移除弃用功能前，至少要有一个次版本包含这个弃用信息，这样使用者才能平顺地转移到新版 API。</p><p>语义化版本对于版本的字串长度是否有限制呢？</p><p>没有，请自行做适当的判断。举例来说，长到 255 个字元的版本已过度夸张。再者，特定的系统对于字串长度可能会有他们自己的限制。</p><h2 id="关于"><a href="#关于" class="headerlink" title="关于"></a>关于</h2><p>语义化版本控制的规范是由 Gravatars 创办者兼 GitHub 共同创办者 Tom Preston-Werner 所建立。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;摘要&quot;&gt;&lt;a href=&quot;#摘要&quot; class=&quot;headerlink&quot; title=&quot;摘要&quot;&gt;&lt;/a&gt;摘要&lt;/h1&gt;&lt;p&gt;版本格式：主版本号.次版本号.修订号，版本号递增规则如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主版本号：当你做了不兼容的 API 修改，&lt;/li&gt;

      
    
    </summary>
    
    
      <category term="杂文" scheme="https://dinghaijun.com/categories/%E6%9D%82%E6%96%87/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="项目" scheme="https://dinghaijun.com/tags/%E9%A1%B9%E7%9B%AE/"/>
    
  </entry>
  
  <entry>
    <title>Composer 自定义 Package 包安装路径</title>
    <link href="https://dinghaijun.com/articles/2016/09/some_composer_installers.html"/>
    <id>https://dinghaijun.com/articles/2016/09/some_composer_installers.html</id>
    <published>2016-09-22T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Composer-自定义-package-包安装路径"><a href="#Composer-自定义-package-包安装路径" class="headerlink" title="Composer 自定义 package 包安装路径"></a>Composer 自定义 package 包安装路径</h1><h2 id="原文"><a href="#原文" class="headerlink" title="原文"></a>原文</h2><p><a href="https://getcomposer.org/doc/faqs/how-do-i-install-a-package-to-a-custom-path-for-my-framework.md" target="_blank" rel="noopener">地址</a></p><h2 id="译文"><a href="#译文" class="headerlink" title="译文"></a>译文</h2><p>每个框架可能需求有一个或多个安装路径不同的依赖包。通过使用composer/installers，Composer能够把包安装到除vendor外的其他文件夹里面。</p><p>如果你是包的作者，并且你想把包安装到指定路径，最简单的办法就是require composer/installers 并且设置合适的type 属性。对于类似CakePHP，Drupal 和 WordPress等框架，这是非常普通的需求。下面以WordPress主题的一个composer.json文件为例：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"name"</span>: <span class="string">"you/themename"</span>,</span><br><span class="line">    <span class="attr">"type"</span>: <span class="string">"wordpress-theme"</span>,</span><br><span class="line">    <span class="attr">"require"</span>: &#123;</span><br><span class="line">        <span class="attr">"composer/installers"</span>: <span class="string">"~1.0"</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当使用Composer安装这个主题时，将会安装到wp-content/themes/themename/文件夹中。目前直接类型，请<a href="https://github.com/composer/installers#current-supported-types" target="_blank" rel="noopener">点击查看</a>。</p><p>使用额外的installer-paths配置，自定包可以定义或重写依赖包的安装路径。一个有用的例子，Drupal配置多站点时，需要将包安装到站点的子目录中。现在，我们使用compser/installers为模块重写安装路径：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"extra"</span>: &#123;</span><br><span class="line">        <span class="attr">"installer-paths"</span>: &#123;</span><br><span class="line">            <span class="attr">"sites/example.com/modules/&#123;$name&#125;"</span>: [<span class="string">"vendor/package"</span>]</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>现在，依赖包就不会安装在composer/installers指定的位置，而是安装到你指定的文件位置中了。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Composer-自定义-package-包安装路径&quot;&gt;&lt;a href=&quot;#Composer-自定义-package-包安装路径&quot; class=&quot;headerlink&quot; title=&quot;Composer 自定义 package 包安装路径&quot;&gt;&lt;/a&gt;Composer
      
    
    </summary>
    
    
      <category term="PHP" scheme="https://dinghaijun.com/categories/PHP/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="composer" scheme="https://dinghaijun.com/tags/composer/"/>
    
  </entry>
  
  <entry>
    <title>全新CentOS安装 PHP7升级笔记</title>
    <link href="https://dinghaijun.com/articles/2016/05/the_newest_php7_installation_tutorial.html"/>
    <id>https://dinghaijun.com/articles/2016/05/the_newest_php7_installation_tutorial.html</id>
    <published>2016-05-30T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<h1 id="全新CentOS安装-PHP7升级笔记"><a href="#全新CentOS安装-PHP7升级笔记" class="headerlink" title="全新CentOS安装 PHP7升级笔记"></a>全新CentOS安装 PHP7升级笔记</h1><p>PHP7已经到7.0.6版本了，是时候来尝试下小象飞的时候了，哈哈，那么就来配个PHP7的环境来玩玩…</p><a id="more"></a><h2 id="更换时区"><a href="#更换时区" class="headerlink" title="更换时区"></a>更换时区</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ sudo cp &#x2F;usr&#x2F;share&#x2F;zoneinfo&#x2F;Asia&#x2F;Shanghai &#x2F;etc&#x2F;localtime</span><br><span class="line">$ ntpdate time.windows.com &amp;&amp; hwclock -w</span><br></pre></td></tr></table></figure><h2 id="查看CentOS-版本"><a href="#查看CentOS-版本" class="headerlink" title="查看CentOS 版本"></a>查看CentOS 版本</h2><p><code>$    cat /etc/redhat-release</code><br>顺便看看linux环境内核、gcc信息<br><code>$ cat /proc/version</code></p><h2 id="更换163-yum-源"><a href="#更换163-yum-源" class="headerlink" title="更换163 yum 源"></a>更换163 yum 源</h2><ul><li>登录mirrors.163.com</li><li>点击centos后面的“centos使用帮助”</li><li><code>$ mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup</code></li><li>下载对应版本repo文件, 放入/etc/yum.repos.d/(操作前请做好相应备份),我的是CentOS6</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ cd &#x2F;etc&#x2F;yum.repos.d&#x2F;&amp;&amp;wget http:&#x2F;&#x2F;mirrors.163.com&#x2F;.help&#x2F;CentOS6-Base-163.repo</span><br><span class="line">$ yum clean all</span><br><span class="line">$ yum makecache</span><br></pre></td></tr></table></figure><h2 id="升级gcc版本"><a href="#升级gcc版本" class="headerlink" title="升级gcc版本"></a>升级gcc版本</h2><ul><li>先升级到4.7版本</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ cd &#x2F;etc&#x2F;yum.repos.d</span><br><span class="line">$ sudo wget http:&#x2F;&#x2F;people.centos.org&#x2F;tru&#x2F;devtools-1.1&#x2F;devtools-1.1.repo </span><br><span class="line">$ sudo yum --enablerepo&#x3D;testing-1.1-devtools-6 install devtoolset-1.1-gcc devtoolset-1.1-gcc-c++</span><br><span class="line">$ mv &#x2F;usr&#x2F;bin&#x2F;gcc &#x2F;usr&#x2F;bin&#x2F;gcc-4.4.7</span><br><span class="line">$ mv &#x2F;usr&#x2F;bin&#x2F;g++ &#x2F;usr&#x2F;bin&#x2F;g++-4.4.7</span><br><span class="line">$ mv &#x2F;usr&#x2F;bin&#x2F;c++ &#x2F;usr&#x2F;bin&#x2F;c++-4.4.7</span><br><span class="line">$ ln -s &#x2F;opt&#x2F;centos&#x2F;devtoolset-1.1&#x2F;root&#x2F;usr&#x2F;bin&#x2F;gcc &#x2F;usr&#x2F;bin&#x2F;gcc</span><br><span class="line">$ ln -s &#x2F;opt&#x2F;centos&#x2F;devtoolset-1.1&#x2F;root&#x2F;usr&#x2F;bin&#x2F;c++ &#x2F;usr&#x2F;bin&#x2F;c++</span><br><span class="line">$ ln -s &#x2F;opt&#x2F;centos&#x2F;devtoolset-1.1&#x2F;root&#x2F;usr&#x2F;bin&#x2F;g++ &#x2F;usr&#x2F;bin&#x2F;g++</span><br><span class="line">$ gcc --version</span><br></pre></td></tr></table></figure><ul><li>升级到4.8</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ cd &#x2F;etc&#x2F;yum.repos.d</span><br><span class="line">$ wget http:&#x2F;&#x2F;people.centos.org&#x2F;tru&#x2F;devtools-2&#x2F;devtools-2.repo</span><br><span class="line">$ yum install devtoolset-2-gcc devtoolset-2-binutils devtoolset-2-gcc-c++</span><br><span class="line">$ mv &#x2F;usr&#x2F;bin&#x2F;gcc &#x2F;usr&#x2F;bin&#x2F;gcc-4.4.7</span><br><span class="line">$ mv &#x2F;usr&#x2F;bin&#x2F;g++ &#x2F;usr&#x2F;bin&#x2F;g++-4.4.7</span><br><span class="line">$ mv &#x2F;usr&#x2F;bin&#x2F;c++ &#x2F;usr&#x2F;bin&#x2F;c++-4.4.7</span><br><span class="line">$ ln -s &#x2F;opt&#x2F;rh&#x2F;devtoolset-2&#x2F;root&#x2F;usr&#x2F;bin&#x2F;gcc &#x2F;usr&#x2F;bin&#x2F;gcc</span><br><span class="line">$ ln -s &#x2F;opt&#x2F;rh&#x2F;devtoolset-2&#x2F;root&#x2F;usr&#x2F;bin&#x2F;c++ &#x2F;usr&#x2F;bin&#x2F;c++</span><br><span class="line">$ ln -s &#x2F;opt&#x2F;rh&#x2F;devtoolset-2&#x2F;root&#x2F;usr&#x2F;bin&#x2F;g++ &#x2F;usr&#x2F;bin&#x2F;g++</span><br><span class="line">$ gcc --version</span><br></pre></td></tr></table></figure><h2 id="安装先导库"><a href="#安装先导库" class="headerlink" title="安装先导库"></a>安装先导库</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install php-mcrypt libmcrypt libmcrypt-devel  libxml2-devel  openssl-devel  libcurl-devel libjpeg.x86_64 libpng.x86_64 freetype.x86_64 libjpeg-devel.x86_64 libpng-devel.x86_64 freetype-devel.x86_64  libjpeg-turbo-devel   libmcrypt-devel   mysql-devel  -y</span><br></pre></td></tr></table></figure><h2 id="安装php7"><a href="#安装php7" class="headerlink" title="安装php7"></a>安装php7</h2><ul><li>下载php7最新源码</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ cd ~&amp;&amp;wget http:&#x2F;&#x2F;cn2.php.net&#x2F;distributions&#x2F;php-7.0.6.tar.gz</span><br></pre></td></tr></table></figure><ul><li>解压</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ tar zxvf php-7.0.6.tar.gz&amp;&amp;cd cd php-7.0.6</span><br></pre></td></tr></table></figure><ul><li>编译安装</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ .&#x2F;configure --prefix&#x3D;&#x2F;usr&#x2F;local&#x2F;php7 --with-config-file-path&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc --with-mcrypt&#x3D;&#x2F;usr&#x2F;include --enable-mysqlnd --with-gd --with-iconv --with-zlib --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --enable-mbregex --enable-fpm --enable-mbstring --enable-ftp --enable-gd-native-ttf --with-openssl --enable-pcntl --enable-sockets --with-xmlrpc --enable-zip --enable-soap --with-gettext --with-curl --with-jpeg-dir --with-freetype-dir --with-mysqli --enable-embedded-mysqli  --with-pdo-mysql --with-config-file-scan-dir&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;conf.d</span><br><span class="line">$make&amp;&amp;sudo make install</span><br></pre></td></tr></table></figure><ul><li>建立软连接到/usr/local/bin/</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo ln -s &#x2F;usr&#x2F;local&#x2F;php7&#x2F;bin&#x2F;php* &#x2F;usr&#x2F;local&#x2F;bin&#x2F;</span><br></pre></td></tr></table></figure><ul><li>配置php</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ cp php.ini-production &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;php.ini</span><br><span class="line">$ cp sapi&#x2F;fpm&#x2F;init.d.php-fpm &#x2F;etc&#x2F;init.d&#x2F;php7-fpm</span><br><span class="line">$ chmod +x &#x2F;etc&#x2F;init.d&#x2F;php7-fpm</span><br><span class="line">$ cp &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;php-fpm.conf.default &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;php-fpm.conf</span><br><span class="line">$ cp &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;php-fpm.d&#x2F;www.conf.default &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;php-fpm.d&#x2F;www.conf</span><br></pre></td></tr></table></figure><ul><li>加入opcache</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo vim &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;conf.d&#x2F;opcache.ini</span><br></pre></td></tr></table></figure><p>加入：    zend_extension=/usr/local/php7/lib/php/extensions/no-debug-non-zts-20151012/opcache.so<br>参考：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">[opcache]</span><br><span class="line">zend_extension&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;lib&#x2F;php&#x2F;extensions&#x2F;no-debug-non-zts-20151012&#x2F;opcache.so</span><br><span class="line">; Determines if Zend OPCache is enabled 启用opcache</span><br><span class="line">opcache.enable&#x3D;1</span><br><span class="line">; Determines if Zend OPCache is enabled for the CLI version of PHP 在cli下启用opcache</span><br><span class="line">opcache.enable_cli&#x3D;1</span><br><span class="line">; The OPcache shared memory storage size. opcache使用的内存,单位MB</span><br><span class="line">opcache.memory_consumption&#x3D;256</span><br><span class="line">; The amount of memory for interned strings in Mbytes.</span><br><span class="line">opcache.interned_strings_buffer&#x3D;8</span><br><span class="line">; The maximum number of keys (scripts) in the OPcache hash table.</span><br><span class="line">; Only numbers between 200 and 100000 are allowed. 存储最大的对象数目</span><br><span class="line">opcache.max_accelerated_files&#x3D;100000</span><br><span class="line">; The maximum percentage of &quot;wasted&quot; memory until a restart is scheduled. 最大浪费内存百分比,超出将会GC</span><br><span class="line">opcache.max_wasted_percentage&#x3D;5</span><br><span class="line">; When this directive is enabled, the OPcache appends the current working</span><br><span class="line">; directory to the script key, thus eliminating possible collisions between</span><br><span class="line">; files with the same name (basename). Disabling the directive improves</span><br><span class="line">; performance, but may break existing applications.</span><br><span class="line">opcache.use_cwd&#x3D;1</span><br><span class="line">; When disabled, you must reset the OPcache manually or restart the</span><br><span class="line">; webserver for changes to the filesystem to take effect. 验证文件改动时间错,开启之后将由validate_timestamps来提供刷新频率,对于加载文件很多的程序性能有极大影响,关闭之后将会杜绝验证文件的时候造成的抖动,但是更新文件之后需要手动重启服务或者刷新全部内容</span><br><span class="line">opcache.validate_timestamps&#x3D;0</span><br><span class="line">; How often (in seconds) to check file timestamps for changes to the shared</span><br><span class="line">; memory storage allocation. (&quot;1&quot; means validate once per second, but only</span><br><span class="line">; once per request. &quot;0&quot; means always validate) 验证时间间隔,1表示1秒,0表示总是验证</span><br><span class="line">opcache.revalidate_freq&#x3D;5</span><br><span class="line">; Enables or disables file search in include_path optimization</span><br><span class="line">opcache.revalidate_path&#x3D;1</span><br><span class="line">; If disabled, all PHPDoc comments are dropped from the code to reduce the</span><br><span class="line">; size of the optimized code. 缓存中保存文档信息</span><br><span class="line">opcache.save_comments&#x3D;1</span><br><span class="line">; If disabled, PHPDoc comments are not loaded from SHM, so &quot;Doc Comments&quot;</span><br><span class="line">; may be always stored (save_comments&#x3D;1), but not loaded by applications</span><br><span class="line">; that don&#39;t need them anyway. 加载文档信息</span><br><span class="line">opcache.load_comments&#x3D;1</span><br></pre></td></tr></table></figure><ul><li>启动</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo &#x2F;etc&#x2F;init.d&#x2F;php7-fpm start</span><br></pre></td></tr></table></figure><ul><li>==为了安全最好是去掉头信息 X-Powered-By: PHP/7.则修改 php.ini 文件 设置 expose_php = Off==</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ vim &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;php.ini</span><br></pre></td></tr></table></figure><p>找到 expose_php = On 改为 expose_php = Off</p><h3 id="安装redis扩展"><a href="#安装redis扩展" class="headerlink" title="安装redis扩展"></a>安装redis扩展</h3><ul><li>首先要安装依赖 igbinary</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ cd ~&amp;&amp;wget https:&#x2F;&#x2F;github.com&#x2F;igbinary&#x2F;igbinary7&#x2F;archive&#x2F;master.zip -O igbinary7.zip&amp;&amp;unzip igbinary7.zip&amp;&amp;cd igbinary7-master</span><br><span class="line">$ phpize</span><br><span class="line">$.&#x2F;configure</span><br><span class="line">$ make&amp;&amp;sudo make install</span><br><span class="line">$ cd &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;conf.d&#x2F;&amp;&amp;sudo cp opcache.ini igbinary.ini</span><br><span class="line">$ vim igbinary.ini加入：extension&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;lib&#x2F;php&#x2F;extensions&#x2F;no-debug-non-zts-20151012&#x2F;igbinary.so</span><br></pre></td></tr></table></figure><ul><li><p>安装redis<br>  安装git</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo yum install git</span><br></pre></td></tr></table></figure></li><li><p>检出redis</p></li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ git clone https:&#x2F;&#x2F;github.com&#x2F;phpredis&#x2F;phpredis.git</span><br><span class="line">$ cd phpredis&amp;&amp;git branch -r</span><br><span class="line">$ git branch --track php7 origin&#x2F;php7</span><br><span class="line">$ git checkout php7</span><br><span class="line">$ phpize&amp;&amp;.&#x2F;configure --enable-redis-igbinary</span><br><span class="line">$ make &amp;&amp;sudo make install</span><br><span class="line">$ cd &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;conf.d&amp;&amp;sudo cp igbinary.ini redis.ini</span><br><span class="line">$ vim redis.ini加入：extension&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;lib&#x2F;php&#x2F;extensions&#x2F;no-debug-non-zts-20151012&#x2F;redis.so</span><br></pre></td></tr></table></figure><h3 id="安装mongodb扩展"><a href="#安装mongodb扩展" class="headerlink" title="安装mongodb扩展"></a>安装mongodb扩展</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ git clone https:&#x2F;&#x2F;github.com&#x2F;mongodb&#x2F;mongo-php-driver.git &amp;&amp;cd mongo-php-driver</span><br><span class="line">$ git submodule sync &amp;&amp; git submodule update --init</span><br><span class="line">$ phpize</span><br><span class="line">$ .&#x2F;configure --with-php-config&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;bin&#x2F;php-config</span><br><span class="line">$ make all -j 5&amp;&amp; sudo make install</span><br><span class="line">$ cd &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;conf.d&amp;&amp;sudo cp redis.ini mongodb.ini</span><br><span class="line">$ vim  mongodb.ini加入：extension&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;lib&#x2F;php&#x2F;extensions&#x2F;no-debug-non-zts-20151012&#x2F;mongodb.so</span><br></pre></td></tr></table></figure><h3 id="安装mamcache扩展"><a href="#安装mamcache扩展" class="headerlink" title="安装mamcache扩展"></a>安装mamcache扩展</h3><ul><li>检出git仓库</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ git clone git clone https:&#x2F;&#x2F;github.com&#x2F;websupport-sk&#x2F;pecl-memcache.git</span><br><span class="line">$ cd pecl-memcache&amp;git branch -r</span><br><span class="line">$ git branch --track php7 origin&#x2F;php7</span><br><span class="line">$ git checkout php7</span><br><span class="line">$ phpize</span><br><span class="line">$ .&#x2F;configure&amp;&amp;make</span><br><span class="line">$ sudo make install</span><br><span class="line">$ cd &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;conf.d&amp;&amp;sudo cp redis.ini memcache.ini</span><br><span class="line">$ sudo vim  memcache.ini加入：extension&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;lib&#x2F;php&#x2F;extensions&#x2F;no-debug-non-zts-20151012&#x2F;memcache.so</span><br></pre></td></tr></table></figure><h3 id="安装mamcached扩展"><a href="#安装mamcached扩展" class="headerlink" title="安装mamcached扩展"></a>安装mamcached扩展</h3><ul><li>源码安装libmemcached</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ wget https:&#x2F;&#x2F;launchpad.net&#x2F;libmemcached&#x2F;1.0&#x2F;1.0.18&#x2F;+download&#x2F;libmemcached-1.0.18.tar.gz&amp;&amp;tar zxvf libmemcached-1.0.18.tar.gz</span><br><span class="line">$ cd libmemcached-1.0.18</span><br><span class="line">$ .&#x2F;configure --prefix&#x3D;&#x2F;usr&#x2F;local&#x2F;libmemcached --enable-sasl</span><br><span class="line">$make &amp;&amp; sudo make install</span><br></pre></td></tr></table></figure><ul><li>检出git仓库</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ git clone https:&#x2F;&#x2F;github.com&#x2F;php-memcached-dev&#x2F;php-memcached.git</span><br><span class="line">$ cd php-memcached&amp;&amp;git checkout php7 orgin&#x2F;php7&amp;&amp;git pull</span><br><span class="line">$ phpize</span><br><span class="line">$ .&#x2F;configure --with-libmemcached-dir&#x3D;&#x2F;usr&#x2F;local&#x2F;libmemcached</span><br><span class="line">$ sudo make &amp;&amp;make install</span><br><span class="line">$ cd &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;conf.d&amp;&amp;sudo cp memcache.ini memcached.ini</span><br><span class="line">$ sudo vim  memcached.ini加入：extension&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;lib&#x2F;php&#x2F;extensions&#x2F;no-debug-non-zts-20151012&#x2F;memcached.so</span><br></pre></td></tr></table></figure><h3 id="安装apcu扩展"><a href="#安装apcu扩展" class="headerlink" title="安装apcu扩展"></a>安装apcu扩展</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$ wget https:&#x2F;&#x2F;github.com&#x2F;krakjoe&#x2F;apcu&#x2F;archive&#x2F;v5.1.4.zip -O apcu-v5.1.4.zip&amp;&amp;unzip apcu-v5.1.4.zip&amp;&amp; cd apcu-5.1.4&#x2F;</span><br><span class="line">$ php phpize&amp;&amp; .&#x2F;configure</span><br><span class="line">$ make&amp;&amp;&amp;&amp;sudo make install</span><br><span class="line">$ cd &#x2F;usr&#x2F;local&#x2F;php7&#x2F;etc&#x2F;conf.d&amp;&amp;sudo cp redis.ini apcu.ini</span><br><span class="line">$ sudo vim  apcu.ini</span><br><span class="line">加入：</span><br><span class="line">extension&#x3D;&#x2F;usr&#x2F;local&#x2F;php7&#x2F;lib&#x2F;php&#x2F;extensions&#x2F;no-debug-non-zts-20151012&#x2F;apcu.so</span><br><span class="line">[apcu]</span><br><span class="line">apc.enabled&#x3D;1</span><br><span class="line">apc.shm_segments&#x3D;1</span><br><span class="line">apc.shm_size&#x3D;64M</span><br><span class="line">apc.ttl&#x3D;0</span><br><span class="line">apc.enable_cli&#x3D;1apcu.so</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;全新CentOS安装-PHP7升级笔记&quot;&gt;&lt;a href=&quot;#全新CentOS安装-PHP7升级笔记&quot; class=&quot;headerlink&quot; title=&quot;全新CentOS安装 PHP7升级笔记&quot;&gt;&lt;/a&gt;全新CentOS安装 PHP7升级笔记&lt;/h1&gt;&lt;p&gt;PHP7已经到7.0.6版本了，是时候来尝试下小象飞的时候了，哈哈，那么就来配个PHP7的环境来玩玩…&lt;/p&gt;
    
    </summary>
    
    
      <category term="PHP" scheme="https://dinghaijun.com/categories/PHP/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="CentOS" scheme="https://dinghaijun.com/tags/CentOS/"/>
    
      <category term="LNMP" scheme="https://dinghaijun.com/tags/LNMP/"/>
    
  </entry>
  
  <entry>
    <title>MySQL语句优化10项基本原则</title>
    <link href="https://dinghaijun.com/articles/2016/05/mysql_optimization.html"/>
    <id>https://dinghaijun.com/articles/2016/05/mysql_optimization.html</id>
    <published>2016-05-12T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<h5 id="原则1、尽量避免在列上进行运算，这样会导致索引消失"><a href="#原则1、尽量避免在列上进行运算，这样会导致索引消失" class="headerlink" title="原则1、尽量避免在列上进行运算，这样会导致索引消失"></a>原则1、尽量避免在列上进行运算，这样会导致索引消失</h5><a id="more"></a><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">table</span> <span class="keyword">WHERE</span> <span class="keyword">YEAR</span>(d) &gt; <span class="number">2011</span>;</span><br><span class="line">优化为：</span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">table</span> <span class="keyword">WHERE</span> d &gt; <span class="string">'2011-01-01'</span>;</span><br></pre></td></tr></table></figure><h5 id="原则2-使用JOIN联表时，应用最小结果集驱动最大结果集。同时把复杂的JOIN查询拆分为多个Query。因为JOIN多张表时，可能会导致更多的锁表和堵塞。"><a href="#原则2-使用JOIN联表时，应用最小结果集驱动最大结果集。同时把复杂的JOIN查询拆分为多个Query。因为JOIN多张表时，可能会导致更多的锁表和堵塞。" class="headerlink" title="原则2.使用JOIN联表时，应用最小结果集驱动最大结果集。同时把复杂的JOIN查询拆分为多个Query。因为JOIN多张表时，可能会导致更多的锁表和堵塞。"></a>原则2.使用JOIN联表时，应用最小结果集驱动最大结果集。同时把复杂的JOIN查询拆分为多个Query。因为JOIN多张表时，可能会导致更多的锁表和堵塞。</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span>   *  <span class="keyword">FROM</span>  a  <span class="keyword">JOIN</span>  b  <span class="keyword">ON</span>  a.id  = b.id</span><br><span class="line"><span class="keyword">LEFT</span> <span class="keyword">JOIN</span>  c  <span class="keyword">ON</span>  c.time  =  d.date</span><br><span class="line"><span class="keyword">LEFT</span> <span class="keyword">JOIN</span>  d  <span class="keyword">ON</span>  c.pid  =  b.aid</span><br><span class="line"><span class="keyword">LEFT</span> <span class="keyword">JOIN</span>  e  <span class="keyword">ON</span>  e.cid  =  a.did;</span><br></pre></td></tr></table></figure><h5 id="原则3-注意LIKE模糊查询的使用，避免使用-。"><a href="#原则3-注意LIKE模糊查询的使用，避免使用-。" class="headerlink" title="原则3.注意LIKE模糊查询的使用，避免使用 %..% 。"></a>原则3.注意LIKE模糊查询的使用，避免使用 %..% 。</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span>  *  <span class="keyword">FROM</span> <span class="keyword">table</span>  <span class="keyword">WHERE</span>  <span class="keyword">name</span>  <span class="keyword">LIKE</span>  %de%;</span><br><span class="line">优化为：</span><br><span class="line"><span class="keyword">SELECT</span>  *  <span class="keyword">FROM</span>  <span class="keyword">table</span>  <span class="keyword">WHERE</span>   <span class="keyword">name</span> &gt;=  <span class="string">'de'</span>   <span class="keyword">AND</span>  <span class="keyword">name</span>  &lt; <span class="string">'df'</span>;</span><br></pre></td></tr></table></figure><p>//局限性很大[慎重]</p><h5 id="原则4-仅列出需要查询的字段，这对速度不会有明显的影响，主要考虑节省内存。"><a href="#原则4-仅列出需要查询的字段，这对速度不会有明显的影响，主要考虑节省内存。" class="headerlink" title="原则4.仅列出需要查询的字段，这对速度不会有明显的影响，主要考虑节省内存。"></a>原则4.仅列出需要查询的字段，这对速度不会有明显的影响，主要考虑节省内存。</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span>  *  <span class="keyword">FROM</span>  tabel;</span><br><span class="line">(据说有公司见遇到一个 * 号 扣10块钱~~~ :(  )</span><br><span class="line">优化为：</span><br><span class="line"><span class="keyword">SELECT</span>  <span class="keyword">id</span>,<span class="keyword">name</span>,age   <span class="keyword">FROM</span>  <span class="keyword">table</span>;</span><br></pre></td></tr></table></figure><p>(需要多少取多少，是 * 的话，如果无用字段数据过大则内存消耗比较大)</p><h5 id="原则5-使用批量插入语句节省数据库交互。"><a href="#原则5-使用批量插入语句节省数据库交互。" class="headerlink" title="原则5.使用批量插入语句节省数据库交互。"></a>原则5.使用批量插入语句节省数据库交互。</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">INSERT  INTO  table  (id,name)  VALUES(1,&#39;sunphp&#39;);</span><br><span class="line">INSERT  INTO  table  (id,name)  VALUES(2,&#39;mysql&#39;);</span><br><span class="line">INSERT  INTO  table  (id,name)  VALUES(3,&#39;优化&#39;);</span><br><span class="line">优化为：</span><br><span class="line">INSERT INTO table (id,name) VALUES(1,&#39;sunphp&#39;),</span><br><span class="line">VALUES(2,&#39;mysql&#39;),VALUES(3,&#39;优化&#39;);</span><br></pre></td></tr></table></figure><h5 id="原则6-Limit的基数比较大时使用between。"><a href="#原则6-Limit的基数比较大时使用between。" class="headerlink" title="原则6.Limit的基数比较大时使用between。"></a>原则6.Limit的基数比较大时使用between。</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span>  *  <span class="keyword">FROM</span>  article  <span class="keyword">ORDER</span> <span class="keyword">BY</span>  <span class="keyword">id</span>  <span class="keyword">LIMIT</span>  <span class="number">1000000</span>, <span class="number">10</span>;</span><br><span class="line">优化为：</span><br><span class="line"><span class="keyword">SELECT</span>  * <span class="keyword">FROM</span>  article  <span class="keyword">WHERE</span>  <span class="keyword">id</span> <span class="keyword">BETWEEN</span>  <span class="number">1000000</span>  <span class="keyword">AND</span>  <span class="number">1000010</span>  <span class="keyword">ORDER</span>  <span class="keyword">BY</span>  <span class="keyword">id</span>;</span><br></pre></td></tr></table></figure><p>Between 限定比Limit快，所以在海量数据访问时，建议用Between 或是 where  替换掉 limit。<br>但是between 也有缺陷，如果id中间有断行或是中间部分id不读取的情况，总读取量可能小于预计读取量(有损服务)。<br>在去比较靠后的数据时，通过 desc 方式吧数据反向查找，以减少对前面数据的扫描，让 limit 得基数越小越好。</p><h5 id="原则7-不要使用-rand-函数获得随机多条记录。"><a href="#原则7-不要使用-rand-函数获得随机多条记录。" class="headerlink" title="原则7.不要使用 rand 函数获得随机多条记录。"></a>原则7.不要使用 rand 函数获得随机多条记录。</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span>  *  <span class="keyword">FROM</span>  <span class="keyword">table</span>  <span class="keyword">ORDER</span> <span class="keyword">BY</span>  <span class="keyword">rand</span>()  <span class="keyword">LIMIT</span>  <span class="number">20</span>;</span><br><span class="line">使用下面的语句代替：</span><br><span class="line"><span class="keyword">SELECT</span>  * <span class="keyword">FROM</span>  <span class="keyword">table</span>  <span class="keyword">AS</span> t1</span><br><span class="line"><span class="keyword">JOIN</span>  (</span><br><span class="line"><span class="keyword">SELECT</span>  <span class="keyword">ROUND</span>(  <span class="keyword">RAND</span>()  *   (  <span class="keyword">SELECT</span>  <span class="keyword">MAX</span>(<span class="keyword">id</span>)  <span class="keyword">FROM</span> <span class="keyword">table</span>  ) - (  <span class="keyword">SELECT</span> <span class="keyword">MIN</span>(<span class="keyword">id</span>)  <span class="keyword">FROM</span> <span class="keyword">table</span>  )  )  + (<span class="keyword">SELECT</span>  <span class="keyword">MIN</span>(<span class="keyword">id</span>) <span class="keyword">FROM</span>  <span class="keyword">table</span> )</span><br><span class="line">)  <span class="keyword">AS</span>  <span class="keyword">id</span></span><br><span class="line">)  <span class="keyword">AS</span>  t2  <span class="keyword">WHERE</span>  t1.id  &gt;=  t2.id  <span class="keyword">ORDER</span> <span class="keyword">BY</span>  t1.id   <span class="keyword">LIMIT</span>  t1;</span><br></pre></td></tr></table></figure><p>这是获得一条随机记录，这样即使执行20次，也比原来的语句效率高。或者先用PHP产生随机数，把这些字符传给MySQL，MySQL 里面使用 in 查询。</p><h5 id="原则8-避免使用-null。"><a href="#原则8-避免使用-null。" class="headerlink" title="原则8.避免使用 null。"></a>原则8.避免使用 null。</h5><h5 id="原则9-不要使用-count-id-，而应该使用-count-。"><a href="#原则9-不要使用-count-id-，而应该使用-count-。" class="headerlink" title="原则9.不要使用 count(id)，而应该使用 count(*)。"></a>原则9.不要使用 count(id)，而应该使用 count(*)。</h5><h5 id="原则10-不要做无谓的排序操作，而应该尽可能在索引中完成排序。"><a href="#原则10-不要做无谓的排序操作，而应该尽可能在索引中完成排序。" class="headerlink" title="原则10.不要做无谓的排序操作，而应该尽可能在索引中完成排序。"></a>原则10.不要做无谓的排序操作，而应该尽可能在索引中完成排序。</h5>]]></content>
    
    <summary type="html">
    
      &lt;h5 id=&quot;原则1、尽量避免在列上进行运算，这样会导致索引消失&quot;&gt;&lt;a href=&quot;#原则1、尽量避免在列上进行运算，这样会导致索引消失&quot; class=&quot;headerlink&quot; title=&quot;原则1、尽量避免在列上进行运算，这样会导致索引消失&quot;&gt;&lt;/a&gt;原则1、尽量避免在列上进行运算，这样会导致索引消失&lt;/h5&gt;
    
    </summary>
    
    
      <category term="MySQL" scheme="https://dinghaijun.com/categories/MySQL/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="MySQL" scheme="https://dinghaijun.com/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>中文文案排版指北</title>
    <link href="https://dinghaijun.com/articles/2016/05/chinese-copywriting-guidelines.html"/>
    <id>https://dinghaijun.com/articles/2016/05/chinese-copywriting-guidelines.html</id>
    <published>2016-05-11T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.891Z</updated>
    
    <content type="html"><![CDATA[<h1 id="中文文案排版指北"><a href="#中文文案排版指北" class="headerlink" title="中文文案排版指北"></a>中文文案排版指北</h1><p>统一中文文案、排版的相关用法，降低团队成员之间的沟通成本，增强网站气质。</p><hr><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#空格">空格</a><ul><li><a href="#中英文之间需要增加空格">中英文之间需要增加空格</a></li><li><a href="#中文与数字之间需要增加空格">中文与数字之间需要增加空格</a></li><li><a href="#数字与单位之间需要增加空格">数字与单位之间需要增加空格</a></li><li><a href="#全角标点与其他字符之间不加空格">全角标点与其他字符之间不加空格</a></li><li><a href="#-ms-text-autospace-to-the-rescue"><code>-ms-text-autospace</code> to the rescue?</a></li></ul></li><li><a href="#标点符号">标点符号</a><ul><li><a href="#不重复使用标点符号">不重复使用标点符号</a></li></ul></li><li><a href="#全角和半角">全角和半角</a><ul><li><a href="#使用全角中文标点">使用全角中文标点</a></li><li><a href="#数字使用半角字符">数字使用半角字符</a></li><li><a href="#遇到完整的英文整句特殊名词其內容使用半角标点">遇到完整的英文整句、特殊名词，其內容使用半角标点</a></li></ul></li><li><a href="#名词">名词</a><ul><li><a href="#专有名词使用正确的大小写">专有名词使用正确的大小写</a></li><li><a href="#不要使用不地道的缩写">不要使用不地道的缩写</a></li></ul></li><li><a href="#争议">争议</a><ul><li><a href="#链接之间增加空格">链接之间增加空格</a></li><li><a href="#简体中文使用直角引号">简体中文使用直角引号</a></li></ul></li><li><a href="#工具">工具</a></li><li><a href="#谁在这样做">谁在这样做？</a></li><li><a href="#参考文献">参考文献</a></li></ul><h2 id="空格"><a href="#空格" class="headerlink" title="空格"></a>空格</h2><p>「有研究显示，打字的时候不喜欢在中文和英文之间加空格的人，感情路都走得很辛苦，有七成的比例会在 34 岁的时候跟自己不爱的人结婚，而其余三成的人最后只能把遗产留给自己的猫。毕竟爱情跟书写都需要适时地留白。</p><p>与大家共勉之。」——<a href="https://github.com/vinta/pangu.js" target="_blank" rel="noopener">vinta/paranoid-auto-spacing</a></p><h3 id="中英文之间需要增加空格"><a href="#中英文之间需要增加空格" class="headerlink" title="中英文之间需要增加空格"></a>中英文之间需要增加空格</h3><p>正确：</p><blockquote><p>在 LeanCloud 上，数据存储是围绕 <code>AVObject</code> 进行的。</p></blockquote><p>错误：</p><blockquote><p>在LeanCloud上，数据存储是围绕<code>AVObject</code>进行的。</p></blockquote><blockquote><p>在 LeanCloud上，数据存储是围绕<code>AVObject</code> 进行的。</p></blockquote><p>完整的正确用法：</p><blockquote><p>在 LeanCloud 上，数据存储是围绕 <code>AVObject</code> 进行的。每个 <code>AVObject</code> 都包含了与 JSON 兼容的 key-value 对应的数据。数据是 schema-free 的，你不需要在每个 <code>AVObject</code> 上提前指定存在哪些键，只要直接设定对应的 key-value 即可。</p></blockquote><p>例外：「豆瓣FM」等产品名词，按照官方所定义的格式书写。</p><h3 id="中文与数字之间需要增加空格"><a href="#中文与数字之间需要增加空格" class="headerlink" title="中文与数字之间需要增加空格"></a>中文与数字之间需要增加空格</h3><p>正确：</p><blockquote><p>今天出去买菜花了 5000 元。</p></blockquote><p>错误：</p><blockquote><p>今天出去买菜花了 5000元。</p></blockquote><blockquote><p>今天出去买菜花了5000元。</p></blockquote><h3 id="数字与单位之间需要增加空格"><a href="#数字与单位之间需要增加空格" class="headerlink" title="数字与单位之间需要增加空格"></a>数字与单位之间需要增加空格</h3><p>正确：</p><blockquote><p>我家的光纤入户宽带有 10 Gbps，SSD 一共有 20 TB。</p></blockquote><p>错误：</p><blockquote><p>我家的光纤入户宽带有 10Gbps，SSD 一共有 10TB。</p></blockquote><p>例外：度／百分比与数字之间不需要增加空格：</p><p>正确：</p><blockquote><p>今天是 233° 的高温。</p></blockquote><blockquote><p>新 MacBook Pro 有 15% 的 CPU 性能提升。</p></blockquote><p>错误：</p><blockquote><p>今天是 233 ° 的高温。</p></blockquote><blockquote><p>新 MacBook Pro 有 15 % 的 CPU 性能提升。</p></blockquote><h3 id="全角标点与其他字符之间不加空格"><a href="#全角标点与其他字符之间不加空格" class="headerlink" title="全角标点与其他字符之间不加空格"></a>全角标点与其他字符之间不加空格</h3><p>正确：</p><blockquote><p>刚刚买了一部 iPhone，好开心！</p></blockquote><p>错误：</p><blockquote><p>刚刚买了一部 iPhone ，好开心！</p></blockquote><h3 id="ms-text-autospace-to-the-rescue"><a href="#ms-text-autospace-to-the-rescue" class="headerlink" title="-ms-text-autospace to the rescue?"></a><code>-ms-text-autospace</code> to the rescue?</h3><p>Microsoft 有个 <a href="http://msdn.microsoft.com/en-us/library/ie/ms531164(v=vs.85).aspx" target="_blank" rel="noopener"><code>-ms-text-autospace</code></a> 的 CSS 属性可以实现自动为中英文之间增加空白。不过目前并未普及，另外在其他应用场景，例如 OS X、iOS 的用户界面目前并不存在这个特性，所以请继续保持随手加空格的习惯。</p><h2 id="标点符号"><a href="#标点符号" class="headerlink" title="标点符号"></a>标点符号</h2><h3 id="不重复使用标点符号"><a href="#不重复使用标点符号" class="headerlink" title="不重复使用标点符号"></a>不重复使用标点符号</h3><p>正确：</p><blockquote><p>德国队竟然战胜了巴西队！</p></blockquote><blockquote><p>她竟然对你说「喵」？！</p></blockquote><p>错误：</p><blockquote><p>德国队竟然战胜了巴西队！！</p></blockquote><blockquote><p>德国队竟然战胜了巴西队！！！！！！！！</p></blockquote><blockquote><p>她竟然对你说「喵」？？！！</p></blockquote><blockquote><p>她竟然对你说「喵」？！？！？？！！</p></blockquote><h2 id="全角和半角"><a href="#全角和半角" class="headerlink" title="全角和半角"></a>全角和半角</h2><p>不明白什么是全角（全形）与半角（半形）符号？请查看维基百科词条『<a href="http://zh.wikipedia.org/wiki/%E5%85%A8%E5%BD%A2%E5%92%8C%E5%8D%8A%E5%BD%A2" target="_blank" rel="noopener">全角和半角</a>』。</p><h3 id="使用全角中文标点"><a href="#使用全角中文标点" class="headerlink" title="使用全角中文标点"></a>使用全角中文标点</h3><p>正确：</p><blockquote><p>嗨！你知道嘛？今天前台的小妹跟我说「喵」了哎！</p></blockquote><blockquote><p>核磁共振成像（NMRI）是什么原理都不知道？JFGI！</p></blockquote><p>错误：</p><blockquote><p>嗨! 你知道嘛? 今天前台的小妹跟我说 “喵” 了哎!</p></blockquote><blockquote><p>嗨!你知道嘛?今天前台的小妹跟我说”喵”了哎!</p></blockquote><blockquote><p>核磁共振成像 (NMRI) 是什么原理都不知道? JFGI!</p></blockquote><blockquote><p>核磁共振成像(NMRI)是什么原理都不知道?JFGI!</p></blockquote><h3 id="数字使用半角字符"><a href="#数字使用半角字符" class="headerlink" title="数字使用半角字符"></a>数字使用半角字符</h3><p>正确：</p><blockquote><p>这件蛋糕只卖 1000 元。</p></blockquote><p>错误：</p><blockquote><p>这件蛋糕只卖 １０００ 元。</p></blockquote><p>例外：在设计稿、宣传海报中如出现极少量数字的情形时，为方便文字对齐，是可以使用全角数字的。</p><h3 id="遇到完整的英文整句、特殊名词，其內容使用半角标点"><a href="#遇到完整的英文整句、特殊名词，其內容使用半角标点" class="headerlink" title="遇到完整的英文整句、特殊名词，其內容使用半角标点"></a>遇到完整的英文整句、特殊名词，其內容使用半角标点</h3><p>正确：</p><blockquote><p>乔布斯那句话是怎么说的？「Stay hungry, stay foolish.」</p></blockquote><blockquote><p>推荐你阅读《Hackers &amp; Painters: Big Ideas from the Computer Age》，非常的有趣。</p></blockquote><p>错误：</p><blockquote><p>乔布斯那句话是怎么说的？「Stay hungry，stay foolish。」</p></blockquote><blockquote><p>推荐你阅读《Hackers＆Painters：Big Ideas from the Computer Age》，非常的有趣。</p></blockquote><h2 id="名词"><a href="#名词" class="headerlink" title="名词"></a>名词</h2><h3 id="专有名词使用正确的大小写"><a href="#专有名词使用正确的大小写" class="headerlink" title="专有名词使用正确的大小写"></a>专有名词使用正确的大小写</h3><p>大小写相关用法原属于英文书写范畴，不属于本 wiki 讨论內容，在这里只对部分易错用法进行简述。</p><p>正确：</p><blockquote><p>使用 GitHub 登录</p></blockquote><blockquote><p>我们的客户有 GitHub、Foursquare、Microsoft Corporation、Google、Facebook, Inc.。</p></blockquote><p>错误：</p><blockquote><p>使用 github 登录</p></blockquote><blockquote><p>使用 GITHUB 登录</p></blockquote><blockquote><p>使用 Github 登录</p></blockquote><blockquote><p>使用 gitHub 登录</p></blockquote><blockquote><p>使用 gｲんĤЦ8 登录</p></blockquote><blockquote><p>我们的客户有 github、foursquare、microsoft corporation、google、facebook, inc.。</p></blockquote><blockquote><p>我们的客户有 GITHUB、FOURSQUARE、MICROSOFT CORPORATION、GOOGLE、FACEBOOK, INC.。</p></blockquote><blockquote><p>我们的客户有 Github、FourSquare、MicroSoft Corporation、Google、FaceBook, Inc.。</p></blockquote><blockquote><p>我们的客户有 gitHub、fourSquare、microSoft Corporation、google、faceBook, Inc.。</p></blockquote><blockquote><p>我们的客户有 gｲんĤЦ8、ｷouЯƧquﾑгє、๓เςг๏ร๏Ŧt ς๏гק๏гคtเ๏ภn、900913、ƒ4ᄃëв๏๏к, IПᄃ.。</p></blockquote><p>注意：当网页中需要配合整体视觉风格而出现全部大写／小写的情形，HTML 中请使用标准的大小写规范进行书写；并通过 <code>text-transform: uppercase;</code>／<code>text-transform: lowercase;</code> 对表现形式进行定义。</p><h3 id="不要使用不地道的缩写"><a href="#不要使用不地道的缩写" class="headerlink" title="不要使用不地道的缩写"></a>不要使用不地道的缩写</h3><p>正确：</p><blockquote><p>我们需要一位熟悉 JavaScript、HTML5，至少理解一种框架（如 Backbone.js、AngularJS、React 等）的前端开发者。</p></blockquote><p>错误：</p><blockquote><p>我们需要一位熟悉 Js、h5，至少理解一种框架（如 backbone、angular、RJS 等）的 FED。</p></blockquote><h2 id="争议"><a href="#争议" class="headerlink" title="争议"></a>争议</h2><p>以下用法略带有个人色彩，既：无论是否遵循下述规则，从语法的角度来讲都是<strong>正确</strong>的。</p><h3 id="链接之间增加空格"><a href="#链接之间增加空格" class="headerlink" title="链接之间增加空格"></a>链接之间增加空格</h3><p>用法：</p><blockquote><p>请 <a href="#">提交一个 issue</a> 并分配给相关同事。</p></blockquote><blockquote><p>访问我们网站的最新动态，请 <a href="#">点击这里</a> 进行订阅！</p></blockquote><p>对比用法：</p><blockquote><p>请<a href="#">提交一个 issue</a> 并分配给相关同事。</p></blockquote><blockquote><p>访问我们网站的最新动态，请<a href="#">点击这里</a>进行订阅！</p></blockquote><h3 id="简体中文使用直角引号"><a href="#简体中文使用直角引号" class="headerlink" title="简体中文使用直角引号"></a>简体中文使用直角引号</h3><p>用法：</p><blockquote><p>「老师，『有条不紊』的『紊』是什么意思？」</p></blockquote><p>对比用法：</p><blockquote><p>“老师，‘有条不紊’的‘紊’是什么意思？”</p></blockquote><h2 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h2><table><thead><tr><th>仓库</th><th>语言</th></tr></thead><tbody><tr><td><a href="https://github.com/vinta/paranoid-auto-spacing" target="_blank" rel="noopener">vinta/paranoid-auto-spacing</a></td><td>JavaScript</td></tr><tr><td><a href="https://github.com/huei90/pangu.node" target="_blank" rel="noopener">huei90/pangu.node</a></td><td>Node.js</td></tr><tr><td><a href="https://github.com/huacnlee/auto-correct" target="_blank" rel="noopener">huacnlee/auto-correct</a></td><td>Ruby</td></tr><tr><td><a href="https://github.com/sparanoid/space-lover" target="_blank" rel="noopener">sparanoid/space-lover</a></td><td>PHP (WordPress)</td></tr><tr><td><a href="https://github.com/NauxLiu/auto-correct" target="_blank" rel="noopener">nauxliu/auto-correct</a></td><td>PHP</td></tr><tr><td><a href="https://github.com/hotoo/pangu.vim" target="_blank" rel="noopener">hotoo/pangu.vim</a></td><td>Vim</td></tr><tr><td><a href="https://github.com/sparanoid/grunt-auto-spacing" target="_blank" rel="noopener">sparanoid/grunt-auto-spacing</a></td><td>Node.js (Grunt)</td></tr><tr><td><a href="https://github.com/hjiang/scripts/blob/master/add-space-between-latin-and-cjk" target="_blank" rel="noopener">hjiang/scripts/add-space-between-latin-and-cjk</a></td><td>Python</td></tr></tbody></table><h2 id="谁在这样做？"><a href="#谁在这样做？" class="headerlink" title="谁在这样做？"></a>谁在这样做？</h2><table><thead><tr><th>网站</th><th>文案</th><th>UGC</th></tr></thead><tbody><tr><td><a href="http://www.apple.com/cn/" target="_blank" rel="noopener">Apple 中国</a></td><td>Yes</td><td>N/A</td></tr><tr><td><a href="http://www.apple.com/hk/" target="_blank" rel="noopener">Apple 香港</a></td><td>Yes</td><td>N/A</td></tr><tr><td><a href="http://www.apple.com/tw/" target="_blank" rel="noopener">Apple 台湾</a></td><td>Yes</td><td>N/A</td></tr><tr><td><a href="http://www.microsoft.com/zh-cn/" target="_blank" rel="noopener">Microsoft 中国</a></td><td>Yes</td><td>N/A</td></tr><tr><td><a href="http://www.microsoft.com/zh-hk/" target="_blank" rel="noopener">Microsoft 香港</a></td><td>Yes</td><td>N/A</td></tr><tr><td><a href="http://www.microsoft.com/zh-tw/" target="_blank" rel="noopener">Microsoft 台湾</a></td><td>Yes</td><td>N/A</td></tr><tr><td><a href="https://leancloud.cn/" target="_blank" rel="noopener">LeanCloud</a></td><td>Yes</td><td>N/A</td></tr><tr><td><a href="https://www.zhihu.com/" target="_blank" rel="noopener">知乎</a></td><td>Yes</td><td>部分用户达成</td></tr><tr><td><a href="https://www.v2ex.com/" target="_blank" rel="noopener">V2EX</a></td><td>Yes</td><td>Yes</td></tr><tr><td><a href="https://segmentfault.com/" target="_blank" rel="noopener">SegmentFault</a></td><td>Yes</td><td>部分用户达成</td></tr><tr><td><a href="http://apple4us.com/" target="_blank" rel="noopener">Apple4us</a></td><td>Yes</td><td>N/A</td></tr><tr><td><a href="https://www.wandoujia.com/" target="_blank" rel="noopener">豌豆荚</a></td><td>Yes</td><td>N/A</td></tr><tr><td><a href="https://ruby-china.org/" target="_blank" rel="noopener">Ruby China</a></td><td>Yes</td><td>标题达成</td></tr><tr><td><a href="https://phphub.org/" target="_blank" rel="noopener">PHPHub</a></td><td>Yes</td><td>标题达成</td></tr></tbody></table><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><ul><li><a href="http://grammar.about.com/od/punctuationandmechanics/a/Guidelines-For-Using-Capital-Letters.htm" target="_blank" rel="noopener">Guidelines for Using Capital Letters</a></li><li><a href="http://en.wikipedia.org/wiki/Letter_case" target="_blank" rel="noopener">Letter case - Wikipedia</a></li><li><a href="http://www.oxforddictionaries.com/words/punctuation" target="_blank" rel="noopener">Punctuation - Oxford Dictionaries</a></li><li><a href="https://owl.english.purdue.edu/owl/section/1/6/" target="_blank" rel="noopener">Punctuation - The Purdue OWL</a></li><li><a href="http://www.wikihow.com/Use-English-Punctuation-Correctly" target="_blank" rel="noopener">How to Use English Punctuation Corrently - wikiHow</a></li><li><a href="https://zh.opensuse.org/index.php?title=Help:%E6%A0%BC%E5%BC%8F" target="_blank" rel="noopener">格式 - openSUSE</a></li><li><a href="http://zh.wikipedia.org/wiki/%E5%85%A8%E5%BD%A2%E5%92%8C%E5%8D%8A%E5%BD%A2" target="_blank" rel="noopener">全角和半角 - 维基百科</a></li><li><a href="http://zh.wikipedia.org/wiki/%E5%BC%95%E8%99%9F" target="_blank" rel="noopener">引号 - 维基百科</a></li><li><a href="http://zh.wikipedia.org/wiki/%E7%96%91%E5%95%8F%E9%A9%9A%E5%98%86%E8%99%9F" target="_blank" rel="noopener">疑问惊叹号 - 维基百科</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;中文文案排版指北&quot;&gt;&lt;a href=&quot;#中文文案排版指北&quot; class=&quot;headerlink&quot; title=&quot;中文文案排版指北&quot;&gt;&lt;/a&gt;中文文案排版指北&lt;/h1&gt;&lt;p&gt;统一中文文案、排版的相关用法，降低团队成员之间的沟通成本，增强网站气质。&lt;/p&gt;
&lt;hr&gt;

      
    
    </summary>
    
    
      <category term="杂文" scheme="https://dinghaijun.com/categories/%E6%9D%82%E6%96%87/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="排版" scheme="https://dinghaijun.com/tags/%E6%8E%92%E7%89%88/"/>
    
      <category term="中文" scheme="https://dinghaijun.com/tags/%E4%B8%AD%E6%96%87/"/>
    
  </entry>
  
  <entry>
    <title>PHP注释规范</title>
    <link href="https://dinghaijun.com/articles/2016/05/php-comments.html"/>
    <id>https://dinghaijun.com/articles/2016/05/php-comments.html</id>
    <published>2016-05-05T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<h1 id="PHP注释规范"><a href="#PHP注释规范" class="headerlink" title="PHP注释规范"></a>PHP注释规范</h1><h2 id="通用注释写法"><a href="#通用注释写法" class="headerlink" title="通用注释写法"></a>通用注释写法</h2><h3 id="一、文件的注释通用样例（普通程序文件，类文件，函数文件，变量定义文件）"><a href="#一、文件的注释通用样例（普通程序文件，类文件，函数文件，变量定义文件）" class="headerlink" title="一、文件的注释通用样例（普通程序文件，类文件，函数文件，变量定义文件）"></a>一、文件的注释通用样例（普通程序文件，类文件，函数文件，变量定义文件）</h3><a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**</span><br><span class="line"> * XXXXX的文件</span><br><span class="line"> * </span><br><span class="line"> * 功能1： xxx</span><br><span class="line"> * 功能2： xxx</span><br><span class="line"> *</span><br><span class="line"> * @file        $Source: &#x2F;home&#x2F;doc&#x2F;php开发注释规范.md  $</span><br><span class="line"> * @package     core</span><br><span class="line"> * @author      Joy &lt;anzhengchao@gmail.com&gt;</span><br><span class="line"> * @version     $Id: php开发注释规范.txt,v 1.1 2014&#x2F;03&#x2F;04 20:37:46 Joy Exp $</span><br><span class="line"> * @link        http:&#x2F;&#x2F;www.joychao.cc</span><br><span class="line"> *&#x2F;</span><br></pre></td></tr></table></figure><ul><li><p><code>@file</code> 行在提交了svn之后，会自动被补充为如下样式：</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">* @file        $Source: &#x2F;home&#x2F;doc&#x2F;php开发注释规范.md $</span><br></pre></td></tr></table></figure></li><li><p><code>@version</code> 行在提交了svn之后，会自动被补充为如下样式：</p>  <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">* @version     $Id: php开发注释规范.txt,v 1.1 2014&#x2F;03&#x2F;04 20:37:46 Joy Exp $</span><br></pre></td></tr></table></figure></li><li><p><code>@package</code> 是团队事先定义好的，在phpdocumentor里同一package的文件可以给出跟踪的链接。项目开发前需要对其定义。</p></li><li><p><code>@link</code> 行后面接的地址是程序开发文档的地址，因为我们目前没有在线的程序开发文档库，所以可不加。<br>注意注释的排版，左端保持对齐。</p></li></ul><h4 id="说明：以上自动更新版本及文件名需要配置svn，具体请自行google-‘SVN自动版本号’"><a href="#说明：以上自动更新版本及文件名需要配置svn，具体请自行google-‘SVN自动版本号’" class="headerlink" title="说明：以上自动更新版本及文件名需要配置svn，具体请自行google ‘SVN自动版本号’"></a>说明：以上自动更新版本及文件名需要配置svn，具体请自行google <em>‘SVN自动版本号’</em></h4><h5 id="二、类的注释，使用如下几个tag"><a href="#二、类的注释，使用如下几个tag" class="headerlink" title="二、类的注释，使用如下几个tag"></a>二、类的注释，使用如下几个tag</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**</span><br><span class="line"> * xxxxx类</span><br><span class="line"> * </span><br><span class="line"> * 功能1：完成xxxx</span><br><span class="line"> * 功能2：完成xxxxx</span><br><span class="line"> *</span><br><span class="line"> * @author      Joy &lt;anzhengchao@gmail.com&gt;</span><br><span class="line"> * @access      public</span><br><span class="line"> * @abstract </span><br><span class="line"> *&#x2F;</span><br><span class="line">public class DemoClass &#123;</span><br><span class="line">&#x2F;&#x2F;code...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>@access</code> (<code>public</code>|<code>private</code>) 标记类是私有的还是公用的。</li><li><code>@abstract</code>标记该类是个抽象类<br>虽然我们使用的是php5，但目前程序类的编写仍然沿用的php4，因此<code>@access</code>和<code>@abstract</code>两项可加可不加。</li></ul><h3 id="三、类的属性定义"><a href="#三、类的属性定义" class="headerlink" title="三、类的属性定义"></a>三、类的属性定义</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">public class DemoClass &#123;</span><br><span class="line"></span><br><span class="line">   &#x2F;**</span><br><span class="line">    * 当前页码</span><br><span class="line">    *</span><br><span class="line">    * @var integer </span><br><span class="line">    *&#x2F;</span><br><span class="line">    public $currentPageNumber;</span><br><span class="line">   </span><br><span class="line">   &#x2F;**</span><br><span class="line">    * 私有变量使用下划线开头</span><br><span class="line">    *</span><br><span class="line">    * @var string</span><br><span class="line">    *&#x2F;</span><br><span class="line">    private $_instance;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="四、普通函数和类中的函数注释"><a href="#四、普通函数和类中的函数注释" class="headerlink" title="四、普通函数和类中的函数注释"></a>四、普通函数和类中的函数注释</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取头像地址</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> string  $imageName  图片文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> integer $size    大小</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> string</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getAvatarUrl</span><span class="params">($imageName, $size = <span class="number">80</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> sprintf(SITE_URL . <span class="string">'/service/images/cropped_%s/'</span>.$imageName, $size);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>顺序按照author、param、return来放，<strong>区块间空行</strong>。</p><h3 id="五、程序段落注释"><a href="#五、程序段落注释" class="headerlink" title="五、程序段落注释"></a>五、程序段落注释</h3><p>段落注释和逻辑注释使用如下方式</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 1 如果$_GET['do']等于buy,则购买条码</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">if</span>($_GET[<span class="string">'do'</span>] == <span class="string">'buy'</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 1.1 验证用户提交变量是否合法</span></span><br><span class="line">    <span class="keyword">if</span>($_POST[<span class="string">'strCodeNum'</span>])</span><br><span class="line">    &#123;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 1.2 验证用户提交的码是否可以购买</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 1.3 ..................</span></span><br><span class="line">&#125; <span class="comment">// end if</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 2 如果$_GET['do']等于list,显示用户选择的条码</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">if</span>($_GET[<span class="string">'do'</span>] == <span class="string">'list'</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 2.1 验证用户提交变量是否合法</span></span><br><span class="line">    <span class="keyword">if</span>($_POST[<span class="string">'strCodeNum'</span>])</span><br><span class="line">    &#123;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 2.2 验证用户提交的码是否可以购买</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 2.3 ..................</span></span><br><span class="line">&#125; <span class="comment">// end if</span></span><br></pre></td></tr></table></figure><h2 id="PHPdoc规范"><a href="#PHPdoc规范" class="headerlink" title="PHPdoc规范"></a>PHPdoc规范</h2><p>文档注释，无非“//”和“/**/”两种 ，自己写代码，就那么点，适当写几句就好了；但是一个人总有融入团队的一天，团队的交流不是那几句注释和一张嘴能解决的，还需要通用的注释标准。</p><p>PHPDoc是PHP文档注释的一个标准，可以帮助我们在注释文档时有规范，查看别人的代码时更方便。下面的表格是我翻译的WIKI上的PHPDoc，个人英文水平有限，可以参照原文。</p><p>文档翻译自：<a href="http://en.wikipedia.org/wiki/Phpdoc" target="_blank" rel="noopener">http://en.wikipedia.org/wiki/Phpdoc</a></p><p>标记|用途|描述<br>-|-<br>@abstract| |抽象类的变量和方法<br>@access|public, private or protected|文档的访问、使用权限. @access private 表明这个文档是被保护的。<br>@author|张三 <a href="mailto:&#122;&#104;&#x61;&#x6e;&#x67;&#115;&#97;&#110;&#x40;&#x31;&#54;&#x33;&#46;&#x63;&#111;&#109;">&#122;&#104;&#x61;&#x6e;&#x67;&#115;&#97;&#110;&#x40;&#x31;&#54;&#x33;&#46;&#x63;&#111;&#109;</a>|文档作者<br>@copyright|名称 时间|文档版权信息<br>@deprecated|version|文档中被废除的方法<br>@deprec| |同 @deprecated<br>@example|/path/to/example|文档的外部保存的示例文件的位置。<br>@exception| |文档中方法抛出的异常，也可参照 @throws.<br>@global|类型：$globalvarname|文档中的全局变量及有关的方法和函数<br>@ignore| |忽略文档中指定的关键字<br>@internal| |开发团队内部信息<br>@link|URL|类似于license 但还可以通过link找到文档中的更多个详细的信息<br>@name|变量别名|为某个变量指定别名<br>@magic| |phpdoc.de compatibility<br>@package|封装包的名称|一组相关类、函数封装的包名称<br>@param|如 $username 用户名|变量含义注释<br>@return|如 返回bool|函数返回结果描述，一般不用在void（空返回结果的）的函数中<br>@see|如 Class Login()|文件关联的任何元素（全局变量，包括，页面，类，函数，定义，方法，变量）。<br>@since|version|记录什么时候对文档的哪些部分进行了更改<br>@static| |记录静态类、方法<br>@staticvar| |在类、函数中使用的静态变量<br>@subpackage| |子版本<br>@throws| |某一方法抛出的异常<br>@todo| |表示文件未完成或者要完善的地方<br>@var|type|文档中的变量及其类型<br>@version| |文档、类、函数的版本信息</p><p>PHPDoc注释实例：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">  * start page for webaccess</span></span><br><span class="line"><span class="comment">  *</span></span><br><span class="line"><span class="comment">  * PHP version 5</span></span><br><span class="line"><span class="comment">  *</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@category</span>  PHP</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@package</span>   PSI_Web</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@author</span>    Michael Cramer &lt;BigMichi1<span class="doctag">@users</span>.sourceforge.net&gt;</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@copyright</span> 2009 phpSysInfo</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@license</span>   http://opensource.org/licenses/gpl-2.0.php GNU General Public License</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@version</span>   SVN: $Id: class.Webpage.inc.php 412 2010-12-29 09:45:53Z Jacky672 $</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@link</span>      http://phpsysinfo.sourceforge.net</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">  * generate the dynamic webpage</span></span><br><span class="line"><span class="comment">  *</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@category</span>  PHP</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@package</span>   PSI_Web</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@author</span>    Michael Cramer &lt;BigMichi1<span class="doctag">@users</span>.sourceforge.net&gt;</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@copyright</span> 2009 phpSysInfo</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@license</span>   http://opensource.org/licenses/gpl-2.0.php GNU General Public License</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@version</span>   Release: 3.0</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@link</span>      http://phpsysinfo.sourceforge.net</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Webpage</span> <span class="keyword">extends</span> <span class="title">Output</span> <span class="keyword">implements</span> <span class="title">PSI_Interface_Output</span></span></span><br><span class="line"><span class="class"> </span>&#123;</span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * configured language</span></span><br><span class="line"><span class="comment">      *</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@var</span> String</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">private</span> $_language;</span><br><span class="line">     </span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * configured template</span></span><br><span class="line"><span class="comment">      *</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@var</span> String</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">private</span> $_template;</span><br><span class="line">     </span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * all available templates</span></span><br><span class="line"><span class="comment">      *</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@var</span> Array</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">private</span> $_templates = <span class="keyword">array</span>();</span><br><span class="line">     </span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * all available languages</span></span><br><span class="line"><span class="comment">      *</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@var</span> Array</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">private</span> $_languages = <span class="keyword">array</span>();</span><br><span class="line">     </span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * check for all extensions that are needed, initialize needed vars and read config.php</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span></span></span><br><span class="line"><span class="function">     </span>&#123;</span><br><span class="line">         <span class="keyword">parent</span>::__construct();</span><br><span class="line">         <span class="keyword">$this</span>-&gt;_getTemplateList();</span><br><span class="line">         <span class="keyword">$this</span>-&gt;_getLanguageList();</span><br><span class="line">     &#125;</span><br><span class="line">     </span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * checking config.php setting for template, if not supportet set phpsysinfo.css as default</span></span><br><span class="line"><span class="comment">      * checking config.php setting for language, if not supported set en as default</span></span><br><span class="line"><span class="comment">      *</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">_checkTemplateLanguage</span><span class="params">()</span></span></span><br><span class="line"><span class="function">     </span>&#123;</span><br><span class="line">         <span class="keyword">$this</span>-&gt;_template = trim(PSI_DEFAULT_TEMPLATE);</span><br><span class="line">         <span class="keyword">if</span> (!file_exists(APP_ROOT.<span class="string">'/templates/'</span>.<span class="keyword">$this</span>-&gt;_template.<span class="string">".css"</span>)) &#123;</span><br><span class="line">             <span class="keyword">$this</span>-&gt;_template = <span class="string">'phpsysinfo'</span>;</span><br><span class="line">         &#125;</span><br><span class="line">         </span><br><span class="line">         <span class="keyword">$this</span>-&gt;_language = trim(PSI_DEFAULT_LANG);</span><br><span class="line">         <span class="keyword">if</span> (!file_exists(APP_ROOT.<span class="string">'/language/'</span>.<span class="keyword">$this</span>-&gt;_language.<span class="string">".xml"</span>)) &#123;</span><br><span class="line">             <span class="keyword">$this</span>-&gt;_language = <span class="string">'en'</span>;</span><br><span class="line">         &#125;</span><br><span class="line">     &#125;</span><br><span class="line">     </span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * get all available tamplates and store them in internal array</span></span><br><span class="line"><span class="comment">      *</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">_getTemplateList</span><span class="params">()</span></span></span><br><span class="line"><span class="function">     </span>&#123;</span><br><span class="line">         $dirlist = CommonFunctions::gdc(APP_ROOT.<span class="string">'/templates/'</span>);</span><br><span class="line">         sort($dirlist);</span><br><span class="line">         <span class="keyword">foreach</span> ($dirlist <span class="keyword">as</span> $file) &#123;</span><br><span class="line">             $tpl_ext = substr($file, strlen($file) - <span class="number">4</span>);</span><br><span class="line">             $tpl_name = substr($file, <span class="number">0</span>, strlen($file) - <span class="number">4</span>);</span><br><span class="line">             <span class="keyword">if</span> ($tpl_ext === <span class="string">".css"</span>) &#123;</span><br><span class="line">                 array_push(<span class="keyword">$this</span>-&gt;_templates, $tpl_name);</span><br><span class="line">             &#125;</span><br><span class="line">         &#125;</span><br><span class="line">     &#125;</span><br><span class="line">     </span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * get all available translations and store them in internal array</span></span><br><span class="line"><span class="comment">      *</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">_getLanguageList</span><span class="params">()</span></span></span><br><span class="line"><span class="function">     </span>&#123;</span><br><span class="line">         $dirlist = CommonFunctions::gdc(APP_ROOT.<span class="string">'/language/'</span>);</span><br><span class="line">         sort($dirlist);</span><br><span class="line">         <span class="keyword">foreach</span> ($dirlist <span class="keyword">as</span> $file) &#123;</span><br><span class="line">             $lang_ext = substr($file, strlen($file) - <span class="number">4</span>);</span><br><span class="line">             $lang_name = substr($file, <span class="number">0</span>, strlen($file) - <span class="number">4</span>);</span><br><span class="line">             <span class="keyword">if</span> ($lang_ext == <span class="string">".xml"</span>) &#123;</span><br><span class="line">                 array_push(<span class="keyword">$this</span>-&gt;_languages, $lang_name);</span><br><span class="line">             &#125;</span><br><span class="line">         &#125;</span><br><span class="line">     &#125;</span><br><span class="line">     </span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * render the page</span></span><br><span class="line"><span class="comment">      *</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">     </span>&#123;</span><br><span class="line">         <span class="keyword">$this</span>-&gt;_checkTemplateLanguage();</span><br><span class="line">         </span><br><span class="line">         $tpl = <span class="keyword">new</span> Template(<span class="string">"/templates/html/index_dynamic.html"</span>);</span><br><span class="line">         </span><br><span class="line">         $tpl-&gt;set(<span class="string">"template"</span>, <span class="keyword">$this</span>-&gt;_template);</span><br><span class="line">         $tpl-&gt;set(<span class="string">"templates"</span>, <span class="keyword">$this</span>-&gt;_templates);</span><br><span class="line">         $tpl-&gt;set(<span class="string">"language"</span>, <span class="keyword">$this</span>-&gt;_language);</span><br><span class="line">         $tpl-&gt;set(<span class="string">"languages"</span>, <span class="keyword">$this</span>-&gt;_languages);</span><br><span class="line">         </span><br><span class="line">         <span class="keyword">echo</span> $tpl-&gt;fetch();</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;PHP注释规范&quot;&gt;&lt;a href=&quot;#PHP注释规范&quot; class=&quot;headerlink&quot; title=&quot;PHP注释规范&quot;&gt;&lt;/a&gt;PHP注释规范&lt;/h1&gt;&lt;h2 id=&quot;通用注释写法&quot;&gt;&lt;a href=&quot;#通用注释写法&quot; class=&quot;headerlink&quot; title=&quot;通用注释写法&quot;&gt;&lt;/a&gt;通用注释写法&lt;/h2&gt;&lt;h3 id=&quot;一、文件的注释通用样例（普通程序文件，类文件，函数文件，变量定义文件）&quot;&gt;&lt;a href=&quot;#一、文件的注释通用样例（普通程序文件，类文件，函数文件，变量定义文件）&quot; class=&quot;headerlink&quot; title=&quot;一、文件的注释通用样例（普通程序文件，类文件，函数文件，变量定义文件）&quot;&gt;&lt;/a&gt;一、文件的注释通用样例（普通程序文件，类文件，函数文件，变量定义文件）&lt;/h3&gt;
    
    </summary>
    
    
      <category term="PHP" scheme="https://dinghaijun.com/categories/PHP/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="PHP" scheme="https://dinghaijun.com/tags/PHP/"/>
    
  </entry>
  
  <entry>
    <title>Mac下配置zsh与oh-My-Zsh笔记</title>
    <link href="https://dinghaijun.com/articles/2014/10/config-zsh-on-osx.html"/>
    <id>https://dinghaijun.com/articles/2014/10/config-zsh-on-osx.html</id>
    <published>2014-10-11T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.891Z</updated>
    
    <content type="html"><![CDATA[<p>之前在看jeffway的laravel教程的时候发现他演示用的终端特别漂亮，有一个云朵一样的标志，然后就一直找不到是怎么弄的，好在今天在GitHub上看到了<a href="https://github.com/zsh-users/zsh" target="_blank" rel="noopener">zsh</a>(不过打开页面的时候我已经发现我star过了… 🐵 )</p><a id="more"></a><h2 id="将bash切换为zsh"><a href="#将bash切换为zsh" class="headerlink" title="将bash切换为zsh"></a>将bash切换为zsh</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chsh -s /bin/zsh</span><br></pre></td></tr></table></figure><p>其实还可以用which来定位（特别是ubuntu的童鞋）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chsh -s `which zsh`</span><br></pre></td></tr></table></figure><p>直接用zsh会很蛋疼，因为zsh功能很强大但是太复杂，所以需要oh-my-zsh来将它简单化。如果要切换回bash：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chsh -s /bin/bash</span><br></pre></td></tr></table></figure><h3 id="下载oh-my-zsh"><a href="#下载oh-my-zsh" class="headerlink" title="下载oh-my-zsh"></a>下载oh-my-zsh</h3><ol><li>直接用git从github上面下载包：</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh</span><br></pre></td></tr></table></figure><ol start="2"><li>备份已有的zshrc(一般不需要)</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp ~/.zshrc ~/.zshrc.orig</span><br></pre></td></tr></table></figure><ol start="3"><li>替换zshrc</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc</span><br></pre></td></tr></table></figure><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="https://img.vim-cn.com/75/613b7d66636a96e77b2f75e1dae30803fefcd4.png" alt="zsh" title="">                </div>                <div class="image-caption">zsh</div>            </figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;之前在看jeffway的laravel教程的时候发现他演示用的终端特别漂亮，有一个云朵一样的标志，然后就一直找不到是怎么弄的，好在今天在GitHub上看到了&lt;a href=&quot;https://github.com/zsh-users/zsh&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;zsh&lt;/a&gt;(不过打开页面的时候我已经发现我star过了… 🐵 )&lt;/p&gt;
    
    </summary>
    
    
      <category term="Mac" scheme="https://dinghaijun.com/categories/Mac/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="Mac" scheme="https://dinghaijun.com/tags/Mac/"/>
    
  </entry>
  
  <entry>
    <title>MySQL中EXPLAIN命令详解</title>
    <link href="https://dinghaijun.com/articles/2014/10/mysql-explain.html"/>
    <id>https://dinghaijun.com/articles/2014/10/mysql-explain.html</id>
    <published>2014-10-11T16:00:00.000Z</published>
    <updated>2024-09-28T13:21:33.895Z</updated>
    
    <content type="html"><![CDATA[<p>EXPLAIN显示了MySQL如何使用索引来处理SELECT语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。</p><a id="more"></a><p>使用方法，在select语句前加上<code>EXPLAIN</code>就可以了：</p><p>如：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">EXPLAIN</span> <span class="keyword">SELECT</span> <span class="string">`surname`</span>,<span class="string">`first_name`</span> <span class="keyword">FORM</span> <span class="string">`a`</span>,<span class="string">`b`</span> <span class="keyword">WHERE</span> <span class="string">`a`</span>.<span class="string">`id`</span>=<span class="string">`b`</span>.<span class="string">`id`</span></span><br></pre></td></tr></table></figure><p><strong>EXPLAIN</strong>列的解释：</p><table><thead><tr><th>列</th><th>描述</th></tr></thead><tbody><tr><td><code>table</code></td><td>显示这一行的数据是关于哪张表的。</td></tr><tr><td><code>type</code></td><td>这是重要的列，显示连接使用了何种类型。从最好到最差的连接类型为 <code>const</code>、<code>eq_reg</code>、<code>ref</code>、<code>range</code>、<code>index</code>和<code>ALL</code>。</td></tr><tr><td><code>possible_keys</code></td><td>显示可能应用在这张表中的索引。如果为空，没有可能的索引。可以为相关的域从<code>WHERE</code>语句中选择一个合适的语句。</td></tr><tr><td><code>key</code></td><td>实际使用的索引。如果为<code>NULL</code>，则没有使用索引。很少的情况下，MySQL会选择优化不足的索引。这种情况下，可以在<code>SELECT</code>语句中使用<code>USE INDEX（indexname）</code> 来强制使用一个索引或者用<code>IGNORE INDEX（indexname）</code>来强制MySQL忽略索引。</td></tr><tr><td><code>key_len</code></td><td>使用的索引的长度。在不损失精确性的情况下，长度越短越好。</td></tr><tr><td><code>ref</code></td><td>显示索引的哪一列被使用了，如果可能的话，是一个常数。</td></tr><tr><td><code>rows</code></td><td>MySQL认为必须检查的用来返回请求数据的行数。</td></tr><tr><td><code>Extra</code></td><td>关于MySQL如何解析查询的额外信息。将在表4.3中讨论，但这里可以看到的坏的例子是<code>Using temporary</code>和<code>Using filesort</code>，意思MySQL根本不能使用索引，结果是检索会很慢。</td></tr></tbody></table><p><strong>extra列返回的描述的意义：</strong></p><table><thead><tr><th>值</th><th>意义</th></tr></thead><tbody><tr><td><code>Distinct</code></td><td>一旦MySQL找到了与行相联合匹配的行，就不再搜索了。</td></tr><tr><td><code>Not exists</code></td><td>MySQL优化了<code>LEFT JOIN</code>，一旦它找到了匹配<code>LEFT JOIN</code>标准的行，就不再搜索了。</td></tr><tr><td><code>Range checked for each Record（index map:#）</code></td><td>没有找到理想的索引，因此对于从前面表中来的每一个行组合，MySQL检查使用哪个索引，并用它来从表中返回行。这是使用索引的最慢的连接之一。</td></tr><tr><td><code>Using filesort</code></td><td>看到这个的时候，查询就需要优化了。MySQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行。</td></tr><tr><td><code>Using index</code></td><td>列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的，这发生在对表的全部的请求列都是同一个索引的部分的时候。</td></tr><tr><td><code>Using temporary</code></td><td>看到这个的时候，查询需要优化了。这里，MySQL需要创建一个临时表来存储结果，这通常发生在对不同的列集进行<code>ORDER BY</code>上，而不是<code>GROUP BY</code>上。</td></tr><tr><td><code>Where used</code></td><td>使用了<code>WHERE</code>从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行，并且连接类型<code>ALL</code>或<code>index</code>，这就会发生，或者是查询有问题不同连接类型的解释（按照效率高低的顺序排序）。</td></tr><tr><td><code>system</code></td><td>表只有一行 <code>system</code> 表。这是const连接类型的特殊情况 。</td></tr><tr><td><code>const</code></td><td>表中的一个记录的最大值能够匹配这个查询（索引可以是主键或惟一索引）。因为只有一行，这个值实际就是常数，因为MySQL先读这个值然后把它当做常数来对待。</td></tr><tr><td><code>eq_ref</code></td><td>在连接中，MySQL在查询时，从前面的表中，对每一个记录的联合都从表中读取一个记录，它在查询使用了索引为主键或惟一键的全部时使用。</td></tr><tr><td><code>ref</code></td><td>这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分（比如，利用最左边前缀）时发生。对于之前的表的每一个行联合，全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好。</td></tr><tr><td><code>range</code></td><td>这个连接类型使用索引返回一个范围中的行，比如使用&gt;或&lt;查找东西时发生的情况。</td></tr><tr><td><code>index</code></td><td>这个连接类型对前面的表中的每一个记录联合进行完全扫描（比<code>ALL</code>更好，因为索引一般小于表数据）。</td></tr><tr><td><code>ALL</code></td><td>这个连接类型对于前面的每一个记录联合进行完全扫描，这一般比较糟糕，应该尽量避免。</td></tr></tbody></table>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;EXPLAIN显示了MySQL如何使用索引来处理SELECT语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。&lt;/p&gt;
    
    </summary>
    
    
      <category term="MySQL" scheme="https://dinghaijun.com/categories/MySQL/"/>
    
    
      <category term="分享" scheme="https://dinghaijun.com/tags/%E5%88%86%E4%BA%AB/"/>
    
      <category term="MySQL" scheme="https://dinghaijun.com/tags/MySQL/"/>
    
  </entry>
  
</feed>
