<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>ZYF.IM BLOG</title>
    <link>https://zyf.im/</link>
    <description>Recent content on ZYF.IM BLOG</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <lastBuildDate>Thu, 23 Apr 2026 15:06:07 +0000</lastBuildDate>
    <atom:link href="https://zyf.im/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从 1Password 迁移到 Vaultwarden</title>
      <link>https://zyf.im/2026/04/23/migrate-from-1password-to-vaultwarden/</link>
      <pubDate>Thu, 23 Apr 2026 15:06:07 +0000</pubDate>
      <guid>https://zyf.im/2026/04/23/migrate-from-1password-to-vaultwarden/</guid>
      <description>&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;
&lt;p&gt;1Password 在尼日利亚价差优惠没有，Family Annual 1 Year ¥498 属实有些贵，同事们都推荐 Vaultwarden。&lt;/p&gt;
&lt;p&gt;Vaultwarden 与 Bitwarden 的关系：&lt;/p&gt;
&lt;h2 id=&#34;前置准备&#34;&gt;前置准备&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一台 Debian 12 VPS（建议 1C/1G 起步即可，家用绰绰有余）&lt;/li&gt;
&lt;li&gt;一个域名，例如 vault.example.com，在 DNS 服务商把 A 记录指向 VPS 公网 IP&lt;/li&gt;
&lt;li&gt;一个 SMTP 邮箱（用于邀请家人、找回密码），推荐 &lt;a href=&#34;https://resend.com/emails&#34;&gt;Resend&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;环境初始化&#34;&gt;环境初始化&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt update &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt upgrade -y
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;adduser evan &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;usermod -aG sudo evan
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt install -y curl ca-certificates
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;timedatectl set-timezone Asia/Shanghai
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -fsSL https://get.docker.com &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; --now docker
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;usermod -aG docker evan
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;部署-vaultwarden--caddy&#34;&gt;部署 Vaultwarden + Caddy&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;su evan &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p ~/vaultwarden &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; ~/vaultwarden &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p vw-data caddy-data caddy-config
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vim docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yml&#34; data-lang=&#34;yml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;vaultwarden&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;vaultwarden/server:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;vaultwarden&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;unless-stopped&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;DOMAIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${DOMAIN}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ADMIN_TOKEN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${ADMIN_TOKEN}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#      EXPERIMENTAL_CLIENT_FEATURE_FLAGS: &amp;#34;ssh-agent-v2,ssh-key-vault-item&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;SIGNUPS_ALLOWED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;SMTP_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;smtp.resend.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;SMTP_FROM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${SMTP_FROM}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;SMTP_PORT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;465&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;SMTP_SECURITY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;force_tls&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;SMTP_USERNAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;resend&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;SMTP_PASSWORD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${SMTP_PASSWORD}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;deploy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;resources&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;limits&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;cpus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;0.5&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;512M&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./vw-data:/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;networks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;internal]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;caddy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;caddy:2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;caddy&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;unless-stopped&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;80:80&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;443:443&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./Caddyfile:/etc/caddy/Caddyfile:ro&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./caddy-data:/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./caddy-config:/config&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;networks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;internal]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;networks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;internal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;driver&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;bridge&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vim .env
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-env&#34; data-lang=&#34;env&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DOMAIN&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://vault.lizhi.dev&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;SMTP_FROM&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;vault@notify.lizhi.dev&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;SMTP_PASSWORD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;re_ArLMVTZ5_F6f9J7QL4ZvsujG4fGm34GHP&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ADMIN_TOKEN&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;$argon2id$v=19$m=65540,t=3,p=4$OhgDeYazJhgmzIWBIRx+wSNlbIZg1yuN7WsvOgfUKrk$EUv0j+9CphudSOMNjR91yblkI+hvVCr72y1shNIfVTw&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vim Caddyfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-caddy&#34; data-lang=&#34;caddy&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;vault.lizhi.dev&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;encode&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;zstd&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;gzip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;reverse_proxy&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vaultwarden&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose logs -f
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;部署完成&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="背景">背景</h2>
<p>1Password 在尼日利亚价差优惠没有，Family Annual 1 Year ¥498 属实有些贵，同事们都推荐 Vaultwarden。</p>
<p>Vaultwarden 与 Bitwarden 的关系：</p>
<h2 id="前置准备">前置准备</h2>
<ul>
<li>一台 Debian 12 VPS（建议 1C/1G 起步即可，家用绰绰有余）</li>
<li>一个域名，例如 vault.example.com，在 DNS 服务商把 A 记录指向 VPS 公网 IP</li>
<li>一个 SMTP 邮箱（用于邀请家人、找回密码），推荐 <a href="https://resend.com/emails">Resend</a></li>
</ul>
<h2 id="环境初始化">环境初始化</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt update <span class="o">&amp;&amp;</span> apt upgrade -y
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">adduser evan <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">usermod -aG sudo evan
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">apt install -y curl ca-certificates
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">timedatectl set-timezone Asia/Shanghai
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">curl -fsSL https://get.docker.com <span class="p">|</span> sh
</span></span><span class="line"><span class="cl">systemctl <span class="nb">enable</span> --now docker
</span></span><span class="line"><span class="cl">usermod -aG docker evan
</span></span></code></pre></div><h2 id="部署-vaultwarden--caddy">部署 Vaultwarden + Caddy</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su evan <span class="o">&amp;&amp;</span> <span class="nb">cd</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">mkdir -p ~/vaultwarden <span class="o">&amp;&amp;</span> <span class="nb">cd</span> ~/vaultwarden <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">mkdir -p vw-data caddy-data caddy-config
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">vim docker-compose.yml
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vaultwarden</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">vaultwarden/server:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">vaultwarden</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">DOMAIN</span><span class="p">:</span><span class="w"> </span><span class="l">${DOMAIN}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">ADMIN_TOKEN</span><span class="p">:</span><span class="w"> </span><span class="l">${ADMIN_TOKEN}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#      EXPERIMENTAL_CLIENT_FEATURE_FLAGS: &#34;ssh-agent-v2,ssh-key-vault-item&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SIGNUPS_ALLOWED</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;false&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_HOST</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;smtp.resend.com&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_FROM</span><span class="p">:</span><span class="w"> </span><span class="l">${SMTP_FROM}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_PORT</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;465&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_SECURITY</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;force_tls&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_USERNAME</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;resend&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">${SMTP_PASSWORD}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">limits</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cpus</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;0.5&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">512M</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./vw-data:/data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">internal]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">caddy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">caddy:2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">caddy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;80:80&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;443:443&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./Caddyfile:/etc/caddy/Caddyfile:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./caddy-data:/data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./caddy-config:/config</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">internal]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">networks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">internal</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l">bridge</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim .env
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-env" data-lang="env"><span class="line"><span class="cl"><span class="nv">DOMAIN</span><span class="o">=</span><span class="s2">&#34;https://vault.lizhi.dev&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">SMTP_FROM</span><span class="o">=</span><span class="s2">&#34;vault@notify.lizhi.dev&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">SMTP_PASSWORD</span><span class="o">=</span><span class="s2">&#34;re_ArLMVTZ5_F6f9J7QL4ZvsujG4fGm34GHP&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">ADMIN_TOKEN</span><span class="o">=</span><span class="s1">&#39;$argon2id$v=19$m=65540,t=3,p=4$OhgDeYazJhgmzIWBIRx+wSNlbIZg1yuN7WsvOgfUKrk$EUv0j+9CphudSOMNjR91yblkI+hvVCr72y1shNIfVTw&#39;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim Caddyfile
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-caddy" data-lang="caddy"><span class="line"><span class="cl"><span class="gh">vault.lizhi.dev</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">encode</span> <span class="s">zstd</span> <span class="s">gzip</span>
</span></span><span class="line"><span class="cl">    <span class="k">reverse_proxy</span> <span class="n">vaultwarden</span><span class="p">:</span><span class="mi">80</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose up -d
</span></span><span class="line"><span class="cl">docker compose logs -f
</span></span></code></pre></div><p>部署完成</p>
<p>可以登录 <a href="https://vault.lizhi.dev/admin">https://vault.lizhi.dev/admin</a> 邀请家人了。</p>
<h2 id="ssh-agent-设置">SSH Agent 设置</h2>
<pre tabindex="0"><code>lsof -U | grep bitwarden-ssh-agent
</code></pre><pre tabindex="0"><code>~/Library/Containers/com.bitwarden.desktop/Data/.bitwarden-ssh-agent.sock
</code></pre><p>SSH_AUTH_SOCK=~/.bitwarden-ssh-agent.sock ssh-add -l</p>
<p>echo &ldquo;$SSH_AUTH_SOCK&rdquo;</p>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP Migrating 8.4 to 8.5</title>
      <link>https://zyf.im/2026/01/05/php-migrating-84-to-85/</link>
      <pubDate>Mon, 05 Jan 2026 21:33:57 +0000</pubDate>
      <guid>https://zyf.im/2026/01/05/php-migrating-84-to-85/</guid>
      <description>&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;PHP 8.5 引入了许多令人兴奋的新特性，包括管道运算符（Pipe Operator）、URI 扩展、Clone With 语法、&lt;code&gt;#[\NoDiscard]&lt;/code&gt; 属性等。本文将介绍主要变化和如何从 PHP 8.4 迁移到 PHP 8.5。&lt;/p&gt;
&lt;h3 id=&#34;参考资源&#34;&gt;参考资源&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在线测试环境：&lt;a href=&#34;https://3v4l.org/&#34;&gt;https://3v4l.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;官方发布说明：&lt;a href=&#34;https://www.php.net/releases/8.5/en.php&#34;&gt;https://www.php.net/releases/8.5/en.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;迁移指南：&lt;a href=&#34;https://www.php.net/manual/en/migration85.php&#34;&gt;https://www.php.net/manual/en/migration85.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;更新详情：&lt;a href=&#34;https://php.watch/versions/8.5&#34;&gt;https://php.watch/versions/8.5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;php-85-新特性&#34;&gt;PHP 8.5 新特性&lt;/h2&gt;
&lt;h3 id=&#34;管道运算符pipe-operator&#34;&gt;管道运算符（Pipe Operator）&lt;/h3&gt;
&lt;p&gt;PHP 8.5 引入了管道运算符 &lt;code&gt;|&amp;gt;&lt;/code&gt;，使函数链式调用更加清晰易读：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP 8.5 之前 - 嵌套函数调用，难以阅读
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$slug&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtolower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;str_replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;str_replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;trim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP 8.5 - 使用管道运算符，从左到右清晰阅读
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$slug&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$title&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;trim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;str_replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;str_replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtolower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;管道运算符将左侧的值作为右侧函数的第一个参数传递，实现函数组合的链式调用。&lt;/p&gt;
&lt;h3 id=&#34;uri-扩展&#34;&gt;URI 扩展&lt;/h3&gt;
&lt;p&gt;PHP 8.5 新增了内置的 URI 扩展，遵循 RFC 3986 和 WHATWG URL 标准来解析和处理 URL。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;URI 与 URL 的关系&lt;/strong&gt;：URI（统一资源标识符）用于标识资源，URL（统一资源定位符）用于定位资源。所有 URL 都是 URI，但不是所有 URI 都是 URL。该扩展命名为 URI 是因为它能处理更广泛的标识符格式。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="简介">简介</h2>
<p>PHP 8.5 引入了许多令人兴奋的新特性，包括管道运算符（Pipe Operator）、URI 扩展、Clone With 语法、<code>#[\NoDiscard]</code> 属性等。本文将介绍主要变化和如何从 PHP 8.4 迁移到 PHP 8.5。</p>
<h3 id="参考资源">参考资源</h3>
<ul>
<li>在线测试环境：<a href="https://3v4l.org/">https://3v4l.org/</a></li>
<li>官方发布说明：<a href="https://www.php.net/releases/8.5/en.php">https://www.php.net/releases/8.5/en.php</a></li>
<li>迁移指南：<a href="https://www.php.net/manual/en/migration85.php">https://www.php.net/manual/en/migration85.php</a></li>
<li>更新详情：<a href="https://php.watch/versions/8.5">https://php.watch/versions/8.5</a></li>
</ul>
<h2 id="php-85-新特性">PHP 8.5 新特性</h2>
<h3 id="管道运算符pipe-operator">管道运算符（Pipe Operator）</h3>
<p>PHP 8.5 引入了管道运算符 <code>|&gt;</code>，使函数链式调用更加清晰易读：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.5 之前 - 嵌套函数调用，难以阅读
</span></span></span><span class="line"><span class="cl"><span class="nv">$slug</span> <span class="o">=</span> <span class="nx">strtolower</span><span class="p">(</span><span class="nx">str_replace</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="nx">str_replace</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">,</span> <span class="s1">&#39;-&#39;</span><span class="p">,</span> <span class="nx">trim</span><span class="p">(</span><span class="nv">$title</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.5 - 使用管道运算符，从左到右清晰阅读
</span></span></span><span class="line"><span class="cl"><span class="nv">$slug</span> <span class="o">=</span> <span class="nv">$title</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nx">trim</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="p">(</span><span class="nx">fn</span><span class="p">(</span><span class="nv">$str</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">str_replace</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">,</span> <span class="s1">&#39;-&#39;</span><span class="p">,</span> <span class="nv">$str</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="p">(</span><span class="nx">fn</span><span class="p">(</span><span class="nv">$str</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">str_replace</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="nv">$str</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nx">strtolower</span><span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span></code></pre></div><p>管道运算符将左侧的值作为右侧函数的第一个参数传递，实现函数组合的链式调用。</p>
<h3 id="uri-扩展">URI 扩展</h3>
<p>PHP 8.5 新增了内置的 URI 扩展，遵循 RFC 3986 和 WHATWG URL 标准来解析和处理 URL。</p>
<blockquote>
<p><strong>URI 与 URL 的关系</strong>：URI（统一资源标识符）用于标识资源，URL（统一资源定位符）用于定位资源。所有 URL 都是 URI，但不是所有 URI 都是 URL。该扩展命名为 URI 是因为它能处理更广泛的标识符格式。</p>
</blockquote>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.5 之前
</span></span></span><span class="line"><span class="cl"><span class="nv">$components</span> <span class="o">=</span> <span class="nx">parse_url</span><span class="p">(</span><span class="s1">&#39;https://php.net/releases/8.5/en.php&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$components</span><span class="p">[</span><span class="s1">&#39;host&#39;</span><span class="p">]);</span> <span class="c1">// &#34;php.net&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.5 - 使用新的 URI 扩展
</span></span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">Uri\Rfc3986\Uri</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$uri</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Uri</span><span class="p">(</span><span class="s1">&#39;https://php.net/releases/8.5/en.php&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$uri</span><span class="o">-&gt;</span><span class="na">getHost</span><span class="p">());</span>   <span class="c1">// &#34;php.net&#34;
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$uri</span><span class="o">-&gt;</span><span class="na">getPath</span><span class="p">());</span>   <span class="c1">// &#34;/releases/8.5/en.php&#34;
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$uri</span><span class="o">-&gt;</span><span class="na">getScheme</span><span class="p">());</span> <span class="c1">// &#34;https&#34;
</span></span></span></code></pre></div><p>新的 URI 扩展由 uriparser 和 Lexbor 库驱动，提供更安全、更符合标准的 URI 解析 API。</p>
<h3 id="clone-with-语法">Clone With 语法</h3>
<p>PHP 8.5 简化了对象克隆时更新属性的操作，特别适用于 readonly 类的 &ldquo;with-er&rdquo; 模式。</p>
<blockquote>
<p><strong>什么是 &ldquo;with-er&rdquo; 模式</strong>：一种用于不可变对象的设计模式，方法名以 <code>with</code> 开头（如 <code>withId()</code>、<code>withName()</code>），不修改原对象，而是返回一个修改了某个属性的新对象。常见于 PSR-7 HTTP 消息接口和 DateTimeImmutable 等。</p>
</blockquote>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Color</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">readonly</span> <span class="nx">int</span> <span class="nv">$red</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">readonly</span> <span class="nx">int</span> <span class="nv">$green</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">readonly</span> <span class="nx">int</span> <span class="nv">$blue</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">readonly</span> <span class="nx">int</span> <span class="nv">$alpha</span> <span class="o">=</span> <span class="mi">255</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// PHP 8.5 之前
</span></span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">withAlphaOld</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$alpha</span><span class="p">)</span><span class="o">:</span> <span class="nx">self</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$values</span> <span class="o">=</span> <span class="nx">get_object_vars</span><span class="p">(</span><span class="nv">$this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$values</span><span class="p">[</span><span class="s1">&#39;alpha&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$alpha</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="nx">self</span><span class="p">(</span><span class="o">...</span><span class="nv">$values</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// PHP 8.5 - 使用 clone with
</span></span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">withAlpha</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$alpha</span><span class="p">)</span><span class="o">:</span> <span class="nx">self</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">clone</span><span class="p">(</span><span class="nv">$this</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;alpha&#39;</span> <span class="o">=&gt;</span> <span class="nv">$alpha</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$color</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Color</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$transparent</span> <span class="o">=</span> <span class="nv">$color</span><span class="o">-&gt;</span><span class="na">withAlpha</span><span class="p">(</span><span class="mi">128</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="nodiscard-属性">#[\NoDiscard] 属性</h3>
<p>新的 <code>#[\NoDiscard]</code> 属性用于标记函数返回值不应被忽略：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">#[\NoDiscard(&#34;检查操作是否成功&#34;)]
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">saveData</span><span class="p">(</span><span class="k">array</span> <span class="nv">$data</span><span class="p">)</span><span class="o">:</span> <span class="nx">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 保存数据...
</span></span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 忽略返回值会触发警告
</span></span></span><span class="line"><span class="cl"><span class="nx">saveData</span><span class="p">([</span><span class="s1">&#39;key&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;value&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Warning: return value of saveData() should be used or cast to (void)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 正确使用
</span></span></span><span class="line"><span class="cl"><span class="nv">$success</span> <span class="o">=</span> <span class="nx">saveData</span><span class="p">([</span><span class="s1">&#39;key&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;value&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$success</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理错误
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 显式忽略（不会警告）
</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nx">void</span><span class="p">)</span> <span class="nx">saveData</span><span class="p">([</span><span class="s1">&#39;key&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;value&#39;</span><span class="p">]);</span>
</span></span></code></pre></div><h3 id="常量表达式中的闭包">常量表达式中的闭包</h3>
<p>PHP 8.5 允许在属性、常量和默认值中使用静态闭包和一等可调用对象：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Validator</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">RULES</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;email&#39;</span> <span class="o">=&gt;</span> <span class="nx">filter_var</span><span class="p">(</span><span class="o">...</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;trim&#39;</span> <span class="o">=&gt;</span> <span class="nx">trim</span><span class="p">(</span><span class="o">...</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">process</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">callable</span> <span class="nv">$callback</span> <span class="o">=</span> <span class="nx">strtoupper</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$callback</span><span class="p">(</span><span class="s1">&#39;hello&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="持久化-curl-share-handles">持久化 cURL Share Handles</h3>
<p>新的 <code>curl_share_init_persistent()</code> 函数可以跨请求维护 cURL 共享句柄：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 创建持久化的共享句柄
</span></span></span><span class="line"><span class="cl"><span class="nv">$share</span> <span class="o">=</span> <span class="nx">curl_share_init_persistent</span><span class="p">(</span><span class="s1">&#39;my-share-handle&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$ch</span> <span class="o">=</span> <span class="nx">curl_init</span><span class="p">(</span><span class="s1">&#39;https://example.com&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">curl_setopt</span><span class="p">(</span><span class="nv">$ch</span><span class="p">,</span> <span class="nx">CURLOPT_SHARE</span><span class="p">,</span> <span class="nv">$share</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">curl_exec</span><span class="p">(</span><span class="nv">$ch</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="php-85-新增函数">PHP 8.5 新增函数</h2>
<h3 id="array_first-和-array_last">array_first() 和 array_last()</h3>
<p>获取数组的第一个或最后一个元素，数组为空时返回 <code>null</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$events</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;login&#39;</span><span class="p">,</span> <span class="s1">&#39;view_page&#39;</span><span class="p">,</span> <span class="s1">&#39;checkout&#39;</span><span class="p">,</span> <span class="s1">&#39;logout&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$first</span> <span class="o">=</span> <span class="nx">array_first</span><span class="p">(</span><span class="nv">$events</span><span class="p">);</span>  <span class="c1">// &#39;login&#39;
</span></span></span><span class="line"><span class="cl"><span class="nv">$last</span> <span class="o">=</span> <span class="nx">array_last</span><span class="p">(</span><span class="nv">$events</span><span class="p">);</span>    <span class="c1">// &#39;logout&#39;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$empty</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">array_first</span><span class="p">(</span><span class="nv">$empty</span><span class="p">));</span>  <span class="c1">// null
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">array_last</span><span class="p">(</span><span class="nv">$empty</span><span class="p">));</span>   <span class="c1">// null
</span></span></span></code></pre></div><h3 id="get_error_handler-和-get_exception_handler">get_error_handler() 和 get_exception_handler()</h3>
<p>获取当前注册的错误处理器和异常处理器：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 设置自定义错误处理器
</span></span></span><span class="line"><span class="cl"><span class="nx">set_error_handler</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$errno</span><span class="p">,</span> <span class="nv">$errstr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">echo</span> <span class="s2">&#34;Error: </span><span class="si">$errstr</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取当前错误处理器
</span></span></span><span class="line"><span class="cl"><span class="nv">$handler</span> <span class="o">=</span> <span class="nx">get_error_handler</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$handler</span><span class="p">);</span> <span class="c1">// Closure
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取当前异常处理器
</span></span></span><span class="line"><span class="cl"><span class="nv">$exceptionHandler</span> <span class="o">=</span> <span class="nx">get_exception_handler</span><span class="p">();</span>
</span></span></code></pre></div><h3 id="intllistformatter-类">IntlListFormatter 类</h3>
<p>新的国际化列表格式化类：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$formatter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">IntlListFormatter</span><span class="p">(</span><span class="s1">&#39;zh-CN&#39;</span><span class="p">,</span> <span class="nx">IntlListFormatter</span><span class="o">::</span><span class="na">TYPE_AND</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$formatter</span><span class="o">-&gt;</span><span class="na">format</span><span class="p">([</span><span class="s1">&#39;苹果&#39;</span><span class="p">,</span> <span class="s1">&#39;香蕉&#39;</span><span class="p">,</span> <span class="s1">&#39;橙子&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 输出: 苹果、香蕉和橙子
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$formatter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">IntlListFormatter</span><span class="p">(</span><span class="s1">&#39;en-US&#39;</span><span class="p">,</span> <span class="nx">IntlListFormatter</span><span class="o">::</span><span class="na">TYPE_OR</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$formatter</span><span class="o">-&gt;</span><span class="na">format</span><span class="p">([</span><span class="s1">&#39;apple&#39;</span><span class="p">,</span> <span class="s1">&#39;banana&#39;</span><span class="p">,</span> <span class="s1">&#39;orange&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 输出: apple, banana, or orange
</span></span></span></code></pre></div><h3 id="curl_multi_get_handles">curl_multi_get_handles()</h3>
<p>从 multi-cURL 会话中获取所有句柄：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$mh</span> <span class="o">=</span> <span class="nx">curl_multi_init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$ch1</span> <span class="o">=</span> <span class="nx">curl_init</span><span class="p">(</span><span class="s1">&#39;https://example.com/1&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$ch2</span> <span class="o">=</span> <span class="nx">curl_init</span><span class="p">(</span><span class="s1">&#39;https://example.com/2&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">curl_multi_add_handle</span><span class="p">(</span><span class="nv">$mh</span><span class="p">,</span> <span class="nv">$ch1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">curl_multi_add_handle</span><span class="p">(</span><span class="nv">$mh</span><span class="p">,</span> <span class="nv">$ch2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$handles</span> <span class="o">=</span> <span class="nx">curl_multi_get_handles</span><span class="p">(</span><span class="nv">$mh</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">count</span><span class="p">(</span><span class="nv">$handles</span><span class="p">));</span> <span class="c1">// 2
</span></span></span></code></pre></div><h3 id="locale_is_right_to_left--localeisrighttoleft">locale_is_right_to_left() / Locale::isRightToLeft()</h3>
<p>判断语言环境是否使用从右到左的文字方向：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">locale_is_right_to_left</span><span class="p">(</span><span class="s1">&#39;ar&#39;</span><span class="p">));</span>    <span class="c1">// true (阿拉伯语)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">locale_is_right_to_left</span><span class="p">(</span><span class="s1">&#39;he&#39;</span><span class="p">));</span>    <span class="c1">// true (希伯来语)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">locale_is_right_to_left</span><span class="p">(</span><span class="s1">&#39;zh-CN&#39;</span><span class="p">));</span> <span class="c1">// false (中文)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">Locale</span><span class="o">::</span><span class="na">isRightToLeft</span><span class="p">(</span><span class="s1">&#39;fa&#39;</span><span class="p">));</span>      <span class="c1">// true (波斯语)
</span></span></span></code></pre></div><h3 id="grapheme_levenshtein">grapheme_levenshtein()</h3>
<p>计算两个字符串之间的 Levenshtein 编辑距离（基于字形簇）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$distance</span> <span class="o">=</span> <span class="nx">grapheme_levenshtein</span><span class="p">(</span><span class="s1">&#39;café&#39;</span><span class="p">,</span> <span class="s1">&#39;cafe&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$distance</span><span class="p">);</span> <span class="c1">// 1
</span></span></span></code></pre></div><h3 id="closuregetcurrent">Closure::getCurrent()</h3>
<p>在闭包内部获取当前闭包实例，便于实现递归：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$factorial</span> <span class="o">=</span> <span class="k">function</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$n</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nv">$n</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$n</span> <span class="o">*</span> <span class="nx">Closure</span><span class="o">::</span><span class="na">getCurrent</span><span class="p">()(</span><span class="nv">$n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$factorial</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> <span class="c1">// 120
</span></span></span></code></pre></div><h2 id="其他改进">其他改进</h2>
<h3 id="致命错误包含堆栈跟踪">致命错误包含堆栈跟踪</h3>
<p>PHP 8.5 中，致命错误现在会包含完整的堆栈跟踪信息，大大提升了调试体验。</p>
<h3 id="新的-dom-方法">新的 DOM 方法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$doc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DOMDocument</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$doc</span><span class="o">-&gt;</span><span class="na">loadHTML</span><span class="p">(</span><span class="s1">&#39;&lt;div class=&#34;box&#34;&gt;A&lt;/div&gt;&lt;div class=&#34;box&#34;&gt;B&lt;/div&gt;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// getElementsByClassName()
</span></span></span><span class="line"><span class="cl"><span class="nv">$elements</span> <span class="o">=</span> <span class="nv">$doc</span><span class="o">-&gt;</span><span class="na">getElementsByClassName</span><span class="p">(</span><span class="s1">&#39;box&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$elements</span><span class="o">-&gt;</span><span class="na">length</span><span class="p">;</span> <span class="c1">// 2
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// insertAdjacentHTML()
</span></span></span><span class="line"><span class="cl"><span class="nv">$div</span> <span class="o">=</span> <span class="nv">$doc</span><span class="o">-&gt;</span><span class="na">querySelector</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$div</span><span class="o">-&gt;</span><span class="na">insertAdjacentHTML</span><span class="p">(</span><span class="s1">&#39;beforeend&#39;</span><span class="p">,</span> <span class="s1">&#39;&lt;span&gt;New&lt;/span&gt;&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="属性可以应用于常量">属性可以应用于常量</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#[Deprecated(&#34;Use NEW_CONST instead&#34;)]
</span></span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">OLD_CONST</span> <span class="o">=</span> <span class="s1">&#39;old&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">NEW_CONST</span> <span class="o">=</span> <span class="s1">&#39;new&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="静态属性支持非对称可见性">静态属性支持非对称可见性</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Counter</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">private</span><span class="p">(</span><span class="nx">set</span><span class="p">)</span> <span class="k">static</span> <span class="nx">int</span> <span class="nv">$count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">increment</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">self</span><span class="o">::</span><span class="nv">$count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">Counter</span><span class="o">::</span><span class="nv">$count</span><span class="p">;</span> <span class="c1">// 可读
</span></span></span><span class="line"><span class="cl"><span class="nx">Counter</span><span class="o">::</span><span class="nv">$count</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> <span class="c1">// 错误：不可写
</span></span></span></code></pre></div><h3 id="构造函数提升支持-final-属性">构造函数提升支持 final 属性</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="k">final</span> <span class="nx">string</span> <span class="nv">$id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">string</span> <span class="nv">$name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="setcookie-支持-partitioned-选项">setcookie() 支持 &ldquo;partitioned&rdquo; 选项</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">setcookie</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="s1">&#39;value&#39;</span><span class="p">,</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;expires&#39;</span> <span class="o">=&gt;</span> <span class="nx">time</span><span class="p">()</span> <span class="o">+</span> <span class="mi">3600</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;path&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;/&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;secure&#39;</span> <span class="o">=&gt;</span> <span class="k">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;partitioned&#39;</span> <span class="o">=&gt;</span> <span class="k">true</span><span class="p">,</span>  <span class="c1">// 新选项
</span></span></span><span class="line"><span class="cl"><span class="p">]);</span>
</span></span></code></pre></div><h3 id="新的全局常量">新的全局常量</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 构建提供者标识
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">PHP_BUILD_PROVIDER</span><span class="p">;</span> <span class="c1">// 例如: &#34;debian&#34; 或 &#34;remi&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 构建日期
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">PHP_BUILD_DATE</span><span class="p">;</span> <span class="c1">// 例如: &#34;Jan 1 2026&#34;
</span></span></span></code></pre></div><h3 id="cli-新选项">CLI 新选项</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 显示与默认值不同的 INI 配置</span>
</span></span><span class="line"><span class="cl">php --ini<span class="o">=</span>diff
</span></span></code></pre></div><h3 id="max_memory_limit-ini-指令">max_memory_limit INI 指令</h3>
<p>管理员可以设置内存限制的上限：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1">; php.ini</span>
</span></span><span class="line"><span class="cl"><span class="na">max_memory_limit</span> <span class="o">=</span> <span class="s">256M</span>
</span></span></code></pre></div><p>应用程序无法将 <code>memory_limit</code> 设置为超过 <code>max_memory_limit</code> 的值。</p>
<h2 id="弃用特性">弃用特性</h2>
<h3 id="反引号运算符shell_exec-别名">反引号运算符（shell_exec 别名）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ PHP 8.5 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nv">$output</span> <span class="o">=</span> <span class="sb">`ls -la`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用 shell_exec()
</span></span></span><span class="line"><span class="cl"><span class="nv">$output</span> <span class="o">=</span> <span class="nx">shell_exec</span><span class="p">(</span><span class="s1">&#39;ls -la&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="非标准类型转换">非标准类型转换</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ PHP 8.5 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nv">$bool</span> <span class="o">=</span> <span class="p">(</span><span class="nx">boolean</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$int</span> <span class="o">=</span> <span class="p">(</span><span class="nx">integer</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$float</span> <span class="o">=</span> <span class="p">(</span><span class="nx">double</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$bin</span> <span class="o">=</span> <span class="p">(</span><span class="nx">binary</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用标准类型转换
</span></span></span><span class="line"><span class="cl"><span class="nv">$bool</span> <span class="o">=</span> <span class="p">(</span><span class="nx">bool</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$int</span> <span class="o">=</span> <span class="p">(</span><span class="nx">int</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$float</span> <span class="o">=</span> <span class="p">(</span><span class="nx">float</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$bin</span> <span class="o">=</span> <span class="p">(</span><span class="nx">string</span><span class="p">)</span> <span class="nv">$value</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="mysqli_execute">mysqli_execute()</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nx">mysqli_execute</span><span class="p">(</span><span class="nv">$stmt</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用 mysqli_stmt_execute()
</span></span></span><span class="line"><span class="cl"><span class="nx">mysqli_stmt_execute</span><span class="p">(</span><span class="nv">$stmt</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="curl_close-和-curl_share_close">curl_close() 和 curl_share_close()</h3>
<p>这些函数自 PHP 8.0 起就是空操作，现在正式弃用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用（实际上不需要调用）
</span></span></span><span class="line"><span class="cl"><span class="nx">curl_close</span><span class="p">(</span><span class="nv">$ch</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">curl_share_close</span><span class="p">(</span><span class="nv">$sh</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// cURL 句柄现在会自动清理
</span></span></span></code></pre></div><h3 id="xml_parser_free">xml_parser_free()</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用（自 PHP 8.0 起是空操作）
</span></span></span><span class="line"><span class="cl"><span class="nx">xml_parser_free</span><span class="p">(</span><span class="nv">$parser</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// XML 解析器现在会自动清理
</span></span></span></code></pre></div><h3 id="socket_set_timeout">socket_set_timeout()</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nx">socket_set_timeout</span><span class="p">(</span><span class="nv">$stream</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用 stream_set_timeout()
</span></span></span><span class="line"><span class="cl"><span class="nx">stream_set_timeout</span><span class="p">(</span><span class="nv">$stream</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="mhash_-常量">MHASH_* 常量</h3>
<p>所有 <code>MHASH_*</code> 相关常量已弃用，应使用 <code>hash()</code> 函数替代。</p>
<h3 id="__sleep-和-__wakeup">__sleep() 和 __wakeup()</h3>
<p>这两个魔术方法被软弃用（soft-deprecated），建议使用 <code>__serialize()</code> 和 <code>__unserialize()</code> 替代：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 软弃用
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">OldStyle</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__sleep</span><span class="p">()</span><span class="o">:</span> <span class="k">array</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">[</span><span class="s1">&#39;data&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__wakeup</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 推荐方式
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">NewStyle</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">__serialize</span><span class="p">()</span><span class="o">:</span> <span class="k">array</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">[</span><span class="s1">&#39;data&#39;</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">data</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">__unserialize</span><span class="p">(</span><span class="k">array</span> <span class="nv">$data</span><span class="p">)</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">data</span> <span class="o">=</span> <span class="nv">$data</span><span class="p">[</span><span class="s1">&#39;data&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="case-语句后使用分号">case 语句后使用分号</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ PHP 8.5 已弃用
</span></span></span><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="nv">$value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// 分号
</span></span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用冒号
</span></span></span><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="nv">$value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="o">:</span>  <span class="c1">// 冒号
</span></span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="null-作为数组偏移量">null 作为数组偏移量</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ PHP 8.5 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nv">$array</span><span class="p">[</span><span class="k">null</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;value&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用空字符串或其他明确的键
</span></span></span><span class="line"><span class="cl"><span class="nv">$array</span><span class="p">[</span><span class="s1">&#39;&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;value&#39;</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="移除的特性">移除的特性</h2>
<h3 id="disable_classes-ini-设置">disable_classes INI 设置</h3>
<p><code>disable_classes</code> INI 设置已被完全移除。</p>
<h3 id="cli--z--zend-extension-选项">CLI -z / &ndash;zend-extension 选项</h3>
<p>从命令行加载 Zend 扩展的选项已被移除。</p>
<h2 id="兼容性变化">兼容性变化</h2>
<h3 id="类型转换警告">类型转换警告</h3>
<ul>
<li>NAN 转换为其他类型时会产生警告</li>
<li>浮点数转整数时的精度损失会产生警告</li>
<li>对非数组使用解构语法会产生警告</li>
</ul>
<h3 id="class_alias-限制">class_alias() 限制</h3>
<p>&ldquo;array&rdquo; 和 &ldquo;callable&rdquo; 不再是 <code>class_alias()</code> 的有效类名。</p>
<h2 id="总结">总结</h2>
<p>PHP 8.5 带来了许多实用的新特性和改进：</p>
<p><strong>主要新特性：</strong></p>
<ul>
<li>管道运算符 <code>|&gt;</code></li>
<li>URI 扩展</li>
<li>Clone With 语法</li>
<li><code>#[\NoDiscard]</code> 属性</li>
<li><code>array_first()</code> 和 <code>array_last()</code> 函数</li>
<li>常量表达式中的闭包</li>
<li>致命错误堆栈跟踪</li>
<li>IntlListFormatter 类</li>
</ul>
<p><strong>需要注意的变化：</strong></p>
<ul>
<li>反引号运算符已弃用</li>
<li>非标准类型转换已弃用</li>
<li><code>__sleep()</code> 和 <code>__wakeup()</code> 软弃用</li>
<li><code>disable_classes</code> INI 设置已移除</li>
</ul>
<p>通过了解这些变化并遵循迁移建议，可以顺利地从 PHP 8.4 迁移到 PHP 8.5。</p>
]]></content:encoded>
    </item>
    <item>
      <title>NGINX 启用 HTTP/3</title>
      <link>https://zyf.im/2025/06/27/nginx-enable-http3/</link>
      <pubDate>Fri, 27 Jun 2025 10:12:12 +0000</pubDate>
      <guid>https://zyf.im/2025/06/27/nginx-enable-http3/</guid>
      <description>&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;HTTP/2 解决了&amp;quot;多路复用、头部压缩、优先级&amp;quot;等 HTTP/1.x 的瓶颈，但仍受 TCP 先天限制。&lt;/p&gt;
&lt;p&gt;HTTP/3 则把同样的语义搬到 QUIC。Quick UDP Internet Connections 是 Google 设计的一种基于 UDP 的多路复用安全传输层协议。标准化后，HTTP/3 = HTTP over QUIC。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;层级&lt;/th&gt;
          &lt;th&gt;HTTP/2&lt;/th&gt;
          &lt;th&gt;HTTP/3&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;传输&lt;/td&gt;
          &lt;td&gt;TCP（必）+ TLS 1.2/1.3（多数场景）&lt;/td&gt;
          &lt;td&gt;QUIC（UDP 之上）+ TLS 1.3（强制）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;流（Stream）&lt;/td&gt;
          &lt;td&gt;Mux 在 TCP 里的逻辑流&lt;/td&gt;
          &lt;td&gt;Mux 在 QUIC 原生流&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;头压缩&lt;/td&gt;
          &lt;td&gt;HPACK&lt;/td&gt;
          &lt;td&gt;QPACK（避免队头阻塞）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;HTTP/2 浏览器要求必须跑在 TLS 之上（ALPN=&amp;ldquo;h2&amp;rdquo;）。栈变为 HTTP/2 → TLS → TCP → IP。&lt;/li&gt;
&lt;li&gt;HTTP/3 使用 QUIC 替换 TCP，栈变为 HTTP/3 → QUIC（拥塞/重传）→ UDP → IP，加密集成在 QUIC（基于 TLS 1.3）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Mux&lt;/code&gt; 即 multiplex，多路复用。&lt;/li&gt;
&lt;li&gt;HTTP/2 必须顺序传输，而 HTTP/3 支持乱序。&lt;/li&gt;
&lt;li&gt;如果沿用 HPACK，丢一个 UDP 包就会让所有流的头部解码卡住，因此 IETF 为 HTTP/3 设计了 QPACK 来&amp;quot;避开队头阻塞（Head-of-Line Blocking, HoL）&amp;quot;。&lt;/li&gt;
&lt;li&gt;连接迁移：通过 Connection ID 识别会话，IP 或端口改变（如移动网络/Wi-Fi 切换）无需重新建立连接。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用 HTTP/3 的网站：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="简介">简介</h2>
<p>HTTP/2 解决了&quot;多路复用、头部压缩、优先级&quot;等 HTTP/1.x 的瓶颈，但仍受 TCP 先天限制。</p>
<p>HTTP/3 则把同样的语义搬到 QUIC。Quick UDP Internet Connections 是 Google 设计的一种基于 UDP 的多路复用安全传输层协议。标准化后，HTTP/3 = HTTP over QUIC。</p>
<table>
  <thead>
      <tr>
          <th>层级</th>
          <th>HTTP/2</th>
          <th>HTTP/3</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>传输</td>
          <td>TCP（必）+ TLS 1.2/1.3（多数场景）</td>
          <td>QUIC（UDP 之上）+ TLS 1.3（强制）</td>
      </tr>
      <tr>
          <td>流（Stream）</td>
          <td>Mux 在 TCP 里的逻辑流</td>
          <td>Mux 在 QUIC 原生流</td>
      </tr>
      <tr>
          <td>头压缩</td>
          <td>HPACK</td>
          <td>QPACK（避免队头阻塞）</td>
      </tr>
  </tbody>
</table>
<ul>
<li>HTTP/2 浏览器要求必须跑在 TLS 之上（ALPN=&ldquo;h2&rdquo;）。栈变为 HTTP/2 → TLS → TCP → IP。</li>
<li>HTTP/3 使用 QUIC 替换 TCP，栈变为 HTTP/3 → QUIC（拥塞/重传）→ UDP → IP，加密集成在 QUIC（基于 TLS 1.3）。</li>
<li><code>Mux</code> 即 multiplex，多路复用。</li>
<li>HTTP/2 必须顺序传输，而 HTTP/3 支持乱序。</li>
<li>如果沿用 HPACK，丢一个 UDP 包就会让所有流的头部解码卡住，因此 IETF 为 HTTP/3 设计了 QPACK 来&quot;避开队头阻塞（Head-of-Line Blocking, HoL）&quot;。</li>
<li>连接迁移：通过 Connection ID 识别会话，IP 或端口改变（如移动网络/Wi-Fi 切换）无需重新建立连接。</li>
</ul>
<p>使用 HTTP/3 的网站：</p>
<ul>
<li><a href="https://cloudflare-quic.com/">https://cloudflare-quic.com/</a></li>
</ul>
<h2 id="开始实验">开始实验</h2>
<p>启用 HTTP/3 需要使用 HTTPS，请先参考 <a href="/2025/06/26/use-https-in-local-environment/">在本地环境使用 HTTPS ｜ ZYF.IM</a>。</p>
<p>修改 NGINX default.conf 配置文件，启用 HTTP/3：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl"><span class="c1"># vi default.conf
</span></span></span><span class="line"><span class="cl"><span class="k">server</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># for better compatibility it&#39;s recommended
</span></span></span><span class="line"><span class="cl">    <span class="c1"># to use the same port for http/3 and https
</span></span></span><span class="line"><span class="cl">    <span class="c1"># 浏览器首次只能走 TCP，因此必须提供一个 HTTPS 入口，再通过 `Alt-Svc` 升级到 HTTP/3
</span></span></span><span class="line"><span class="cl">    <span class="kn">listen</span> <span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Quick UDP Internet Connections
</span></span></span><span class="line"><span class="cl">    <span class="c1"># quic 关键字表示使用 UDP 监听，承载 QUIC/HTTP-3
</span></span></span><span class="line"><span class="cl">    <span class="c1"># reuseport 关键字表示复用端口，允许多个 NGINX 实例共享同一端口
</span></span></span><span class="line"><span class="cl">    <span class="kn">listen</span> <span class="mi">443</span> <span class="s">quic</span> <span class="s">reuseport</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kn">http2</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 启用 HTTP/3，默认是 on
</span></span></span><span class="line"><span class="cl">    <span class="kn">http3</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kn">server_name</span> <span class="s">localhost</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_certificate</span> <span class="s">/etc/ssl/certs/localhost.pem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_certificate_key</span> <span class="s">/etc/ssl/certs/localhost-key.pem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 启用 TLS 允许 0-RTT（Early Data），允许客户端在 TLS 1.3 的重连阶段携带早期数据
</span></span></span><span class="line"><span class="cl">    <span class="c1"># 对 HTTP/3 非必需，但 QUIC 天生支持 0-RTT；此处保持一致
</span></span></span><span class="line"><span class="cl">    <span class="kn">ssl_early_data</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 仅启用 TLSv1.3
</span></span></span><span class="line"><span class="cl">    <span class="kn">ssl_protocols</span> <span class="s">TLSv1.3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_ciphers</span> <span class="s">HIGH:!aNULL:!MD5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_prefer_server_ciphers</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 添加 Alt-Svc 头，向客户端声明可升级到 HTTP/3
</span></span></span><span class="line"><span class="cl">    <span class="c1"># Alt-Svc 头告诉浏览器&#34;该资源在同一主机的 UDP/443 上能用 h3 协议&#34;，浏览器会自动重试
</span></span></span><span class="line"><span class="cl">    <span class="c1"># ma max-age 缓存时间，86400 秒 = 1 天
</span></span></span><span class="line"><span class="cl">    <span class="c1"># always 关键字保证即使发生 4xx/5xx 也发送 Alt-Svc，便于调试
</span></span></span><span class="line"><span class="cl">    <span class="kn">add_header</span> <span class="s">alt-svc</span> <span class="s">&#39;h3=&#34;:443&#34;</span><span class="p">;</span> <span class="kn">ma=86400&#39;</span> <span class="s">always</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kn">root</span> <span class="s">/usr/share/nginx/html</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kn">index</span>  <span class="s">index.html</span> <span class="s">index.htm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># docker rm -f nginx-ssl</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 添加监听 UDP 端口</span>
</span></span><span class="line"><span class="cl">docker run -d --name nginx-ssl <span class="se">\
</span></span></span><span class="line"><span class="cl">  -p 443:443 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -p 443:443/udp <span class="se">\
</span></span></span><span class="line"><span class="cl">  -v ./default.conf:/etc/nginx/conf.d/default.conf:ro <span class="se">\
</span></span></span><span class="line"><span class="cl">  -v ./localhost.pem:/etc/ssl/certs/localhost.pem:ro <span class="se">\
</span></span></span><span class="line"><span class="cl">  -v ./localhost-key.pem:/etc/ssl/certs/localhost-key.pem:ro <span class="se">\
</span></span></span><span class="line"><span class="cl">  -v ./html/:/usr/share/nginx/html/:ro <span class="se">\
</span></span></span><span class="line"><span class="cl">  nginx:alpine
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># docker container restart nginx-ssl</span>
</span></span></code></pre></div><h2 id="测试">测试</h2>
<h3 id="curl">curl</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 使用支持 HTTP/3 的 curl</span>
</span></span><span class="line"><span class="cl"><span class="c1"># docker run -ti --rm alpine/curl-http3 curl -V</span>
</span></span><span class="line"><span class="cl">docker run -ti --network host --rm alpine/curl-http3 curl -I --http3 https://localhost/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">HTTP/3 <span class="m">200</span>
</span></span><span class="line"><span class="cl">server: nginx/1.29.0
</span></span><span class="line"><span class="cl">date: Fri, <span class="m">27</span> Jun <span class="m">2025</span> 03:26:09 GMT
</span></span><span class="line"><span class="cl">content-type: text/html
</span></span><span class="line"><span class="cl">content-length: <span class="m">656</span>
</span></span><span class="line"><span class="cl">last-modified: Fri, <span class="m">27</span> Jun <span class="m">2025</span> 03:17:07 GMT
</span></span><span class="line"><span class="cl">etag: <span class="s2">&#34;685e0d33-290&#34;</span>
</span></span><span class="line"><span class="cl">alt-svc: <span class="nv">h3</span><span class="o">=</span><span class="s2">&#34;:443&#34;</span><span class="p">;</span> <span class="nv">ma</span><span class="o">=</span><span class="m">86400</span>
</span></span><span class="line"><span class="cl">accept-ranges: bytes
</span></span></code></pre></div><h3 id="浏览器">浏览器</h3>
<p>无法验证成功！分析原因：</p>
<ol>
<li>之前 UDP 使用的是 8443，当将相关配置都改为 443 仍然在 Chrome Network 里无法看见 h3。</li>
<li>可能 Chrome 对 localhost 或者域名解析到 127.0.0.1 的访问阻止了 HTTP/3。</li>
</ol>
<p>为了验证第 2 种情况，通过 AWS EC2 + 自己的域名 + Let&rsquo;s Encrypt 颁发的证书，再仿照本地配置的方法启用 HTTP/3。最终在 Chrome 中成功验证 HTTP/3。</p>
<p>使用 Chrome 插件 <a href="https://chromewebstore.google.com/detail/http-indicator/hgcomhbcacfkpffiphlmnlhpppcjgmbl?hl=en-US">HTTP Indicator</a>：</p>
<ul>
<li>HTTP/1.1 显示为灰色</li>
<li>HTTP/2 显示为蓝色</li>
<li>HTTP/3 显示为绿色</li>
</ul>
<p>如何配置 AWS EC2 + 自己的域名 + Let&rsquo;s Encrypt 颁发的证书，参考：</p>
<ul>
<li><a href="/2018/04/26/lets-encrypt-wildcard-certificates/">使用 Certbot 获取 Let&rsquo;s Encrypt 颁发的 TLS 证书 ｜ ZYF.IM</a></li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="https://nginx.org/en/docs/http/ngx_http_v3_module.html">NGINX HTTP/3 模块</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>在本地环境使用 HTTPS</title>
      <link>https://zyf.im/2025/06/26/use-https-in-local-environment/</link>
      <pubDate>Thu, 26 Jun 2025 12:00:00 +0000</pubDate>
      <guid>https://zyf.im/2025/06/26/use-https-in-local-environment/</guid>
      <description>&lt;p&gt;公网环境使用 HTTPS 参考：&lt;a href=&#34;https://zyf.im/2018/04/26/lets-encrypt-wildcard-certificates/&#34;&gt;使用 Certbot 获取 Let&amp;rsquo;s Encrypt 颁发的 TLS 证书 ｜ ZYF.IM&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;HTTPS（HyperText Transfer Protocol Secure）是在 HTTP 之上通过 TLS/SSL 实现加密的安全通信协议，用来保护浏览器与服务器之间的数据机密性、完整性与身份可信性。&lt;/p&gt;
&lt;h2 id=&#34;开始实验&#34;&gt;开始实验&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/FiloSottile/mkcert&#34;&gt;mkcert&lt;/a&gt; 会在本地生成一套根 CA（包含根证书和私钥），并把根证书导入系统/浏览器的信任列表。随后，它用这套 CA 的私钥为 localhost 等域名签发服务器证书；因为根证书已被信任，浏览器访问时就会把服务器证书视为可信。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install mkcert
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkcert -install
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkcert localhost
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# The certificate is at &amp;#34;./localhost.pem&amp;#34; and the key at &amp;#34;./localhost-key.pem&amp;#34; ✅&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# vi default.conf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;443&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;ssl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;localhost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_certificate&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/ssl/certs/localhost.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_certificate_key&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/ssl/certs/localhost-key.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 只允许 TLSv1.2 和 TLSv1.3 协议，显式禁止 TLSv1.0 和 TLSv1.1 协议
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_protocols&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;TLSv1.2&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;TLSv1.3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 只保留高强度、带身份验证、且不使用 MD5 的 TLS 1.2（及以下）套件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_ciphers&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;HIGH:!aNULL:!MD5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 优先使用服务器端加密套件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;index&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;index.html&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;index.htm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker run -d --name nginx-ssl &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -p 8443:443 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -v ./default.conf:/etc/nginx/conf.d/default.conf:ro &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -v ./localhost.pem:/etc/ssl/certs/localhost.pem:ro &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -v ./localhost-key.pem:/etc/ssl/certs/localhost-key.pem:ro &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  nginx:alpine
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# docker rm -f nginx-ssl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# docker container restart nginx-ssl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;测试&#34;&gt;测试&lt;/h2&gt;
&lt;h3 id=&#34;浏览器&#34;&gt;浏览器&lt;/h3&gt;
&lt;p&gt;浏览器访问 &lt;code&gt;https://localhost:8443/&lt;/code&gt;，如果证书是绿色的，说明成功了。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>公网环境使用 HTTPS 参考：<a href="/2018/04/26/lets-encrypt-wildcard-certificates/">使用 Certbot 获取 Let&rsquo;s Encrypt 颁发的 TLS 证书 ｜ ZYF.IM</a>。</p>
<h2 id="简介">简介</h2>
<p>HTTPS（HyperText Transfer Protocol Secure）是在 HTTP 之上通过 TLS/SSL 实现加密的安全通信协议，用来保护浏览器与服务器之间的数据机密性、完整性与身份可信性。</p>
<h2 id="开始实验">开始实验</h2>
<p><a href="https://github.com/FiloSottile/mkcert">mkcert</a> 会在本地生成一套根 CA（包含根证书和私钥），并把根证书导入系统/浏览器的信任列表。随后，它用这套 CA 的私钥为 localhost 等域名签发服务器证书；因为根证书已被信任，浏览器访问时就会把服务器证书视为可信。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">brew install mkcert
</span></span><span class="line"><span class="cl">mkcert -install
</span></span><span class="line"><span class="cl">mkcert localhost
</span></span><span class="line"><span class="cl"><span class="c1"># The certificate is at &#34;./localhost.pem&#34; and the key at &#34;./localhost-key.pem&#34; ✅</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl"><span class="c1"># vi default.conf
</span></span></span><span class="line"><span class="cl"><span class="k">server</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kn">listen</span> <span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kn">server_name</span> <span class="s">localhost</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_certificate</span> <span class="s">/etc/ssl/certs/localhost.pem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_certificate_key</span> <span class="s">/etc/ssl/certs/localhost-key.pem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 只允许 TLSv1.2 和 TLSv1.3 协议，显式禁止 TLSv1.0 和 TLSv1.1 协议
</span></span></span><span class="line"><span class="cl">    <span class="kn">ssl_protocols</span> <span class="s">TLSv1.2</span> <span class="s">TLSv1.3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 只保留高强度、带身份验证、且不使用 MD5 的 TLS 1.2（及以下）套件
</span></span></span><span class="line"><span class="cl">    <span class="kn">ssl_ciphers</span> <span class="s">HIGH:!aNULL:!MD5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 优先使用服务器端加密套件
</span></span></span><span class="line"><span class="cl">    <span class="kn">ssl_prefer_server_ciphers</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kn">root</span> <span class="s">/usr/share/nginx/html</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kn">index</span>  <span class="s">index.html</span> <span class="s">index.htm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -d --name nginx-ssl <span class="se">\
</span></span></span><span class="line"><span class="cl">  -p 8443:443 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -v ./default.conf:/etc/nginx/conf.d/default.conf:ro <span class="se">\
</span></span></span><span class="line"><span class="cl">  -v ./localhost.pem:/etc/ssl/certs/localhost.pem:ro <span class="se">\
</span></span></span><span class="line"><span class="cl">  -v ./localhost-key.pem:/etc/ssl/certs/localhost-key.pem:ro <span class="se">\
</span></span></span><span class="line"><span class="cl">  nginx:alpine
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># docker rm -f nginx-ssl</span>
</span></span><span class="line"><span class="cl"><span class="c1"># docker container restart nginx-ssl</span>
</span></span></code></pre></div><h2 id="测试">测试</h2>
<h3 id="浏览器">浏览器</h3>
<p>浏览器访问 <code>https://localhost:8443/</code>，如果证书是绿色的，说明成功了。</p>
<h3 id="curl">curl</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -I https://localhost:8443/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">HTTP/1.1 <span class="m">200</span> OK
</span></span><span class="line"><span class="cl">Server: nginx/1.29.0
</span></span><span class="line"><span class="cl">Date: Fri, <span class="m">27</span> Jun <span class="m">2025</span> 01:54:36 GMT
</span></span><span class="line"><span class="cl">Content-Type: text/html
</span></span><span class="line"><span class="cl">Content-Length: <span class="m">615</span>
</span></span><span class="line"><span class="cl">Last-Modified: Tue, <span class="m">24</span> Jun <span class="m">2025</span> 17:57:38 GMT
</span></span><span class="line"><span class="cl">Connection: keep-alive
</span></span><span class="line"><span class="cl">ETag: <span class="s2">&#34;685ae712-267&#34;</span>
</span></span><span class="line"><span class="cl">Accept-Ranges: bytes
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># -v 显示详细信息，查看 TLS 版本</span>
</span></span><span class="line"><span class="cl">curl -I https://localhost:8443/ -v
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / <span class="o">[</span>blank<span class="o">]</span> / UNDEF
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><h2 id="深入探索">深入探索</h2>
<h3 id="http-的三大安全缺陷">HTTP 的三大安全缺陷</h3>
<blockquote>
<p>网络早已布满监听、篡改与伪装。</p>
</blockquote>
<ul>
<li><strong>明文传输</strong>：任何中间节点（Wi-Fi 热点、ISP、CDN、政府网关）都能直接读取用户名、密码、Cookie、表单内容等敏感信息。</li>
<li><strong>缺少完整性保护</strong>：中间人可随意插入、删除或修改页面内容（广告注入、恶意脚本、暗链植入），客户端亦无法察觉。</li>
<li><strong>无法验证身份</strong>：客户端无法确认自己连接的是&quot;真正的 server&quot;，攻击者可通过 DNS 欺骗、ARP 攻击或流量劫持将流量导向钓鱼站点。</li>
</ul>
<h3 id="为什么需要-https">为什么需要 HTTPS</h3>
<blockquote>
<p>TLS 通过&quot;加密 + 身份认证 + 完整性校验&quot;应对上述风险。</p>
</blockquote>
<ul>
<li>加密（Confidentiality）：对称加密保护数据内容；密钥通过非对称算法安全协商。</li>
<li>数据完整性（Integrity）：MAC（消息鉴别码）或 AEAD 算法确保内容未被篡改。</li>
<li>身份认证（Authentication）：服务器出示数字证书；浏览器验证证书链与域名匹配。</li>
</ul>
<h3 id="mac-vs-aead">MAC vs AEAD</h3>
<ul>
<li>MAC 提供数据完整性与对称实体认证，需要与独立的加密步骤结合来保障机密性。
<ul>
<li>Message Authentication Code</li>
<li>只负责&quot;认证&quot;，不加密数据；与加密算法解耦。典型实现是 HMAC-SHA1/HMAC-SHA256。</li>
</ul>
</li>
<li>AEAD 将&quot;加密 + 认证&quot;合二为一，接口更简单、实现更安全、性能更佳，是现代协议的首选。
<ul>
<li>Authenticated Encryption with Associated Data</li>
<li>加密数据 + 认证数据</li>
<li>典型实现是 AES-GCM/ChaCha20-Poly1305</li>
</ul>
</li>
<li>在 TLS/QUIC/Noise 等现代协议中，AEAD 已全面取代传统的&quot;MAC-then-Encrypt&quot;。</li>
</ul>
<h3 id="证书类型">证书类型</h3>
<p>按层级角色：</p>
<ul>
<li>根证书（Root CA）：最高级别，Subject = Issuer 自签名，预装在操作系统/浏览器的&quot;根信任库&quot;内，用于验证下级证书。</li>
<li>中级证书（Intermediate CA）：由根证书签名，用于验证下级证书，降低根密钥泄露风险。</li>
<li>最终实体证书（End Entity Certificate）：由中级证书签名，绑定具体域名或主体，用于验证服务器身份。</li>
</ul>
<p>按验证级别：</p>
<ul>
<li>DV（Domain Validation）证书仅验证域名所有权，通过自动化流程颁发，价格低廉甚至免费（如 Let&rsquo;s Encrypt）。</li>
<li>OV（Organization Validation）证书需要验证组织实体信息，在浏览器证书详情中可查看公司名称。</li>
<li>EV（Extended Validation）证书验证最为严格，地址栏会显示公司名称，但多数现代浏览器已弱化此功能的 UI 展示。</li>
</ul>
<h3 id="证书链">证书链</h3>
<p>在 HTTPS、TLS、S/MIME 等基于 X.509 的公钥基础设施（PKI）中，任何一张服务器或用户证书都必须被&quot;信任根（Root CA）&ldquo;间接或直接签名。从根证书一路向下到最终实体证书所形成的&quot;逐级签名&quot;路径，就称为证书链，也叫信任链（Chain of Trust）。</p>
<h3 id="https-在网络堆栈中的位置">HTTPS 在网络堆栈中的位置</h3>
<ol>
<li>HTTP/HTTPS 仍是应用层协议。</li>
<li>TLS（Transport Layer Security）位于 HTTP 与 TCP 之间；它把上层的明文字节（HTTP 报文）加密后再交给 TCP。</li>
<li>TCP 提供可靠的字节流；IP 负责寻址和路由；最底层是物理/链路层。</li>
<li>HTTPS = HTTP over TLS over TCP over IP。</li>
</ol>
<table>
  <thead>
      <tr>
          <th>协议</th>
          <th>主要职责</th>
          <th>典型端口</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>HTTP</td>
          <td>应用语义：URL、方法、头、状态码…</td>
          <td>80（无加密）</td>
      </tr>
      <tr>
          <td>TLS</td>
          <td>机密性、完整性、身份认证</td>
          <td>443（惯例）</td>
      </tr>
      <tr>
          <td>TCP</td>
          <td>可靠传输、流量控制、重传</td>
          <td>任意（443）</td>
      </tr>
      <tr>
          <td>IP</td>
          <td>路由、分片、寻址</td>
          <td>—</td>
      </tr>
  </tbody>
</table>
<ol>
<li>HTTPS ≠ 新协议。它只是&quot;HTTP 报文透过 TLS 隧道运输&quot;的俗称。协议栈中依然能看到标准的 HTTP 语法，只是先被 TLS 加密，运输途中看不到明文。</li>
<li>TLS 与 TCP 的独立性。TLS 不关心丢包、重传；这些由 TCP 处理。相反，TCP 不理解证书、握手；这些由 TLS 负责。</li>
</ol>
<h3 id="tls-握手过程">TLS 握手过程</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">客户端                           服务器
</span></span><span class="line"><span class="cl">   |&lt;--- SYN → SYN/ACK → ACK ---&gt;| 建立可靠的 TCP 连接
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   |--- ClientHello (TLS)    ---&gt;| 浏览器发起 TLS 握手，告诉服务器自己支持的协议版本、密码套件、随机数（Client Random）等信息
</span></span><span class="line"><span class="cl">   |&lt;-- ServerHello + 证书     ---|
</span></span><span class="line"><span class="cl">   |--- 密钥协商/Finished -------&gt;|
</span></span><span class="line"><span class="cl">   |&lt;-- Finished ----------------|
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   TLS 信道建立完成
</span></span><span class="line"><span class="cl">   |--- HTTP GET / -------------&gt;|
</span></span><span class="line"><span class="cl">   |&lt;-- HTTP/1.1 200 OK ---------|
</span></span></code></pre></div><pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">sequenceDiagram
  participant Client
  participant Server
  Client-&gt;&gt;Server: ClientHello&lt;br/&gt;– 支持的 TLS 版本、随机数、加密套件
  Server--&gt;&gt;Client: ServerHello&lt;br/&gt;– 选定版本/套件、随机数
  Server--&gt;&gt;Client: Certificate
  Server--&gt;&gt;Client: ServerHelloDone
  Client-&gt;&gt;Server: ClientKeyExchange&lt;br/&gt;– 预主密钥（用公钥加密）
  Client-&gt;&gt;Server: ChangeCipherSpec
  Client-&gt;&gt;Server: Finished（用对称密钥加密）
  Server--&gt;&gt;Client: ChangeCipherSpec
  Server--&gt;&gt;Client: Finished
  note over Client,Server: 之后开始对称加密的数据传输
</code></pre><ul>
<li>双方首先协商算法与随机数</li>
<li>客户端用服务器公钥加密预主密钥</li>
<li>双方基于随机数与预主密钥生成会话密钥</li>
<li>后续通信全部使用对称加密，提高效率</li>
</ul>
<h3 id="tls-13">TLS 1.3</h3>
<p>TLS 1.3（RFC 8446）是在 2018 年 8 月正式发布的最新版本，相比 TLS 1.2 做了深度&quot;瘦身 + 加速 + 强化安全&rdquo;。</p>
<ul>
<li>TLS 1.3 把握手变成 1-RTT 并引入 0-RTT，显著降低延迟。</li>
<li>强制 ECDHE + AEAD + SHA-2，淘汰所有过时与高危算法，默认提供前向保密。</li>
<li>握手更多字段被加密，降低被动监听价值，同时带来运维与抓包新挑战。</li>
<li>配置侧仅需保留少量推荐套件（TLS_AES_128_GCM_SHA256、TLS_CHACHA20_POLY1305_SHA256 等），比 TLS 1.2 更易运维。</li>
</ul>
<h3 id="rtt-vs-0-rtt">RTT vs. 0-RTT</h3>
<p>&ldquo;RTT&quot;本身确实是一个时间量（Round-Trip Time），指数据包从客户端发出、到达服务端，再把响应返回客户端所经历的往返延迟，常用毫秒（ms）表示。</p>
<p>在协议说明里看到的&quot;1-RTT 握手 / 2-RTT 握手&quot;属于约定俗成的缩写：把&quot;一次往返时间&quot;抽象成&quot;一次来回（round trip）&ldquo;这个计数单位来用。</p>
<h3 id="证书与密钥">证书与密钥</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">openssl version
</span></span><span class="line"><span class="cl"><span class="c1"># 看看证书</span>
</span></span><span class="line"><span class="cl">openssl x509 -in localhost.pem -noout -text
</span></span></code></pre></div><p>证书的结构：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Certificate:
</span></span><span class="line"><span class="cl">｜- Data:
</span></span><span class="line"><span class="cl">  ｜- Version: 3
</span></span><span class="line"><span class="cl">  ｜- Serial Number: ...
</span></span><span class="line"><span class="cl">  ｜- Signature Algorithm: sha256WithRSAEncryption
</span></span><span class="line"><span class="cl">  ｜- Issuer: O=mkcert development CA, OU=..., CN=mkcert ...
</span></span><span class="line"><span class="cl">  ｜- Validity:
</span></span><span class="line"><span class="cl">    ｜- Not Before: Jun 27 03:39:39 2025 GMT
</span></span><span class="line"><span class="cl">    ｜- Not After : Sep 27 03:39:39 2027 GMT
</span></span><span class="line"><span class="cl">  ｜- Subject: O=mkcert development CA, OU=...
</span></span><span class="line"><span class="cl">  ｜- Subject Public Key Info: &lt;网站的公钥&gt;
</span></span><span class="line"><span class="cl">    ｜- Public Key Algorithm: rsaEncryption
</span></span><span class="line"><span class="cl">      ｜- Public-Key: (2048 bit)
</span></span><span class="line"><span class="cl">      ｜- Modulus: &lt;模数&gt;
</span></span><span class="line"><span class="cl">      ｜- Exponent: 65537 (0x10001)
</span></span><span class="line"><span class="cl">   |- X509v3 extensions
</span></span><span class="line"><span class="cl">｜- Signature Algorithm: sha256WithRSAEncryption
</span></span><span class="line"><span class="cl">｜- Signature Value: &lt;颁发机构（CA）用自己私钥算出的数字签名，浏览器用 CA 公钥验证&gt;
</span></span></code></pre></div><p>在 RSA 体系里，公钥由两部分组成：</p>
<ul>
<li>n：模数（Modulus）</li>
<li>e：公钥指数（Exponent）</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 导出公钥</span>
</span></span><span class="line"><span class="cl">openssl x509 -in localhost.pem -pubkey -noout &gt; pubkey.pem
</span></span><span class="line"><span class="cl"><span class="c1"># 查看导出的公钥</span>
</span></span><span class="line"><span class="cl">openssl pkey -pubin -in pubkey.pem -text -noout
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 看看私钥里有什么</span>
</span></span><span class="line"><span class="cl">openssl rsa -in localhost-key.pem -text -noout
</span></span></code></pre></div><p>私钥的结构：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Private-Key: (2048 bit, 2 primes)
</span></span><span class="line"><span class="cl"># 模 n = p × q
</span></span><span class="line"><span class="cl">modulus: ...
</span></span><span class="line"><span class="cl"># e 公钥指数
</span></span><span class="line"><span class="cl">publicExponent: 65537 (0x10001)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># m = c^d mod n
</span></span><span class="line"><span class="cl">privateExponent: ...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># p 和 q 是两个大质数
</span></span><span class="line"><span class="cl">prime1: ...
</span></span><span class="line"><span class="cl">prime2: ...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># exponent1 = d mod (p-1)
</span></span><span class="line"><span class="cl">exponent1: ...
</span></span><span class="line"><span class="cl"># exponent2 = d mod (q-1)
</span></span><span class="line"><span class="cl">exponent2: ...
</span></span><span class="line"><span class="cl"># coefficient = inv(q) mod p
</span></span><span class="line"><span class="cl">coefficient: ...
</span></span></code></pre></div><h3 id="http2">HTTP/2</h3>
<p>当前使用的是 HTTP/1.1，如何启用 HTTP/2，请移步 <a href="/2017/06/06/nginx-enable-http2/">NGINX 启用 HTTP/2 ｜ ZYF.IM</a>。</p>
<h3 id="http3">HTTP/3</h3>
<p>请移步 <a href="/2025/06/27/nginx-enable-http3/">NGINX 启用 HTTP/3 ｜ ZYF.IM</a>。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://nginx.org/en/docs/http/ngx_http_ssl_module.html">NGINX HTTPS 模块</a></li>
<li><a href="http://httpforever.com/">http://httpforever.com/</a></li>
<li><a href="http://neverssl.com/">http://neverssl.com/</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP Migrating 8.3 to 8.4</title>
      <link>https://zyf.im/2025/02/01/php-migrating-83-to-84/</link>
      <pubDate>Sat, 01 Feb 2025 17:42:42 +0000</pubDate>
      <guid>https://zyf.im/2025/02/01/php-migrating-83-to-84/</guid>
      <description>&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;PHP 8.4 引入了许多新特性，如属性钩子（Property Hooks）、非对称可见性（Asymmetric Visibility）、&lt;code&gt;#[Deprecated]&lt;/code&gt; 属性以及更新的 DOM API 等。本文将介绍主要变化和如何进行迁移。&lt;/p&gt;
&lt;h3 id=&#34;参考资源&#34;&gt;参考资源&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在线测试环境：&lt;a href=&#34;https://onlinephp.io/&#34;&gt;https://onlinephp.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;官方发布说明：&lt;a href=&#34;https://www.php.net/releases/8.4/en.php&#34;&gt;https://www.php.net/releases/8.4/en.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;迁移指南：&lt;a href=&#34;https://www.php.net/manual/en/migration84.php&#34;&gt;https://www.php.net/manual/en/migration84.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;更新详情：&lt;a href=&#34;https://php.watch/versions/8.4&#34;&gt;https://php.watch/versions/8.4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;php-84-新特性&#34;&gt;PHP 8.4 新特性&lt;/h2&gt;
&lt;h3 id=&#34;属性钩子与非对称可见性&#34;&gt;属性钩子与非对称可见性&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 属性钩子（Property Hooks）与非对称可见性（Asymmetric Visibility）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;User&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$isModified&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$last&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fullName&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;last&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;last&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;explode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;isModified&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Ming&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Li&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;fullName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 输出: Ming Li
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;fullName&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Hong Xiao&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;fullName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 输出: Hong Xiao
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 非对称可见性示例
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 输出: Hong
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Hua&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 错误: Fatal error: Uncaught Error: Cannot modify private(set) property User::$first
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;属性钩子允许我们定义属性的获取和设置行为，而非对称可见性允许我们为属性设置不同的读写权限。在上例中，&lt;code&gt;$first&lt;/code&gt; 属性可以公开读取但只能私有写入。&lt;/p&gt;
&lt;h3 id=&#34;deprecated-属性&#34;&gt;#[Deprecated] 属性&lt;/h3&gt;
&lt;p&gt;PHP 8.4 引入了 &lt;code&gt;#[Deprecated]&lt;/code&gt; 属性，可以用来标记已弃用的代码：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="简介">简介</h2>
<p>PHP 8.4 引入了许多新特性，如属性钩子（Property Hooks）、非对称可见性（Asymmetric Visibility）、<code>#[Deprecated]</code> 属性以及更新的 DOM API 等。本文将介绍主要变化和如何进行迁移。</p>
<h3 id="参考资源">参考资源</h3>
<ul>
<li>在线测试环境：<a href="https://onlinephp.io/">https://onlinephp.io/</a></li>
<li>官方发布说明：<a href="https://www.php.net/releases/8.4/en.php">https://www.php.net/releases/8.4/en.php</a></li>
<li>迁移指南：<a href="https://www.php.net/manual/en/migration84.php">https://www.php.net/manual/en/migration84.php</a></li>
<li>更新详情：<a href="https://php.watch/versions/8.4">https://php.watch/versions/8.4</a></li>
</ul>
<h2 id="php-84-新特性">PHP 8.4 新特性</h2>
<h3 id="属性钩子与非对称可见性">属性钩子与非对称可见性</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 属性钩子（Property Hooks）与非对称可见性（Asymmetric Visibility）
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="nx">bool</span> <span class="nv">$isModified</span> <span class="o">=</span> <span class="k">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span><span class="k">public</span> <span class="k">private</span><span class="p">(</span><span class="nx">set</span><span class="p">)</span> <span class="nx">string</span> <span class="nv">$first</span><span class="p">,</span> <span class="k">private</span> <span class="nx">string</span> <span class="nv">$last</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">string</span> <span class="nv">$fullName</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">get</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">first</span> <span class="o">.</span> <span class="s2">&#34; &#34;</span> <span class="o">.</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">last</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">set</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">[</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">first</span><span class="p">,</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">last</span><span class="p">]</span> <span class="o">=</span> <span class="nx">explode</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">,</span> <span class="nv">$value</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">isModified</span> <span class="o">=</span> <span class="k">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nv">$user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="s2">&#34;Ming&#34;</span><span class="p">,</span> <span class="s2">&#34;Li&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">fullName</span><span class="p">;</span> <span class="c1">// 输出: Ming Li
</span></span></span><span class="line"><span class="cl"><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">fullName</span> <span class="o">=</span> <span class="s2">&#34;Hong Xiao&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">fullName</span><span class="p">;</span> <span class="c1">// 输出: Hong Xiao
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 非对称可见性示例
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">first</span><span class="p">;</span> <span class="c1">// 输出: Hong
</span></span></span><span class="line"><span class="cl"><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">first</span> <span class="o">=</span> <span class="s2">&#34;Hua&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 错误: Fatal error: Uncaught Error: Cannot modify private(set) property User::$first
</span></span></span></code></pre></div><p>属性钩子允许我们定义属性的获取和设置行为，而非对称可见性允许我们为属性设置不同的读写权限。在上例中，<code>$first</code> 属性可以公开读取但只能私有写入。</p>
<h3 id="deprecated-属性">#[Deprecated] 属性</h3>
<p>PHP 8.4 引入了 <code>#[Deprecated]</code> 属性，可以用来标记已弃用的代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">#[Deprecated(&#34;Use newFunction() instead&#34;, since: &#34;2.0&#34;)]
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">oldFunction</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">oldFunction</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Warning: Function oldFunction() is deprecated, Use newFunction() instead
</span></span></span></code></pre></div><p>这使得开发者可以在自己的代码库中使用与 PHP 内部相同的弃用机制。</p>
<h3 id="增强的-dom-api">增强的 DOM API</h3>
<p>PHP 8.4 更新了 DOM API，提供了更现代、更易用的接口：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 新的 DOM API 示例
</span></span></span><span class="line"><span class="cl"><span class="nv">$doc</span> <span class="o">=</span> <span class="nx">\Dom\HTMLDocument</span><span class="o">::</span><span class="na">createFromString</span><span class="p">(</span><span class="s1">&#39;&lt;div&gt;&lt;span&gt;Hello&lt;/span&gt;&lt;/div&gt;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用 querySelector 和 querySelectorAll
</span></span></span><span class="line"><span class="cl"><span class="nv">$span</span> <span class="o">=</span> <span class="nv">$doc</span><span class="o">-&gt;</span><span class="na">querySelector</span><span class="p">(</span><span class="s1">&#39;span&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$span</span><span class="o">-&gt;</span><span class="na">textContent</span><span class="p">;</span> <span class="c1">// 输出: Hello
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用 innerHTML/outerHTML
</span></span></span><span class="line"><span class="cl"><span class="nv">$div</span> <span class="o">=</span> <span class="nv">$doc</span><span class="o">-&gt;</span><span class="na">querySelector</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$div</span><span class="o">-&gt;</span><span class="na">innerHTML</span><span class="p">;</span> <span class="c1">// 输出: &lt;span&gt;Hello&lt;/span&gt;
</span></span></span><span class="line"><span class="cl"><span class="nv">$div</span><span class="o">-&gt;</span><span class="na">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;&lt;p&gt;World&lt;/p&gt;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$div</span><span class="o">-&gt;</span><span class="na">outerHTML</span><span class="p">;</span> <span class="c1">// 输出: &lt;div&gt;&lt;p&gt;World&lt;/p&gt;&lt;/div&gt;
</span></span></span></code></pre></div><h3 id="bcmath-的对象-api">BCMath 的对象 API</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// BCMath 的对象 API
</span></span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">BcMath\Number</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$num1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Number</span><span class="p">(</span><span class="s1">&#39;0.12345&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$num2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Number</span><span class="p">(</span><span class="s1">&#39;2&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nv">$num1</span> <span class="o">+</span> <span class="nv">$num2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$result</span><span class="p">;</span> <span class="c1">// 输出: &#39;2.12345&#39;
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$num1</span> <span class="o">&gt;</span> <span class="nv">$num2</span><span class="p">);</span> <span class="c1">// 输出: false
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.4 之前的写法
</span></span></span><span class="line"><span class="cl"><span class="nv">$num1</span> <span class="o">=</span> <span class="s1">&#39;0.12345&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$num2</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nx">bcadd</span><span class="p">(</span><span class="nv">$num1</span><span class="p">,</span> <span class="nv">$num2</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$result</span><span class="p">;</span> <span class="c1">// 输出: &#39;2.12345&#39;
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">bccomp</span><span class="p">(</span><span class="nv">$num1</span><span class="p">,</span> <span class="nv">$num2</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 输出: false
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 新增 bcdivmod() 函数 - 同时返回商和余数
</span></span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nv">$quotient</span><span class="p">,</span> <span class="nv">$remainder</span><span class="p">]</span> <span class="o">=</span> <span class="nx">bcdivmod</span><span class="p">(</span><span class="s1">&#39;10&#39;</span><span class="p">,</span> <span class="s1">&#39;3&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$quotient</span><span class="p">;</span>  <span class="c1">// 输出: 3
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$remainder</span><span class="p">;</span> <span class="c1">// 输出: 1.00
</span></span></span></code></pre></div><p>PHP 8.4 为 BCMath 引入了对象 API，使用起来更加直观和面向对象。</p>
<h3 id="http3-支持">HTTP/3 支持</h3>
<p>PHP 8.4 的 cURL 扩展新增了 HTTP/3 支持：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$ch</span> <span class="o">=</span> <span class="nx">curl_init</span><span class="p">(</span><span class="s1">&#39;https://example.com&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">curl_setopt</span><span class="p">(</span><span class="nv">$ch</span><span class="p">,</span> <span class="nx">CURLOPT_HTTP_VERSION</span><span class="p">,</span> <span class="nx">CURL_HTTP_VERSION_3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 或强制使用 HTTP/3
</span></span></span><span class="line"><span class="cl"><span class="nx">curl_setopt</span><span class="p">(</span><span class="nv">$ch</span><span class="p">,</span> <span class="nx">CURLOPT_HTTP_VERSION</span><span class="p">,</span> <span class="nx">CURL_HTTP_VERSION_3ONLY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$response</span> <span class="o">=</span> <span class="nx">curl_exec</span><span class="p">(</span><span class="nv">$ch</span><span class="p">);</span>
</span></span></code></pre></div><p>其他新增的 cURL 选项包括：<code>CURLOPT_PREREQFUNCTION</code>、<code>CURLOPT_DEBUGFUNCTION</code>、<code>CURLOPT_SERVER_RESPONSE_TIMEOUT</code>、<code>CURLOPT_TCP_KEEPCNT</code>。</p>
<h2 id="php-84-新增函数">PHP 8.4 新增函数</h2>
<h3 id="新的数组操作函数">新的数组操作函数</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// array_find() - 查找第一个满足条件的元素
</span></span></span><span class="line"><span class="cl"><span class="nv">$animal</span> <span class="o">=</span> <span class="nx">array_find</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="s1">&#39;dog&#39;</span><span class="p">,</span> <span class="s1">&#39;cat&#39;</span><span class="p">,</span> <span class="s1">&#39;cow&#39;</span><span class="p">,</span> <span class="s1">&#39;duck&#39;</span><span class="p">,</span> <span class="s1">&#39;goose&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="nx">fn</span> <span class="p">(</span><span class="nx">string</span> <span class="nv">$value</span><span class="p">)</span><span class="o">:</span> <span class="nx">bool</span> <span class="o">=&gt;</span> <span class="nx">str_starts_with</span><span class="p">(</span><span class="nv">$value</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$animal</span><span class="p">);</span> <span class="c1">// 输出: string(3) &#34;cat&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// array_find_key() - 查找第一个满足条件的元素的键
</span></span></span><span class="line"><span class="cl"><span class="nv">$key</span> <span class="o">=</span> <span class="nx">array_find_key</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="s1">&#39;a&#39;</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fn</span><span class="p">(</span><span class="nv">$v</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nv">$v</span> <span class="o">&gt;</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$key</span><span class="p">);</span> <span class="c1">// 输出: string(1) &#34;b&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// array_any() - 检查是否至少有一个元素满足条件
</span></span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nx">array_any</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span> <span class="nx">fn</span><span class="p">(</span><span class="nv">$n</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nv">$n</span> <span class="o">&gt;</span> <span class="mi">3</span><span class="p">);</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// array_all() - 检查是否所有元素都满足条件
</span></span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nx">array_all</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span> <span class="nx">fn</span><span class="p">(</span><span class="nv">$n</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nv">$n</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// true
</span></span></span></code></pre></div><h3 id="新的字符串处理函数">新的字符串处理函数</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 多字节字符串修剪函数
</span></span></span><span class="line"><span class="cl"><span class="nv">$str</span> <span class="o">=</span> <span class="s2">&#34;　こんにちは　&#34;</span><span class="p">;</span> <span class="c1">// 包含全角空格
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">mb_trim</span><span class="p">(</span><span class="nv">$str</span><span class="p">);</span>   <span class="c1">// &#34;こんにちは&#34;
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">mb_ltrim</span><span class="p">(</span><span class="nv">$str</span><span class="p">);</span>  <span class="c1">// &#34;こんにちは　&#34;
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">mb_rtrim</span><span class="p">(</span><span class="nv">$str</span><span class="p">);</span>  <span class="c1">// &#34;　こんにちは&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 多字节首字母大小写转换
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">mb_ucfirst</span><span class="p">(</span><span class="s1">&#39;ñoño&#39;</span><span class="p">);</span> <span class="c1">// &#34;Ñoño&#34;
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">mb_lcfirst</span><span class="p">(</span><span class="s1">&#39;ÑoÑo&#39;</span><span class="p">);</span> <span class="c1">// &#34;ñoÑo&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 按字形簇拆分字符串
</span></span></span><span class="line"><span class="cl"><span class="nv">$chars</span> <span class="o">=</span> <span class="nx">grapheme_str_split</span><span class="p">(</span><span class="s1">&#39;👨‍👩‍👧‍👦&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$chars</span><span class="p">);</span> <span class="c1">// [&#39;👨‍👩‍👧‍👦&#39;]
</span></span></span></code></pre></div><h3 id="日期时间新方法">日期时间新方法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 从时间戳创建 DateTime 对象
</span></span></span><span class="line"><span class="cl"><span class="nv">$dt</span> <span class="o">=</span> <span class="nx">DateTime</span><span class="o">::</span><span class="na">createFromTimestamp</span><span class="p">(</span><span class="mi">1704067200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$dti</span> <span class="o">=</span> <span class="nx">DateTimeImmutable</span><span class="o">::</span><span class="na">createFromTimestamp</span><span class="p">(</span><span class="mf">1704067200.123456</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 微秒处理
</span></span></span><span class="line"><span class="cl"><span class="nv">$dt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DateTime</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$dt</span><span class="o">-&gt;</span><span class="na">getMicrosecond</span><span class="p">();</span> <span class="c1">// 获取微秒
</span></span></span><span class="line"><span class="cl"><span class="nv">$dt</span><span class="o">-&gt;</span><span class="na">setMicrosecond</span><span class="p">(</span><span class="mi">500000</span><span class="p">);</span> <span class="c1">// 设置微秒
</span></span></span></code></pre></div><h3 id="round-函数新舍入模式">round() 函数新舍入模式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.4 新增舍入模式
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">round</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">PHP_ROUND_HALF_UP</span><span class="p">);</span>       <span class="c1">// 2 (四舍五入)
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">round</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">PHP_ROUND_HALF_DOWN</span><span class="p">);</span>     <span class="c1">// 1 (五舍六入)
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">round</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">PHP_ROUND_HALF_EVEN</span><span class="p">);</span>     <span class="c1">// 2 (银行家舍入)
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">round</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">PHP_ROUND_HALF_ODD</span><span class="p">);</span>      <span class="c1">// 1 (舍入到奇数)
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">round</span><span class="p">(</span><span class="mf">1.2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">PHP_ROUND_CEILING</span><span class="p">);</span>       <span class="c1">// 2 (向上取整)
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">round</span><span class="p">(</span><span class="mf">1.8</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">PHP_ROUND_FLOOR</span><span class="p">);</span>         <span class="c1">// 1 (向下取整)
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">round</span><span class="p">(</span><span class="o">-</span><span class="mf">1.5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">PHP_ROUND_TOWARD_ZERO</span><span class="p">);</span>  <span class="c1">// -1 (向零取整)
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">round</span><span class="p">(</span><span class="o">-</span><span class="mf">1.5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">PHP_ROUND_AWAY_FROM_ZERO</span><span class="p">);</span> <span class="c1">// -2 (远离零取整)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 注意：无效的舍入模式现在会抛出 ValueError
</span></span></span></code></pre></div><h3 id="其他新函数">其他新函数</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// request_parse_body() - 解析请求体
</span></span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nv">$_POST</span><span class="p">,</span> <span class="nv">$_FILES</span><span class="p">]</span> <span class="o">=</span> <span class="nx">request_parse_body</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// HTTP 响应头处理
</span></span></span><span class="line"><span class="cl"><span class="nv">$headers</span> <span class="o">=</span> <span class="nx">http_get_last_response_headers</span><span class="p">();</span> <span class="c1">// 获取最后一次响应头
</span></span></span><span class="line"><span class="cl"><span class="nx">http_clear_last_response_headers</span><span class="p">();</span> <span class="c1">// 清除响应头缓存
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// IANA 时区 ID
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">intltz_get_iana_id</span><span class="p">(</span><span class="s1">&#39;Asia/Shanghai&#39;</span><span class="p">);</span> <span class="c1">// 获取规范时区 ID
</span></span></span></code></pre></div><h2 id="兼容性与迁移">兼容性与迁移</h2>
<h3 id="弃用特性">弃用特性</h3>
<h4 id="隐式可空类型">隐式可空类型</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ PHP 8.4 已弃用
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">save</span><span class="p">(</span><span class="nx">Book</span> <span class="nv">$book</span> <span class="o">=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 修复方法：显式声明可空类型
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">save</span><span class="p">(</span><span class="o">?</span><span class="nx">Book</span> <span class="nv">$book</span> <span class="o">=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{}</span>
</span></span></code></pre></div><p>在 PHP 8.4 中，隐式可空类型已被弃用，需要使用问号（<code>?</code>）显式声明可空类型。</p>
<h4 id="其他弃用">其他弃用</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ E_STRICT 常量已弃用
</span></span></span><span class="line"><span class="cl"><span class="nx">error_reporting</span><span class="p">(</span><span class="k">E_ALL</span> <span class="o">&amp;</span> <span class="o">~</span><span class="nx">E_STRICT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ session_set_save_handler() 超过 2 个参数已弃用
</span></span></span><span class="line"><span class="cl"><span class="nx">session_set_save_handler</span><span class="p">(</span><span class="nv">$open</span><span class="p">,</span> <span class="nv">$close</span><span class="p">,</span> <span class="nv">$read</span><span class="p">,</span> <span class="nv">$write</span><span class="p">,</span> <span class="nv">$destroy</span><span class="p">,</span> <span class="nv">$gc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用 SessionHandlerInterface
</span></span></span><span class="line"><span class="cl"><span class="nx">session_set_save_handler</span><span class="p">(</span><span class="k">new</span> <span class="nx">MySessionHandler</span><span class="p">(),</span> <span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ CSV 函数的 $escape 参数默认值已弃用，需显式传递
</span></span></span><span class="line"><span class="cl"><span class="nx">fgetcsv</span><span class="p">(</span><span class="nv">$handle</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;,&#39;</span><span class="p">,</span> <span class="s1">&#39;&#34;&#39;</span><span class="p">);</span> <span class="c1">// 需要传递第四个参数
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ CURLOPT_BINARYTRANSFER 已弃用（实际上从未有任何效果）
</span></span></span></code></pre></div><h3 id="行为变化">行为变化</h3>
<h4 id="exitdie-变为函数">exit/die 变为函数</h4>
<p><code>exit</code> 和 <code>die</code> 从语言结构变为函数，这意味着：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 现在可以作为 callable 使用
</span></span></span><span class="line"><span class="cl"><span class="nv">$callback</span> <span class="o">=</span> <span class="k">exit</span><span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 参数类型更严格
</span></span></span><span class="line"><span class="cl"><span class="k">exit</span><span class="p">(</span><span class="k">new</span> <span class="k">stdClass</span><span class="p">());</span> <span class="c1">// TypeError: exit(): Argument #1 ($status) must be of type string|int
</span></span></span></code></pre></div><h4 id="常量类型变化">常量类型变化</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP_ZTS 和 PHP_DEBUG 从 int 变为 bool
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">PHP_ZTS</span><span class="p">);</span>   <span class="c1">// bool(true) 或 bool(false)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">PHP_DEBUG</span><span class="p">);</span> <span class="c1">// bool(true) 或 bool(false)
</span></span></span></code></pre></div><h3 id="已移除的扩展">已移除的扩展</h3>
<p>以下扩展从 PHP 核心移至 PECL：</p>
<ul>
<li><strong>Pspell</strong> - 拼写检查扩展</li>
<li><strong>IMAP</strong> - 邮件协议扩展</li>
<li><strong>OCI8</strong> - Oracle 数据库扩展</li>
<li><strong>PDO-OCI</strong> - Oracle PDO 驱动</li>
</ul>
<p>如需继续使用，需从 PECL 安装。</p>
<h3 id="最低版本要求">最低版本要求</h3>
<ul>
<li><strong>OpenSSL</strong>：最低版本 1.1.1</li>
<li><strong>libcurl</strong>：最低版本 7.61.0</li>
<li><strong>Bcrypt</strong>：密码哈希默认 cost 从 10 提升到 12</li>
</ul>
<h2 id="总结">总结</h2>
<p>PHP 8.4 带来了许多实用的新特性和改进：</p>
<p><strong>主要新特性：</strong></p>
<ul>
<li>属性钩子和非对称可见性</li>
<li><code>#[Deprecated]</code> 属性</li>
<li>现代化的 DOM API</li>
<li>BCMath 对象 API</li>
<li>HTTP/3 支持</li>
<li>新的数组和字符串函数</li>
</ul>
<p><strong>需要注意的变化：</strong></p>
<ul>
<li>隐式可空类型已弃用</li>
<li><code>exit</code>/<code>die</code> 变为函数</li>
<li>部分扩展移至 PECL</li>
<li>依赖库最低版本要求提升</li>
</ul>
<p>通过了解这些变化并遵循迁移建议，可以顺利地从 PHP 8.3 迁移到 PHP 8.4。</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP Migrating 8.2 to 8.3</title>
      <link>https://zyf.im/2024/08/01/php-migrating-82-to-83/</link>
      <pubDate>Thu, 01 Aug 2024 17:37:13 +0000</pubDate>
      <guid>https://zyf.im/2024/08/01/php-migrating-82-to-83/</guid>
      <description>&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;PHP 8.3 引入了许多新特性，包括类型化类常量（Typed Class Constants）、&lt;code&gt;#[\Override]&lt;/code&gt; 属性、&lt;code&gt;json_validate()&lt;/code&gt; 函数、只读属性的深度克隆等。本文将介绍主要变化和如何从 PHP 8.2 迁移到 PHP 8.3。&lt;/p&gt;
&lt;h3 id=&#34;参考资源&#34;&gt;参考资源&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在线测试环境：&lt;a href=&#34;https://onlinephp.io/&#34;&gt;https://onlinephp.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;官方发布说明：&lt;a href=&#34;https://www.php.net/releases/8.3/en.php&#34;&gt;https://www.php.net/releases/8.3/en.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;迁移指南：&lt;a href=&#34;https://www.php.net/manual/en/migration83.php&#34;&gt;https://www.php.net/manual/en/migration83.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;更新详情：&lt;a href=&#34;https://php.watch/versions/8.3&#34;&gt;https://php.watch/versions/8.3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;php-83-新特性&#34;&gt;PHP 8.3 新特性&lt;/h2&gt;
&lt;h3 id=&#34;类型化类常量typed-class-constants&#34;&gt;类型化类常量（Typed Class Constants）&lt;/h3&gt;
&lt;p&gt;PHP 8.3 允许为类常量声明类型，增强了类型安全性：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DatabaseInterface&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ENGINE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;mysql&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;trait&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ConfigTrait&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MAX_CONNECTIONS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;DEFAULT&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Pending&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Active&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;active&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Database&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DatabaseInterface&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ConfigTrait&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 类型遵循 LSP（里氏替换原则），子类可以收窄类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ENGINE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;postgresql&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;类常量类型遵循里氏替换原则（LSP），子类可以收窄父类常量的类型：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ParentClass&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;VALUE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;MyValue&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ChildClass&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ParentClass&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ✅ 可以将 string|int 收窄为 string
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;VALUE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;MyValue&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;override-属性&#34;&gt;#[\Override] 属性&lt;/h3&gt;
&lt;p&gt;新的 &lt;code&gt;#[\Override]&lt;/code&gt; 属性确保方法确实覆盖了父类方法，有助于捕获拼写错误和简化重构：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ParentClass&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setUp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ChildClass&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ParentClass&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#[\Override]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setUp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// ✅ 正确覆盖
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#[\Override]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setUpP&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// ❌ Fatal error: 父类没有该方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这在重构时特别有用 - 如果父类方法被重命名或删除，PHP 会立即报错而不是静默失败。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="简介">简介</h2>
<p>PHP 8.3 引入了许多新特性，包括类型化类常量（Typed Class Constants）、<code>#[\Override]</code> 属性、<code>json_validate()</code> 函数、只读属性的深度克隆等。本文将介绍主要变化和如何从 PHP 8.2 迁移到 PHP 8.3。</p>
<h3 id="参考资源">参考资源</h3>
<ul>
<li>在线测试环境：<a href="https://onlinephp.io/">https://onlinephp.io/</a></li>
<li>官方发布说明：<a href="https://www.php.net/releases/8.3/en.php">https://www.php.net/releases/8.3/en.php</a></li>
<li>迁移指南：<a href="https://www.php.net/manual/en/migration83.php">https://www.php.net/manual/en/migration83.php</a></li>
<li>更新详情：<a href="https://php.watch/versions/8.3">https://php.watch/versions/8.3</a></li>
</ul>
<h2 id="php-83-新特性">PHP 8.3 新特性</h2>
<h3 id="类型化类常量typed-class-constants">类型化类常量（Typed Class Constants）</h3>
<p>PHP 8.3 允许为类常量声明类型，增强了类型安全性：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">interface</span> <span class="nx">DatabaseInterface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">string</span> <span class="nx">ENGINE</span> <span class="o">=</span> <span class="s1">&#39;mysql&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">trait</span> <span class="nx">ConfigTrait</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">final</span> <span class="k">protected</span> <span class="k">const</span> <span class="no">int</span> <span class="nx">MAX_CONNECTIONS</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">enum</span> <span class="nx">Status</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">string</span> <span class="k">DEFAULT</span> <span class="o">=</span> <span class="s1">&#39;pending&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Pending</span> <span class="o">=</span> <span class="s1">&#39;pending&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Active</span> <span class="o">=</span> <span class="s1">&#39;active&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Database</span> <span class="k">implements</span> <span class="nx">DatabaseInterface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">use</span> <span class="nx">ConfigTrait</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 类型遵循 LSP（里氏替换原则），子类可以收窄类型
</span></span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">string</span> <span class="nx">ENGINE</span> <span class="o">=</span> <span class="s1">&#39;postgresql&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>类常量类型遵循里氏替换原则（LSP），子类可以收窄父类常量的类型：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ParentClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">string</span><span class="o">|</span><span class="nx">int</span> <span class="nx">VALUE</span> <span class="o">=</span> <span class="s1">&#39;MyValue&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ChildClass</span> <span class="k">extends</span> <span class="nx">ParentClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ✅ 可以将 string|int 收窄为 string
</span></span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">string</span> <span class="nx">VALUE</span> <span class="o">=</span> <span class="s1">&#39;MyValue&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="override-属性">#[\Override] 属性</h3>
<p>新的 <code>#[\Override]</code> 属性确保方法确实覆盖了父类方法，有助于捕获拼写错误和简化重构：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ParentClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">function</span> <span class="nf">setUp</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ChildClass</span> <span class="k">extends</span> <span class="nx">ParentClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#[\Override]
</span></span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">function</span> <span class="nf">setUp</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{}</span> <span class="c1">// ✅ 正确覆盖
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">#[\Override]
</span></span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">function</span> <span class="nf">setUpP</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{}</span> <span class="c1">// ❌ Fatal error: 父类没有该方法
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这在重构时特别有用 - 如果父类方法被重命名或删除，PHP 会立即报错而不是静默失败。</p>
<h3 id="动态类常量和枚举成员访问">动态类常量和枚举成员访问</h3>
<p>PHP 8.3 支持使用变量语法访问类常量，无需使用 <code>constant()</code> 函数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="no">DATABASE</span> <span class="o">=</span> <span class="s1">&#39;mysql&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="no">CACHE</span> <span class="o">=</span> <span class="s1">&#39;redis&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$key</span> <span class="o">=</span> <span class="s1">&#39;DATABASE&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.3 之前
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">constant</span><span class="p">(</span><span class="nx">Config</span><span class="o">::</span><span class="na">class</span> <span class="o">.</span> <span class="s2">&#34;::</span><span class="si">{</span><span class="nv">$key</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.3 - 新语法
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">Config</span><span class="o">::</span><span class="p">{</span><span class="nv">$key</span><span class="p">};</span> <span class="c1">// 输出: mysql
</span></span></span></code></pre></div><p>同样适用于枚举：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">enum</span> <span class="nx">Color</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Red</span> <span class="o">=</span> <span class="s1">&#39;#FF0000&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Green</span> <span class="o">=</span> <span class="s1">&#39;#00FF00&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$color</span> <span class="o">=</span> <span class="s1">&#39;Red&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">Color</span><span class="o">::</span><span class="p">{</span><span class="nv">$color</span><span class="p">}</span><span class="o">-&gt;</span><span class="na">value</span><span class="p">;</span> <span class="c1">// 输出: #FF0000
</span></span></span></code></pre></div><h3 id="只读属性的深度克隆">只读属性的深度克隆</h3>
<p>PHP 8.3 允许在 <code>__clone()</code> 方法中重新初始化只读属性，解决了深度克隆的问题：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">readonly</span> <span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">string</span> <span class="nv">$name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">DateTime</span> <span class="nv">$createdAt</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__clone</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// PHP 8.3 允许在 __clone() 中修改只读属性
</span></span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">createdAt</span> <span class="o">=</span> <span class="k">clone</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">createdAt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$user1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="s1">&#39;Alice&#39;</span><span class="p">,</span> <span class="k">new</span> <span class="nx">DateTime</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="nv">$user2</span> <span class="o">=</span> <span class="k">clone</span> <span class="nv">$user1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// $user2-&gt;createdAt 现在是独立的副本
</span></span></span></code></pre></div><h3 id="json_validate-函数">json_validate() 函数</h3>
<p>新的 <code>json_validate()</code> 函数用于验证 JSON 字符串语法，比 <code>json_decode()</code> 更高效：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 验证 JSON 语法
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">json_validate</span><span class="p">(</span><span class="s1">&#39;{&#34;name&#34;: &#34;PHP&#34;, &#34;version&#34;: 8.3}&#39;</span><span class="p">));</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">json_validate</span><span class="p">(</span><span class="s1">&#39;{invalid json}&#39;</span><span class="p">));</span> <span class="c1">// false
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">json_validate</span><span class="p">(</span><span class="s1">&#39;[1, 2, 3]&#39;</span><span class="p">));</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 相比 json_decode()，不需要解码完整内容，更节省内存
</span></span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">json_validate</span><span class="p">(</span><span class="nv">$jsonString</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$data</span> <span class="o">=</span> <span class="nx">json_decode</span><span class="p">(</span><span class="nv">$jsonString</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="random-扩展增强">Random 扩展增强</h3>
<p>PHP 8.3 为 Random 扩展增加了新方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$randomizer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Random\Randomizer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// getFloat() - 生成指定范围的随机浮点数
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$randomizer</span><span class="o">-&gt;</span><span class="na">getFloat</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span> <span class="c1">// 例如: 6.7810757668383
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用边界类型
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$randomizer</span><span class="o">-&gt;</span><span class="na">getFloat</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">10</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Random\IntervalBoundary</span><span class="o">::</span><span class="na">ClosedOpen</span>  <span class="c1">// [0, 10)
</span></span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// nextFloat() - 生成 [0, 1) 范围的随机浮点数
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$randomizer</span><span class="o">-&gt;</span><span class="na">nextFloat</span><span class="p">();</span> <span class="c1">// 例如: 0.21185336351144
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// getBytesFromString() - 从指定字符集生成随机字符串
</span></span></span><span class="line"><span class="cl"><span class="nv">$chars</span> <span class="o">=</span> <span class="s1">&#39;abcdefghijklmnopqrstuvwxyz0123456789&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$randomizer</span><span class="o">-&gt;</span><span class="na">getBytesFromString</span><span class="p">(</span><span class="nv">$chars</span><span class="p">,</span> <span class="mi">16</span><span class="p">);</span> <span class="c1">// 例如: a7b3x9k2m5n8p1q4
</span></span></span></code></pre></div><h3 id="匿名类可以是只读的">匿名类可以是只读的</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">readonly</span> <span class="k">class</span> <span class="err">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">string</span> <span class="nv">$name</span> <span class="o">=</span> <span class="s1">&#39;Anonymous&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$instance</span><span class="o">-&gt;</span><span class="na">name</span><span class="p">;</span> <span class="c1">// Anonymous
</span></span></span></code></pre></div><h3 id="ini-环境变量回退值">INI 环境变量回退值</h3>
<p>PHP 8.3 支持在 INI 文件中为环境变量设置回退值：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1">; 如果 DB_HOST 环境变量未设置，使用 localhost 作为默认值</span>
</span></span><span class="line"><span class="cl"><span class="na">database.host</span> <span class="o">=</span> <span class="s">${DB_HOST:-localhost}</span>
</span></span></code></pre></div><h2 id="php-83-新增函数">PHP 8.3 新增函数</h2>
<h3 id="mb_str_pad---多字节字符串填充">mb_str_pad() - 多字节字符串填充</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 填充多字节字符串到指定长度
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">mb_str_pad</span><span class="p">(</span><span class="s1">&#39;中国&#39;</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="s1">&#39;爱&#39;</span><span class="p">,</span> <span class="nx">STR_PAD_RIGHT</span><span class="p">)</span> <span class="o">.</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span> <span class="c1">// 中国爱爱爱爱
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">mb_str_pad</span><span class="p">(</span><span class="s1">&#39;中国&#39;</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="s1">&#39;爱&#39;</span><span class="p">,</span> <span class="nx">STR_PAD_LEFT</span><span class="p">)</span> <span class="o">.</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>  <span class="c1">// 爱爱爱爱中国
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">mb_str_pad</span><span class="p">(</span><span class="s1">&#39;中国人&#39;</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="s1">&#39;爱&#39;</span><span class="p">,</span> <span class="nx">STR_PAD_BOTH</span><span class="p">)</span> <span class="o">.</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span> <span class="c1">// 爱中国人爱爱
</span></span></span></code></pre></div><h3 id="str_increment-和-str_decrement">str_increment() 和 str_decrement()</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 字符串递增
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">str_increment</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">);</span>   <span class="c1">// &#39;b&#39;
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">str_increment</span><span class="p">(</span><span class="s1">&#39;z&#39;</span><span class="p">);</span>   <span class="c1">// &#39;aa&#39;
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">str_increment</span><span class="p">(</span><span class="s1">&#39;A9&#39;</span><span class="p">);</span>  <span class="c1">// &#39;B0&#39;
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">str_increment</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">);</span> <span class="c1">// &#39;fop&#39;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 字符串递减
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">str_decrement</span><span class="p">(</span><span class="s1">&#39;b&#39;</span><span class="p">);</span>   <span class="c1">// &#39;a&#39;
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">str_decrement</span><span class="p">(</span><span class="s1">&#39;aa&#39;</span><span class="p">);</span>  <span class="c1">// &#39;z&#39;
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">str_decrement</span><span class="p">(</span><span class="s1">&#39;B0&#39;</span><span class="p">);</span>  <span class="c1">// &#39;A9&#39;
</span></span></span></code></pre></div><h3 id="stream_context_set_options">stream_context_set_options()</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 设置流上下文选项
</span></span></span><span class="line"><span class="cl"><span class="nv">$context</span> <span class="o">=</span> <span class="nx">stream_context_create</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">stream_context_set_options</span><span class="p">(</span><span class="nv">$context</span><span class="p">,</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;http&#39;</span> <span class="o">=&gt;</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;method&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;header&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;Accept: application/json&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">]);</span>
</span></span></code></pre></div><h3 id="datetime-新方法">DateTime 新方法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 从 DateTime 创建 DateTimeImmutable
</span></span></span><span class="line"><span class="cl"><span class="nv">$dt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DateTime</span><span class="p">(</span><span class="s1">&#39;2024-01-01&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$immutable</span> <span class="o">=</span> <span class="nx">DateTimeImmutable</span><span class="o">::</span><span class="na">createFromMutable</span><span class="p">(</span><span class="nv">$dt</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// IntlCalendar 新方法
</span></span></span><span class="line"><span class="cl"><span class="nv">$calendar</span> <span class="o">=</span> <span class="nx">IntlCalendar</span><span class="o">::</span><span class="na">createInstance</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$calendar</span><span class="o">-&gt;</span><span class="na">setDate</span><span class="p">(</span><span class="mi">2024</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">15</span><span class="p">);</span>           <span class="c1">// 设置日期
</span></span></span><span class="line"><span class="cl"><span class="nv">$calendar</span><span class="o">-&gt;</span><span class="na">setDateTime</span><span class="p">(</span><span class="mi">2024</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 设置日期时间
</span></span></span></code></pre></div><h3 id="dom-扩展新方法">DOM 扩展新方法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$doc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DOMDocument</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$doc</span><span class="o">-&gt;</span><span class="na">loadHTML</span><span class="p">(</span><span class="s1">&#39;&lt;div class=&#34;box&#34; id=&#34;main&#34;&gt;Hello&lt;/div&gt;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$element</span> <span class="o">=</span> <span class="nv">$doc</span><span class="o">-&gt;</span><span class="na">getElementById</span><span class="p">(</span><span class="s1">&#39;main&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取所有属性名
</span></span></span><span class="line"><span class="cl"><span class="nv">$names</span> <span class="o">=</span> <span class="nv">$element</span><span class="o">-&gt;</span><span class="na">getAttributeNames</span><span class="p">();</span> <span class="c1">// [&#39;class&#39;, &#39;id&#39;]
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 切换属性
</span></span></span><span class="line"><span class="cl"><span class="nv">$element</span><span class="o">-&gt;</span><span class="na">toggleAttribute</span><span class="p">(</span><span class="s1">&#39;hidden&#39;</span><span class="p">);</span> <span class="c1">// 添加 hidden 属性
</span></span></span><span class="line"><span class="cl"><span class="nv">$element</span><span class="o">-&gt;</span><span class="na">toggleAttribute</span><span class="p">(</span><span class="s1">&#39;hidden&#39;</span><span class="p">);</span> <span class="c1">// 移除 hidden 属性
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 检查包含关系
</span></span></span><span class="line"><span class="cl"><span class="nv">$parent</span> <span class="o">=</span> <span class="nv">$doc</span><span class="o">-&gt;</span><span class="na">documentElement</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$parent</span><span class="o">-&gt;</span><span class="na">contains</span><span class="p">(</span><span class="nv">$element</span><span class="p">));</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取根节点
</span></span></span><span class="line"><span class="cl"><span class="nv">$root</span> <span class="o">=</span> <span class="nv">$element</span><span class="o">-&gt;</span><span class="na">getRootNode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 比较节点
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$element</span><span class="o">-&gt;</span><span class="na">isEqualNode</span><span class="p">(</span><span class="nv">$element</span><span class="p">));</span> <span class="c1">// true
</span></span></span></code></pre></div><h3 id="命令行-linter-改进">命令行 Linter 改进</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># PHP 8.3 支持同时检查多个文件</span>
</span></span><span class="line"><span class="cl">php -l file1.php file2.php file3.php
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 输出:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># No syntax errors detected in file1.php</span>
</span></span><span class="line"><span class="cl"><span class="c1"># No syntax errors detected in file2.php</span>
</span></span><span class="line"><span class="cl"><span class="c1"># No syntax errors detected in file3.php</span>
</span></span></code></pre></div><h2 id="弃用特性">弃用特性</h2>
<h3 id="get_class-和-get_parent_class-无参数调用">get_class() 和 get_parent_class() 无参数调用</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">getName</span><span class="p">()</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ❌ PHP 8.3 已弃用
</span></span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">get_class</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// ✅ 使用 $this 或 self
</span></span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">get_class</span><span class="p">(</span><span class="nv">$this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 或
</span></span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">self</span><span class="o">::</span><span class="na">class</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">getParentName</span><span class="p">()</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ❌ PHP 8.3 已弃用
</span></span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">get_parent_class</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// ✅ 使用 $this 或 self
</span></span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">get_parent_class</span><span class="p">(</span><span class="nv">$this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 或
</span></span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">parent</span><span class="o">::</span><span class="na">class</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="assert-相关设置">Assert 相关设置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 以下 INI 设置已弃用
</span></span></span><span class="line"><span class="cl"><span class="c1">// assert.active
</span></span></span><span class="line"><span class="cl"><span class="c1">// assert.bail
</span></span></span><span class="line"><span class="cl"><span class="c1">// assert.callback
</span></span></span><span class="line"><span class="cl"><span class="c1">// assert.exception
</span></span></span><span class="line"><span class="cl"><span class="c1">// assert.warning
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ assert_options() 函数已弃用
</span></span></span><span class="line"><span class="cl"><span class="nx">assert_options</span><span class="p">(</span><span class="nx">ASSERT_ACTIVE</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用 ini_set() 替代
</span></span></span><span class="line"><span class="cl"><span class="nx">ini_set</span><span class="p">(</span><span class="s1">&#39;zend.assertions&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="mt_rand_php-变体">MT_RAND_PHP 变体</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ PHP 8.3 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nx">mt_srand</span><span class="p">(</span><span class="mi">1234</span><span class="p">,</span> <span class="nx">MT_RAND_PHP</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用默认的 Mersenne Twister 实现
</span></span></span><span class="line"><span class="cl"><span class="nx">mt_srand</span><span class="p">(</span><span class="mi">1234</span><span class="p">,</span> <span class="nx">MT_RAND_MT19937</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="兼容性变化">兼容性变化</h2>
<h3 id="空数组负索引行为变化">空数组负索引行为变化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$arr</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$arr</span><span class="p">[</span><span class="o">-</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;a&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$arr</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">&#39;b&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$arr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.3:    [-5 =&gt; &#39;a&#39;, -4 =&gt; &#39;b&#39;]
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP &lt; 8.3: [-5 =&gt; &#39;a&#39;, 0 =&gt; &#39;b&#39;]
</span></span></span></code></pre></div><h3 id="range-函数行为变化">range() 函数行为变化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.3 更严格地处理参数类型
</span></span></span><span class="line"><span class="cl"><span class="nx">range</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;z&#39;</span><span class="p">);</span>           <span class="c1">// ✅ 正常
</span></span></span><span class="line"><span class="cl"><span class="nx">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>              <span class="c1">// ✅ 正常
</span></span></span><span class="line"><span class="cl"><span class="nx">range</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="mf">5.5</span><span class="p">);</span>           <span class="c1">// ✅ 正常
</span></span></span><span class="line"><span class="cl"><span class="nx">range</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>             <span class="c1">// ⚠️ PHP 8.3 会产生警告
</span></span></span></code></pre></div><h3 id="sqlite3-默认错误模式">SQLite3 默认错误模式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.3 中 SQLite3 默认使用异常模式
</span></span></span><span class="line"><span class="cl"><span class="nv">$db</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">SQLite3</span><span class="p">(</span><span class="s1">&#39;:memory:&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 错误现在会抛出异常而不是返回 false
</span></span></span></code></pre></div><h3 id="datetime-异常类型细化">DateTime 异常类型细化</h3>
<p>PHP 8.3 为 DateTime 相关操作引入了更具体的异常类型：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="nx">DateTime</span><span class="p">(</span><span class="s1">&#39;invalid date&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">DateMalformedStringException</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// PHP 8.3 新增的具体异常类型
</span></span></span><span class="line"><span class="cl">    <span class="k">echo</span> <span class="nv">$e</span><span class="o">-&gt;</span><span class="na">getMessage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="unserialize-错误级别提升">unserialize() 错误级别提升</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.3 将 E_NOTICE 提升为 E_WARNING
</span></span></span><span class="line"><span class="cl"><span class="c1">// 反序列化时的错误现在产生警告而不是通知
</span></span></span><span class="line"><span class="cl"><span class="nv">$data</span> <span class="o">=</span> <span class="nx">unserialize</span><span class="p">(</span><span class="s1">&#39;invalid&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="其他改进">其他改进</h2>
<h3 id="gc_status-返回更多信息">gc_status() 返回更多信息</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$status</span> <span class="o">=</span> <span class="nx">gc_status</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.3 返回额外的垃圾回收信息
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$status</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 包含 running, protected, full 等新字段
</span></span></span></code></pre></div><h3 id="类别名支持内置类">类别名支持内置类</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.3 允许为内置类创建别名
</span></span></span><span class="line"><span class="cl"><span class="nx">class_alias</span><span class="p">(</span><span class="s1">&#39;stdClass&#39;</span><span class="p">,</span> <span class="s1">&#39;MyStdClass&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MyStdClass</span><span class="p">();</span>
</span></span></code></pre></div><h3 id="openssl-ec-密钥生成">OpenSSL EC 密钥生成</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 使用自定义参数生成 EC 密钥
</span></span></span><span class="line"><span class="cl"><span class="nv">$config</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;curve_name&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;prime256v1&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;private_key_type&#39;</span> <span class="o">=&gt;</span> <span class="nx">OPENSSL_KEYTYPE_EC</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$key</span> <span class="o">=</span> <span class="nx">openssl_pkey_new</span><span class="p">(</span><span class="nv">$config</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>PHP 8.3 带来了许多实用的新特性和改进：</p>
<p><strong>主要新特性：</strong></p>
<ul>
<li>类型化类常量</li>
<li><code>#[\Override]</code> 属性</li>
<li>动态类常量访问</li>
<li>只读属性的深度克隆</li>
<li><code>json_validate()</code> 函数</li>
<li>Random 扩展增强</li>
<li><code>mb_str_pad()</code> 函数</li>
</ul>
<p><strong>需要注意的变化：</strong></p>
<ul>
<li>空数组负索引行为变化</li>
<li><code>get_class()</code> 和 <code>get_parent_class()</code> 无参数调用已弃用</li>
<li>Assert 相关 INI 设置已弃用</li>
<li>SQLite3 默认使用异常模式</li>
<li>DateTime 异常类型细化</li>
</ul>
<p>通过了解这些变化并遵循迁移建议，可以顺利地从 PHP 8.2 迁移到 PHP 8.3。</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP Migrating 8.1 to 8.2</title>
      <link>https://zyf.im/2024/07/01/php-migrating-81-to-82/</link>
      <pubDate>Mon, 01 Jul 2024 09:24:50 +0000</pubDate>
      <guid>https://zyf.im/2024/07/01/php-migrating-81-to-82/</guid>
      <description>&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;PHP 8.2 引入了许多类型系统改进，包括只读类（Readonly Classes）、析取范式类型（DNF Types）、独立的 &lt;code&gt;null&lt;/code&gt;、&lt;code&gt;true&lt;/code&gt;、&lt;code&gt;false&lt;/code&gt; 类型、敏感参数隐藏、新的 Random 扩展等。本文将介绍主要变化和如何从 PHP 8.1 迁移到 PHP 8.2。&lt;/p&gt;
&lt;h3 id=&#34;参考资源&#34;&gt;参考资源&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在线测试环境：&lt;a href=&#34;https://onlinephp.io/&#34;&gt;https://onlinephp.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;官方发布说明：&lt;a href=&#34;https://www.php.net/releases/8.2/en.php&#34;&gt;https://www.php.net/releases/8.2/en.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;迁移指南：&lt;a href=&#34;https://www.php.net/manual/en/migration82.php&#34;&gt;https://www.php.net/manual/en/migration82.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;更新详情：&lt;a href=&#34;https://php.watch/versions/8.2&#34;&gt;https://php.watch/versions/8.2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;php-82-新特性&#34;&gt;PHP 8.2 新特性&lt;/h2&gt;
&lt;h3 id=&#34;只读类readonly-classes&#34;&gt;只读类（Readonly Classes）&lt;/h3&gt;
&lt;p&gt;PHP 8.2 允许将整个类声明为只读，类中的所有属性自动成为只读属性：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;User&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$username&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;john_doe&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;john@example.com&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// john_doe
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;email&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;new@example.com&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Fatal error: Cannot modify readonly property User::$email
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只读类的特性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有属性自动成为只读&lt;/li&gt;
&lt;li&gt;不能有动态属性&lt;/li&gt;
&lt;li&gt;所有属性必须有类型声明&lt;/li&gt;
&lt;li&gt;子类也必须是只读的&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;析取范式类型dnf-types&#34;&gt;析取范式类型（DNF Types）&lt;/h3&gt;
&lt;p&gt;析取范式（Disjunctive Normal Form）允许组合联合类型和交叉类型，交叉类型必须用括号分组：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 接受同时实现 Countable 和 Iterator 的对象，或者 null
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Countable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Iterator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 更复杂的 DNF 类型示例
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;HTMLRequest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;RequestInterface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;APIRequest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Response&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 处理请求
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;DNF 类型必须遵循特定格式：交叉类型用括号包裹，再与其他类型用 &lt;code&gt;|&lt;/code&gt; 连接。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="简介">简介</h2>
<p>PHP 8.2 引入了许多类型系统改进，包括只读类（Readonly Classes）、析取范式类型（DNF Types）、独立的 <code>null</code>、<code>true</code>、<code>false</code> 类型、敏感参数隐藏、新的 Random 扩展等。本文将介绍主要变化和如何从 PHP 8.1 迁移到 PHP 8.2。</p>
<h3 id="参考资源">参考资源</h3>
<ul>
<li>在线测试环境：<a href="https://onlinephp.io/">https://onlinephp.io/</a></li>
<li>官方发布说明：<a href="https://www.php.net/releases/8.2/en.php">https://www.php.net/releases/8.2/en.php</a></li>
<li>迁移指南：<a href="https://www.php.net/manual/en/migration82.php">https://www.php.net/manual/en/migration82.php</a></li>
<li>更新详情：<a href="https://php.watch/versions/8.2">https://php.watch/versions/8.2</a></li>
</ul>
<h2 id="php-82-新特性">PHP 8.2 新特性</h2>
<h3 id="只读类readonly-classes">只读类（Readonly Classes）</h3>
<p>PHP 8.2 允许将整个类声明为只读，类中的所有属性自动成为只读属性：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">readonly</span> <span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">string</span> <span class="nv">$username</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">string</span> <span class="nv">$email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="s1">&#39;john_doe&#39;</span><span class="p">,</span> <span class="s1">&#39;john@example.com&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">username</span><span class="p">;</span> <span class="c1">// john_doe
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">email</span> <span class="o">=</span> <span class="s1">&#39;new@example.com&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Fatal error: Cannot modify readonly property User::$email
</span></span></span></code></pre></div><p>只读类的特性：</p>
<ul>
<li>所有属性自动成为只读</li>
<li>不能有动态属性</li>
<li>所有属性必须有类型声明</li>
<li>子类也必须是只读的</li>
</ul>
<h3 id="析取范式类型dnf-types">析取范式类型（DNF Types）</h3>
<p>析取范式（Disjunctive Normal Form）允许组合联合类型和交叉类型，交叉类型必须用括号分组：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 接受同时实现 Countable 和 Iterator 的对象，或者 null
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">process</span><span class="p">((</span><span class="nx">Countable</span><span class="o">&amp;</span><span class="nx">Iterator</span><span class="p">)</span><span class="o">|</span><span class="k">null</span> <span class="nv">$item</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$item</span> <span class="o">?</span> <span class="nx">count</span><span class="p">(</span><span class="nv">$item</span><span class="p">)</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 更复杂的 DNF 类型示例
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">handle</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nx">HTMLRequest</span><span class="o">&amp;</span><span class="nx">RequestInterface</span><span class="p">)</span><span class="o">|</span><span class="nx">APIRequest</span><span class="o">|</span><span class="k">null</span> <span class="nv">$request</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span><span class="o">:</span> <span class="nx">Response</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理请求
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>DNF 类型必须遵循特定格式：交叉类型用括号包裹，再与其他类型用 <code>|</code> 连接。</p>
<h3 id="独立的-nulltruefalse-类型">独立的 null、true、false 类型</h3>
<p>PHP 8.2 允许将 <code>null</code>、<code>true</code>、<code>false</code> 作为独立的类型使用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 函数总是返回 false
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">alwaysFalse</span><span class="p">()</span><span class="o">:</span> <span class="k">false</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 函数总是返回 true
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">alwaysTrue</span><span class="p">()</span><span class="o">:</span> <span class="k">true</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 函数总是返回 null
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">alwaysNull</span><span class="p">()</span><span class="o">:</span> <span class="k">null</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 实际应用示例：失败时返回 false
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">findUser</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$id</span><span class="p">)</span><span class="o">:</span> <span class="nx">User</span><span class="o">|</span><span class="k">false</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$user</span> <span class="o">=</span> <span class="c1">// 查询数据库...
</span></span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$user</span> <span class="o">??</span> <span class="k">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这比使用 <code>bool</code> 或 <code>?type</code> 提供了更精确的类型信息。</p>
<h3 id="trait-中的常量">Trait 中的常量</h3>
<p>PHP 8.2 允许在 Trait 中定义常量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">trait</span> <span class="nx">NetworkTrait</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">TIMEOUT</span> <span class="o">=</span> <span class="mi">60</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">const</span> <span class="no">MAX_RETRIES</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">getTimeout</span><span class="p">()</span><span class="o">:</span> <span class="nx">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">self</span><span class="o">::</span><span class="na">TIMEOUT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">HttpClient</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">use</span> <span class="nx">NetworkTrait</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">connect</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">echo</span> <span class="s2">&#34;Timeout: &#34;</span> <span class="o">.</span> <span class="nx">self</span><span class="o">::</span><span class="na">TIMEOUT</span><span class="p">;</span> <span class="c1">// 60
</span></span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 通过使用 Trait 的类访问常量
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">HttpClient</span><span class="o">::</span><span class="na">TIMEOUT</span><span class="p">;</span> <span class="c1">// 60
</span></span></span></code></pre></div><h3 id="敏感参数隐藏sensitiveparameter">敏感参数隐藏（#[\SensitiveParameter]）</h3>
<p>新的 <code>#[\SensitiveParameter]</code> 属性可以在堆栈跟踪中隐藏敏感数据：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">hashPassword</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#[\SensitiveParameter] string $password
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">debug_print_backtrace</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">password_hash</span><span class="p">(</span><span class="nv">$password</span><span class="p">,</span> <span class="nx">PASSWORD_DEFAULT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">hashPassword</span><span class="p">(</span><span class="s1">&#39;my_secret_password&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.1: #0 example.php(8): hashPassword(&#39;my_secret_password&#39;)
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.2: #0 example.php(8): hashPassword(Object(SensitiveParameterValue))
</span></span></span></code></pre></div><p>这对于防止密码、API 密钥等敏感信息在错误日志中泄露非常有用。</p>
<h3 id="新的-random-扩展">新的 Random 扩展</h3>
<p>PHP 8.2 重构了随机数生成，提供了面向对象的 API：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 使用默认引擎
</span></span></span><span class="line"><span class="cl"><span class="nv">$randomizer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">\Random\Randomizer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 生成随机整数
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$randomizer</span><span class="o">-&gt;</span><span class="na">getInt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 生成随机字节
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">bin2hex</span><span class="p">(</span><span class="nv">$randomizer</span><span class="o">-&gt;</span><span class="na">getBytes</span><span class="p">(</span><span class="mi">16</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 随机打乱数组
</span></span></span><span class="line"><span class="cl"><span class="nv">$array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$shuffled</span> <span class="o">=</span> <span class="nv">$randomizer</span><span class="o">-&gt;</span><span class="na">shuffleArray</span><span class="p">(</span><span class="nv">$array</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 随机打乱字符串
</span></span></span><span class="line"><span class="cl"><span class="nv">$string</span> <span class="o">=</span> <span class="s2">&#34;Hello&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$randomizer</span><span class="o">-&gt;</span><span class="na">shuffleBytes</span><span class="p">(</span><span class="nv">$string</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用特定引擎
</span></span></span><span class="line"><span class="cl"><span class="nv">$secure</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">\Random\Randomizer</span><span class="p">(</span><span class="k">new</span> <span class="nx">\Random\Engine\Secure</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="nv">$mt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">\Random\Randomizer</span><span class="p">(</span><span class="k">new</span> <span class="nx">\Random\Engine\Mt19937</span><span class="p">(</span><span class="nx">seed</span><span class="o">:</span> <span class="mi">1234</span><span class="p">));</span>
</span></span></code></pre></div><p>可用的引擎：</p>
<ul>
<li><code>\Random\Engine\Secure</code> - 加密安全的随机数</li>
<li><code>\Random\Engine\Mt19937</code> - Mersenne Twister 算法</li>
<li><code>\Random\Engine\PcgOneseq128XslRr64</code> - PCG 算法</li>
<li><code>\Random\Engine\Xoshiro256StarStar</code> - Xoshiro256** 算法</li>
</ul>
<h3 id="allowdynamicproperties-属性">#[\AllowDynamicProperties] 属性</h3>
<p>由于动态属性已被弃用，如果仍需使用，可以用此属性标记类：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">#[\AllowDynamicProperties]
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">LegacyClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">string</span> <span class="nv">$declared</span> <span class="o">=</span> <span class="s1">&#39;value&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">LegacyClass</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$obj</span><span class="o">-&gt;</span><span class="na">undeclared</span> <span class="o">=</span> <span class="s1">&#39;dynamic&#39;</span><span class="p">;</span> <span class="c1">// 不会产生弃用警告
</span></span></span></code></pre></div><h3 id="枚举中获取常量">枚举中获取常量</h3>
<p>PHP 8.2 允许在枚举表达式中获取常量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">enum</span> <span class="nx">Status</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Active</span> <span class="o">=</span> <span class="s1">&#39;active&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Inactive</span> <span class="o">=</span> <span class="s1">&#39;inactive&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="no">DEFAULT</span> <span class="o">=</span> <span class="nx">self</span><span class="o">::</span><span class="na">Active</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">Status</span><span class="o">::</span><span class="na">DEFAULT</span><span class="o">-&gt;</span><span class="na">value</span><span class="p">;</span> <span class="c1">// active
</span></span></span></code></pre></div><h2 id="php-82-新增函数">PHP 8.2 新增函数</h2>
<h3 id="ini_parse_quantity">ini_parse_quantity()</h3>
<p>解析 PHP INI 格式的数据大小值：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 解析 INI 格式的容量值
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">ini_parse_quantity</span><span class="p">(</span><span class="s1">&#39;256M&#39;</span><span class="p">);</span>  <span class="c1">// 268435456 (256 * 1024 * 1024)
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">ini_parse_quantity</span><span class="p">(</span><span class="s1">&#39;1G&#39;</span><span class="p">);</span>    <span class="c1">// 1073741824
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">ini_parse_quantity</span><span class="p">(</span><span class="s1">&#39;512K&#39;</span><span class="p">);</span>  <span class="c1">// 524288
</span></span></span></code></pre></div><h3 id="memory_reset_peak_usage">memory_reset_peak_usage()</h3>
<p>重置峰值内存使用量跟踪：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 初始峰值
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">memory_get_peak_usage</span><span class="p">();</span> <span class="c1">// 例如: 2097152
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 进行一些内存密集操作
</span></span></span><span class="line"><span class="cl"><span class="nv">$data</span> <span class="o">=</span> <span class="nx">str_repeat</span><span class="p">(</span><span class="s1">&#39;x&#39;</span><span class="p">,</span> <span class="mi">1000000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">memory_get_peak_usage</span><span class="p">();</span> <span class="c1">// 例如: 3145728
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 重置峰值
</span></span></span><span class="line"><span class="cl"><span class="nx">memory_reset_peak_usage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">unset</span><span class="p">(</span><span class="nv">$data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">memory_get_peak_usage</span><span class="p">();</span> <span class="c1">// 重置后的新峰值
</span></span></span></code></pre></div><h3 id="mysqli_execute_query">mysqli_execute_query()</h3>
<p>简化 MySQLi 预处理语句的执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.2 之前
</span></span></span><span class="line"><span class="cl"><span class="nv">$stmt</span> <span class="o">=</span> <span class="nv">$mysqli</span><span class="o">-&gt;</span><span class="na">prepare</span><span class="p">(</span><span class="s1">&#39;SELECT * FROM users WHERE id = ?&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$stmt</span><span class="o">-&gt;</span><span class="na">bind_param</span><span class="p">(</span><span class="s1">&#39;i&#39;</span><span class="p">,</span> <span class="nv">$id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$stmt</span><span class="o">-&gt;</span><span class="na">execute</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nv">$stmt</span><span class="o">-&gt;</span><span class="na">get_result</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.2 - 使用 mysqli_execute_query()
</span></span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nx">mysqli_execute_query</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$mysqli</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;SELECT * FROM users WHERE id = ?&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="nv">$id</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 面向对象写法
</span></span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nv">$mysqli</span><span class="o">-&gt;</span><span class="na">execute_query</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;SELECT * FROM users WHERE id = ?&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="nv">$id</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><h3 id="curl_upkeep">curl_upkeep()</h3>
<p>保持持久 HTTP 连接活跃：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$ch</span> <span class="o">=</span> <span class="nx">curl_init</span><span class="p">(</span><span class="s1">&#39;https://example.com&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">curl_setopt</span><span class="p">(</span><span class="nv">$ch</span><span class="p">,</span> <span class="nx">CURLOPT_RETURNTRANSFER</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">curl_exec</span><span class="p">(</span><span class="nv">$ch</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 保持连接活跃
</span></span></span><span class="line"><span class="cl"><span class="nx">curl_upkeep</span><span class="p">(</span><span class="nv">$ch</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="openssl_cipher_key_length">openssl_cipher_key_length()</h3>
<p>获取 OpenSSL 加密算法所需的密钥长度：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">openssl_cipher_key_length</span><span class="p">(</span><span class="s1">&#39;aes-256-cbc&#39;</span><span class="p">);</span> <span class="c1">// 32
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">openssl_cipher_key_length</span><span class="p">(</span><span class="s1">&#39;aes-128-gcm&#39;</span><span class="p">);</span> <span class="c1">// 16
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">openssl_cipher_key_length</span><span class="p">(</span><span class="s1">&#39;des-ede3-cbc&#39;</span><span class="p">);</span> <span class="c1">// 24
</span></span></span></code></pre></div><h3 id="sodium_crypto_stream_xchacha20_xor_ic">sodium_crypto_stream_xchacha20_xor_ic()</h3>
<p>XChaCha20 加密函数，支持初始计数器：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$key</span> <span class="o">=</span> <span class="nx">sodium_crypto_stream_xchacha20_keygen</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$nonce</span> <span class="o">=</span> <span class="nx">random_bytes</span><span class="p">(</span><span class="nx">SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$message</span> <span class="o">=</span> <span class="s1">&#39;Hello, World!&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用初始计数器加密
</span></span></span><span class="line"><span class="cl"><span class="nv">$encrypted</span> <span class="o">=</span> <span class="nx">sodium_crypto_stream_xchacha20_xor_ic</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$message</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$nonce</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">1</span><span class="p">,</span>  <span class="c1">// 初始计数器
</span></span></span><span class="line"><span class="cl">    <span class="nv">$key</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><h2 id="弃用特性">弃用特性</h2>
<h3 id="动态属性弃用">动态属性弃用</h3>
<p>在未声明的类属性上进行读写操作已被弃用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">string</span> <span class="nv">$name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">name</span> <span class="o">=</span> <span class="s1">&#39;John&#39;</span><span class="p">;</span>        <span class="c1">// ✅ 正常
</span></span></span><span class="line"><span class="cl"><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">age</span> <span class="o">=</span> <span class="mi">25</span><span class="p">;</span>             <span class="c1">// ⚠️ Deprecated: Creation of dynamic property
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 解决方案 1：声明属性
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">string</span> <span class="nv">$name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">int</span> <span class="nv">$age</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 解决方案 2：使用 #[AllowDynamicProperties]
</span></span></span><span class="line"><span class="cl"><span class="c1">#[\AllowDynamicProperties]
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">string</span> <span class="nv">$name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 解决方案 3：使用 __get/__set 魔术方法
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">array</span> <span class="nv">$data</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__get</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$name</span><span class="p">)</span><span class="o">:</span> <span class="nx">mixed</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">data</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span> <span class="o">??</span> <span class="k">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__set</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$name</span><span class="p">,</span> <span class="nx">mixed</span> <span class="nv">$value</span><span class="p">)</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">data</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>stdClass</code> 和从 <code>__get()/__set()</code> 继承的类不受影响。</p>
<h3 id="var-字符串插值弃用">${var} 字符串插值弃用</h3>
<p>旧的 <code>${var}</code> 字符串插值语法已被弃用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$name</span> <span class="o">=</span> <span class="s1">&#39;PHP&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;Hello </span><span class="si">${</span><span class="nv">name</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Deprecated: Using ${var} in strings is deprecated, use {$var} instead
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ❌ 变量变量也已弃用
</span></span></span><span class="line"><span class="cl"><span class="nv">$var</span> <span class="o">=</span> <span class="s1">&#39;name&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;Hello </span><span class="si">${</span><span class="nv">$var</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Deprecated: Using ${expr} in strings is deprecated, use {${expr}} instead
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 推荐写法
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;Hello </span><span class="si">{</span><span class="nv">$name</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;Hello </span><span class="si">{</span><span class="nv">$$var</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="utf8_encode-和-utf8_decode-弃用">utf8_encode() 和 utf8_decode() 弃用</h3>
<p>这两个函数已被弃用，因为它们只支持 Latin-1 编码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nv">$encoded</span> <span class="o">=</span> <span class="nx">utf8_encode</span><span class="p">(</span><span class="nv">$string</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$decoded</span> <span class="o">=</span> <span class="nx">utf8_decode</span><span class="p">(</span><span class="nv">$string</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用 mbstring
</span></span></span><span class="line"><span class="cl"><span class="nv">$encoded</span> <span class="o">=</span> <span class="nx">mb_convert_encoding</span><span class="p">(</span><span class="nv">$string</span><span class="p">,</span> <span class="s1">&#39;UTF-8&#39;</span><span class="p">,</span> <span class="s1">&#39;ISO-8859-1&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$decoded</span> <span class="o">=</span> <span class="nx">mb_convert_encoding</span><span class="p">(</span><span class="nv">$string</span><span class="p">,</span> <span class="s1">&#39;ISO-8859-1&#39;</span><span class="p">,</span> <span class="s1">&#39;UTF-8&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 或使用 iconv
</span></span></span><span class="line"><span class="cl"><span class="nv">$encoded</span> <span class="o">=</span> <span class="nx">iconv</span><span class="p">(</span><span class="s1">&#39;ISO-8859-1&#39;</span><span class="p">,</span> <span class="s1">&#39;UTF-8&#39;</span><span class="p">,</span> <span class="nv">$string</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$decoded</span> <span class="o">=</span> <span class="nx">iconv</span><span class="p">(</span><span class="s1">&#39;UTF-8&#39;</span><span class="p">,</span> <span class="s1">&#39;ISO-8859-1&#39;</span><span class="p">,</span> <span class="nv">$string</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="部分可调用模式弃用">部分可调用模式弃用</h3>
<p>一些可调用模式已被弃用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用的可调用模式
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;self::method&#34;</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;parent::method&#34;</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;static::method&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="s2">&#34;self&#34;</span><span class="p">,</span> <span class="s2">&#34;method&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="s2">&#34;parent&#34;</span><span class="p">,</span> <span class="s2">&#34;method&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="s2">&#34;static&#34;</span><span class="p">,</span> <span class="s2">&#34;method&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="s2">&#34;Foo&#34;</span><span class="p">,</span> <span class="s2">&#34;Bar::method&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="k">new</span> <span class="nx">Foo</span><span class="p">,</span> <span class="s2">&#34;Bar::method&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用一等可调用语法
</span></span></span><span class="line"><span class="cl"><span class="nv">$callable</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">method</span><span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$callable</span> <span class="o">=</span> <span class="nx">self</span><span class="o">::</span><span class="na">method</span><span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$callable</span> <span class="o">=</span> <span class="nx">Foo</span><span class="o">::</span><span class="na">method</span><span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="mbstring-编码弃用">Mbstring 编码弃用</h3>
<p>以下 Mbstring 编码已被弃用：</p>
<ul>
<li><code>BASE64</code></li>
<li><code>UUENCODE</code></li>
<li><code>HTML-ENTITIES</code></li>
<li><code>Quoted-Printable</code></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nv">$encoded</span> <span class="o">=</span> <span class="nx">mb_convert_encoding</span><span class="p">(</span><span class="nv">$data</span><span class="p">,</span> <span class="s1">&#39;BASE64&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用专门的函数
</span></span></span><span class="line"><span class="cl"><span class="nv">$encoded</span> <span class="o">=</span> <span class="nx">base64_encode</span><span class="p">(</span><span class="nv">$data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$decoded</span> <span class="o">=</span> <span class="nx">base64_decode</span><span class="p">(</span><span class="nv">$encoded</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ HTML 实体使用
</span></span></span><span class="line"><span class="cl"><span class="nv">$encoded</span> <span class="o">=</span> <span class="nx">htmlentities</span><span class="p">(</span><span class="nv">$string</span><span class="p">,</span> <span class="nx">ENT_QUOTES</span><span class="p">,</span> <span class="s1">&#39;UTF-8&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$decoded</span> <span class="o">=</span> <span class="nx">html_entity_decode</span><span class="p">(</span><span class="nv">$encoded</span><span class="p">,</span> <span class="nx">ENT_QUOTES</span><span class="p">,</span> <span class="s1">&#39;UTF-8&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="兼容性变化">兼容性变化</h2>
<h3 id="strtolower-和-strtoupper-不再受区域设置影响">strtolower() 和 strtoupper() 不再受区域设置影响</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">setlocale</span><span class="p">(</span><span class="nx">LC_ALL</span><span class="p">,</span> <span class="s1">&#39;tr_TR&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.1: 受区域设置影响
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.2: 始终按 ASCII 规则处理
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">strtolower</span><span class="p">(</span><span class="s1">&#39;I&#39;</span><span class="p">);</span> <span class="c1">// &#39;i&#39;（不再是土耳其语的 &#39;ı&#39;）
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">strtoupper</span><span class="p">(</span><span class="s1">&#39;i&#39;</span><span class="p">);</span> <span class="c1">// &#39;I&#39;（不再是土耳其语的 &#39;İ&#39;）
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 需要区域敏感转换时使用 mbstring
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">mb_strtolower</span><span class="p">(</span><span class="s1">&#39;I&#39;</span><span class="p">,</span> <span class="s1">&#39;tr_TR&#39;</span><span class="p">);</span> <span class="c1">// &#39;ı&#39;
</span></span></span></code></pre></div><h3 id="str_split-空字符串行为变化">str_split() 空字符串行为变化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.1: 返回 [&#39;&#39;]
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.2: 返回 []
</span></span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nx">str_split</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$result</span><span class="p">);</span> <span class="c1">// array(0) {}
</span></span></span></code></pre></div><h3 id="ksort-sort_regular-行为变化">ksort() SORT_REGULAR 行为变化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$arr</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;b&#39;</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">10</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span> <span class="o">=&gt;</span> <span class="mi">4</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nx">ksort</span><span class="p">(</span><span class="nv">$arr</span><span class="p">,</span> <span class="nx">SORT_REGULAR</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.2 中排序更加一致
</span></span></span><span class="line"><span class="cl"><span class="c1">// 数字键和字符串键的比较规则更加明确
</span></span></span></code></pre></div><h3 id="日期时间扩展变化">日期时间扩展变化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// DateTimeImmutable 和 DateTime 的行为更加一致
</span></span></span><span class="line"><span class="cl"><span class="c1">// DatePeriod 现在是可迭代的
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$period</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DatePeriod</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="nx">DateTime</span><span class="p">(</span><span class="s1">&#39;2024-01-01&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="nx">DateInterval</span><span class="p">(</span><span class="s1">&#39;P1D&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="mi">5</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 可以使用 foreach 遍历
</span></span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$period</span> <span class="k">as</span> <span class="nv">$date</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">echo</span> <span class="nv">$date</span><span class="o">-&gt;</span><span class="na">format</span><span class="p">(</span><span class="s1">&#39;Y-m-d&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="其他改进">其他改进</h2>
<h3 id="reflectionmethod-和-reflectionproperty-改进">ReflectionMethod 和 ReflectionProperty 改进</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 检查方法是否可以作为静态方法调用
</span></span></span><span class="line"><span class="cl"><span class="nv">$reflection</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ReflectionMethod</span><span class="p">(</span><span class="nx">MyClass</span><span class="o">::</span><span class="na">class</span><span class="p">,</span> <span class="s1">&#39;myMethod&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$reflection</span><span class="o">-&gt;</span><span class="na">isStatic</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 检查属性是否是只读
</span></span></span><span class="line"><span class="cl"><span class="nv">$reflection</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ReflectionProperty</span><span class="p">(</span><span class="nx">User</span><span class="o">::</span><span class="na">class</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$reflection</span><span class="o">-&gt;</span><span class="na">isReadOnly</span><span class="p">());</span>
</span></span></code></pre></div><h3 id="ziparchive-新方法">ZipArchive 新方法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$zip</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ZipArchive</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$zip</span><span class="o">-&gt;</span><span class="na">open</span><span class="p">(</span><span class="s1">&#39;archive.zip&#39;</span><span class="p">,</span> <span class="nx">ZipArchive</span><span class="o">::</span><span class="na">CREATE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取文件流
</span></span></span><span class="line"><span class="cl"><span class="nv">$stream</span> <span class="o">=</span> <span class="nv">$zip</span><span class="o">-&gt;</span><span class="na">getStreamIndex</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$stream</span> <span class="o">=</span> <span class="nv">$zip</span><span class="o">-&gt;</span><span class="na">getStreamName</span><span class="p">(</span><span class="s1">&#39;file.txt&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 清除错误状态
</span></span></span><span class="line"><span class="cl"><span class="nv">$zip</span><span class="o">-&gt;</span><span class="na">clearError</span><span class="p">();</span>
</span></span></code></pre></div><h3 id="odbc-和-pdo_odbc-改进">ODBC 和 PDO_ODBC 改进</h3>
<p>连接字符串和凭证检查得到改进，支持更多的 ODBC 功能。</p>
<h2 id="总结">总结</h2>
<p>PHP 8.2 带来了许多实用的新特性和改进：</p>
<p><strong>主要新特性：</strong></p>
<ul>
<li>只读类</li>
<li>析取范式类型（DNF Types）</li>
<li>独立的 null、true、false 类型</li>
<li>Trait 中的常量</li>
<li>敏感参数隐藏</li>
<li>新的 Random 扩展</li>
</ul>
<p><strong>需要注意的变化：</strong></p>
<ul>
<li>动态属性已弃用</li>
<li><code>${var}</code> 字符串插值已弃用</li>
<li><code>utf8_encode()</code> 和 <code>utf8_decode()</code> 已弃用</li>
<li><code>strtolower()</code> 和 <code>strtoupper()</code> 不再受区域设置影响</li>
<li>部分可调用模式已弃用</li>
</ul>
<p>通过了解这些变化并遵循迁移建议，可以顺利地从 PHP 8.1 迁移到 PHP 8.2。</p>
]]></content:encoded>
    </item>
    <item>
      <title>MongoDB 高手课</title>
      <link>https://zyf.im/2023/04/19/mongodb-course/</link>
      <pubDate>Wed, 19 Apr 2023 10:47:10 +0000</pubDate>
      <guid>https://zyf.im/2023/04/19/mongodb-course/</guid>
      <description>&lt;h2 id=&#34;04-特色及优势&#34;&gt;04 特色及优势&lt;/h2&gt;
&lt;p&gt;对象模型，快速响应业务变化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多形性：同一个集合中可以包含不同字段（类型）的文档对象。&lt;/li&gt;
&lt;li&gt;动态性：线上修改数据模式，修改是应用与数据库均无须下线。&lt;/li&gt;
&lt;li&gt;数据治理：支持使用JSONSchema 来规范数据模式。在保证模式灵活动态的前提下，提供数据治理能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;快速的开发：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只存储在一个存储区读写。&lt;/li&gt;
&lt;li&gt;反范式、无关联的组织极大优化查询速度。&lt;/li&gt;
&lt;li&gt;程序 API 自然，开发速度快。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;原生的高可用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replica Set - 2 to 50 个成员，建议单数。&lt;/li&gt;
&lt;li&gt;自恢复。&lt;/li&gt;
&lt;li&gt;多中心容灾能力。&lt;/li&gt;
&lt;li&gt;滚动服务，最小化服务终端。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;横向扩展能力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要的时候无缝扩展。&lt;/li&gt;
&lt;li&gt;应用全透明。&lt;/li&gt;
&lt;li&gt;多种数据分布策略。&lt;/li&gt;
&lt;li&gt;轻松支持 TB-PB 数量级。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;06-基本操作&#34;&gt;06 基本操作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://cloud.mongodb.com/v2/&#34;&gt;cloud.mongodb.com 云服务&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/tapdata/geektime-mongodb-course/blob/master/aggregation/dump.tar.gz&#34;&gt;样本数据 | geektime-mongodb-course&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -xvf dump.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mongorestore --uri&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;mongodb://root:root@10.130.0.12/?&amp;amp;authMechanism=SCRAM-SHA-1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 插入
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fruit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;insertOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;apple&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fruit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;insertMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;apple&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;pear&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;orange&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 查询
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;fmiller&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Elizabeth Ray&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^f/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$or&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^f/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^E/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}]})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;a &amp;lt;&amp;gt; 1&lt;/code&gt; &lt;code&gt;{a: {$ne: 1}}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a &amp;gt; 1&lt;/code&gt; &lt;code&gt;{a: {$gt: 1}}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a &amp;gt;= 1&lt;/code&gt; &lt;code&gt;{a: {$gte: 1}}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a &amp;lt; 1&lt;/code&gt; &lt;code&gt;{a: {$lt: 1}}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a &amp;lt;= 1&lt;/code&gt; &lt;code&gt;{a: {$lte: 1}}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a=1 OR b=1&lt;/code&gt; &lt;code&gt;{$or: [{a: 1}, {b: 1}]}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a IS NULL&lt;/code&gt; &lt;code&gt;{a: {$exists: false}}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a IS (1,2,3)&lt;/code&gt; &lt;code&gt;{a: {$in: [1, 2, 3]}}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 同时满足
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$eleMatch&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;city&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Rome&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;country&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;USA&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 投影 projection
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// like select
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^f/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;/^f/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// remove
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;remove&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;abrown&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// update
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;updateOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;fmiller&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$set&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;China&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;updateMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;fmiller&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$set&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;China&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// $set $unset
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// $push $pushAll $pop 数组操作
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// $pull $pullAll 如果匹配，从数组中删除相应的对象
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// $addToSet 如果不存在则增加一个值到数组
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// drop
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fruit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;drop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;show&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;collections&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dropDatabase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;show&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dbs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;08-聚合查询&#34;&gt;08 聚合查询&lt;/h2&gt;
&lt;p&gt;Aggregation Framework&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="04-特色及优势">04 特色及优势</h2>
<p>对象模型，快速响应业务变化：</p>
<ul>
<li>多形性：同一个集合中可以包含不同字段（类型）的文档对象。</li>
<li>动态性：线上修改数据模式，修改是应用与数据库均无须下线。</li>
<li>数据治理：支持使用JSONSchema 来规范数据模式。在保证模式灵活动态的前提下，提供数据治理能力。</li>
</ul>
<p>快速的开发：</p>
<ul>
<li>只存储在一个存储区读写。</li>
<li>反范式、无关联的组织极大优化查询速度。</li>
<li>程序 API 自然，开发速度快。</li>
</ul>
<p>原生的高可用：</p>
<ul>
<li>Replica Set - 2 to 50 个成员，建议单数。</li>
<li>自恢复。</li>
<li>多中心容灾能力。</li>
<li>滚动服务，最小化服务终端。</li>
</ul>
<p>横向扩展能力：</p>
<ul>
<li>需要的时候无缝扩展。</li>
<li>应用全透明。</li>
<li>多种数据分布策略。</li>
<li>轻松支持 TB-PB 数量级。</li>
</ul>
<h2 id="06-基本操作">06 基本操作</h2>
<ul>
<li><a href="https://cloud.mongodb.com/v2/">cloud.mongodb.com 云服务</a></li>
<li><a href="https://github.com/tapdata/geektime-mongodb-course/blob/master/aggregation/dump.tar.gz">样本数据 | geektime-mongodb-course</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">tar -xvf dump.tar.gz
</span></span><span class="line"><span class="cl">mongorestore --uri<span class="o">=</span><span class="s2">&#34;mongodb://root:root@10.130.0.12/?&amp;authMechanism=SCRAM-SHA-1&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 插入
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">fruit</span><span class="p">.</span><span class="nx">insertOne</span><span class="p">({</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;apple&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">fruit</span><span class="p">.</span><span class="nx">insertMany</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;apple&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;pear&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;orange&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 查询
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">customers</span><span class="p">.</span><span class="nx">find</span><span class="p">({</span><span class="nx">username</span><span class="o">:</span> <span class="s2">&#34;fmiller&#34;</span><span class="p">,</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Elizabeth Ray&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">customers</span><span class="p">.</span><span class="nx">find</span><span class="p">({</span><span class="nx">username</span><span class="o">:</span> <span class="sr">/^f/</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">customers</span><span class="p">.</span><span class="nx">find</span><span class="p">({</span><span class="nx">$or</span><span class="o">:</span> <span class="p">[{</span><span class="nx">username</span><span class="o">:</span> <span class="sr">/^f/</span><span class="p">},</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="sr">/^E/</span><span class="p">}]})</span>
</span></span></code></pre></div><ul>
<li><code>a &lt;&gt; 1</code> <code>{a: {$ne: 1}}</code></li>
<li><code>a &gt; 1</code> <code>{a: {$gt: 1}}</code></li>
<li><code>a &gt;= 1</code> <code>{a: {$gte: 1}}</code></li>
<li><code>a &lt; 1</code> <code>{a: {$lt: 1}}</code></li>
<li><code>a &lt;= 1</code> <code>{a: {$lte: 1}}</code></li>
<li><code>a=1 OR b=1</code> <code>{$or: [{a: 1}, {b: 1}]}</code></li>
<li><code>a IS NULL</code> <code>{a: {$exists: false}}</code></li>
<li><code>a IS (1,2,3)</code> <code>{a: {$in: [1, 2, 3]}}</code></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 同时满足
</span></span></span><span class="line"><span class="cl"><span class="p">{</span><span class="nx">$eleMatch</span><span class="o">:</span> <span class="p">{</span><span class="s2">&#34;city&#34;</span><span class="o">:</span> <span class="s2">&#34;Rome&#34;</span><span class="p">,</span> <span class="s2">&#34;country&#34;</span><span class="o">:</span> <span class="s2">&#34;USA&#34;</span><span class="p">}}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 投影 projection
</span></span></span><span class="line"><span class="cl"><span class="c1">// like select
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">customers</span><span class="p">.</span><span class="nx">find</span><span class="p">({</span><span class="nx">username</span><span class="o">:</span> <span class="sr">/^f/</span><span class="p">},</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">email</span><span class="o">:</span> <span class="mi">0</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">customers</span><span class="p">.</span><span class="nx">find</span><span class="p">({</span><span class="nx">username</span><span class="o">:</span> <span class="sr">/^f/</span><span class="p">},</span> <span class="p">{</span><span class="nx">_id</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">name</span><span class="o">:</span> <span class="mi">1</span><span class="p">})</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// remove
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">customers</span><span class="p">.</span><span class="nx">remove</span><span class="p">({</span><span class="nx">username</span><span class="o">:</span> <span class="s2">&#34;abrown&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// update
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">customers</span><span class="p">.</span><span class="nx">updateOne</span><span class="p">({</span><span class="nx">username</span><span class="o">:</span> <span class="s2">&#34;fmiller&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nx">$set</span><span class="o">:</span> <span class="p">{</span><span class="nx">from</span><span class="o">:</span> <span class="s2">&#34;China&#34;</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">customers</span><span class="p">.</span><span class="nx">updateMany</span><span class="p">({</span><span class="nx">username</span><span class="o">:</span> <span class="s2">&#34;fmiller&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nx">$set</span><span class="o">:</span> <span class="p">{</span><span class="nx">from</span><span class="o">:</span> <span class="s2">&#34;China&#34;</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="c1">// $set $unset
</span></span></span><span class="line"><span class="cl"><span class="c1">// $push $pushAll $pop 数组操作
</span></span></span><span class="line"><span class="cl"><span class="c1">// $pull $pullAll 如果匹配，从数组中删除相应的对象
</span></span></span><span class="line"><span class="cl"><span class="c1">// $addToSet 如果不存在则增加一个值到数组
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// drop
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">fruit</span><span class="p">.</span><span class="nx">drop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nx">show</span> <span class="nx">collections</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">db</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">dropDatabase</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nx">show</span> <span class="nx">dbs</span>
</span></span></code></pre></div><h2 id="08-聚合查询">08 聚合查询</h2>
<p>Aggregation Framework</p>
<ul>
<li>Pipeline</li>
<li>Stage</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">pipeline</span> <span class="o">=</span> <span class="p">[</span><span class="nx">stage1</span><span class="p">,</span> <span class="nx">stage2</span><span class="p">,</span> <span class="p">...]</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="o">&lt;</span><span class="nx">collection</span><span class="o">&gt;</span><span class="p">.</span><span class="nx">aggregate</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nx">pipeline</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">option</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>$match 过滤</li>
<li>$project 投影</li>
<li>$sort 排序</li>
<li>$group 分组</li>
<li>$skip $limit 结果限制</li>
<li>$lookup 左外连接</li>
</ul>
<h2 id="09-聚合查询实验">09 聚合查询实验</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 计算总合计
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">orders</span><span class="p">.</span><span class="nx">aggregate</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$group</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">_id</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">total</span><span class="o">:</span> <span class="p">{</span><span class="nx">$sum</span><span class="o">:</span> <span class="s2">&#34;$total&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="c1">// {
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;_id&#34;: null,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;total&#34;: NumberDecimal(&#34;44019609&#34;)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 查询 2019 第一季度，已完成订单（completed）总金额（金额+运费）和订单总数
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">orders</span><span class="p">.</span><span class="nx">aggregate</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$match</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">status</span><span class="o">:</span> <span class="s2">&#34;completed&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">orderDate</span><span class="o">:</span> <span class="p">{</span> <span class="nx">$gte</span><span class="o">:</span> <span class="nx">ISODate</span><span class="p">(</span><span class="s2">&#34;2019-01-01&#34;</span><span class="p">),</span> <span class="nx">$lt</span><span class="o">:</span> <span class="nx">ISODate</span><span class="p">(</span><span class="s2">&#34;2019-04-01&#34;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$group</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">_id</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">total</span><span class="o">:</span> <span class="p">{</span> <span class="nx">$sum</span><span class="o">:</span> <span class="s2">&#34;$total&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">shippingFee</span><span class="o">:</span> <span class="p">{</span> <span class="nx">$sum</span><span class="o">:</span> <span class="s2">&#34;$shippingFee&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">count</span><span class="o">:</span> <span class="p">{</span> <span class="nx">$sum</span><span class="o">:</span> <span class="mi">1</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$project</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">grandTotal</span><span class="o">:</span> <span class="p">{</span> <span class="nx">$add</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;$total&#34;</span><span class="p">,</span> <span class="s2">&#34;$shippingFee&#34;</span><span class="p">]</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">count</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">_id</span><span class="o">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="c1">// {
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;count&#34;: 5875,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;grandTotal&#34;: NumberDecimal(&#34;2636376&#34;)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><h2 id="10-复制集机制及原理">10 复制集机制及原理</h2>
<p>由3个以上具有投票权的节点组成：</p>
<ul>
<li>1个主节点 PRIMARY：接收写入操作和选举时投票。</li>
<li>两个或多个从节点 SECONDARY：复制主节点上的新数据和选举时投票。</li>
</ul>
<p>数据是如何复制的：</p>
<ul>
<li>当一个修改操作，无论是插入、更新或删除，到达主节点时它对数据的操作将被记录下来(经过些必要的转换)，这些记录称为 oplog。</li>
<li>从节点通过在主节点上打开一个 tailable 游标不断获取新进入主节点的 oplog，并在自己的数据上回放，以此保持跟主节点的数据一致。</li>
</ul>
<p>通过选举完成故障恢复：</p>
<ul>
<li>具有投票权的节点之间两两互相发送心跳。</li>
<li>当5次心跳未收到时判断为节点失联。</li>
<li>如果失联的是主节点，从节点会发起选举，选出新的主节点。</li>
<li>如果失联的是从节点则不会产生新的选举。</li>
<li>选举基于RAFT一致性算法实现，选举成功的必要条件是大多数投票节点存活。</li>
<li>复制集中最多可以有50个节点，但具有投票权的节点最多7个。</li>
</ul>
<p>影响选举的因素：</p>
<ul>
<li>整个集群必须有大多数节点存活。</li>
<li>被选举为主节点的节点必须：
<ul>
<li>能够与多数节点建立连接</li>
<li>具有较新的 oplog</li>
<li>具有较高的优先级(如果有配置)</li>
</ul>
</li>
</ul>
<p>复制集节点有以下常见的选配项：</p>
<ul>
<li>是否具有投票权（v 参数）：有则参与投票。</li>
<li>优先级（priority 参数）：优先级越高的节点越优先成为主节点。优先级为0的节点无法成为主节点。</li>
<li>隐藏（hidden 参数）：复制数据，但对应用不可见。隐藏节点可以具有投票仅，但优先级必须为0。</li>
<li>延迟（slaveDelay 参数）：复制 n 秒之前的数据，保持与主节点的时间差。</li>
</ul>
<p>复制集注意事项：</p>
<ul>
<li>关于硬件：
<ul>
<li>因为正常的复制集节点都有可能成为主节点，它们的地位是一样的，因此硬件配置上必须致；</li>
<li>为了保证节点不会同时岩机，各节点使用的硬件必须具有独立性。</li>
</ul>
</li>
<li>关于软件：
<ul>
<li>复制集各节点软件版本必须一致，以避免出现不可预知的问题。</li>
</ul>
</li>
<li>增加节点不会增加系统写性能！</li>
</ul>
<h2 id="11-搭建-mongodb-复制集">11 搭建 MongoDB 复制集</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p runtime/data_db<span class="o">{</span>1,2,3<span class="o">}</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">mkdir -p runtime/data_configdb<span class="o">{</span>1,2,3<span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">hostname -f
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">rs.status<span class="o">()</span>
</span></span></code></pre></div><h2 id="12-全家桶">12 全家桶</h2>
<ul>
<li><a href="https://www.mongodb.com/docs/launch-manage/">launch-manage | mongodb</a></li>
<li><a href="https://www.mongodb.com/docs/database-tools/">database-tools | mongodb</a></li>
</ul>
<h2 id="13-模型设计基础">13 模型设计基础</h2>
<ul>
<li>Entity</li>
<li>Attribute</li>
<li>Relationship</li>
</ul>
<p>概念模型 CDM -&gt; 逻辑模型 LDM -&gt; 物理模型 PDM
对象 -&gt; 实体、属性、关系 -&gt; 表结构、字段列表、主外建</p>
<h2 id="14-json-文档模型设计">14 JSON 文档模型设计</h2>
<p>无模式的由来：可以省略无论建模的具体过程，物理模型可省。</p>
<p>设计原则：</p>
<ul>
<li>性能 Performance</li>
<li>开发易用 Ease of Development</li>
</ul>
<h2 id="15-基础设计">15 基础设计</h2>
<p>集合、字段、基础形状 -&gt; 引用及关联 -&gt; 最终模式</p>
<p>业务需求及逻辑模型 &ndash;逻辑导向-&gt; 基础建模 &mdash;&gt; 集合、字段、基础形状</p>
<p>一个文档 16MB max.</p>
<p>内嵌为主。</p>
<h2 id="16-工况细化">16 工况细化</h2>
<p>技术需求、读写比例、方式及数据 &ndash;技术导向-&gt; 工况细化 &mdash;&gt; 引用及关联</p>
<p>引用模式 $lookup：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">contacts</span><span class="p">.</span><span class="nx">aggregate</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$lookup</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">form</span><span class="o">:</span> <span class="s2">&#34;groups&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">localField</span><span class="o">:</span> <span class="s2">&#34;groups_ids&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">foreignField</span><span class="o">:</span> <span class="s2">&#34;groups_id&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">as</span><span class="o">:</span> <span class="s2">&#34;groups&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">])</span>
</span></span></code></pre></div><p>使用引用方式：</p>
<ul>
<li>内嵌文档太大</li>
<li>内嵌文档或数组元素频繁修改</li>
<li>内嵌文档数组元素持续增长且没有封顶</li>
</ul>
<p>使用引用的设计：</p>
<ul>
<li>没有主外键的检查</li>
<li>$lookup 只支持 left outer join</li>
<li>$lookup 的关系目标（from）不能是分片表</li>
</ul>
<h2 id="17-模式套用">17 模式套用</h2>
<p>经验和学习 &ndash;模式导向-&gt; 套用设计模式 -&gt; 优化的模型</p>
<p>时序数据，分桶设计：利用文档内嵌组，将一个时间段的数据聚合到一个文档里。大量减少文档数据量，大量减少索引占用空间。</p>
<h2 id="18-设计模式集锦">18 设计模式集锦</h2>
<p>大文档，很多字段，很多索引。列转行。列数据变化为数组。多语言多国家属性，类似字段需要建立很多索引。转化为数组，一个索引解决所有查询问题。</p>
<p>模型灵活了，如何管理文档不同版本？增加一个版本字段。schema_version。</p>
<p>统计网页点击流量。近似计算。if random(0,9)==0 increment by 10。</p>
<p>业绩排名、游戏排名等精确统计。消耗资源多，聚合计算时间长。用预聚合字段。模型中直接增加统计字段，每次更新数据时同时更新统计值。</p>
<h2 id="19-写操作事务-writeconcern">19 写操作事务 writeConcern</h2>
<p><a href="https://www.mongodb.com/docs/manual/reference/write-concern/">write-concern | mongodb</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">{ w: &lt;value&gt;, j: &lt;boolean&gt;, wtimeout: &lt;number&gt; }
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">rs</span><span class="p">.</span><span class="nx">status</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">drop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="nx">count</span><span class="o">:</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="nx">writeConcern</span><span class="o">:</span> <span class="p">{</span><span class="nx">w</span><span class="o">:</span> <span class="s2">&#34;majority&#34;</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="nx">count</span><span class="o">:</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="nx">writeConcern</span><span class="o">:</span> <span class="p">{</span><span class="nx">w</span><span class="o">:</span> <span class="mi">3</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="nx">count</span><span class="o">:</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="nx">writeConcern</span><span class="o">:</span> <span class="p">{</span><span class="nx">w</span><span class="o">:</span> <span class="mi">1</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="c1">// WriteResult({ &#34;nInserted&#34; : 1, &#34;writeConcernError&#34; : [ ] })
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="nx">count</span><span class="o">:</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="nx">writeConcern</span><span class="o">:</span> <span class="p">{</span><span class="nx">w</span><span class="o">:</span> <span class="mi">4</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [Error] 100 - Not enough data-bearing nodes
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">find</span><span class="p">()</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// 配置延迟节点，模拟网络延迟
</span></span></span><span class="line"><span class="cl"><span class="nx">conf</span><span class="o">=</span><span class="nx">rs</span><span class="p">.</span><span class="nx">conf</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="c1">// {
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;_id&#34;: 3,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;host&#34;: &#34;mongo3:27017&#34;,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;arbiterOnly&#34;: false,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;buildIndexes&#34;: true,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;hidden&#34;: false,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;priority&#34;: 1,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;tags&#34;: { },
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;slaveDelay&#34;: NumberLong(&#34;0&#34;),
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;votes&#34;: 1
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="nx">conf</span><span class="p">.</span><span class="nx">members</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nx">slaveDelay</span><span class="o">=</span><span class="mi">5</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 没有选举权
</span></span></span><span class="line"><span class="cl"><span class="nx">conf</span><span class="p">.</span><span class="nx">members</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nx">priority</span><span class="o">=</span><span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="nx">rs</span><span class="p">.</span><span class="nx">reconfig</span><span class="p">(</span><span class="nx">conf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// {
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;_id&#34;: 3,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;host&#34;: &#34;mongo3:27017&#34;,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;arbiterOnly&#34;: false,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;buildIndexes&#34;: true,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;hidden&#34;: false,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;priority&#34;: 0,
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;tags&#34;: { },
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;slaveDelay&#34;: NumberLong(&#34;5&#34;),
</span></span></span><span class="line"><span class="cl"><span class="c1">//     &#34;votes&#34;: 1
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="nx">count</span><span class="o">:</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="nx">writeConcern</span><span class="o">:</span> <span class="p">{</span><span class="nx">w</span><span class="o">:</span> <span class="mi">3</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Result Time 5s
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 3s 超时
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="nx">count</span><span class="o">:</span><span class="mi">5</span><span class="p">},</span> <span class="p">{</span><span class="nx">writeConcern</span><span class="o">:</span> <span class="p">{</span><span class="nx">w</span><span class="o">:</span> <span class="mi">3</span><span class="p">,</span> <span class="nx">wtimeout</span><span class="o">:</span><span class="mi">3000</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="c1">// writeResult({
</span></span></span><span class="line"><span class="cl"><span class="c1">//         &#34;nInserted&#34; : 1,
</span></span></span><span class="line"><span class="cl"><span class="c1">//         &#34;writeConcernError&#34; : {
</span></span></span><span class="line"><span class="cl"><span class="c1">//                 &#34;code&#34; : 64,
</span></span></span><span class="line"><span class="cl"><span class="c1">//                 &#34;codeName&#34; : &#34;WriteConcernFailed&#34;,
</span></span></span><span class="line"><span class="cl"><span class="c1">//                 &#34;errmsg&#34; : &#34;waiting for replication timed out&#34;,
</span></span></span><span class="line"><span class="cl"><span class="c1">//                 &#34;errInfo&#34; : {
</span></span></span><span class="line"><span class="cl"><span class="c1">//                         &#34;wtimeout&#34; : true,
</span></span></span><span class="line"><span class="cl"><span class="c1">//                         &#34;writeConcern&#34; : {
</span></span></span><span class="line"><span class="cl"><span class="c1">//                                 &#34;w&#34; : 3,
</span></span></span><span class="line"><span class="cl"><span class="c1">//                                 &#34;wtimeout&#34; : 3000,
</span></span></span><span class="line"><span class="cl"><span class="c1">//                                 &#34;provenance&#34; : &#34;clientSupplied&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//                         }
</span></span></span><span class="line"><span class="cl"><span class="c1">//                 }
</span></span></span><span class="line"><span class="cl"><span class="c1">//         }
</span></span></span><span class="line"><span class="cl"><span class="c1">// })
</span></span></span></code></pre></div><h2 id="20-读操作事务-readpreference">20 读操作事务 readPreference</h2>
<ul>
<li><a href="https://www.mongodb.com/docs/manual/core/read-preference/">Read Preference | mongodb</a></li>
<li><a href="https://www.mongodb.com/docs/v4.4/core/read-preference-tags/">Read Preference Tags | mongodb</a></li>
</ul>
<p>配置：</p>
<ul>
<li>mongodb://&hellip;/?replicaSet=rs&amp;readPrefence=secondary</li>
<li>通过驱动API，MongoCollection.withReadPrefence(ReadPrefence readPref)</li>
<li>Mongo Shell: db.collection.find({}).readPref(&ldquo;secondary&rdquo;)</li>
</ul>
<p>实验：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// 主节点
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">drop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="nx">count</span><span class="o">:</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="nx">writeConcern</span><span class="o">:</span> <span class="p">{</span><span class="nx">w</span><span class="o">:</span> <span class="mi">3</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">find</span><span class="p">({})</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1 line
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">find</span><span class="p">({}).</span><span class="nx">readPref</span><span class="p">(</span><span class="s2">&#34;secondary&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1 line
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 从节点 1、2
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">find</span><span class="p">({})</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1 line
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">fsyncLock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="c1">// now locked against writes, use db.fsyncUnlock() to unlock 锁住写入
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 主节点
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="nx">count</span><span class="o">:</span><span class="mi">3</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">find</span><span class="p">({})</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 2 line
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">find</span><span class="p">().</span><span class="nx">readPref</span><span class="p">(</span><span class="s2">&#34;secondary&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1 line
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 从节点 1、2
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">fsyncUnlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 主节点
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">find</span><span class="p">().</span><span class="nx">readPref</span><span class="p">(</span><span class="s2">&#34;secondary&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="21-读操作事务-readconcern">21 读操作事务 readConcern</h2>
<ul>
<li><a href="https://www.mongodb.com/docs/manual/reference/read-concern/">read-concern | mongodb</a></li>
</ul>
<p>enableMajorityReadConcern: true</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// 从节点 1、2
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">fsyncLock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 主节点
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="nx">count</span><span class="o">:</span><span class="mi">3</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">find</span><span class="p">().</span><span class="nx">readConcern</span><span class="p">(</span><span class="s2">&#34;local&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 如果在一个写操作到达大多数节点前读取了这个写操作，然后因为系统故障该操作回滚了，则发生了脏读
</span></span></span><span class="line"><span class="cl"><span class="c1">// {readConcern: &#34;majority&#34;} 可以避免脏读
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">test</span><span class="p">.</span><span class="nx">find</span><span class="p">().</span><span class="nx">readConcern</span><span class="p">(</span><span class="s2">&#34;majority&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>majority 约为：Read Committed。</p>
<h2 id="22-多文档事务">22 多文档事务</h2>
<ul>
<li>Atomocity 原子性</li>
<li>Consistency 一致性 writeConcern,readConcern</li>
<li>Isolation 隔离性 readConcern</li>
<li>Durability 持久性 Journal and Replication</li>
</ul>
<p>实验启用事务后的隔离性：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">drop</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">insertMany</span><span class="p">([{</span><span class="nx">x</span><span class="o">:</span><span class="mi">1</span><span class="p">},{</span><span class="nx">x</span><span class="o">:</span><span class="mi">2</span><span class="p">}]);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">session</span> <span class="o">=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">getMongo</span><span class="p">().</span><span class="nx">startSession</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">session</span><span class="p">.</span><span class="nx">startTransaction</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">coll</span> <span class="o">=</span> <span class="nx">session</span><span class="p">.</span><span class="nx">getDatabase</span><span class="p">(</span><span class="s1">&#39;test&#39;</span><span class="p">).</span><span class="nx">getCollection</span><span class="p">(</span><span class="s1">&#39;tx&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">coll</span><span class="p">.</span><span class="nx">updateOne</span><span class="p">({</span><span class="nx">x</span><span class="o">:</span><span class="mi">2</span><span class="p">},</span> <span class="p">{</span><span class="nx">$set</span><span class="o">:</span> <span class="p">{</span><span class="nx">y</span><span class="o">:</span><span class="mi">1</span><span class="p">}});</span>
</span></span><span class="line"><span class="cl"><span class="c1">// {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   acknowledged: true,
</span></span></span><span class="line"><span class="cl"><span class="c1">//   insertedId: null,
</span></span></span><span class="line"><span class="cl"><span class="c1">//   matchedCount: 1,
</span></span></span><span class="line"><span class="cl"><span class="c1">//   modifiedCount: 1,
</span></span></span><span class="line"><span class="cl"><span class="c1">//   upsertedCount: 0
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="nx">coll</span><span class="p">.</span><span class="nx">find</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [
</span></span></span><span class="line"><span class="cl"><span class="c1">//   { _id: ObjectId(&#34;64478074fcfac90fb90f1a65&#34;), x: 1 },
</span></span></span><span class="line"><span class="cl"><span class="c1">//   { _id: ObjectId(&#34;64478074fcfac90fb90f1a66&#34;), x: 2, y: 1 }
</span></span></span><span class="line"><span class="cl"><span class="c1">// ]
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">find</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [
</span></span></span><span class="line"><span class="cl"><span class="c1">//   { _id: ObjectId(&#34;64478074fcfac90fb90f1a65&#34;), x: 1 },
</span></span></span><span class="line"><span class="cl"><span class="c1">//   { _id: ObjectId(&#34;64478074fcfac90fb90f1a66&#34;), x: 2 }
</span></span></span><span class="line"><span class="cl"><span class="c1">// ]
</span></span></span><span class="line"><span class="cl"><span class="nx">session</span><span class="p">.</span><span class="nx">commitTransaction</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">find</span><span class="p">();</span>
</span></span></code></pre></div><p>实验可重复读 Repeatable Read：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">drop</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">insertMany</span><span class="p">([{</span><span class="nx">x</span><span class="o">:</span><span class="mi">1</span><span class="p">},{</span><span class="nx">x</span><span class="o">:</span><span class="mi">2</span><span class="p">}]);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">session</span> <span class="o">=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">getMongo</span><span class="p">().</span><span class="nx">startSession</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">session</span><span class="p">.</span><span class="nx">startTransaction</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">readConcern</span><span class="o">:</span> <span class="p">{</span><span class="s2">&#34;level&#34;</span><span class="o">:</span> <span class="s2">&#34;snapshot&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">writeConcern</span><span class="o">:</span> <span class="p">{</span><span class="s2">&#34;w&#34;</span><span class="o">:</span> <span class="s2">&#34;majority&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">coll</span> <span class="o">=</span> <span class="nx">session</span><span class="p">.</span><span class="nx">getDatabase</span><span class="p">(</span><span class="s1">&#39;test&#39;</span><span class="p">).</span><span class="nx">getCollection</span><span class="p">(</span><span class="s1">&#39;tx&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">coll</span><span class="p">.</span><span class="nx">findOne</span><span class="p">({</span><span class="nx">x</span><span class="o">:</span> <span class="mi">1</span><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 模拟事务之外的操作
</span></span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">updateOne</span><span class="p">({</span><span class="nx">x</span><span class="o">:</span><span class="mi">1</span><span class="p">},</span> <span class="p">{</span><span class="nx">$set</span><span class="o">:</span> <span class="p">{</span><span class="nx">y</span><span class="o">:</span><span class="mi">1</span><span class="p">}});</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">find</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [
</span></span></span><span class="line"><span class="cl"><span class="c1">//   { _id: ObjectId(&#34;644782c3fcfac90fb90f1a69&#34;), x: 1, y: 1 },
</span></span></span><span class="line"><span class="cl"><span class="c1">//   { _id: ObjectId(&#34;644782c3fcfac90fb90f1a6a&#34;), x: 2 }
</span></span></span><span class="line"><span class="cl"><span class="c1">// ]
</span></span></span><span class="line"><span class="cl"><span class="nx">coll</span><span class="p">.</span><span class="nx">findOne</span><span class="p">({</span><span class="nx">x</span><span class="o">:</span> <span class="mi">1</span><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="c1">// { _id: ObjectId(&#34;644782c3fcfac90fb90f1a69&#34;), x: 1 }
</span></span></span><span class="line"><span class="cl"><span class="nx">session</span><span class="p">.</span><span class="nx">abortTransaction</span><span class="p">();</span>
</span></span></code></pre></div><p>实验写冲突：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">drop</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">tx</span><span class="p">.</span><span class="nx">insertMany</span><span class="p">([{</span><span class="nx">x</span><span class="o">:</span><span class="mi">1</span><span class="p">},{</span><span class="nx">x</span><span class="o">:</span><span class="mi">2</span><span class="p">}]);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// shell 1、2
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">session</span> <span class="o">=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">getMongo</span><span class="p">().</span><span class="nx">startSession</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">session</span><span class="p">.</span><span class="nx">startTransaction</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">readConcern</span><span class="o">:</span> <span class="p">{</span><span class="s2">&#34;level&#34;</span><span class="o">:</span> <span class="s2">&#34;snapshot&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">writeConcern</span><span class="o">:</span> <span class="p">{</span><span class="s2">&#34;w&#34;</span><span class="o">:</span> <span class="s2">&#34;majority&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">coll</span> <span class="o">=</span> <span class="nx">session</span><span class="p">.</span><span class="nx">getDatabase</span><span class="p">(</span><span class="s1">&#39;test&#39;</span><span class="p">).</span><span class="nx">getCollection</span><span class="p">(</span><span class="s1">&#39;tx&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// shell 1
</span></span></span><span class="line"><span class="cl"><span class="nx">coll</span><span class="p">.</span><span class="nx">updateOne</span><span class="p">({</span><span class="nx">x</span><span class="o">:</span><span class="mi">1</span><span class="p">},</span> <span class="p">{</span><span class="nx">$set</span><span class="o">:</span> <span class="p">{</span><span class="nx">y</span><span class="o">:</span><span class="mi">1</span><span class="p">}});</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ok
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// shell 2
</span></span></span><span class="line"><span class="cl"><span class="nx">coll</span><span class="p">.</span><span class="nx">updateOne</span><span class="p">({</span><span class="nx">x</span><span class="o">:</span><span class="mi">1</span><span class="p">},</span> <span class="p">{</span><span class="nx">$set</span><span class="o">:</span> <span class="p">{</span><span class="nx">y</span><span class="o">:</span><span class="mi">1</span><span class="p">}});</span>
</span></span><span class="line"><span class="cl"><span class="c1">// MongoServerError: WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.
</span></span></span><span class="line"><span class="cl"><span class="c1">// session.abortTransaction();
</span></span></span></code></pre></div><ul>
<li>事务默认 60s 内完成。</li>
<li>多文档事务中的读操作必须使用主节点读。</li>
</ul>
<h2 id="23-change-stream">23 Change Stream</h2>
<p>类似触发器。</p>
<ul>
<li>触发方式：异步 | 同步（事务保证）</li>
<li>触发位置：回调事件 | 数据库触发器</li>
<li>触发次数：每个订阅事件的客户端 | 1次</li>
<li>故障恢复：从上此断点重新触发 | 事务回滚</li>
</ul>
<p>基于 oplog 实现。被追踪的变更事件主要包括：</p>
<ul>
<li>insert/update/delete</li>
<li>drop</li>
<li>rename</li>
<li>dropDatabase</li>
<li>invalidate：drop/rename/dropDatabase 将导致 invalidate 被触发，并关闭 chage stream</li>
</ul>
<p>可重复读。未开启 majority readConcern 的集群无法使用 Change Stream。当集群无法满足 {w: &ldquo;majority&rdquo;} 时，不会触发 Change Stream。</p>
<p>可以使用集合管道的过滤步骤过滤事件。</p>
<h2 id="25-分片集群机制及原理">25 分片集群机制及原理</h2>
<ul>
<li>路由节点 mongos</li>
<li>配置节点 mongod</li>
<li>数据节点 mongod</li>
</ul>
<p>分片集群数据发布方式</p>
<ul>
<li>基于范围 查询性能好，数据分布可能不均匀</li>
<li>基于哈希 发布均匀，范围查询效率低；日志、物联网</li>
<li>Zone 数据天然分区</li>
</ul>
<h2 id="25-分片集群设计">25 分片集群设计</h2>
<ul>
<li>片键 shard key 文档中的一个字段</li>
<li>文档 doc</li>
<li>块 chunk</li>
<li>分片 shard</li>
<li>集群 cluster</li>
</ul>
<p>片键：</p>
<ul>
<li>取值基数范围要大</li>
<li>取值范围应尽可能均匀</li>
<li>对主要查询要具有定向能力</li>
</ul>
<p>组合片键。{user_id:1, time:1}</p>
<h2 id="27-分片集群搭建及扩容">27 分片集群搭建及扩容</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1 配置域名解析</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 2 准备分片目录</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3 创建第1个分片复制集 member1:27010 member3:27010 member5:27010</span>
</span></span><span class="line"><span class="cl">mongod --bind_ip_all --replSet shard1 --shardsvr --wiredTigerCacheSizeGB <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># --shardsvr 标注为分片节点</span>
</span></span><span class="line"><span class="cl"><span class="c1"># --wiredTigerCacheSizeGB 1 缓存大小</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 4 初始化分片复制集</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 5 创建 config server 复制集 member1:27019 member3:27019 member5:27019</span>
</span></span><span class="line"><span class="cl">mongod --bind_ip_all --replSet config --configsvr --wiredTigerCacheSizeGB <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># --configsvr 标注为配置节点</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 6 创建 config server 复制集</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 7 搭建 mongos member1:27017</span>
</span></span><span class="line"><span class="cl">mongos --bind_ip_all --configdb config/member1:27019,member3:27019,member5:27019,
</span></span><span class="line"><span class="cl"><span class="c1"># 连接到 mongos 添加分片</span>
</span></span><span class="line"><span class="cl">sh.addShard<span class="o">(</span><span class="s2">&#34;shard1/member1:27010,member3:27010,member5:27010&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 8 创建分片表</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 连接到 mongos member1:27017</span>
</span></span><span class="line"><span class="cl">sh.enableSharding<span class="o">(</span><span class="s2">&#34;foo&#34;</span><span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">sh.shardCollection<span class="o">(</span><span class="s2">&#34;foo.bar&#34;</span>, <span class="o">{</span>_id: <span class="s2">&#34;hashed&#34;</span><span class="o">})</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">sh.status<span class="o">()</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 插入测试数据</span>
</span></span><span class="line"><span class="cl">use foo
</span></span><span class="line"><span class="cl"><span class="k">for</span><span class="o">(</span>var <span class="nv">i</span><span class="o">=</span>0<span class="p">;</span>1&lt;10000<span class="p">;</span>i++<span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  db.bar.insert<span class="o">({</span>i:i<span class="o">))</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 9 创建第2个分片复制集  member2:27011 member4:27011 member6:27011</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 10 初始化第2个分片复制集</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 11 加入第2个分片</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 连接到 mongos 添加分片</span>
</span></span><span class="line"><span class="cl">sh.addShard<span class="o">(</span><span class="s2">&#34;shard2/member2:27011,member4:27011,member6:27011&#34;</span><span class="o">)</span>
</span></span></code></pre></div><h2 id="28-监控最佳实践">28 监控最佳实践</h2>
<ul>
<li>MongoDB Ops Manager</li>
<li>Percona</li>
<li>程序脚本</li>
</ul>
<p>如何获取监控数据：</p>
<ul>
<li>db.serverStatus() 从上次开机到现在为止</li>
<li>db.isMaster()</li>
<li>monogostats</li>
<li>db.serverStatus().opcounters</li>
</ul>
<h2 id="29-备份与恢复">29 备份与恢复</h2>
<ul>
<li>延迟节点备份</li>
<li>全量备份+Oplog增量，Oplog 幂等性 支持重放</li>
<li>mongodump &ndash;oplog；不遗漏 dump 时的数据</li>
<li>mongorestore &ndash;oplogReplay</li>
</ul>
<h2 id="31-安全架构">31 安全架构</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">createRole</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">   <span class="nx">role</span><span class="o">:</span> <span class="s2">&#34;readWriteRole&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="nx">privileges</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">resource</span><span class="o">:</span> <span class="p">{</span> <span class="nx">db</span><span class="o">:</span> <span class="s2">&#34;myDatabase&#34;</span><span class="p">,</span> <span class="nx">collection</span><span class="o">:</span> <span class="s2">&#34;sample&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">actions</span><span class="o">:</span> <span class="p">[</span> <span class="s2">&#34;find&#34;</span><span class="p">,</span> <span class="s2">&#34;insert&#34;</span><span class="p">,</span> <span class="s2">&#34;update&#34;</span><span class="p">,</span> <span class="s2">&#34;remove&#34;</span> <span class="p">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">   <span class="p">],</span>
</span></span><span class="line"><span class="cl">   <span class="nx">roles</span><span class="o">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">role</span><span class="o">:</span> <span class="s1">&#39;read&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">db</span><span class="o">:</span> <span class="s1">&#39;sampledb&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">createUser</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">user</span><span class="o">:</span> <span class="s1">&#39;sampleUser&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">pwd</span><span class="o">:</span> <span class="s1">&#39;pwd&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">roles</span><span class="o">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">role</span><span class="o">:</span> <span class="s1">&#39;readWriteRole&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">db</span><span class="o">:</span> <span class="s1">&#39;admin&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span></code></pre></div><h2 id="32-安全加固实践">32 安全加固实践</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 禁止脚本执行</span>
</span></span><span class="line"><span class="cl">--noscripting
</span></span><span class="line"><span class="cl"><span class="c1"># 必须鉴权</span>
</span></span><span class="line"><span class="cl">--auth
</span></span></code></pre></div><h2 id="33-索引机制一">33 索引机制（一）</h2>
<ul>
<li>Index、Key、DataPage</li>
<li>Covered Query/FETCH</li>
<li>IXSCAN/COLLSCAN 索引扫描/集合扫描</li>
<li>Big O Notation 时间复杂度</li>
<li>Query Shape 查询的形状</li>
<li>Index Prefix 索引前缀</li>
<li>Selectivity 过滤器 选择区别度大的</li>
<li>B-数结构</li>
</ul>
<p>B-树和B+树都是常见的多路搜索树结构，常用于数据库索引和文件系统中。它们的主要区别在于如何存储和检索数据。</p>
<p>B-树是一种自平衡的搜索树，其中每个节点可以存储多个键和对应的值，并支持在O(log n)时间内进行搜索、插入和删除操作。B-树的每个节点都包含了一个子节点数组，可以用来搜索和遍历树。在B-树中，所有节点都可以存储键和值，而非仅仅是叶子节点。</p>
<p>B+树与B-树非常相似，但是只有叶节点包含了所有的键和值，而且所有叶节点都通过指针链接在一起。这意味着在B+树上进行查找只需要搜索一条从根节点到叶节点的路径，而在B-树中可能需要搜索多个节点。B+树的非叶子节点只包含键，而不包含值，这使得B+树在维护索引时更加高效。</p>
<p>因此，B+树比B-树更适用于存储和检索大量数据，尤其是数据库和文件系统中的索引。B+树的叶子节点形成了一个有序链表，可以方便地进行区间查找和遍历。而B-树则更适合内存较小的情况下，例如缓存。</p>
<h2 id="34-索引机制二">34 索引机制（二）</h2>
<p>.explain(true) 查看：</p>
<ul>
<li><code>executionTimeMillis</code></li>
<li><code>totalDocsExamined</code></li>
<li><code>executionStages.docsExamined</code></li>
<li><code>executionStages.inputStage.stage</code></li>
</ul>
<p>组合索引 ESR 原则，Equal(最前) Sort(中间) Range(最后)</p>
<p>db.member.createIndex({city:1}, {background: true})</p>
<h2 id="36-性能诊断工具">36 性能诊断工具</h2>
<p>mongostat 了解 MongoDB 运行状态的工具。</p>
<ul>
<li>dirty 没有刷盘的比例 &lt;5%</li>
<li>used</li>
<li>qrw 排队的请求</li>
<li>con 连接数量</li>
</ul>
<p>mongotop 了解集合压力状态</p>
<p><a href="https://github.com/rueckstiess/mtools">mtools</a></p>
<h2 id="41-应用场景及选型">41 应用场景及选型</h2>
<p>优点：</p>
<ul>
<li>横向扩展能力，数据量或并发量增加时候架构可以自动扩容</li>
<li>灵活模型，适合迭代开发，数据模型多变场景</li>
<li>JSON 数据结构，适合微服务/REST API</li>
</ul>
<h2 id="44-关系型数据库迁移">44 关系型数据库迁移</h2>
<p>从基于关系型数据库应用迁移到 MongoDB 的理由：</p>
<ul>
<li>高并发需求（数千 - 数十万 ops），关系型数据库不容易扩展</li>
<li>快速迭代 - 关系型模式太严谨</li>
<li>灵活的 JSON 模式</li>
<li>大数据量需求</li>
<li>地理位置查询</li>
<li>多数据中心跨地域部署</li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="https://time.geekbang.org/course/intro/100040001">MongoDB 高手课</a></li>
<li><a href="https://github.com/minhhungit/mongodb-cluster-docker-compose">mongodb-cluster-docker-compose</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>手把手教你落地 DDD</title>
      <link>https://zyf.im/2023/03/01/hands-on-ddd/</link>
      <pubDate>Wed, 01 Mar 2023 17:00:00 +0000</pubDate>
      <guid>https://zyf.im/2023/03/01/hands-on-ddd/</guid>
      <description>&lt;h2 id=&#34;01-ddd-小传&#34;&gt;01 DDD 小传&lt;/h2&gt;
&lt;p&gt;DDD（Domain-Driven Design）由 Eric Evans 于 2003 年提出，本质是一套用于开发复杂软件的系统化方法学与思想，核心关注“领域建模”。它源于面向对象方法学与敏捷开发，可理解为“把面向对象做对”（OO Done right），并将建模、设计与落地实践更体系化。&lt;/p&gt;
&lt;p&gt;传统面向对象在企业应用中常见问题是重技术轻业务、领域建模难以掌握、协作与沟通不足、难以适应频繁变化。DDD 通过模式、原则与实践（如通用语言、限界上下文、模型驱动设计等）促进业务与技术协作沉淀知识，并用“柔性设计/演进”避免过度设计、支持持续重构。&lt;/p&gt;
&lt;p&gt;近年因数字化带来更高复杂度与变化，以及敏捷/DevOps 与 Spring Boot、微服务、整洁架构、事件驱动、CQRS 等生态成熟，DDD 才从“屠龙术”变成广泛可用的方法。&lt;/p&gt;
&lt;h2 id=&#34;02-迭代一-企业管理系统&#34;&gt;02 迭代一 企业管理系统&lt;/h2&gt;
&lt;p&gt;企业为了满足管理诉求，希望员工每周在系统中填报自己在哪些项目上花了多少时间，也就是所谓的报工时。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;需求&lt;/th&gt;
          &lt;th&gt;核心对象/范围&lt;/th&gt;
          &lt;th&gt;主要功能（CRUD/流程）&lt;/th&gt;
          &lt;th&gt;关键规则/约束&lt;/th&gt;
          &lt;th&gt;关键字段/记录&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;多租户&lt;/td&gt;
          &lt;td&gt;租户（企业）&lt;/td&gt;
          &lt;td&gt;管理多个租户（企业）&lt;/td&gt;
          &lt;td&gt;每个租户代表一个使用 SaaS 的企业&lt;/td&gt;
          &lt;td&gt;—&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;人员与组织管理&lt;/td&gt;
          &lt;td&gt;部门、员工&lt;/td&gt;
          &lt;td&gt;部门：增删改查；员工：增删改查；员工分配部门&lt;/td&gt;
          &lt;td&gt;员工只能从属于一个部门&lt;/td&gt;
          &lt;td&gt;部门层级示例：开发中心 → 开发组；职能部门（人事/财务等）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;项目管理&lt;/td&gt;
          &lt;td&gt;客户、合同、项目&lt;/td&gt;
          &lt;td&gt;客户：增删改查；合同：增删改查 + 开始/结束；项目：增删改查 + 开始/结束&lt;/td&gt;
          &lt;td&gt;客户对应客户经理；合同对应销售；项目对应项目经理；合同下可有多个项目&lt;/td&gt;
          &lt;td&gt;合同/项目开始时间、结束时间等&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;人员分配&lt;/td&gt;
          &lt;td&gt;项目成员关系&lt;/td&gt;
          &lt;td&gt;为项目分配人员；人员退出项目&lt;/td&gt;
          &lt;td&gt;项目可多人；员工可同时参与多个项目；需记录投入比例&lt;/td&gt;
          &lt;td&gt;投入百分比（人-项目）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;工时登记&lt;/td&gt;
          &lt;td&gt;工时（周报/日记录）&lt;/td&gt;
          &lt;td&gt;员工每周填报工时；查询；修改；填写备注&lt;/td&gt;
          &lt;td&gt;仅当员工已分配到项目后，才可在该项目报工时&lt;/td&gt;
          &lt;td&gt;日期、项目、投入时长、备注&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img alt=&#34;image&#34; loading=&#34;lazy&#34; src=&#34;https://github.com/user-attachments/assets/24f28aeb-1862-4289-ad76-3d7c0ba502e7&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;03-事件风暴-识别领域事件&#34;&gt;03 事件风暴 识别领域事件&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&#34;image&#34; loading=&#34;lazy&#34; src=&#34;https://github.com/user-attachments/assets/a7cef40f-519f-4be2-b514-b577546ca51e&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;领域事件是什么&#34;&gt;领域事件是什么&lt;/h3&gt;
&lt;p&gt;**领域事件（Domain Event）**用来表达：在业务流程的某一步完成后，所引发并被业务关心的“结果”。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="01-ddd-小传">01 DDD 小传</h2>
<p>DDD（Domain-Driven Design）由 Eric Evans 于 2003 年提出，本质是一套用于开发复杂软件的系统化方法学与思想，核心关注“领域建模”。它源于面向对象方法学与敏捷开发，可理解为“把面向对象做对”（OO Done right），并将建模、设计与落地实践更体系化。</p>
<p>传统面向对象在企业应用中常见问题是重技术轻业务、领域建模难以掌握、协作与沟通不足、难以适应频繁变化。DDD 通过模式、原则与实践（如通用语言、限界上下文、模型驱动设计等）促进业务与技术协作沉淀知识，并用“柔性设计/演进”避免过度设计、支持持续重构。</p>
<p>近年因数字化带来更高复杂度与变化，以及敏捷/DevOps 与 Spring Boot、微服务、整洁架构、事件驱动、CQRS 等生态成熟，DDD 才从“屠龙术”变成广泛可用的方法。</p>
<h2 id="02-迭代一-企业管理系统">02 迭代一 企业管理系统</h2>
<p>企业为了满足管理诉求，希望员工每周在系统中填报自己在哪些项目上花了多少时间，也就是所谓的报工时。</p>
<table>
  <thead>
      <tr>
          <th>需求</th>
          <th>核心对象/范围</th>
          <th>主要功能（CRUD/流程）</th>
          <th>关键规则/约束</th>
          <th>关键字段/记录</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>多租户</td>
          <td>租户（企业）</td>
          <td>管理多个租户（企业）</td>
          <td>每个租户代表一个使用 SaaS 的企业</td>
          <td>—</td>
      </tr>
      <tr>
          <td>人员与组织管理</td>
          <td>部门、员工</td>
          <td>部门：增删改查；员工：增删改查；员工分配部门</td>
          <td>员工只能从属于一个部门</td>
          <td>部门层级示例：开发中心 → 开发组；职能部门（人事/财务等）</td>
      </tr>
      <tr>
          <td>项目管理</td>
          <td>客户、合同、项目</td>
          <td>客户：增删改查；合同：增删改查 + 开始/结束；项目：增删改查 + 开始/结束</td>
          <td>客户对应客户经理；合同对应销售；项目对应项目经理；合同下可有多个项目</td>
          <td>合同/项目开始时间、结束时间等</td>
      </tr>
      <tr>
          <td>人员分配</td>
          <td>项目成员关系</td>
          <td>为项目分配人员；人员退出项目</td>
          <td>项目可多人；员工可同时参与多个项目；需记录投入比例</td>
          <td>投入百分比（人-项目）</td>
      </tr>
      <tr>
          <td>工时登记</td>
          <td>工时（周报/日记录）</td>
          <td>员工每周填报工时；查询；修改；填写备注</td>
          <td>仅当员工已分配到项目后，才可在该项目报工时</td>
          <td>日期、项目、投入时长、备注</td>
      </tr>
  </tbody>
</table>
<p><img alt="image" loading="lazy" src="https://github.com/user-attachments/assets/24f28aeb-1862-4289-ad76-3d7c0ba502e7"></p>
<h2 id="03-事件风暴-识别领域事件">03 事件风暴 识别领域事件</h2>
<p><img alt="image" loading="lazy" src="https://github.com/user-attachments/assets/a7cef40f-519f-4be2-b514-b577546ca51e"></p>
<h3 id="领域事件是什么">领域事件是什么</h3>
<p>**领域事件（Domain Event）**用来表达：在业务流程的某一步完成后，所引发并被业务关心的“结果”。</p>
<p>常见表达方式有一个很实用的习惯：</p>
<ol>
<li>完成时</li>
<li>被动语态</li>
</ol>
<p>在 DDD 的各种命名中，优先使用约定俗成的业务术语，因为这些词本身就承载着业务知识与共识。</p>
<p><img alt="image" loading="lazy" src="https://github.com/user-attachments/assets/552968ac-8fdb-4929-aa7c-deb31693f9ea"></p>
<h3 id="领域事件的边界两条关键提醒">领域事件的边界：两条关键提醒</h3>
<p>在识别领域事件时，有两点特别容易踩坑：</p>
<ol>
<li>不要把技术事件当成领域事件。领域事件强调的是 <strong>业务上发生了什么</strong>，而不是 <strong>系统内部做了什么</strong>。</li>
<li>查询功能不算领域事件。查询行为通常不会改变业务世界的状态，因此一般 <strong>不构成领域事件</strong>。领域事件应当满足一个直觉标准：
<ol>
<li>对某样事物产生了影响</li>
<li>该影响是业务要记录/追踪的</li>
<li>或者它触发了对外通知（发消息给其他人或系统）</li>
</ol>
</li>
</ol>
<p>换句话说，领域事件通常对应“业务状态发生改变”或“业务需要对外告知”。</p>
<h3 id="如何识别领域事件先发散再收敛">如何识别领域事件：先发散，再收敛</h3>
<ol>
<li>个人发散：参与者各自写出自己理解的领域事件（尽量不互相干扰）</li>
<li>集体收敛：一起讨论、对齐含义、合并同类项、澄清歧义</li>
</ol>
<p>这种“先发散、后收敛、反复迭代”的方式，本质上就是一种结构化的头脑风暴：先把可能性铺开，再通过讨论把共识沉淀下来。</p>
<h3 id="统一语言事件风暴的隐性产出">统一语言：事件风暴的隐性产出</h3>
<p>在上述讨论过程中，其实已经在生成并强化 统一语言（Ubiquitous Language）。统一语言的核心是：</p>
<ul>
<li>业务人员和开发人员使用同一套词。</li>
<li>同一个词在不同人心里对应同一个概念。</li>
<li>语言一致意味着领域理解一致（语言是知识的载体）。</li>
</ul>
<p>它不是某个阶段的文档，而是贯穿 DDD 全过程的基础设施。</p>
<h3 id="事件风暴的精髓是协作">事件风暴的精髓是“协作”</h3>
<p>事件风暴表面上是在贴事件、理流程，但真正关键的价值在于：</p>
<ul>
<li>让不同角色共同参与。</li>
<li>通过讨论对齐概念与边界。</li>
<li>在协作中形成统一语言与共同理解。</li>
</ul>
<h2 id="04-事件风暴-识别命令和识别领域名词">04 事件风暴 识别命令和识别领域名词</h2>
<h3 id="命令是什么从领域事件反推">命令是什么：从领域事件反推</h3>
<p>命令 = 引发领域事件的操作。做法上可以理解为一条简单的逆向链路：</p>
<ol>
<li>先有领域事件（结果）</li>
<li>再问：是谁做了什么，才让这个结果发生？</li>
</ol>
<p>除了命令本身，事件风暴里往往还会顺手补齐两类“命令的上下文信息”：</p>
<ul>
<li>执行者是谁？命令由谁发起/执行：可能是某个用户、某个岗位角色，也可能是系统内的某个领域对象在扮演某种角色，甚至与权限体系的角色有关。</li>
<li>执行命令前需要查询什么数据？为了执行该命令，需要先拿到哪些信息（用于校验、决策、填充表单、定位对象等）。</li>
</ul>
<h3 id="识别领域名词从贴纸里提取名词性概念">识别领域名词：从贴纸里提取“名词性概念”</h3>
<p>这里的领域名词指：从命令、领域事件、执行者、查询数据中提取出来的名词性概念。举例理解：</p>
<ul>
<li>命令：签订合同。被影响的核心名词：合同</li>
<li>领域事件：合同已签订。本质上是名词“合同”的状态发生变化，并被业务记录为一个重要结果。</li>
</ul>
<h3 id="领域规则如何保存用领域规则表沉淀">领域规则如何保存：用“领域规则表”沉淀</h3>
<p>领域规则是关键领域知识，必须可维护、可追溯。仅靠便利贴或图：</p>
<ul>
<li>难长期保存</li>
<li>难版本化管理</li>
<li>难持续更新</li>
</ul>
<p>更可行的方式是建立一个领域规则表：</p>
<ul>
<li>将事件风暴阶段识别出的规则先记录进去</li>
<li>在后续领域建模中补充新规则</li>
<li>与领域模型放在一起，作为领域知识的核心资产共同维护</li>
</ul>
<h2 id="05-领域建模实践-上">05 领域建模实践 上</h2>
<p>从事件风暴产出的 <strong>领域名词</strong> 出发做领域建模：先将名词 <strong>暂定为实体</strong>，再梳理实体间的 <strong>关联类型（1:1 / 1:N / M:N）</strong>；建模时持续做 <strong>抽象提炼</strong> 以发现 <strong>隐含实体</strong>、让模型更贴近业务本质；最后用 <strong>注释</strong> 与 <strong>约束</strong> 补齐业务知识，其中 <strong>约束必须落到代码或数据库</strong> 并纳入<strong>业务规则表</strong>。</p>
<p><img alt="Image" loading="lazy" src="https://github.com/user-attachments/assets/8614d402-1b43-4249-b549-d39f04558424"></p>
<h2 id="06-领域建模实践-下">06 领域建模实践 下</h2>
<p>通过引入 <strong>关联实体</strong> 将 <strong>多对多</strong> 关系拆成两个 <strong>一对多</strong>，并为实体补齐必要的 <strong>业务操作</strong>；当模型复杂时，可按 <strong>模块拆分</strong> 来提升可理解性。领域模型完成后，还要抓住两项落地 DDD 的关键实践：补齐 <strong>业务规则</strong>、建立 <strong>词汇表（统一语言）</strong>，它们是确保模型可执行、团队认知一致的重点。</p>
<p><img alt="Image" loading="lazy" src="https://github.com/user-attachments/assets/8f2c8f7f-115c-48e4-965a-2bf3cb044959"></p>
<h2 id="07-领域建模原理">07 领域建模原理</h2>
<p>DDD 的 <strong>领域模型</strong> 是对领域知识的 <strong>提炼抽象</strong>，同时兼顾 <strong>业务表达</strong> 与 <strong>技术落地</strong> ；在 <strong>模型驱动设计</strong> 下要做到 <strong>领域模型 ↔ 业务需求一致</strong>、<strong>系统实现 ↔ 领域模型一致</strong>，从而确保 <strong>实现 ↔ 需求一致</strong>；而 <strong>统一语言</strong> 以领域模型与词汇表为基础，通过 <strong>持续协作</strong> 不断维护 <strong>模型、语言与实现</strong> 三者的 <strong>一致性</strong>。</p>
<h2 id="08-数据库设计">08 数据库设计</h2>
<p>数据库三范式（3 Normal Form）。1NF 字段原子性：</p>
<ol>
<li>表中的每一列都必须是不可再分的原子值。</li>
<li>不允许在一个字段里存多个值、列表或重复组。</li>
</ol>
<blockquote>
<p>例：主键是(订单ID, 商品ID)，若订单日期只依赖订单ID，就不符合 2NF，应把订单信息拆到订单表。</p>
</blockquote>
<p>2NF 消除对主键的部分依赖：</p>
<ol>
<li>在满足 1NF 的基础上，要求非主属性必须完全依赖于整个主键。</li>
<li>主要针对联合主键的情况：不能只依赖主键的一部分。</li>
</ol>
<blockquote>
<p>例：主键是(订单ID, 商品ID)，若订单日期只依赖订单ID，就不符合 2NF，应把订单信息拆到订单表。</p>
</blockquote>
<p>3NF 消除传递依赖</p>
<ol>
<li>在满足 2NF 的基础上，要求非主属性不依赖于其他非主属性（即没有传递依赖）。</li>
<li>非主属性应当直接依赖主键。</li>
</ol>
<blockquote>
<p>例：员工ID -&gt; 部门ID -&gt; 部门名称，则部门名称是通过部门ID间接决定的，应拆出部门表存部门ID, 部门名称。</p>
</blockquote>
<p>DDD 强调用 <strong>领域模型驱动数据库设计</strong>，以保证 <strong>业务、代码、数据一致</strong>：<strong>实体→表</strong>、<strong>属性→字段</strong>（按需求补充），并通过 <strong>词汇表/统一语言</strong> 规范命名；关系设计上，<strong>一对多用外键</strong>（云上常见 <strong>“虚拟外键”</strong>），<strong>多对多用关联表</strong>；相较“拍脑袋”式建模，这种方式更容易 <strong>与业务对齐</strong>、更符合 <strong>范式</strong>，还能把 <strong>静态数据结构</strong> 与 <strong>动态业务逻辑</strong> 统一纳入同一套模型中。</p>
<h2 id="09-分层架构">09 分层架构</h2>
<p>分层架构的原则是让<strong>不稳定的部分依赖稳定的部分</strong>：越靠内层越稳定、越靠外层越易变；其中<strong>领域层</strong>封装领域数据与规则，是系统核心，<strong>应用层</strong>作为门面把领域能力组织成粗粒度服务并处理事务、日志等横切关注点；外部交互由<strong>适配器层</strong>承担，主动适配器接收外部请求、被动适配器访问外部资源，二者只是在调用方向上不同，从而把技术细节隔离在外层，使应用层与领域层保持技术无关；数据访问上，<strong>仓库以聚合为单位</strong>（此处可暂按“一个实体一个仓库”理解），而 <strong>DAO 以表为单位</strong>；另设 <strong>common 层</strong>承载工具与框架，为各层提供支撑。</p>
<h2 id="10-代码实现-要贫血还是要充血">10 代码实现 要“贫血”还是要“充血”</h2>
<p>贫血模型（<strong>Anemic Domain Model</strong>，Fowler 2003）指领域对象 <strong>只有数据没有行为</strong>，违背面向对象原则；相对的富/充血模型（<strong>Rich Domain Model</strong>）强调领域对象应同时包含<strong>数据 + 行为</strong>，更贴近真正的面向对象（可理解为贫血更偏 <strong>面向过程</strong>、富模型更偏 <strong>面向对象</strong>），但企业实践中两者并非非黑即白，应结合多种范式；落到编码，核心要求是 <strong>代码与模型一致</strong>（发现模型问题要 <strong>及时改模型</strong>），并解决 <strong>层间依赖</strong>：将 <strong>DTO 移到应用层</strong>、用 <strong>依赖倒置</strong> 让适配器层依赖领域层，同时理解 <strong>领域模型 vs 设计模型（UML）</strong> 的区别，以便明确哪些和业务讨论、哪些仅在工程内部讨论，从而提升抽象分层下的沟通与效率。</p>
<h2 id="11-代码实现-创建领域对象实现领域逻辑">11 代码实现 创建领域对象、实现领域逻辑</h2>
<p>实现领域逻辑时应使用能表达领域知识的命名，即 <strong>DDD 表意接口（Intention-Revealing Interfaces）</strong>；一旦违背，常见坏味道是 <strong>函数过长</strong> 与 <strong>注释过多</strong>，可用 <strong>抽取函数</strong> 重构解决。需区分 <strong>领域逻辑</strong> 与 <strong>应用逻辑</strong>，关键在于是否包含领域专家关心的 <strong>领域知识</strong>；领域逻辑应优先放入 <strong>领域对象</strong>，不适合放入对象的则放入 <strong>领域服务</strong>。复杂对象的创建推荐用 <strong>工厂（Factory）</strong>，参数少可直接调用工厂，参数多可配合 <strong>Builder</strong>。模块划分有按性质（<strong>横切</strong>）与按耦合/业务（<strong>纵切</strong>）两种，建议采用 <strong>先横后竖</strong> 的策略。</p>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>正则表达式</title>
      <link>https://zyf.im/2023/03/01/regular-expression-getting-started/</link>
      <pubDate>Wed, 01 Mar 2023 15:31:15 +0000</pubDate>
      <guid>https://zyf.im/2023/03/01/regular-expression-getting-started/</guid>
      <description>&lt;h2 id=&#34;00&#34;&gt;00&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://regex101.com/&#34;&gt;regex101&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://jex.im/regulex/#!flags=&amp;amp;re=%5E%28a%7Cb%29*%3F%24&#34;&gt;regulex&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://ihateregex.io/expr/username/&#34;&gt;ihateregex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;01-元字符&#34;&gt;01 元字符&lt;/h2&gt;
&lt;p&gt;正则表达式 —— 字符串的规则。&lt;/p&gt;
&lt;p&gt;元字符就是指那些在正则表达式中具有特殊意义的专用字符。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;特殊单字符
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.&lt;/code&gt; 任意字符（换行除外）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\d&lt;/code&gt; 任意数字 &lt;code&gt;\D&lt;/code&gt; 任意非数字&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\w&lt;/code&gt; A-Za-z0-9_ &lt;code&gt;\W&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\s&lt;/code&gt; 空白符 &lt;code&gt;\S&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;空白符
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;\r&lt;/code&gt; 回车符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\n&lt;/code&gt; 换行符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\f&lt;/code&gt; 换页符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\t&lt;/code&gt; 制表符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\v&lt;/code&gt; 垂直制表符&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;范围
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;|&lt;/code&gt; 或&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[abc]&lt;/code&gt; 多选一&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[a-z]&lt;/code&gt; 之间&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[^abc]&lt;/code&gt; 取反，不能是括号中的任意单个元素&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;量词
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*&lt;/code&gt; 0&amp;lt;=&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+&lt;/code&gt; 1&amp;lt;=&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?&lt;/code&gt; 0或1&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{m}&lt;/code&gt; m&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{m,}&lt;/code&gt; m&amp;lt;=&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{m,n}&lt;/code&gt; m-n&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;02-量词与贪婪&#34;&gt;02 量词与贪婪&lt;/h2&gt;
&lt;p&gt;贪婪(Greedy) &lt;code&gt;*&lt;/code&gt;：匹配最长。在贪婪量词模式下，正则表达式会尽可能长地去匹配符合规则的字符串，且会回溯。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;preg_match_all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/a*/i&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;aaabb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$matches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$matches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;非贪婪(Reluctant) &lt;code&gt;+?&lt;/code&gt;：匹配最短。在非贪婪量词模式下，正则表达式会匹配尽可能短的字符串。&lt;/p&gt;
&lt;p&gt;ENV：Python3&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;re&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;findall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;a*&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;aaabb&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;# 贪婪模式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# [&amp;#39;aaa&amp;#39;, &amp;#39;&amp;#39;, &amp;#39;&amp;#39;, &amp;#39;&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;findall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;a*?&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;aaabb&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;# 非贪婪模式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# [&amp;#39;&amp;#39;, &amp;#39;a&amp;#39;, &amp;#39;&amp;#39;, &amp;#39;a&amp;#39;, &amp;#39;&amp;#39;, &amp;#39;a&amp;#39;, &amp;#39;&amp;#39;, &amp;#39;&amp;#39;, &amp;#39;&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;findall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#34;.+&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#34;the little cat&amp;#34; is a toy, it lokks &amp;#34;a little bad&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;# 贪婪模式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# [&amp;#39;&amp;#34;the little cat&amp;#34; is a toy, it lokks &amp;#34;a little bad&amp;#34;&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;findall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#34;.+?&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#34;the little cat&amp;#34; is a toy, it lokks &amp;#34;a little bad&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;# 非贪婪模式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# [&amp;#39;&amp;#34;the little cat&amp;#34;&amp;#39;, &amp;#39;&amp;#34;a little bad&amp;#34;&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;独占模式(Possessive) &lt;code&gt;++&lt;/code&gt;：同贪婪一样匹配最长。不过在独占量词模式下，正则表达式尽可能长地去匹配字符串，一旦匹配不成功就会结束匹配而 &lt;strong&gt;不会回溯&lt;/strong&gt;。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="00">00</h2>
<ul>
<li><a href="https://regex101.com/">regex101</a></li>
<li><a href="https://jex.im/regulex/#!flags=&amp;re=%5E%28a%7Cb%29*%3F%24">regulex</a></li>
<li><a href="https://ihateregex.io/expr/username/">ihateregex</a></li>
</ul>
<h2 id="01-元字符">01 元字符</h2>
<p>正则表达式 —— 字符串的规则。</p>
<p>元字符就是指那些在正则表达式中具有特殊意义的专用字符。</p>
<ul>
<li>特殊单字符
<ul>
<li><code>.</code> 任意字符（换行除外）</li>
<li><code>\d</code> 任意数字 <code>\D</code> 任意非数字</li>
<li><code>\w</code> A-Za-z0-9_ <code>\W</code></li>
<li><code>\s</code> 空白符 <code>\S</code></li>
</ul>
</li>
<li>空白符
<ul>
<li><code>\r</code> 回车符</li>
<li><code>\n</code> 换行符</li>
<li><code>\f</code> 换页符</li>
<li><code>\t</code> 制表符</li>
<li><code>\v</code> 垂直制表符</li>
</ul>
</li>
<li>范围
<ul>
<li><code>|</code> 或</li>
<li><code>[abc]</code> 多选一</li>
<li><code>[a-z]</code> 之间</li>
<li><code>[^abc]</code> 取反，不能是括号中的任意单个元素</li>
</ul>
</li>
<li>量词
<ul>
<li><code>*</code> 0&lt;=</li>
<li><code>+</code> 1&lt;=</li>
<li><code>?</code> 0或1</li>
<li><code>{m}</code> m</li>
<li><code>{m,}</code> m&lt;=</li>
<li><code>{m,n}</code> m-n</li>
</ul>
</li>
</ul>
<h2 id="02-量词与贪婪">02 量词与贪婪</h2>
<p>贪婪(Greedy) <code>*</code>：匹配最长。在贪婪量词模式下，正则表达式会尽可能长地去匹配符合规则的字符串，且会回溯。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">preg_match_all</span><span class="p">(</span><span class="s2">&#34;/a*/i&#34;</span><span class="p">,</span> <span class="s2">&#34;aaabb&#34;</span><span class="p">,</span> <span class="nv">$matches</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$matches</span><span class="p">);</span>
</span></span></code></pre></div><p>非贪婪(Reluctant) <code>+?</code>：匹配最短。在非贪婪量词模式下，正则表达式会匹配尽可能短的字符串。</p>
<p>ENV：Python3</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;a*&#39;</span><span class="p">,</span> <span class="s1">&#39;aaabb&#39;</span><span class="p">)</span> <span class="c1"># 贪婪模式</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;aaa&#39;, &#39;&#39;, &#39;&#39;, &#39;&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;a*?&#39;</span><span class="p">,</span> <span class="s1">&#39;aaabb&#39;</span><span class="p">)</span> <span class="c1"># 非贪婪模式</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;&#39;, &#39;a&#39;, &#39;&#39;, &#39;a&#39;, &#39;&#39;, &#39;a&#39;, &#39;&#39;, &#39;&#39;, &#39;&#39;]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;&#34;.+&#34;&#39;</span><span class="p">,</span> <span class="s1">&#39;&#34;the little cat&#34; is a toy, it lokks &#34;a little bad&#34;&#39;</span><span class="p">)</span> <span class="c1"># 贪婪模式</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;&#34;the little cat&#34; is a toy, it lokks &#34;a little bad&#34;&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;&#34;.+?&#34;&#39;</span><span class="p">,</span> <span class="s1">&#39;&#34;the little cat&#34; is a toy, it lokks &#34;a little bad&#34;&#39;</span><span class="p">)</span> <span class="c1"># 非贪婪模式</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;&#34;the little cat&#34;&#39;, &#39;&#34;a little bad&#34;&#39;]</span>
</span></span></code></pre></div><p>独占模式(Possessive) <code>++</code>：同贪婪一样匹配最长。不过在独占量词模式下，正则表达式尽可能长地去匹配字符串，一旦匹配不成功就会结束匹配而 <strong>不会回溯</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 回溯示例：</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;xy{1,3}z&#39;</span><span class="p">,</span> <span class="s1">&#39;xyyz&#39;</span><span class="p">)</span> <span class="c1"># 回溯</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;xyyz&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 xy{1,3} 会尽可能长地去匹配到 xyyz，无法匹配 z，向前回溯 xyy</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 z 匹配到剩下字符串 z</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;xy{1,3}?z&#39;</span><span class="p">,</span> <span class="s1">&#39;xyyz&#39;</span><span class="p">)</span> <span class="c1"># 非贪婪</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;xyyz&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 xy{1,3} 会尽可能短地去匹配到 xy</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 z 匹配到字符串 y，无法匹配，向前回溯</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 xy{1,3} 会尽可能短地去匹配 xyy</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 z 匹配到剩下字符串 z</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 独占模式示例：</span>
</span></span><span class="line"><span class="cl"><span class="c1"># pip install regex -i https://mirrors.aliyun.com/pypi/simple/</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">regex</span>
</span></span><span class="line"><span class="cl"><span class="n">regex</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;xy{1,3}+z&#39;</span><span class="p">,</span> <span class="s1">&#39;xyyz&#39;</span><span class="p">)</span> <span class="c1"># 独占</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;xyyz&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 xy{1,3}+ 会尽可能长地去匹配到 xyy 并占用</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 z 匹配到字符串 z</span>
</span></span><span class="line"><span class="cl"><span class="n">regex</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;xy{1,3}+yz&#39;</span><span class="p">,</span> <span class="s1">&#39;xyyz&#39;</span><span class="p">)</span> <span class="c1"># 独占</span>
</span></span><span class="line"><span class="cl"><span class="c1"># []</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 xy{1,3}+ 会尽可能长地去匹配到 xyy 并占用</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 正则 yz 无法匹配到剩下字符串 z</span>
</span></span></code></pre></div><h2 id="03-分组与引用">03 分组与引用</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">regex</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 不保存分组 (?:正则)</span>
</span></span><span class="line"><span class="cl"><span class="n">regex</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(\d</span><span class="si">{4}</span><span class="s1">)-(?:\d</span><span class="si">{2}</span><span class="s1">)-(\d</span><span class="si">{2}</span><span class="s1">)&#39;</span><span class="p">,</span> <span class="sa">r</span><span class="s2">&#34;年：\1  日：\2&#34;</span><span class="p">,</span> <span class="s1">&#39;2023-03-01&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;年：2023  日：01&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 去除重复连续单词</span>
</span></span><span class="line"><span class="cl"><span class="n">regex</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(\w+)(\s\1)+&#39;</span><span class="p">,</span> <span class="sa">r</span><span class="s2">&#34;\1&#34;</span><span class="p">,</span> <span class="s1">&#39;the little cat cat is in the hat hat hat, we like it.&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;the little cat is in the hat, we like it.&#39;</span>
</span></span></code></pre></div><h2 id="04-匹配模式">04 匹配模式</h2>
<p>指改变元字符匹配行为。</p>
<p>不区分大小写模式（Case-Insensitive）<code>(?模式标识)</code> <code>(?i)</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">regex</span>
</span></span><span class="line"><span class="cl"><span class="n">regex</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s2">&#34;(?i)cat&#34;</span><span class="p">,</span> <span class="s2">&#34;cat Cat CAt&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;cat&#39;, &#39;Cat&#39;, &#39;CAt&#39;]</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-re" data-lang="re"><span class="line"><span class="cl"><span class="ow">#</span> <span class="n">https</span><span class="ow">:</span><span class="c1">//regex101.com/r/3OUJda/1
</span></span></span><span class="line"><span class="cl"><span class="ow">#</span> <span class="n">二次重复时的大小写一致</span>
</span></span><span class="line"><span class="cl"><span class="ow">((?</span><span class="n">i</span><span class="ow">)</span><span class="n">cat</span><span class="ow">)</span> <span class="err">\</span><span class="n">1</span>
</span></span></code></pre></div><p>点号通配模式（Dot All）<code>(?s)</code> 让英文的点 <code>.</code> 可以匹配上包括换行的任何字符。等价 <code>[\s\S]</code> <code>[\d\D]</code> <code>[\w\W]</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-re" data-lang="re"><span class="line"><span class="cl"><span class="ow">#</span> <span class="n">https</span><span class="ow">:</span><span class="c1">//regex101.com/r/zXtwLv/1
</span></span></span><span class="line"><span class="cl"><span class="ow">#</span> <span class="n">匹配包括换行符</span>
</span></span><span class="line"><span class="cl"><span class="ow">(?</span><span class="n">s</span><span class="ow">).+</span>
</span></span></code></pre></div><p>多行匹配模式（Multiline）<code>(?m)</code> 使 <code>^</code> 和 <code>$</code> 能匹配上每行的开头或结尾。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-re" data-lang="re"><span class="line"><span class="cl"><span class="ow">#</span> <span class="n">分行匹配</span>
</span></span><span class="line"><span class="cl"><span class="ow">(?</span><span class="n">m</span><span class="ow">)</span><span class="o">^</span><span class="n">cat</span><span class="ow">|</span><span class="n">dog</span><span class="o">$</span>
</span></span></code></pre></div><p>注释模式（Comment）<code>(?#)</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-re" data-lang="re"><span class="line"><span class="cl"><span class="ow">(</span><span class="err">\</span><span class="n">w</span><span class="ow">+)(?#</span><span class="n">word</span><span class="ow">)</span> <span class="err">\</span><span class="n">1</span><span class="ow">(?#</span><span class="n">word</span> <span class="n">repeat</span> <span class="n">again</span><span class="ow">)</span>
</span></span></code></pre></div><h2 id="05-断言-assertion">05 断言 Assertion</h2>
<p>对要匹配的文本的位置也有一定的要求。只用于匹配位置，而不是文本内容本身，这种结构就是断言。</p>
<p>边界（Boundary）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 单词边界 \b</span>
</span></span><span class="line"><span class="cl"><span class="c1"># tom -&gt; jerry, tomorrow 不受影响</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\btom\b&#39;</span><span class="p">,</span> <span class="s1">&#39;jerry&#39;</span><span class="p">,</span> <span class="s2">&#34;tom asked me if I would go fishing with him tomorrow.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;jerry asked me if I would go fishing with him tomorrow.&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 行的开始结束</span>
</span></span><span class="line"><span class="cl"><span class="c1"># \A \z 不受模式影响</span>
</span></span><span class="line"><span class="cl"><span class="c1"># \A -&gt; ^, \z -&gt; $</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\Atom&#39;</span><span class="p">,</span> <span class="s1">&#39;jerry&#39;</span><span class="p">,</span> <span class="s2">&#34;tom asked me if I would go fishing with him tomorrow.&#34;</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 环视 左尖括号代表看左边，没有尖括号是看右边，感叹号是非的意思</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (?&lt;=Y) 左边是Y</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (?&lt;!Y) 左边不是Y</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (?=Y) 右边是Y</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (?!Y) 右边不是Y</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[1-9]\d</span><span class="si">{5}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s2">&#34;138001380002&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;138001&#39;, &#39;380002&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(?&lt;!\d)[1-9]\d</span><span class="si">{5}</span><span class="s1">(?!\d)&#39;</span><span class="p">,</span> <span class="s2">&#34;138001380002&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 左边不是数字、右边不是数字</span>
</span></span><span class="line"><span class="cl"><span class="c1"># []</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(?&lt;!\d)[1-9]\d</span><span class="si">{5}</span><span class="s1">(?!\d)&#39;</span><span class="p">,</span> <span class="s2">&#34;code138001code&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 左边不是数字、右边不是数字</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;138001&#39;]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># \b\w+\b -&gt; (?&lt;!\w)\w+(?!\w) -&gt; (?&lt;=\W)\w+(?=\W)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># https://regex101.com/r/PBEKxY/1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># (\w+)(\s+\b\1\b)+</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 单词，单词的左边是单词边界、可以有一个及以上空格，右边是单词边界</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 比 (\w+)(\s+\1)+ 更严谨 eg: the little cat cat2 is in the hat hat2</span>
</span></span></code></pre></div><h2 id="06-转义">06 转义</h2>
<p>转义字符 Escape Character 后面的字符，不是原来的意思了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;</span><span class="se">\\</span><span class="s1">d&#39;</span><span class="p">,</span> <span class="s1">&#39;abc</span><span class="se">\\</span><span class="s1">d123d</span><span class="se">\\</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;\\d&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\\</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;a*b+c?</span><span class="se">\\</span><span class="s1">d123d</span><span class="se">\\</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># bad escape (end of pattern) at position 0</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\\\\</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;a*b+c?</span><span class="se">\\</span><span class="s1">d123d</span><span class="se">\\</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;\\&#39;, &#39;\\&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 字符串-&gt;正则表达式：字符串转义和正则转义</span>
</span></span><span class="line"><span class="cl"><span class="c1"># \\\\ 字符串转义 \\</span>
</span></span><span class="line"><span class="cl"><span class="c1"># \\ 正则转义 \</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;</span><span class="se">\\</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;a*b+c?</span><span class="se">\\</span><span class="s1">d123d</span><span class="se">\\</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;\\&#39;, &#39;\\&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;\(\)\[]\</span><span class="si">{}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;()[]</span><span class="si">{}</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;()[]{}&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 方括号和花括号的转义一般转义开括号就可以，但圆括号两个都需要转义</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="s1">&#39;\d&#39;</span><span class="p">)</span> <span class="c1"># 反斜杠和字母d转义</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;\\\\d&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="n">re</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="s1">&#39;\d&#39;</span><span class="p">),</span> <span class="s1">&#39;\d&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;\\d&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="s1">&#39;[+]&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;\\[\\+\\]&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="n">re</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="s1">&#39;[+]&#39;</span><span class="p">),</span> <span class="s1">&#39;[+]&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;[+]&#39;]</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[^ab]&#39;</span><span class="p">,</span> <span class="s1">&#39;^ab&#39;</span><span class="p">)</span>  <span class="c1"># 转义前代表&#34;非&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;^&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[^cd]&#39;</span><span class="p">,</span> <span class="s1">&#39;^ab&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;^&#39;, &#39;a&#39;, &#39;b&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[\^ab]&#39;</span><span class="p">,</span> <span class="s1">&#39;^ab&#39;</span><span class="p">)</span>  <span class="c1"># 转义后代表普通字符</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;^&#39;, &#39;a&#39;, &#39;b&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[a-c]&#39;</span><span class="p">,</span> <span class="s1">&#39;abc-&#39;</span><span class="p">)</span>  <span class="c1"># 中划线在中间，代表&#34;范围&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[a\-c]&#39;</span><span class="p">,</span> <span class="s1">&#39;abc-&#39;</span><span class="p">)</span>  <span class="c1"># 中划线在中间，转义后的</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[-ac]&#39;</span><span class="p">,</span> <span class="s1">&#39;abc-&#39;</span><span class="p">)</span>  <span class="c1"># 在开头，不需要转义</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[ac-]&#39;</span><span class="p">,</span> <span class="s1">&#39;abc-&#39;</span><span class="p">)</span>  <span class="c1"># 在结尾，不需要转义</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;a&#39;, &#39;c&#39;, &#39;-&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[]ab]&#39;</span><span class="p">,</span> <span class="s1">&#39;]ab&#39;</span><span class="p">)</span>  <span class="c1"># 右括号不转义，在首位</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;]&#39;, &#39;a&#39;, &#39;b&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[a]b]&#39;</span><span class="p">,</span> <span class="s1">&#39;]ab&#39;</span><span class="p">)</span>  <span class="c1"># 右括号不转义，不在首位</span>
</span></span><span class="line"><span class="cl"><span class="c1"># []</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[a\]b]&#39;</span><span class="p">,</span> <span class="s1">&#39;]ab&#39;</span><span class="p">)</span>  <span class="c1"># 转义后代表普通字符</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;]&#39;, &#39;a&#39;, &#39;b&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[.*+?()]&#39;</span><span class="p">,</span> <span class="s1">&#39;[.*+?()]&#39;</span><span class="p">)</span>  <span class="c1"># 单个长度的元字符在中括号里，可以不转义</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;.&#39;, &#39;*&#39;, &#39;+&#39;, &#39;?&#39;, &#39;(&#39;, &#39;)&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[\d]&#39;</span><span class="p">,</span> <span class="s1">&#39;d12</span><span class="se">\\</span><span class="s1">&#39;</span><span class="p">)</span>  <span class="c1"># \w，\d等在中括号中还是元字符的功能</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;1&#39;, &#39;2&#39;]</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;</span><span class="se">\\</span><span class="s1">n</span><span class="se">\n\\</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;\n&#39;] \n -&gt; (\n) -&gt; (\n)</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\\</span><span class="s1">n&#39;</span><span class="p">,</span> <span class="s1">&#39;</span><span class="se">\\</span><span class="s1">n</span><span class="se">\n\\</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;\n&#39;] \\n -&gt; \n -&gt; (\n)</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\\\n</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;</span><span class="se">\\</span><span class="s1">n</span><span class="se">\n\\</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;\n&#39;] \\\n -&gt; \n -&gt; (\n)</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;\\\n&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\\\\</span><span class="s1">n&#39;</span><span class="p">,</span> <span class="s1">&#39;</span><span class="se">\\</span><span class="s1">n</span><span class="se">\n\\</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;\\n&#39;] \\\\n -&gt; \\\n -&gt; \(\n)</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\\</span><span class="s1">n&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;\\\\n&#39;</span>
</span></span></code></pre></div><h2 id="07-流派及其特性">07 流派及其特性</h2>
<ul>
<li>POSIX Portable Operating System Interface。不能使用 <code>\d</code>。
<ul>
<li>BRE Basic Regular Expression 基本正则表达式。<code>grep</code> <code>sed</code> 花园问管家 <code>{}()?|+</code> 要转义。</li>
<li>ERE Extended Regular Expression 扩展正则表达式。<code>egrep</code> <code>grep -E</code> <code>sed -E</code>。</li>
</ul>
</li>
<li>PCRE Perl Compatible Regular Expressions。可以使用 <code>\d</code> <code>\w</code> <code>\s</code>。<code>grep -P</code> <code>sed -P</code>。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">grep --help <span class="p">|</span> grep PATTERN
</span></span><span class="line"><span class="cl"><span class="c1"># PATTERN is, by default, a basic regular expression (BRE).</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   -E, --extended-regexp     PATTERN is an extended regular expression (ERE)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   -F, --fixed-strings       PATTERN is a set of newline-separated fixed strings</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   -G, --basic-regexp        PATTERN is a basic regular expression (BRE)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   -P, --perl-regexp         PATTERN is a Perl regular expression</span>
</span></span></code></pre></div><blockquote>
<p><a href="https://www.infoq.cn/article/2011/07/regular-expressions-6-posix">Linux/Unix 工具与正则表达式的 POSIX 规范 | 余晟</a></p>
</blockquote>
<h2 id="08-处理-unicode-文本">08 处理 Unicode 文本</h2>
<p>Unicode 相当于规定了字符对应的码值，这个码值得编码成字节的形式去传输和存储。最常见的编码方式是 UTF-8，另外还有 UTF-16，UTF-32 等。UTF-8 之所以能够流行起来，是因为其编码比较巧妙，采用的是变长的方法。也就是一个 Unicode 字符，在使用 UTF-8 编码表示时占用 1 到 4 个字节不等。最重要的是 Unicode 兼容 ASCII 编码，在表示纯英文时，并不会占用更多存储空间。而汉字呢，在 UTF-8 中，通常是用三个字节来表示。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># python2.7</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="sa">u</span><span class="s1">&#39;极客&#39;</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;\xe6\x9e\x81\xe5\xae\xa2&#39;</span>
</span></span><span class="line"><span class="cl"><span class="sa">u</span><span class="s1">&#39;时间&#39;</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;\xe6\x97\xb6\xe9\x97\xb4&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 都含有 e6</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[时间]&#39;</span><span class="p">,</span> <span class="s1">&#39;极客&#39;</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"><span class="c1"># True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[时间]&#39;</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># in</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 230</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 151</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 182</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 233</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 151</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 180</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &lt;_sre.SRE_Pattern object at 0x10ab44d78&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;[极客]&#39;</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># in</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 230</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 158</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 129</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 229</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 174</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 162</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &lt;_sre.SRE_Pattern object at 0x10ab44e40&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="n">ur</span><span class="s1">&#39;[时间]&#39;</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># in</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 26102</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   literal 38388</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &lt;_sre.SRE_Pattern object at 0x10ac02710&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">ur</span><span class="s1">&#39;[时间]&#39;</span><span class="p">,</span> <span class="s1">&#39;时间&#39;</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"><span class="kc">False</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">ur</span><span class="s1">&#39;[时间]&#39;</span><span class="p">,</span> <span class="sa">u</span><span class="s1">&#39;时间&#39;</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"><span class="kc">True</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># python2.7</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^.$&#39;</span><span class="p">,</span> <span class="s1">&#39;学&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># []</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^.$&#39;</span><span class="p">,</span> <span class="sa">u</span><span class="s1">&#39;学&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [u&#39;\u5b66&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="n">ur</span><span class="s1">&#39;^.$&#39;</span><span class="p">,</span> <span class="sa">u</span><span class="s1">&#39;学&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [u&#39;\u5b66&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">unichr</span><span class="p">(</span><span class="mh">0x5B66</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 学</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># python3</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^.$&#39;</span><span class="p">,</span> <span class="s1">&#39;学&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;学&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(?a)^.$&#39;</span><span class="p">,</span> <span class="s1">&#39;学&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;学&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (?a) 表示启用 ASCII 模式</span>
</span></span><span class="line"><span class="cl"><span class="nb">chr</span><span class="p">(</span><span class="mh">0x5B66</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;学&#39;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 可以匹配汉语 in PHP
</span></span></span><span class="line"><span class="cl"><span class="nx">\p</span><span class="p">{</span><span class="nx">Han</span><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># python2.7</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;客</span><span class="si">{3}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;极客客客客&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># []</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="n">ur</span><span class="s1">&#39;客</span><span class="si">{3}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;极客客客客&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># []</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;客</span><span class="si">{3}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="sa">u</span><span class="s1">&#39;极客客客客&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># []</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="n">ur</span><span class="s1">&#39;客</span><span class="si">{3}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="sa">u</span><span class="s1">&#39;极客客客客&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [u&#39;\u5ba2\u5ba2\u5ba2&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(客)</span><span class="si">{3}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;极客客客客&#39;</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># python3</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;客</span><span class="si">{3}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;极客客客客&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;客客客&#39;]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 在 Python3 中，不需要在正则表达式字符串前面添加 u 前缀，因为所有字符串都默认为 Unicode 字符串。</span>
</span></span></code></pre></div><ul>
<li><a href="https://en.wikipedia.org/wiki/Script_%28Unicode%29#Hani">Script (Unicode) | wikipedia</a></li>
</ul>
<h2 id="09-编辑器中使用正则">09 编辑器中使用正则</h2>
<p>竖向编辑：MacOS alt + 鼠标纵向滑动。</p>
<h2 id="10-语言中用正则">10 语言中用正则</h2>
<p>校验文本内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\A\d</span><span class="si">{4}</span><span class="s1">-\d</span><span class="si">{2}</span><span class="s1">-\d</span><span class="si">{2}</span><span class="s1">\Z&#39;</span><span class="p">)</span>  <span class="c1"># 建议先编译，提高效率</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s1">&#39;2020-06-01&#39;</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"><span class="c1"># True</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="s1">&#39;2020-06-01&#39;</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>  <span class="c1"># 使用 match 时 \A 可省略，match 就是从头匹配</span>
</span></span><span class="line"><span class="cl"><span class="c1"># True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">reg</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\d</span><span class="si">{4}</span><span class="s1">-\d</span><span class="si">{2}</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;2020-05 2020-06&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;2020-05&#39;, &#39;2020-06&#39;]</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="sr">/^\d{4}-\d{2}-\d{2}$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="s2">&#34;2020-06-01&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">regex</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="sr">/^\d{4}-\d{2}-\d{2}$/</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">regex</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="s2">&#34;2020-01-01&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">regex</span> <span class="o">=</span> <span class="sr">/^\d{4}-\d{2}-\d{2}$/</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;2020-06-01&#34;</span><span class="p">.</span><span class="nx">search</span><span class="p">(</span><span class="nx">regex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 0
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$regex</span> <span class="o">=</span> <span class="s1">&#39;/^\d{4}-\d{2}-\d{2}$/&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$ret</span> <span class="o">=</span> <span class="nx">preg_match</span><span class="p">(</span><span class="nv">$regex</span><span class="p">,</span> <span class="s2">&#34;2020-06-01&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$ret</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// int(1)
</span></span></span></code></pre></div><p>提取文本内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 没有子组时</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\d</span><span class="si">{4}</span><span class="s1">-\d</span><span class="si">{2}</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;2020-05 2020-06&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;2020-05&#39;, &#39;2020-06&#39;]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 有子组时</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(\d</span><span class="si">{4}</span><span class="s1">)-(\d</span><span class="si">{2}</span><span class="s1">)&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">&#39;2020-05 2020-06&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">[(</span><span class="s1">&#39;2020&#39;</span><span class="p">,</span> <span class="s1">&#39;05&#39;</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;2020&#39;</span><span class="p">,</span> <span class="s1">&#39;06&#39;</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">reg</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(\d</span><span class="si">{4}</span><span class="s1">)-(\d</span><span class="si">{2}</span><span class="s1">)&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="k">match</span> <span class="ow">in</span> <span class="n">reg</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="s1">&#39;2020-05 2020-06&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;date: &#39;</span><span class="p">,</span> <span class="k">match</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>  <span class="c1"># 整个正则匹配到的内容</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;year: &#39;</span><span class="p">,</span> <span class="k">match</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>  <span class="c1"># 第一个子组</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;month:&#39;</span><span class="p">,</span> <span class="k">match</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>  <span class="c1"># 第二个子组</span>
</span></span><span class="line"><span class="cl"><span class="c1"># date:  2020-05</span>
</span></span><span class="line"><span class="cl"><span class="c1"># year:  2020</span>
</span></span><span class="line"><span class="cl"><span class="c1"># month: 05</span>
</span></span><span class="line"><span class="cl"><span class="c1"># date:  2020-06</span>
</span></span><span class="line"><span class="cl"><span class="c1"># year:  2020</span>
</span></span><span class="line"><span class="cl"><span class="c1"># month: 06</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// 使用g模式，查找所有符合要求的内容
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;2020-06 2020-07&#34;</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/\d{4}-\d{2}/g</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [&#39;2020-06&#39;, &#39;2020-07&#39;]
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 不使用g模式，找到第一个就会停下来
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;2020-06 2020-07&#34;</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/\d{4}-\d{2}/</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [&#39;2020-06&#39;, index: 0, input: &#39;2020-06 2020-07&#39;, groups: undefined]
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$regex</span> <span class="o">=</span> <span class="s2">&#34;/\d{4}-\d{2}/&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$str</span> <span class="o">=</span> <span class="s2">&#34;2020-05 2020-04&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$matchs</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="nx">preg_match_all</span><span class="p">(</span><span class="nv">$regex</span><span class="p">,</span> <span class="nv">$str</span><span class="p">,</span> <span class="nv">$matchs</span><span class="p">,</span> <span class="nx">PREG_SET_ORDER</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$matchs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// array(2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [0] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//     [0] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//     string(7) &#34;2020-05&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   }
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [1] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//     [0] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//     string(7) &#34;2020-04&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   }
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PREG_PATTERN_ORDER: 结果排序为$matches[0]保存完整模式的所有匹配, $matches[1]保存第一个子组的所有匹配，以此类推。
</span></span></span><span class="line"><span class="cl"><span class="c1">// PREG_SET_ORDER: 结果排序为$matches[0]包含第一次匹配得到的所有匹配(包含子组)，$matches[1]是包含第二次匹配到的所有匹配(包含子组)的数组，以此类推。
</span></span></span></code></pre></div><p>替换文本内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">reg</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;(\d</span><span class="si">{2}</span><span class="s1">)-(\d</span><span class="si">{2}</span><span class="s1">)-(\d</span><span class="si">{4}</span><span class="s1">)&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\3年\1月\2日&#39;</span><span class="p">,</span> <span class="s1">&#39;02-20-2020 05-21-2020&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;2020年02月20日 2020年05月21日&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 可以在替换中使用 \g&lt;数字&gt;，如果分组多于10个时避免歧义</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\g&lt;3&gt;年\g&lt;1&gt;月\g&lt;2&gt;日&#39;</span><span class="p">,</span> <span class="s1">&#39;02-20-2020 05-21-2020&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#39;2020年02月20日 2020年05月21日&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 返回替换次数</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="n">subn</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\3年\1月\2日&#39;</span><span class="p">,</span> <span class="s1">&#39;02-20-2020 05-21-2020&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (&#39;2020年02月20日 2020年05月21日&#39;, 2)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// 使用g模式，替换所有的
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;02-20-2020 05-21-2020&#34;</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/(\d{2})-(\d{2})-(\d{4})/g</span><span class="p">,</span> <span class="s2">&#34;$3年$1月$2日&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;2020年02月20日 2020年05月21日&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 不使用 g 模式时，只替换一次
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;02-20-2020 05-21-2020&#34;</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/(\d{2})-(\d{2})-(\d{4})/</span><span class="p">,</span> <span class="s2">&#34;$3年$1月$2日&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;2020年02月20日 05-21-2020&#34;
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$ret</span> <span class="o">=</span> <span class="nx">preg_replace</span><span class="p">(</span><span class="s1">&#39;/(\d{2})-(\d{2})-(\d{4})/&#39;</span><span class="p">,</span> <span class="s1">&#39;\3年\1月\2日&#39;</span><span class="p">,</span> <span class="s2">&#34;02-20-2020 05-21-2020&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$ret</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(35) &#34;2020年02月20日 2020年05月21日&#34;
</span></span></span></code></pre></div><p>切割文本内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">reg</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;\W+&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;apple, pear! orange; tea&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;apple&#39;, &#39;pear&#39;, &#39;orange&#39;, &#39;tea&#39;]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 限制切割次数，比如切一刀，变成两部分</span>
</span></span><span class="line"><span class="cl"><span class="n">reg</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;apple, pear! orange; tea&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># [&#39;apple&#39;, &#39;pear! orange; tea&#39;]</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="s2">&#34;apple, pear! orange; tea&#34;</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="sr">/\W+/</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [&#34;apple&#34;, &#34;pear&#34;, &#34;orange&#34;, &#34;tea&#34;]
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 传入第二个参数的情况
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;apple, pear! orange; tea&#34;</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="sr">/\W+/</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [&#34;apple&#34;]
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;apple, pear! orange; tea&#34;</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="sr">/\W+/</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [&#34;apple&#34;, &#34;pear&#34;]
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;apple, pear! orange; tea&#34;</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="sr">/\W+/</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [&#34;apple&#34;, &#34;pear&#34;, &#34;orange&#34;, &#34;tea&#34;]
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$ret</span> <span class="o">=</span> <span class="nx">preg_split</span><span class="p">(</span><span class="s1">&#39;/\W+/&#39;</span><span class="p">,</span> <span class="s1">&#39;apple, pear! orange; tea&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$ret</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// array(4) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [0] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(5) &#34;apple&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [1] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(4) &#34;pear&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [2] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(6) &#34;orange&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [3] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(3) &#34;tea&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="nv">$ret</span> <span class="o">=</span> <span class="nx">preg_split</span><span class="p">(</span><span class="s1">&#39;/\W+/&#39;</span><span class="p">,</span> <span class="s1">&#39;apple, pear! orange; tea&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$ret</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// array(2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [0] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(5) &#34;apple&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [1] =&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(17) &#34;pear! orange; tea&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><h2 id="11-匹配原理以及优化原则">11 匹配原理以及优化原则</h2>
<p>回溯不可怕，我们要尽量减少回溯后的判断</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="s1">&#39;-&#39;</span> <span class="o">*</span> <span class="mi">1000000</span> <span class="o">+</span> <span class="s1">&#39;abc&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">timeit</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s1">&#39;abc&#39;</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>提前编译好正则。</li>
<li>尽量准确表示匹配范围：匹配引号里面的内容 <code>.+?</code> 改写为 <code>[^&quot;]+</code>。</li>
<li>提取出公共部分：<code>(abcd|abxy)</code> =&gt; <code>ab(cd|xy)</code>，<code>(^this|^that)</code> =&gt; <code>^th(is|at)</code>。</li>
<li>出现可能性大的放左边：<code>\.(?:com|net)\b</code>。</li>
<li>只在必要时才使用子组：把不需要保存子组的括号中加上 <code>?:</code> 来表示只用于归组。</li>
<li>警惕嵌套的子组重复：<code>(.*)*</code> 匹配的次数会呈指数级增长，尽量不要写这样的正则。</li>
<li>避免不同分支重复匹配。</li>
</ul>
<p>NFA 是以表达式为主导的，先看正则表达式，再看文本。而 DFA 则是以文本为主导的，先看文本，再看正则表达式。POSIX NFA 是指符合 POSIX 标准的 NFA 引擎，它会不断回溯，以确保找到最左侧最长匹配。</p>
<h2 id="12-常见问题">12 常见问题</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^(?:(?!\d\d)\w)</span><span class="si">{6}</span><span class="s1">$&#39;</span><span class="p">,</span> <span class="s1">&#39;11abcd&#39;</span><span class="p">)</span> <span class="c1"># 不能匹配上</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 否定预测先行断言的语法&#34;(?!)&#34;来排除两个数字字符结尾的情况</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (?!) 表示匹配不满足某个条件的位置</span>
</span></span><span class="line"><span class="cl"><span class="n">re</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^(?:\w(?!\d\d))</span><span class="si">{6}</span><span class="s1">$&#39;</span><span class="p">,</span> <span class="s1">&#39;11abcd&#39;</span><span class="p">)</span> <span class="c1"># 错误正则示范</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &lt;re.Match object; span=(0, 6), match=&#39;11abcd&#39;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (11) 回溯</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 1(1a) ok</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 11ab... ok</span>
</span></span></code></pre></div><ul>
<li>正负号、可二位小数、小数位末尾 0 无影响 <a href="https://jex.im/regulex/#!flags=&amp;re=%5E%5B-%2B%5D%3F%5Cd%2B%28%3F%3A%5C.%28%3F%3A%5Cd%29%7B0%2C2%7D0*%29%3F%24">Regulex</a>：<code>^[-+]?\d+(?:\.(?:\d){0,2}0*)?$</code></li>
<li>手机号码：<code>1(?:3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[1389])\d{8}</code></li>
<li>身份证：<code>[1-9]\d{14}(\d\d[0-9Xx])?</code></li>
<li>邮政编码：<code>(?&lt;!\d)\d{6}(?!\d)</code></li>
<li>中文字符：<code>[\u4E00-\u9FFF]</code> <code>\p{Han}</code></li>
<li>邮箱：<code>a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+</code></li>
</ul>
<h2 id="程语言的角度来理解正则">程语言的角度来理解正则</h2>
<ul>
<li>命令式编程的世界观是：程序是由若干行动指令组成的有序列表；</li>
<li>命令式编程的方法论是：用变量来存储数据，用语句来执行指令。</li>
<li>声明式编程的世界观是：程序是由若干目标任务组成的有序列表；</li>
<li>声明式编程的方法论是：用语法元素来描述任务，由解析引擎转化为指令并执行。</li>
</ul>
<h2 id="references">References</h2>
<ul>
<li>《精通正则表达式（第三版）》</li>
<li>《正则指引（第二版）》</li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Kubernetes 入门实战 Part3</title>
      <link>https://zyf.im/2023/02/24/kubernetes-getting-started-part3/</link>
      <pubDate>Fri, 24 Feb 2023 17:24:14 +0000</pubDate>
      <guid>https://zyf.im/2023/02/24/kubernetes-getting-started-part3/</guid>
      <description>&lt;h2 id=&#34;24-persistentvolume-数据持久化&#34;&gt;24 PersistentVolume 数据持久化&lt;/h2&gt;
&lt;p&gt;PersistentVolume 属于集群的系统资源，是和 Node 平级的一种对象，Pod 对它没有管理权，只有使用权。&lt;/p&gt;
&lt;p&gt;StorageClass 它抽象了特定类型的存储系统（比如 Ceph、NFS），在 PVC 和 PV 之间充当“协调人”的角色，帮助 PVC 找到合适的 PV。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# vim host-path-pv-pvc.yml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;apiVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;v1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;kind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;PersistentVolume&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 只有 10MB 容量的存储设备&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;host-10m-pv&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;spec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# kubectl explain PersistentVolume.spec.storageClassName&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;storageClassName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;host-vol&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# kubectl explain PersistentVolume.spec.accessModes&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;accessModes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# ReadWriteOnce：存储卷可读可写，但只能被一个节点上的 Pod 挂载。&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# ReadOnlyMany：存储卷只读不可写，可以被任意节点上的 Pod 多次挂载。&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# ReadWriteMany：存储卷可读可写，也可以被任意节点上的 Pod 多次挂载。&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;ReadWriteOnce&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;capacity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;#  Ki/Mi/Gi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;storage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;10Mi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;hostPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/tmp/host-10m-pv/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nn&#34;&gt;---&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# PVC 不表示实际的存储，而是一个“申请”或者“声明”&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;apiVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;v1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;kind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;PersistentVolumeClaim&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;host-5m-pvc&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;spec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;storageClassName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;host-vol&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;accessModes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;ReadWriteOnce&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;resources&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;requests&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;storage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;5Mi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir /tmp/host-10m-pv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kubectl apply -f host-path-pv-pvc.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kubectl get pv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# host-10m-pv   10Mi       RWO            Retain           Bound    default/host-5m-pvc   host-vol                4s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kubectl get pvc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# NAME          STATUS   VOLUME        CAPACITY   ACCESS MODES   STORAGECLASS   AGE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# host-5m-pvc   Bound    host-10m-pv   10Mi       RWO            host-vol       6s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# vim host-pvc-pod.yml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;apiVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;v1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;kind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Pod&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;host-pvc-pod&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;spec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;host-pvc-vol&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;claimName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;host-5m-pvc&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;containers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ngx-pvc-pod&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;nginx:alpine&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;containerPort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumeMounts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;host-pvc-vol&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mountPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/tmp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kubectl apply -f host-pvc-pod.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kubectl get pod -o wide
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kubectl &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; -it host-pvc-pod -- sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /tmp &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; touch a.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# check in worker node&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/tmp/host-10m-pv/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;25-nfs-网络共享存储&#34;&gt;25 NFS 网络共享存储&lt;/h2&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="24-persistentvolume-数据持久化">24 PersistentVolume 数据持久化</h2>
<p>PersistentVolume 属于集群的系统资源，是和 Node 平级的一种对象，Pod 对它没有管理权，只有使用权。</p>
<p>StorageClass 它抽象了特定类型的存储系统（比如 Ceph、NFS），在 PVC 和 PV 之间充当“协调人”的角色，帮助 PVC 找到合适的 PV。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim host-path-pv-pvc.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">PersistentVolume</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># 只有 10MB 容量的存储设备</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-10m-pv</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># kubectl explain PersistentVolume.spec.storageClassName</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="l">host-vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># kubectl explain PersistentVolume.spec.accessModes</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">accessModes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># ReadWriteOnce：存储卷可读可写，但只能被一个节点上的 Pod 挂载。</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># ReadOnlyMany：存储卷只读不可写，可以被任意节点上的 Pod 多次挂载。</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># ReadWriteMany：存储卷可读可写，也可以被任意节点上的 Pod 多次挂载。</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">ReadWriteOnce</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">capacity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c">#  Ki/Mi/Gi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">10Mi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">hostPath</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp/host-10m-pv/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># PVC 不表示实际的存储，而是一个“申请”或者“声明”</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">PersistentVolumeClaim</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-5m-pvc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w"> </span><span class="l">host-vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">accessModes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">ReadWriteOnce</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">requests</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">5Mi</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir /tmp/host-10m-pv
</span></span><span class="line"><span class="cl">kubectl apply -f host-path-pv-pvc.yml
</span></span><span class="line"><span class="cl">kubectl get pv
</span></span><span class="line"><span class="cl"><span class="c1"># NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># host-10m-pv   10Mi       RWO            Retain           Bound    default/host-5m-pvc   host-vol                4s</span>
</span></span><span class="line"><span class="cl">kubectl get pvc
</span></span><span class="line"><span class="cl"><span class="c1"># NAME          STATUS   VOLUME        CAPACITY   ACCESS MODES   STORAGECLASS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># host-5m-pvc   Bound    host-10m-pv   10Mi       RWO            host-vol       6s</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim host-pvc-pod.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-pvc-pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-pvc-vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">persistentVolumeClaim</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">claimName</span><span class="p">:</span><span class="w"> </span><span class="l">host-5m-pvc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-pvc-pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-pvc-vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f host-pvc-pod.yml
</span></span><span class="line"><span class="cl">kubectl get pod -o wide
</span></span><span class="line"><span class="cl">kubectl <span class="nb">exec</span> -it host-pvc-pod -- sh
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /tmp <span class="o">&amp;&amp;</span> touch a.md
</span></span><span class="line"><span class="cl"><span class="c1"># check in worker node</span>
</span></span><span class="line"><span class="cl">/tmp/host-10m-pv/
</span></span></code></pre></div><h2 id="25-nfs-网络共享存储">25 NFS 网络共享存储</h2>
<p>&hellip;</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/chronolaw/k8s_study">chronolaw/k8s_study | GitHub</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Kubernetes 入门实战 Part2</title>
      <link>https://zyf.im/2023/02/22/kubernetes-getting-started-part2/</link>
      <pubDate>Wed, 22 Feb 2023 17:11:27 +0000</pubDate>
      <guid>https://zyf.im/2023/02/22/kubernetes-getting-started-part2/</guid>
      <description>&lt;h2 id=&#34;17-多节点的-kubernetes-集群&#34;&gt;17 多节点的 Kubernetes 集群&lt;/h2&gt;
&lt;p&gt;在腾讯云 TencentOS Server 3.1 (TK4) 下测试：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;master SA3.MEDIUM4 2 核 4GB 5Mbps&lt;/li&gt;
&lt;li&gt;worker S5.SMALL2 1 核 2GB 1Mbps&lt;/li&gt;
&lt;li&gt;worker S5.SMALL2 1 核 2GB 1Mbps&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 修改源 https://mirrors.cloud.tencent.com/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.cloud.tencent.com/repo/centos7_base.repo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yum clean all
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yum makecache
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# install docker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yum remove docker &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            docker-client &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            docker-client-latest &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            docker-common &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            docker-latest &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            docker-latest-logrotate &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            docker-logrotate &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            docker-engine
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yum install -y yum-utils
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yum-config-manager &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    --add-repo &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    https://download.docker.com/linux/centos/docker-ce.repo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl start docker
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker -v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker run hello-world
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一些准备工作：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="17-多节点的-kubernetes-集群">17 多节点的 Kubernetes 集群</h2>
<p>在腾讯云 TencentOS Server 3.1 (TK4) 下测试：</p>
<ul>
<li>master SA3.MEDIUM4 2 核 4GB 5Mbps</li>
<li>worker S5.SMALL2 1 核 2GB 1Mbps</li>
<li>worker S5.SMALL2 1 核 2GB 1Mbps</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 修改源 https://mirrors.cloud.tencent.com/</span>
</span></span><span class="line"><span class="cl">mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
</span></span><span class="line"><span class="cl">wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.cloud.tencent.com/repo/centos7_base.repo
</span></span><span class="line"><span class="cl">yum clean all
</span></span><span class="line"><span class="cl">yum makecache
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># install docker</span>
</span></span><span class="line"><span class="cl">yum remove docker <span class="se">\
</span></span></span><span class="line"><span class="cl">            docker-client <span class="se">\
</span></span></span><span class="line"><span class="cl">            docker-client-latest <span class="se">\
</span></span></span><span class="line"><span class="cl">            docker-common <span class="se">\
</span></span></span><span class="line"><span class="cl">            docker-latest <span class="se">\
</span></span></span><span class="line"><span class="cl">            docker-latest-logrotate <span class="se">\
</span></span></span><span class="line"><span class="cl">            docker-logrotate <span class="se">\
</span></span></span><span class="line"><span class="cl">            docker-engine
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">yum install -y yum-utils
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">yum-config-manager <span class="se">\
</span></span></span><span class="line"><span class="cl">    --add-repo <span class="se">\
</span></span></span><span class="line"><span class="cl">    https://download.docker.com/linux/centos/docker-ce.repo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">systemctl start docker
</span></span><span class="line"><span class="cl">docker -v
</span></span><span class="line"><span class="cl">docker run hello-world
</span></span></code></pre></div><p>一些准备工作：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 改主机名</span>
</span></span><span class="line"><span class="cl">vi /etc/hostname
</span></span><span class="line"><span class="cl"><span class="c1"># reboot</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 把 cgroup 的驱动程序改成 systemd</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 使用 Docker 作为 Kubernetes 的底层支持</span>
</span></span><span class="line"><span class="cl">cat <span class="s">&lt;&lt;EOF | sudo tee /etc/docker/daemon.json
</span></span></span><span class="line"><span class="cl"><span class="s">{
</span></span></span><span class="line"><span class="cl"><span class="s">  &#34;exec-opts&#34;: [&#34;native.cgroupdriver=systemd&#34;],
</span></span></span><span class="line"><span class="cl"><span class="s">  &#34;log-driver&#34;: &#34;json-file&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s">  &#34;log-opts&#34;: {
</span></span></span><span class="line"><span class="cl"><span class="s">    &#34;max-size&#34;: &#34;100m&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">  },
</span></span></span><span class="line"><span class="cl"><span class="s">  &#34;storage-driver&#34;: &#34;overlay2&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">}
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">systemctl <span class="nb">enable</span> docker
</span></span><span class="line"><span class="cl">systemctl daemon-reload
</span></span><span class="line"><span class="cl">systemctl restart docker
</span></span><span class="line"><span class="cl">docker version
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 转发 IPv4 并让 iptables 看到桥接流量</span>
</span></span><span class="line"><span class="cl">cat <span class="s">&lt;&lt;EOF | sudo tee /etc/modules-load.d/k8s.conf
</span></span></span><span class="line"><span class="cl"><span class="s">overlay
</span></span></span><span class="line"><span class="cl"><span class="s">br_netfilter
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo modprobe overlay
</span></span><span class="line"><span class="cl">sudo modprobe br_netfilter
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># check</span>
</span></span><span class="line"><span class="cl">lsmod <span class="p">|</span> grep br_netfilter
</span></span><span class="line"><span class="cl">lsmod <span class="p">|</span> grep overlay
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置所需的 sysctl 参数，参数在重新启动后保持不变</span>
</span></span><span class="line"><span class="cl">cat <span class="s">&lt;&lt;EOF | sudo tee /etc/sysctl.d/k8s.conf
</span></span></span><span class="line"><span class="cl"><span class="s">net.bridge.bridge-nf-call-iptables  = 1
</span></span></span><span class="line"><span class="cl"><span class="s">net.bridge.bridge-nf-call-ip6tables = 1
</span></span></span><span class="line"><span class="cl"><span class="s">net.ipv4.ip_forward                 = 1
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 应用 sysctl 参数而不重新启动</span>
</span></span><span class="line"><span class="cl">sudo sysctl --system
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward
</span></span><span class="line"><span class="cl"><span class="c1"># net.bridge.bridge-nf-call-iptables = 1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># net.bridge.bridge-nf-call-ip6tables = 1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># net.ipv4.ip_forward = 1</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 关闭 Linux 的 swap 分区</span>
</span></span><span class="line"><span class="cl">swapoff -a
</span></span><span class="line"><span class="cl">sed -ri <span class="s1">&#39;/\sswap\s/s/^#?/#/&#39;</span> /etc/fstab
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># https://developer.aliyun.com/mirror/kubernetes</span>
</span></span><span class="line"><span class="cl">cat <span class="s">&lt;&lt;EOF &gt; /etc/yum.repos.d/kubernetes.repo
</span></span></span><span class="line"><span class="cl"><span class="s">[kubernetes]
</span></span></span><span class="line"><span class="cl"><span class="s">name=Kubernetes
</span></span></span><span class="line"><span class="cl"><span class="s">baseurl=https://mirrors.cloud.tencent.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">yum clean all
</span></span><span class="line"><span class="cl">yum makecache
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 将 SELinux 设置为 permissive 模式（相当于将其禁用）</span>
</span></span><span class="line"><span class="cl">setenforce <span class="m">0</span>
</span></span><span class="line"><span class="cl">sed -i <span class="s1">&#39;s/^SELINUX=enforcing$/SELINUX=permissive/&#39;</span> /etc/selinux/config
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 新版本搞不定</span>
</span></span><span class="line"><span class="cl"><span class="c1"># yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes --nogpgcheck</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">yum --showduplicate list kubelet
</span></span><span class="line"><span class="cl">yum install -y kubelet-1.23.16-0 kubeadm-1.23.16-0 kubectl-1.23.16-0 --disableexcludes<span class="o">=</span>kubernetes --nogpgcheck
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">systemctl <span class="nb">enable</span> --now kubelet
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubeadm version
</span></span><span class="line"><span class="cl">kubectl version --output<span class="o">=</span>yaml
</span></span><span class="line"><span class="cl">kubelet --version
</span></span></code></pre></div><p>下载 Kubernetes 组件镜像：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># kubeadm config images list</span>
</span></span><span class="line"><span class="cl">kubeadm config images list --kubernetes-version v1.23.16
</span></span></code></pre></div><p>安装 Master 节点：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim /etc/containerd/config.toml
</span></span><span class="line"><span class="cl"><span class="c1">#disabled_plugins = [&#34;cri&#34;]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">systemctl <span class="nb">enable</span> containerd
</span></span><span class="line"><span class="cl">systemctl restart containerd
</span></span><span class="line"><span class="cl">systemctl status containerd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">systemctl <span class="nb">enable</span> kubelet.service
</span></span><span class="line"><span class="cl">systemctl restart kubelet
</span></span><span class="line"><span class="cl">systemctl status kubelet
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">containerd config default &gt; /etc/containerd/config.toml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">yum install -y nc
</span></span><span class="line"><span class="cl">nc 127.0.0.1 <span class="m">6443</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubeadm init -h
</span></span><span class="line"><span class="cl">kubeadm reset -f
</span></span><span class="line"><span class="cl">rm -rf ~/.kube/config
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubeadm init <span class="se">\
</span></span></span><span class="line"><span class="cl">    --image-repository<span class="o">=</span>registry.aliyuncs.com/google_containers <span class="se">\
</span></span></span><span class="line"><span class="cl">    --pod-network-cidr<span class="o">=</span>10.10.0.0/16 <span class="se">\
</span></span></span><span class="line"><span class="cl">    --kubernetes-version<span class="o">=</span>v1.23.16 <span class="se">\
</span></span></span><span class="line"><span class="cl">    --v<span class="o">=</span><span class="m">9</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Run &#34;kubectl apply -f [podnetwork].yaml&#34; with one of the options listed at:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   https://kubernetes.io/docs/concepts/cluster-administration/addons/</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># success</span>
</span></span><span class="line"><span class="cl">mkdir -p <span class="nv">$HOME</span>/.kube
</span></span><span class="line"><span class="cl">sudo cp -i /etc/kubernetes/admin.conf <span class="nv">$HOME</span>/.kube/config
</span></span><span class="line"><span class="cl">sudo chown <span class="k">$(</span>id -u<span class="k">)</span>:<span class="k">$(</span>id -g<span class="k">)</span> <span class="nv">$HOME</span>/.kube/config
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">KUBECONFIG</span><span class="o">=</span>/etc/kubernetes/admin.conf
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get node
</span></span><span class="line"><span class="cl"><span class="c1"># NAME     STATUS     ROLES                  AGE    VERSION</span>
</span></span><span class="line"><span class="cl"><span class="c1"># master   NotReady   control-plane,master   2m4s   v1.23.16</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># dubug</span>
</span></span><span class="line"><span class="cl">systemctl restart docker
</span></span><span class="line"><span class="cl">systemctl restart kubelet
</span></span><span class="line"><span class="cl">systemctl restart containerd
</span></span><span class="line"><span class="cl">journalctl -xeu kubelet
</span></span><span class="line"><span class="cl">crictr ps -a
</span></span><span class="line"><span class="cl">crictl --runtime-endpoint unix:///var/run/containerd/containerd.sock ps -a <span class="p">|</span> grep kube <span class="p">|</span> grep -v pause
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl get pods -n kube-system
</span></span><span class="line"><span class="cl">kubectl describe pods -n kube-system
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Flannel 网络插件 https://github.com/flannel-io/flannel/tree/master</span>
</span></span><span class="line"><span class="cl"><span class="c1"># curl https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml --output kube-flannel.yml</span>
</span></span><span class="line"><span class="cl"><span class="c1">#  net-conf.json: |</span>
</span></span><span class="line"><span class="cl"><span class="c1">#     {</span>
</span></span><span class="line"><span class="cl"><span class="c1">#       &#34;Network&#34;: &#34;10.10.0.0/16&#34;,</span>
</span></span><span class="line"><span class="cl"><span class="c1">#       &#34;Backend&#34;: {</span>
</span></span><span class="line"><span class="cl"><span class="c1">#         &#34;Type&#34;: &#34;vxlan&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#       }</span>
</span></span><span class="line"><span class="cl"><span class="c1">#     }</span>
</span></span><span class="line"><span class="cl">kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
</span></span><span class="line"><span class="cl">kubectl get node
</span></span><span class="line"><span class="cl"><span class="c1"># NAME     STATUS   ROLES                  AGE   VERSION</span>
</span></span><span class="line"><span class="cl"><span class="c1"># master   Ready    control-plane,master   14h   v1.23.16</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># show join command in control-plane</span>
</span></span><span class="line"><span class="cl">kubeadm token create --print-join-command
</span></span><span class="line"><span class="cl"><span class="c1"># work join; 云服务记得开放入站端口</span>
</span></span><span class="line"><span class="cl">telnet 172.21.0.5 <span class="m">6443</span>
</span></span><span class="line"><span class="cl">systemctl <span class="nb">enable</span> kubelet.service
</span></span><span class="line"><span class="cl">kubeadm join 172.21.0.5:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx --v<span class="o">=</span><span class="m">9</span>
</span></span><span class="line"><span class="cl"><span class="c1"># check in control-plane</span>
</span></span><span class="line"><span class="cl">kubectl get nodes
</span></span><span class="line"><span class="cl"><span class="c1"># NAME            STATUS   ROLES                  AGE     VERSION</span>
</span></span><span class="line"><span class="cl"><span class="c1"># master          Ready    control-plane,master   14h     v1.23.16</span>
</span></span><span class="line"><span class="cl"><span class="c1"># vm-0-9-centos   Ready    &lt;none&gt;                 3m27s   v1.23.16</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># run nginx</span>
</span></span><span class="line"><span class="cl">kubectl run ngx --image<span class="o">=</span>nginx:alpine
</span></span><span class="line"><span class="cl">kubectl get pod -o wide
</span></span><span class="line"><span class="cl"><span class="c1"># NAME   READY   STATUS    RESTARTS   AGE   IP          NODE      NOMINATED NODE   READINESS GATES</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx    1/1     Running   0          52m   10.10.1.2   woker01   &lt;none&gt;           &lt;none&gt;</span>
</span></span></code></pre></div><h2 id="18-deployment-部署应用">18 Deployment 部署应用</h2>
<p>“单一职责”和“对象组合”。既然 Pod 管理不了自己，那么我们就再创建一个新的对象，由它来管理 Pod，采用和 Job/CronJob 一样的形式——“对象套对象”。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl api-resources
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">out</span><span class="o">=</span><span class="s2">&#34;--dry-run=client -o yaml&#34;</span>
</span></span><span class="line"><span class="cl">kubectl create deploy ngx-dep --image<span class="o">=</span>nginx:alpine <span class="nv">$out</span> &gt; ngx-dep.yml
</span></span><span class="line"><span class="cl">vim ngx-dep.yml
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># 定义了 Pod 的“期望数量”，Kubernetes 会自动维护 Pod 数量到正常水平</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># 定义了基于 labels 筛选 Pod 的规则，它必须与 template 里 Pod 的 labels 一致</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">strategy</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 贴标签</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span></code></pre></div><p>Deployment 实际上并不“持有”Pod 对象，它只是帮助 Pod 对象能够有足够的副本数量运行。</p>
<p>通过标签这种设计，Kubernetes 就解除了 Deployment 和模板里 Pod 的强绑定，把组合关系变成了“弱引用”。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># replicas: 2</span>
</span></span><span class="line"><span class="cl">kubectl apply -f ngx-dep.yml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl get deploy
</span></span><span class="line"><span class="cl"><span class="c1"># NAME      READY   UP-TO-DATE   AVAILABLE   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep   2/2     2            2           57s</span>
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl"><span class="c1"># NAME                      READY   STATUS    RESTARTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep-bfbb5f64b-96scb   1/1     Running   0          3m20s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep-bfbb5f64b-qnzbh   1/1     Running   0          3m20s</span>
</span></span></code></pre></div><ul>
<li>READY：运行的 Pod 数量，当前数量/期望数量。</li>
<li>UP-TO-DATE：当前已经更新到最新状态的 Pod 数量。</li>
<li>AVAILABLE：不仅要求已经运行，还必须是健康状态，能够正常对外提供服务，它才是我们最关心的 Deployment 指标。</li>
<li>AGE：从创建到现在所经过的时间。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 测试自启恢复</span>
</span></span><span class="line"><span class="cl">kubectl delete pod ngx-dep-bfbb5f64b-qnzbh
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl"><span class="c1"># NAME                      READY   STATUS    RESTARTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep-bfbb5f64b-7n724   1/1     Running   0          33s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep-bfbb5f64b-96scb   1/1     Running   0          4m52s</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 测试伸缩</span>
</span></span><span class="line"><span class="cl">kubectl scale --replicas<span class="o">=</span><span class="m">5</span> deploy ngx-dep
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl"><span class="c1"># NAME                      READY   STATUS    RESTARTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep-bfbb5f64b-7n724   1/1     Running   0          77s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep-bfbb5f64b-7xhbs   1/1     Running   0          7s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep-bfbb5f64b-96scb   1/1     Running   0          5m36s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep-bfbb5f64b-97qp5   1/1     Running   0          7s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-dep-bfbb5f64b-vjn4q   1/1     Running   0          7s</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 筛选标签 ==、!=、in、notin</span>
</span></span><span class="line"><span class="cl">kubectl get pod -l <span class="nv">app</span><span class="o">=</span>nginx
</span></span><span class="line"><span class="cl">kubectl get pod -l <span class="s1">&#39;app in (ngx, nginx, ngx-dep)&#39;</span>
</span></span></code></pre></div><h2 id="19-daemonset-看门狗">19 DaemonSet 看门狗</h2>
<p>在 Deployment 看来，Pod 的运行环境与功能是无关的，只要 Pod 的数量足够，应用程序应该会正常工作。</p>
<p>有些场景下，要在集群里的每个节点上都运行 Pod，也就是说 Pod 的数量与节点数量保持同步。防止在集群里漂移。</p>
<p>DaemonSet 的目标是在集群的每个节点上运行且仅运行一个 Pod。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl api-resources
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># export out=&#34;--dry-run=client -o yaml&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># kubectl create deploy redis-ds --image=redis:5-alpine $out</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># kind modify DaemonSet, delete spec.replicas</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/daemonset/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># vim redis-ds.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">DaemonSet</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-ds</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">redis-ds</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># 和 deplayment 比没有 replicas</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-ds</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-ds</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">redis:5-alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">6379</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f redis-ds.yml
</span></span><span class="line"><span class="cl">kubectl get ds
</span></span><span class="line"><span class="cl"><span class="c1"># NAME       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds   2         2         2       2            2           &lt;none&gt;          30m</span>
</span></span><span class="line"><span class="cl">kubectl get pod -o wide
</span></span><span class="line"><span class="cl"><span class="c1"># 两个 worker 的场景</span>
</span></span><span class="line"><span class="cl"><span class="c1"># NAME                      READY   STATUS    RESTARTS   AGE   IP           NODE      NOMINATED NODE   READINESS GATES</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds-9r96k            1/1     Running   0          2m52s   10.10.3.2    woker02   &lt;none&gt;           &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds-hdl28            1/1     Running   0          21m     10.10.1.11   woker01   &lt;none&gt;           &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Master 节点却被排除在外了</span>
</span></span></code></pre></div><p>污点（taint）作用也是给节点“贴标签”。容忍度（toleration）Pod 能否“容忍”污点。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl describe node master
</span></span><span class="line"><span class="cl"><span class="c1"># Taints:             node-role.kubernetes.io/master:NoSchedule</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 污点会拒绝 Pod 调度到本节点上运行</span>
</span></span><span class="line"><span class="cl">kubectl describe node woker01
</span></span><span class="line"><span class="cl"><span class="c1"># Taints:             &lt;none&gt;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># - 出掉 master 污点</span>
</span></span><span class="line"><span class="cl">kubectl taint node master node-role.kubernetes.io/master:NoSchedule-
</span></span><span class="line"><span class="cl"><span class="c1"># NAME       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds   3         3         3       3            3           &lt;none&gt;          31m</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># Pod 添加 tolerations</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># kubectl explain ds.spec.template.spec.tolerations</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># vim redis-ds-t.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">DaemonSet</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-ds-t</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">redis-ds-t</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># 和 deplayment 比没有 replicas</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-ds-t</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-ds-t</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">redis:5-alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">6379</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 容忍 node-role.kubernetes.io/master</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">tolerations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">node-role.kubernetes.io/master</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">effect</span><span class="p">:</span><span class="w"> </span><span class="l">NoSchedule</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">Exists</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f redis-ds-t.yml
</span></span><span class="line"><span class="cl">kubectl get ds
</span></span><span class="line"><span class="cl"><span class="c1"># NAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds     2         2         2       2            2           &lt;none&gt;          41m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds-t   3         3         3       3            3           &lt;none&gt;          6s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 差别在 master</span>
</span></span><span class="line"><span class="cl">kubectl get pod -o wide
</span></span><span class="line"><span class="cl"><span class="c1"># NAME                      READY   STATUS    RESTARTS   AGE   IP           NODE      NOMINATED NODE   READINESS GATES</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds-9r96k            1/1     Running   0          23m   10.10.3.2    woker02   &lt;none&gt;           &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds-hdl28            1/1     Running   0          42m   10.10.1.11   woker01   &lt;none&gt;           &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds-t-4mptv          1/1     Running   0          80s   10.10.3.4    woker02   &lt;none&gt;           &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds-t-dpcl8          1/1     Running   0          80s   10.10.1.12   woker01   &lt;none&gt;           &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># redis-ds-t-kdjmn          1/1     Running   0          80s   10.10.0.6    master    &lt;none&gt;           &lt;none&gt;</span>
</span></span></code></pre></div><p><a href="https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/taint-and-toleration/">https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/taint-and-toleration/</a></p>
<p>静态 Pod：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ll -a /etc/kubernetes/manifests
</span></span><span class="line"><span class="cl"><span class="c1"># -rw------- 1 root root 2274 Feb 22 12:47 etcd.yaml</span>
</span></span><span class="line"><span class="cl"><span class="c1"># -rw------- 1 root root 3358 Feb 22 12:47 kube-apiserver.yaml</span>
</span></span><span class="line"><span class="cl"><span class="c1"># -rw------- 1 root root 2878 Feb 22 12:47 kube-controller-manager.yaml</span>
</span></span><span class="line"><span class="cl"><span class="c1"># -rw------- 1 root root 1465 Feb 22 12:47 kube-scheduler.yaml</span>
</span></span></code></pre></div><p>Kubernetes 的 4 个核心组件 apiserver、etcd、scheduler、controller-manager 原来都以静态 Pod 的形式存在的，这也是为什么它们能够先于 Kubernetes 集群启动的原因。</p>
<p>kubelet 会定期检查目录里的文件。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># flannel 就是一个 DaemonSet</span>
</span></span><span class="line"><span class="cl">kubectl get ns
</span></span><span class="line"><span class="cl">kubectl get ds -n kube-flannel
</span></span><span class="line"><span class="cl"><span class="c1"># NAME              DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kube-flannel-ds   3         3         3       3            3           &lt;none&gt;          3h54m</span>
</span></span></code></pre></div><h2 id="20-service-服务发现">20 Service 服务发现</h2>
<p>由 kube-proxy 控制的四层负载均衡，在 TCP/IP 协议栈上转发流量。</p>
<p>Pod 的生命周期很短暂，会不停地创建销毁，所以就需要用 Service 来实现负载均衡，它由 Kubernetes 分配固定的 IP 地址，能够屏蔽后端的 Pod 变化。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">out</span><span class="o">=</span><span class="s2">&#34;--dry-run=client -o yaml&#34;</span>
</span></span><span class="line"><span class="cl">kubectl expose deploy ngx-dep --port<span class="o">=</span><span class="m">80</span> --target-port<span class="o">=</span><span class="m">80</span> <span class="nv">$ou</span>
</span></span></code></pre></div><img width="600" alt="image" src="https://user-images.githubusercontent.com/9289792/220598245-da0f7319-8c28-4c08-b003-1dd209af1ee6.png">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim ngx-svc.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">loadBalancer</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ConfigMap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-conf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">default.conf</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">    server {
</span></span></span><span class="line"><span class="cl"><span class="sd">      listen 80;
</span></span></span><span class="line"><span class="cl"><span class="sd">      location / {
</span></span></span><span class="line"><span class="cl"><span class="sd">        default_type text/plain;
</span></span></span><span class="line"><span class="cl"><span class="sd">        return 200
</span></span></span><span class="line"><span class="cl"><span class="sd">          &#39;srv : $server_addr:$server_port\nhost: $hostname\nuri : $request_method $host $request_uri\ndate: $time_iso8601\n&#39;;
</span></span></span><span class="line"><span class="cl"><span class="sd">      }
</span></span></span><span class="line"><span class="cl"><span class="sd">    }</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-conf-vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">configMap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-conf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/nginx/conf.d</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-conf-vol</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f ngx-svc.yml
</span></span><span class="line"><span class="cl">kubectl get svc
</span></span><span class="line"><span class="cl"><span class="c1"># NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kubernetes   ClusterIP   10.96.0.1        &lt;none&gt;        443/TCP   5h10m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-svc      ClusterIP   10.109.131.132   &lt;none&gt;        80/TCP    35s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 虚地址 10.109.131.132</span>
</span></span><span class="line"><span class="cl">kubectl describe svc ngx-svc
</span></span><span class="line"><span class="cl"><span class="c1"># Name:              ngx-svc</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Namespace:         default</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Labels:            &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Annotations:       &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Selector:          app=ngx-dep</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Type:              ClusterIP</span>
</span></span><span class="line"><span class="cl"><span class="c1"># IP Family Policy:  SingleStack</span>
</span></span><span class="line"><span class="cl"><span class="c1"># IP Families:       IPv4</span>
</span></span><span class="line"><span class="cl"><span class="c1"># IP:                10.109.131.132</span>
</span></span><span class="line"><span class="cl"><span class="c1"># IPs:               10.109.131.132</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Port:              &lt;unset&gt;  80/TCP</span>
</span></span><span class="line"><span class="cl"><span class="c1"># TargetPort:        80/TCP</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Endpoints:         10.10.1.13:80,10.10.3.5:80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Session Affinity:  None</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Events:            &lt;none&gt;</span>
</span></span><span class="line"><span class="cl">kubectl get pod -o wide
</span></span><span class="line"><span class="cl">NAME                       READY   STATUS    RESTARTS   AGE    IP           NODE
</span></span><span class="line"><span class="cl">ngx-dep-6796688696-cwm8f   1/1     Running   <span class="m">0</span>          2m4s   10.10.3.5    woker02
</span></span><span class="line"><span class="cl">ngx-dep-6796688696-khjnv   1/1     Running   <span class="m">0</span>          2m2s   10.10.1.13   woker01
</span></span><span class="line"><span class="cl"><span class="c1"># same Endpoints</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 因为 Service、 Pod 的 IP 地址都是 Kubernetes 集群的内部网段</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   所以我们需要用 kubectl exec 进入到 Pod 内部</span>
</span></span><span class="line"><span class="cl">kubectl <span class="nb">exec</span> -it ngx-dep-6796688696-cwm8f -- sh
</span></span><span class="line"><span class="cl">curl 10.109.131.132
</span></span><span class="line"><span class="cl"><span class="c1"># srv : 10.10.3.5:80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># host: ngx-dep-6796688696-cwm8f</span>
</span></span><span class="line"><span class="cl"><span class="c1"># uri : GET 10.109.131.132 /</span>
</span></span><span class="line"><span class="cl"><span class="c1"># date: 2023-02-22T10:09:49+00:00</span>
</span></span><span class="line"><span class="cl">curl 10.109.131.132
</span></span><span class="line"><span class="cl"><span class="c1"># srv : 10.10.1.13:80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># host: ngx-dep-6796688696-khjnv</span>
</span></span><span class="line"><span class="cl"><span class="c1"># uri : GET 10.109.131.132 /</span>
</span></span><span class="line"><span class="cl"><span class="c1"># date: 2023-02-22T10:09:50+00:00</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 测试恢复</span>
</span></span><span class="line"><span class="cl">kubectl delete pod ngx-dep-6796688696-khjnv
</span></span><span class="line"><span class="cl">kubectl describe svc ngx-svc
</span></span><span class="line"><span class="cl"><span class="c1"># Endpoints:         10.10.1.14:80,10.10.3.5:80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 之前是 10.10.1.13:80,10.10.3.5:80</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 测试扩容</span>
</span></span><span class="line"><span class="cl">kubectl scale --replicas<span class="o">=</span><span class="m">5</span> deploy ngx-dep
</span></span><span class="line"><span class="cl">kubectl describe svc ngx-svc
</span></span><span class="line"><span class="cl"><span class="c1"># Endpoints:         10.10.1.14:80,10.10.1.15:80,10.10.3.5:80 + 2 more...</span>
</span></span></code></pre></div><p>Service 对象的域名完全形式是 <code>对象.名字空间.svc.cluster.local</code>，但很多时候也可以省略后面的部分，直接写 <code>对象.名字空间</code> 甚至 <code>对象名</code> 就足够了，默认会使用对象所在的名字空间（比如这里就是 default）。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Name:              ngx-svc</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Namespace:         default</span>
</span></span><span class="line"><span class="cl">kubectl <span class="nb">exec</span> -it ngx-dep-6796688696-cwm8f -- sh
</span></span><span class="line"><span class="cl">curl ngx-svc
</span></span><span class="line"><span class="cl"><span class="c1"># srv : 10.10.3.5:80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># host: ngx-dep-6796688696-cwm8f</span>
</span></span><span class="line"><span class="cl"><span class="c1"># uri : GET ngx-svc /</span>
</span></span><span class="line"><span class="cl"><span class="c1"># date: 2023-02-22T10:19:25+00:00</span>
</span></span><span class="line"><span class="cl">curl ngx-svc.default
</span></span><span class="line"><span class="cl"><span class="c1"># srv : 10.10.3.6:80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># host: ngx-dep-6796688696-lpcfs</span>
</span></span><span class="line"><span class="cl"><span class="c1"># uri : GET ngx-svc.default /</span>
</span></span><span class="line"><span class="cl"><span class="c1"># date: 2023-02-22T10:19:41+00:00</span>
</span></span><span class="line"><span class="cl">curl ngx-svc.default.svc.cluster.local
</span></span><span class="line"><span class="cl"><span class="c1"># srv : 10.10.3.5:80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># host: ngx-dep-6796688696-cwm8f</span>
</span></span><span class="line"><span class="cl"><span class="c1"># uri : GET ngx-svc.default.svc.cluster.local /</span>
</span></span><span class="line"><span class="cl"><span class="c1"># date: 2023-02-22T10:20:04+00:00</span>
</span></span></code></pre></div><p>Pod 分配了域名：<code>IP 地址.名字空间.pod.cluster.local</code> IP 地址 . 改成 -。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># kubectl explain svc.spec.type</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># vim ngx-svc.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="l">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">NodePort</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f ngx-svc.yml
</span></span><span class="line"><span class="cl">kubectl get svc
</span></span><span class="line"><span class="cl"><span class="c1"># NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kubernetes   ClusterIP   10.96.0.1        &lt;none&gt;        443/TCP        5h56m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-svc      NodePort    10.109.131.132   &lt;none&gt;        80:30916/TCP   46m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Service 的默认类型是“ClusterIP”，只能在集群内部访问，</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   如果改成“NodePort”，就会在节点上开启一个随机端口号，让外界也能够访问内部的服务。</span>
</span></span><span class="line"><span class="cl">curl localhost:30916
</span></span><span class="line"><span class="cl"><span class="c1"># srv : 10.10.1.15:80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># host: ngx-dep-6796688696-l6skl</span>
</span></span><span class="line"><span class="cl"><span class="c1"># uri : GET localhost /</span>
</span></span><span class="line"><span class="cl"><span class="c1"># date: 2023-02-22T10:48:32+00:00</span>
</span></span></code></pre></div><img width="600" alt="image" src="https://user-images.githubusercontent.com/9289792/220598107-2899f665-3977-4e19-894d-bd273046fbae.png">
<h2 id="21-ingress-流量总管">21 Ingress 流量总管</h2>
<p>Service 本身是没有服务能力的，它只是一些 iptables 规则，真正配置、应用这些规则的实际上是节点里的 kube-proxy 组件。</p>
<p>Ingress 也只是一些 HTTP 路由规则的集合，相当于一份静态的描述文件，真正要把这些规则在集群里实施运行，还需要有另外一个东西，这就是 Ingress Controller，它的作用就相当于 Service 的 kube-proxy，能够读取、应用 Ingress 规则，处理、调度流量。</p>
<p>Ingress Class 是插在 Ingress 和 Ingress Controller 中间，作为流量规则和控制器的协调人，解除了 Ingress 和 Ingress Controller 的强绑定关系。</p>
<p>Kubernetes 用户可以转向管理 Ingress Class，用它来定义不同的业务逻辑分组，简化 Ingress 规则的复杂度。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">out</span><span class="o">=</span><span class="s2">&#34;--dry-run=client -o yaml&#34;</span>
</span></span><span class="line"><span class="cl">kubectl create ing ngx-ing --rule<span class="o">=</span><span class="s2">&#34;ngx.test/=ngx-svc:80&#34;</span> --class<span class="o">=</span>ngx-ink <span class="nv">$out</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim ingress.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">networking.k8s.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Ingress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-ing</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ingressClassName</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-ink</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">ngx.test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">http</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># 路径的匹配方式</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">backend</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">service</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">port</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">number</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="c"># 精确匹配（Exact）或前缀匹配（Prefix）</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">pathType</span><span class="p">:</span><span class="w"> </span><span class="l">Exact</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">loadBalancer</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">networking.k8s.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">IngressClass</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-ink</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">controller</span><span class="p">:</span><span class="w"> </span><span class="l">nginx.org/ingress-controller</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f ingress.yml
</span></span><span class="line"><span class="cl"><span class="c1"># NAME      CONTROLLER                     PARAMETERS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-ink   nginx.org/ingress-controller   &lt;none&gt;       15s</span>
</span></span><span class="line"><span class="cl">kubectl get ing
</span></span><span class="line"><span class="cl"><span class="c1"># NAME      CLASS     HOSTS      ADDRESS   PORTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx-ing   ngx-ink   ngx.test             80      84s</span>
</span></span><span class="line"><span class="cl">kubectl describe ing ngx-ing
</span></span><span class="line"><span class="cl"><span class="c1"># Name:             ngx-ing</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Labels:           &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Namespace:        default</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Address:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Default backend:  default-http-backend:80 (&lt;error: endpoints &#34;default-http-backend&#34; not found&gt;)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Rules:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   Host        Path  Backends</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   ----        ----  --------</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   ngx.test</span>
</span></span><span class="line"><span class="cl"><span class="c1">#               /   ngx-svc:80 (10.10.1.14:80,10.10.1.15:80)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Annotations:  &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Events:       &lt;none&gt;</span>
</span></span></code></pre></div><p>在 Kubernetes 里使用 Ingress Controller：</p>
<p><a href="https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/">https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/nginxinc/kubernetes-ingress.git --branch v3.0.2
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> kubernetes-ingress/deployments
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Configure RBAC</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 为Ingress控制器创建一个命名空间和一个服务账户</span>
</span></span><span class="line"><span class="cl">kubectl apply -f common/ns-and-sa.yaml
</span></span><span class="line"><span class="cl"><span class="c1"># 为服务账户创建一个集群角色和集群角色绑定</span>
</span></span><span class="line"><span class="cl">kubectl apply -f rbac/rbac.yaml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Create Common Resources</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 为NGINX的默认服务器创建一个带有TLS证书和密钥的秘密</span>
</span></span><span class="line"><span class="cl">kubectl apply -f common/default-server-secret.yaml
</span></span><span class="line"><span class="cl"><span class="c1"># 创建一个 config map，用于定制NGINX配置。</span>
</span></span><span class="line"><span class="cl">kubectl apply -f common/nginx-config.yaml
</span></span><span class="line"><span class="cl"><span class="c1"># 创建一个IngressClass资源</span>
</span></span><span class="line"><span class="cl">kubectl apply -f common/ingress-class.yaml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Create Custom Resources</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kubectl apply -f common/crds/</span>
</span></span><span class="line"><span class="cl">vim deployment/nginx-ingress.yaml
</span></span><span class="line"><span class="cl"><span class="c1"># args add:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># -enable-custom-resources=false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Run the Ingress Controller</span>
</span></span><span class="line"><span class="cl">kubectl apply -f deployment/nginx-ingress.yaml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># check</span>
</span></span><span class="line"><span class="cl">kubectl get pods --namespace<span class="o">=</span>nginx-ingress
</span></span><span class="line"><span class="cl"><span class="c1"># NAME                             READY   STATUS    RESTARTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># nginx-ingress-5f98f8f5f9-nnkv7   1/1     Running   0          3m14s</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Get Access to the Ingress Controller</span>
</span></span><span class="line"><span class="cl">kubectl create -f service/nodeport.yaml
</span></span><span class="line"><span class="cl">kubectl get service -n nginx-ingress
</span></span><span class="line"><span class="cl"><span class="c1"># NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># nginx-ingress   NodePort   10.111.210.52   &lt;none&gt;        80:31754/TCP,443:30188/TCP   5s</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># debug</span>
</span></span><span class="line"><span class="cl">kubectl get IngressClass
</span></span><span class="line"><span class="cl">kubectl get ing -n nginx-ingress
</span></span><span class="line"><span class="cl">kubectl get deploy -n nginx-ingress
</span></span><span class="line"><span class="cl">kubectl get pod -n nginx-ingress -o wide
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl describe service -n nginx-ingress -o wide
</span></span><span class="line"><span class="cl">kubectl describe pod -n nginx-ingress
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 命令kubectl port-forward，它可以直接把本地的端口映射到 Kubernetes 集群的某个 Pod 里</span>
</span></span><span class="line"><span class="cl">kubectl port-forward -n nginx-ingress nginx-ingress-5f98f8f5f9-nnkv7 8080:80 <span class="p">&amp;</span>
</span></span></code></pre></div><img width="900" alt="image" src="https://user-images.githubusercontent.com/9289792/221135639-3b31c1be-f624-4abf-8851-cff396e9be45.png">
<h2 id="22-玩转-kubernetes-2">22 玩转 Kubernetes 2</h2>
<p>Kubernetes 部署 WordPress：</p>
<img width="900" alt="image" src="https://user-images.githubusercontent.com/9289792/221116386-7f08aa66-941c-42ac-a0c3-bb8a111fc42d.png">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim wp-maria.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ConfigMap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-cm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">DATABASE</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;db&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">USER</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;wp&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;123&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ROOT_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;123&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">mariadb:10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mariadb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">envFrom</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">prefix</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;MARIADB_&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">configMapRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-cm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f wp-maria.yml
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl">kubectl get deploy
</span></span><span class="line"><span class="cl">kubectl get svc
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim wp-app.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ConfigMap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-cm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># DNS HOST</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">HOST</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;maria-svc&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">USER</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;wp&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;123&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">NAME</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;db&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress:5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">envFrom</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">prefix</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;WORDPRESS_DB_&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">configMapRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-cm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">http80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 指定端口</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">nodePort</span><span class="p">:</span><span class="w"> </span><span class="m">30088</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># NodePort</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">NodePort</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f wp-app.yml
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl">kubectl get deploy
</span></span><span class="line"><span class="cl">kubectl get svc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl port-forward service/wp-svc 80:80 --address 0.0.0.0
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim wp-ing.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">networking.k8s.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">IngressClass</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-ink</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">controller</span><span class="p">:</span><span class="w"> </span><span class="l">nginx.org/ingress-controller</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># kubectl create ing wp-ing --rule=&#34;wp.test/=wp-svc:80&#34; --class=wp-ink $out</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">networking.k8s.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Ingress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-ing</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ingressClassName</span><span class="p">:</span><span class="w"> </span><span class="l">wp-ink</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">wp.test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">http</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">pathType</span><span class="p">:</span><span class="w"> </span><span class="l">Prefix</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">backend</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">service</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">port</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">number</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim wp-kic-dep.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-kic-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">nginx-ingress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-kic-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-kic-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># kubectl explain Deployment.spec.template.spec.serviceAccountName</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">serviceAccountName</span><span class="p">:</span><span class="w"> </span><span class="l">nginx-ingress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># kubectl explain Deployment.spec.template.spec.hostNetwork</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">hostNetwork</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx/nginx-ingress:3.0.2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">imagePullPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">IfNotPresent</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nginx-ingress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">http</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">https</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">443</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">readiness-port</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">8081</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">9113</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">readinessProbe</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">httpGet</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/nginx-ready</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="l">readiness-port</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">periodSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">securityContext</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">allowPrivilegeEscalation</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">runAsUser</span><span class="p">:</span><span class="w"> </span><span class="m">101</span><span class="w"> </span><span class="c">#nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">runAsNonRoot</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">capabilities</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">drop</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- <span class="l">ALL</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">add</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- <span class="l">NET_BIND_SERVICE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">POD_NAMESPACE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">valueFrom</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">fieldRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">fieldPath</span><span class="p">:</span><span class="w"> </span><span class="l">metadata.namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">POD_NAME</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">valueFrom</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">fieldRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">fieldPath</span><span class="p">:</span><span class="w"> </span><span class="l">metadata.name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">args</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="c"># 默认是 nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- -<span class="l">ingress-class=wp-ink</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- -<span class="l">enable-custom-resources=false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- -<span class="l">nginx-configmaps=$(POD_NAMESPACE)/nginx-config</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- -<span class="l">default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-kic-svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">nginx-ingress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">nodePort</span><span class="p">:</span><span class="w"> </span><span class="m">30080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-kic-dep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">NodePort</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f wp-ing.yml -f wp-kic-dep.yml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl get ing
</span></span><span class="line"><span class="cl">kubectl get ingressclass
</span></span><span class="line"><span class="cl">kubectl get pod -n<span class="o">=</span>nginx-ingress
</span></span><span class="line"><span class="cl">kubectl describe pod -n<span class="o">=</span>nginx-ingress
</span></span><span class="line"><span class="cl">kubectl get deploy -n<span class="o">=</span>nginx-ingress
</span></span><span class="line"><span class="cl">kubectl get svc -n<span class="o">=</span>nginx-ingress
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 在服务器上</span>
</span></span><span class="line"><span class="cl">kubectl get pod -n<span class="o">=</span>nginx-ingress -o<span class="o">=</span>wide
</span></span><span class="line"><span class="cl"><span class="c1"># NAME                          READY   STATUS    RESTARTS   AGE   IP           NODE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wp-kic-dep-68579bc688-d64zs   1/1     Running   0          10m   172.21.0.9   woker01</span>
</span></span><span class="line"><span class="cl">curl 172.21.0.9 -H <span class="s2">&#34;HOST: wp.test&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 在服务器外</span>
</span></span><span class="line"><span class="cl">kubectl port-forward service/wp-kic-svc -n<span class="o">=</span>nginx-ingress 80:80 --address 0.0.0.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">vim /etc/hosts
</span></span><span class="line"><span class="cl"><span class="o">[</span>master ip<span class="o">]</span> wp.test
</span></span><span class="line"><span class="cl"><span class="c1"># 游览器访问 wp.test</span>
</span></span></code></pre></div><img width="900" alt="image" src="https://user-images.githubusercontent.com/9289792/221134023-69087496-f390-4b6a-90ca-9e7dde86bfc1.png">
<h2 id="23-中级篇实操总结">23 中级篇实操总结</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># DaemonSet 模板生成</span>
</span></span><span class="line"><span class="cl">kubectl create deploy redis-ds --image<span class="o">=</span>redis:5-alpine <span class="nv">$out</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> sed <span class="s1">&#39;s/Deployment/DaemonSet/g&#39;</span> - <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> sed -e <span class="s1">&#39;/replicas/d&#39;</span> -
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/chronolaw/k8s_study">chronolaw/k8s_study | GitHub</a></li>
<li><a href="https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/">nginx-ingress-controller Installation with Manifests | nginx.com</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Kubernetes 入门实战 Part1</title>
      <link>https://zyf.im/2023/02/15/kubernetes-getting-started-part1/</link>
      <pubDate>Wed, 15 Feb 2023 14:46:38 +0000</pubDate>
      <guid>https://zyf.im/2023/02/15/kubernetes-getting-started-part1/</guid>
      <description>&lt;h2 id=&#34;01-初识-docker&#34;&gt;01 初识 Docker&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.docker.com/get-started/overview/&#34;&gt;https://docs.docker.com/get-started/overview/&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -fsSL https://get.docker.com &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# sudo usermod -aG docker $USER&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# newgrp docker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dockerd-rootless-setuptool.sh install
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker info
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker ps
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker pull busybox
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# show all images&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker images
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;02-被隔离的进程&#34;&gt;02 被隔离的进程&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 拉取轻量级 Alpine Linux 镜像&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker pull alpine
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 以交互模式运行容器并进入 shell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker run -it alpine sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看容器内的操作系统信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /etc/os-release
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Docker 实现资源隔离的三种核心技术：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;namespace：隔离进程、网络、文件系统等资源视图&lt;/li&gt;
&lt;li&gt;cgroup：限制和监控资源使用（CPU、内存、磁盘 I/O 等）&lt;/li&gt;
&lt;li&gt;chroot/pivot_root：改变进程的根目录，实现文件系统隔离&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;03-容器化的应用&#34;&gt;03 容器化的应用&lt;/h2&gt;
&lt;p&gt;Build once, Run anywhere.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="01-初识-docker">01 初识 Docker</h2>
<p><a href="https://docs.docker.com/get-started/overview/">https://docs.docker.com/get-started/overview/</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -fsSL https://get.docker.com <span class="p">|</span> sudo sh
</span></span><span class="line"><span class="cl"><span class="c1"># sudo usermod -aG docker $USER</span>
</span></span><span class="line"><span class="cl"><span class="c1"># newgrp docker</span>
</span></span><span class="line"><span class="cl">dockerd-rootless-setuptool.sh install
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">docker version
</span></span><span class="line"><span class="cl">docker info
</span></span><span class="line"><span class="cl">docker ps
</span></span><span class="line"><span class="cl">docker pull busybox
</span></span><span class="line"><span class="cl"><span class="c1"># show all images</span>
</span></span><span class="line"><span class="cl">docker images
</span></span></code></pre></div><h2 id="02-被隔离的进程">02 被隔离的进程</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 拉取轻量级 Alpine Linux 镜像</span>
</span></span><span class="line"><span class="cl">docker pull alpine
</span></span><span class="line"><span class="cl"><span class="c1"># 以交互模式运行容器并进入 shell</span>
</span></span><span class="line"><span class="cl">docker run -it alpine sh
</span></span><span class="line"><span class="cl"><span class="c1"># 查看容器内的操作系统信息</span>
</span></span><span class="line"><span class="cl">cat /etc/os-release
</span></span></code></pre></div><p>Docker 实现资源隔离的三种核心技术：</p>
<ul>
<li>namespace：隔离进程、网络、文件系统等资源视图</li>
<li>cgroup：限制和监控资源使用（CPU、内存、磁盘 I/O 等）</li>
<li>chroot/pivot_root：改变进程的根目录，实现文件系统隔离</li>
</ul>
<h2 id="03-容器化的应用">03 容器化的应用</h2>
<p>Build once, Run anywhere.</p>
<p>应用程序不再直接和操作系统打交道，而是封装成镜像，再交给容器环境去运行。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># remove image</span>
</span></span><span class="line"><span class="cl">docker rmi busybox
</span></span><span class="line"><span class="cl"><span class="c1"># 开启一个交互式操作的 Shell</span>
</span></span><span class="line"><span class="cl">docker run -it busybox
</span></span><span class="line"><span class="cl"><span class="c1"># 在后台运行</span>
</span></span><span class="line"><span class="cl">docker run -d busybox
</span></span><span class="line"><span class="cl"><span class="c1"># 为容器起一个名字</span>
</span></span><span class="line"><span class="cl">docker run -d --name xxx busybox
</span></span><span class="line"><span class="cl"><span class="c1"># 不保存容器，只要运行完毕就自动清除</span>
</span></span><span class="line"><span class="cl">docker run --rm busybox <span class="nb">echo</span> <span class="s2">&#34;hello docker&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">docker stop xxx
</span></span><span class="line"><span class="cl"><span class="c1"># remove container</span>
</span></span><span class="line"><span class="cl">docker rm xxx
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># show all container</span>
</span></span><span class="line"><span class="cl">docker ps -a
</span></span><span class="line"><span class="cl">docker <span class="nb">exec</span> xxx <span class="nb">echo</span> <span class="s2">&#34;hello docker&#34;</span>
</span></span></code></pre></div><h2 id="05-镜像仓库">05 镜像仓库</h2>
<ul>
<li><a href="https://github.com/docker-library/official-images">https://github.com/docker-library/official-images</a></li>
<li><a href="https://hub.docker.com/u/library">https://hub.docker.com/u/library</a></li>
</ul>
<p><code>用户名/应用名:标签</code> 官方镜像用户名是 <code>library</code>。</p>
<ul>
<li>slim 经过精简的</li>
<li>fat 包含了较多的辅助工具</li>
<li>rc 候选版本，release candidate</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t
</span></span><span class="line"><span class="cl">docker tag ngx-app chronolaw/ngx-app:1.0
</span></span><span class="line"><span class="cl">docker push chronolaw/ngx-app:1.0
</span></span></code></pre></div><p>save 和 load 这两个镜像归档命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker save ngx-app:latest -o ngx.tar
</span></span><span class="line"><span class="cl">docker load -i ngx.tar
</span></span></code></pre></div><h2 id="06-打破次元壁">06 打破次元壁</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -d --rm --name ubu phusion/baseimage:jammy-1.0.1
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;hello&#34;</span> &gt; a.txt
</span></span><span class="line"><span class="cl"><span class="c1"># a.txt 拷贝到容器 tmp</span>
</span></span><span class="line"><span class="cl">docker cp a.txt ubu:/tmp
</span></span><span class="line"><span class="cl">docker <span class="nb">exec</span> -it ubu bash
</span></span><span class="line"><span class="cl"><span class="c1"># 考出容器</span>
</span></span><span class="line"><span class="cl">docker cp ubu:/tmp/a.txt ./b.txt
</span></span></code></pre></div><p>容器和主机共享本地目录：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># --mount</span>
</span></span><span class="line"><span class="cl"><span class="c1"># -v /tmp:/tmp:ro 只读</span>
</span></span><span class="line"><span class="cl">docker run -d --rm -v /tmp:/tmp --name ubu phusion/baseimage:jammy-1.0.1
</span></span><span class="line"><span class="cl">docker <span class="nb">exec</span> -it ubu bash
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker pull python:alpine
</span></span><span class="line"><span class="cl">docker run -it --rm -v <span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>:/tmp python:alpine sh
</span></span></code></pre></div><p>网络模式：<code>null</code> <code>host</code> <code>bridge</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># host</span>
</span></span><span class="line"><span class="cl">docker run -d --rm --net<span class="o">=</span>host --name<span class="o">=</span>ng nginx:alpine
</span></span><span class="line"><span class="cl">docker <span class="nb">exec</span> ng ip addr
</span></span><span class="line"><span class="cl">docker inspect ng <span class="p">|</span> grep IPAddress
</span></span><span class="line"><span class="cl">docker stop ng
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># bridge 默认模式</span>
</span></span><span class="line"><span class="cl">docker run -d --rm --name<span class="o">=</span>ng nginx:alpine
</span></span><span class="line"><span class="cl">docker inspect ng <span class="p">|</span> grep IPAddress
</span></span><span class="line"><span class="cl"><span class="c1"># &#34;IPAddress&#34;: &#34;172.17.0.3&#34;</span>
</span></span><span class="line"><span class="cl">docker run -d --rm --name<span class="o">=</span>rd redis
</span></span><span class="line"><span class="cl">docker inspect rd <span class="p">|</span> grep IPAddress
</span></span><span class="line"><span class="cl"><span class="c1"># &#34;IPAddress&#34;: &#34;172.17.0.4&#34;</span>
</span></span></code></pre></div><p>分配服务端口号</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -d -p 80:80 --rm nginx:alpine
</span></span><span class="line"><span class="cl">docker run -d -p 8080:80 --rm nginx:alpine
</span></span><span class="line"><span class="cl"><span class="c1"># 分别“映射”到了两个容器里的 80 端口</span>
</span></span></code></pre></div><h2 id="07-玩转-docker">07 玩转 Docker</h2>
<p>Container Image Registry</p>
<p><a href="https://registry.hub.docker.com/_/registry/">https://registry.hub.docker.com/_/registry/</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -d -p 5000:5000 registry
</span></span><span class="line"><span class="cl"><span class="c1"># 使用 docker tag 命令给镜像打标签再上传</span>
</span></span><span class="line"><span class="cl">docker tag nginx:alpine 127.0.0.1:5000/nginx:alpine
</span></span><span class="line"><span class="cl">docker push 127.0.0.1:5000/nginx:alpine
</span></span><span class="line"><span class="cl"><span class="c1"># 本次重新拉取测试</span>
</span></span><span class="line"><span class="cl">docker rmi 127.0.0.1:5000/nginx:alpine
</span></span><span class="line"><span class="cl">docker pull 127.0.0.1:5000/nginx:alpine
</span></span></code></pre></div><p><a href="https://docs.docker.com/registry/spec/api/">https://docs.docker.com/registry/spec/api/</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Listing Repositories</span>
</span></span><span class="line"><span class="cl">curl 127.1:5000/v2/_catalog
</span></span><span class="line"><span class="cl"><span class="c1"># {&#34;repositories&#34;:[&#34;nginx&#34;]}</span>
</span></span><span class="line"><span class="cl">curl 127.1:5000/v2/nginx/tags/list
</span></span><span class="line"><span class="cl"><span class="c1"># {&#34;name&#34;:&#34;nginx&#34;,&#34;tags&#34;:[&#34;alpine&#34;]}</span>
</span></span></code></pre></div><p>registry 默认会把镜像存储在 Docker 内部目录 <code>/var/lib/registry</code>。</p>
<p>搭建 WordPress：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -d --rm <span class="se">\
</span></span></span><span class="line"><span class="cl">    --env <span class="nv">MARIADB_DATABASE</span><span class="o">=</span>db <span class="se">\
</span></span></span><span class="line"><span class="cl">    --env <span class="nv">MARIADB_USER</span><span class="o">=</span>wp <span class="se">\
</span></span></span><span class="line"><span class="cl">    --env <span class="nv">MARIADB_PASSWORD</span><span class="o">=</span><span class="m">123</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    --env <span class="nv">MARIADB_ROOT_PASSWORD</span><span class="o">=</span><span class="m">123</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    --name mariadb <span class="se">\
</span></span></span><span class="line"><span class="cl">    mariadb:10
</span></span><span class="line"><span class="cl"><span class="c1"># 进入 mariadb</span>
</span></span><span class="line"><span class="cl">docker <span class="nb">exec</span> -it mariadb mysql -uwp -p123
</span></span><span class="line"><span class="cl"><span class="c1"># show mariadb ip</span>
</span></span><span class="line"><span class="cl">docker inspect mariadb <span class="p">|</span> grep IPAddress
</span></span><span class="line"><span class="cl"><span class="c1"># &#34;IPAddress&#34;: &#34;172.17.0.2&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">docker run -d --rm <span class="se">\
</span></span></span><span class="line"><span class="cl">    --env <span class="nv">WORDPRESS_DB_HOST</span><span class="o">=</span>172.17.0.2 <span class="se">\
</span></span></span><span class="line"><span class="cl">    --env <span class="nv">WORDPRESS_DB_USER</span><span class="o">=</span>wp <span class="se">\
</span></span></span><span class="line"><span class="cl">    --env <span class="nv">WORDPRESS_DB_PASSWORD</span><span class="o">=</span><span class="m">123</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    --env <span class="nv">WORDPRESS_DB_NAME</span><span class="o">=</span>db <span class="se">\
</span></span></span><span class="line"><span class="cl">    --name wp <span class="se">\
</span></span></span><span class="line"><span class="cl">    wordpress:5
</span></span><span class="line"><span class="cl">docker inspect wp <span class="p">|</span> grep IPAddress
</span></span><span class="line"><span class="cl"><span class="c1"># &#34;IPAddress&#34;: &#34;172.17.0.4&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim wp.conf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">server <span class="o">{</span>
</span></span><span class="line"><span class="cl">  listen 80<span class="p">;</span>
</span></span><span class="line"><span class="cl">  default_type text/html<span class="p">;</span>
</span></span><span class="line"><span class="cl">  location / <span class="o">{</span>
</span></span><span class="line"><span class="cl">      proxy_http_version 1.1<span class="p">;</span>
</span></span><span class="line"><span class="cl">      proxy_set_header Host <span class="nv">$host</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># wordpress server</span>
</span></span><span class="line"><span class="cl">      proxy_pass http://172.17.0.4<span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 感觉可以直接用 wordpress 的 80，无需再代理一次</span>
</span></span><span class="line"><span class="cl">docker run -d --rm <span class="se">\
</span></span></span><span class="line"><span class="cl">    -p 80:80 <span class="se">\
</span></span></span><span class="line"><span class="cl">    -v <span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/wp.conf:/etc/nginx/conf.d/default.conf <span class="se">\
</span></span></span><span class="line"><span class="cl">    --name ng <span class="se">\
</span></span></span><span class="line"><span class="cl">    nginx:alpine
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># show logs</span>
</span></span><span class="line"><span class="cl">docker logs mariadb
</span></span><span class="line"><span class="cl">docker logs ng
</span></span><span class="line"><span class="cl">docker logs wp
</span></span></code></pre></div><h2 id="08-入门篇总结">08 入门篇总结</h2>
<p>构建自己的镜像：</p>
<p><a href="https://github.com/chronolaw/k8s_study/blob/master/ch1/Dockerfile">https://github.com/chronolaw/k8s_study/blob/master/ch1/Dockerfile</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">IMAGE_BASE</span><span class="o">=</span><span class="s2">&#34;nginx&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">IMAGE_TAG</span><span class="o">=</span><span class="s2">&#34;1.21-alpine&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">${IMAGE_BASE}:${IMAGE_TAG</span><span class="o">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ENV</span> <span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:/tmp<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ENV</span> <span class="nv">DEBUG</span><span class="o">=</span>OFF
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">COPY</span> ./default.conf /etc/nginx/conf.d/<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> <span class="nb">cd</span> /usr/share/nginx/html <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">&#34;hello nginx&#34;</span> &gt; a.txt<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">EXPOSE</span><span class="w"> </span><span class="s">8081</span> <span class="m">8082</span> <span class="m">8083</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/etc/nginx</span><span class="err">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t ngx-app:1.0 .
</span></span><span class="line"><span class="cl">docker run -it --rm ngx-app:1.0 sh
</span></span><span class="line"><span class="cl">docker save ngx-app:1.0 -o ngx.tar
</span></span><span class="line"><span class="cl">docker load -i ngx.tar
</span></span></code></pre></div><h2 id="09-kubernetes-环境">09 Kubernetes 环境</h2>
<p>Kubernetes 就是一个 <a href="https://kubernetes.io/">Production-Grade Container Orchestration</a> 生产级别的容器编排平台和集群管理系统。</p>
<p><a href="https://minikube.sigs.k8s.io/docs/start/">minikube</a> quickly sets up a local Kubernetes cluster on macOS, Linux, and Windows.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">brew install minikube
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">minikube version
</span></span><span class="line"><span class="cl"><span class="c1"># minikube version: v1.37.0</span>
</span></span><span class="line"><span class="cl"><span class="c1"># commit: 65318f4cfff9c12cc87ec9eb8f4cdd57b25047f3</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 统一实验环境</span>
</span></span><span class="line"><span class="cl">minikube start
</span></span><span class="line"><span class="cl"><span class="c1"># 查看状态</span>
</span></span><span class="line"><span class="cl">minikube status
</span></span><span class="line"><span class="cl"><span class="c1"># type: Control Plane</span>
</span></span><span class="line"><span class="cl"><span class="c1"># host: Running</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kubelet: Running</span>
</span></span><span class="line"><span class="cl"><span class="c1"># apiserver: Running</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kubeconfig: Configured</span>
</span></span><span class="line"><span class="cl">minikube node list
</span></span><span class="line"><span class="cl"><span class="c1"># minikube 192.168.49.2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 登录到这个节点上</span>
</span></span><span class="line"><span class="cl">minikube ssh
</span></span><span class="line"><span class="cl">uname -a
</span></span><span class="line"><span class="cl"><span class="c1"># Linux minikube 6.17.8-orbstack-00308-g8f9c941121b1 #1 SMP PREEMPT Thu Nov 20 09:34:02 UTC 2025 aarch64 aarch64 aarch64 GNU/Linux</span>
</span></span><span class="line"><span class="cl">ip add
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># minikube 管理 Kubernetes 集群环境</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kubectl 操作实际的 Kubernetes 功能</span>
</span></span><span class="line"><span class="cl"><span class="c1"># install kubectl</span>
</span></span><span class="line"><span class="cl">minikube kubectl
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">minikube kubectl -- version
</span></span><span class="line"><span class="cl"><span class="c1"># Client Version: v1.34.0</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Kustomize Version: v5.7.1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Server Version: v1.34.0</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">kubectl</span><span class="o">=</span><span class="s2">&#34;minikube kubectl --&#34;</span>
</span></span><span class="line"><span class="cl">kubectl version
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 启动一个镜像</span>
</span></span><span class="line"><span class="cl">kubectl run ngx --image<span class="o">=</span>nginx:alpine
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># like docker ps</span>
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl"><span class="c1"># NAME   READY   STATUS              RESTARTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ngx    0/1     ContainerCreating   0          7s</span>
</span></span></code></pre></div><h2 id="10-kubernetes-工作机制">10 Kubernetes 工作机制</h2>
<p>Kubernetes 架构基于流行的“控制面 Control Plane / 数据面 Data Plane”设计。集群由多个“节点”（Node）组成，底层环境支持物理机或虚拟机。</p>
<p>按照功能划分，集群节点主要分为两类：负责“大脑”工作的 Master 节点（控制面），用于执行管理和调度任务；以及负责“体力”工作的 Worker 节点（数据面），用于运行具体的业务应用。</p>
<img width="600" alt="image" src="https://user-images.githubusercontent.com/9289792/219293821-a4e1620a-c024-4edf-ba29-a5bc1f8d3f57.png">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get node
</span></span><span class="line"><span class="cl"><span class="c1"># NAME       STATUS   ROLES           AGE   VERSION</span>
</span></span><span class="line"><span class="cl"><span class="c1"># minikube   Ready    control-plane   11m   v1.34.0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看 master Component pod</span>
</span></span><span class="line"><span class="cl">kubectl get pod -n kube-system
</span></span><span class="line"><span class="cl"><span class="c1"># NAME                               READY   STATUS    RESTARTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># coredns-66bc5c9577-6c27s           1/1     Running   0          11m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># etcd-minikube                      1/1     Running   0          11m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kube-apiserver-minikube            1/1     Running   0          11m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kube-controller-manager-minikube   1/1     Running   0          11m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kube-proxy-9v5wf                   1/1     Running   0          11m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># kube-scheduler-minikube            1/1     Running   0          11m</span>
</span></span><span class="line"><span class="cl"><span class="c1"># storage-provisioner                1/1     Running   0          11m</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># worker node</span>
</span></span><span class="line"><span class="cl">minikube ssh
</span></span><span class="line"><span class="cl"><span class="c1"># show kube-proxy</span>
</span></span><span class="line"><span class="cl">docker ps <span class="p">|</span> grep kube-proxy
</span></span><span class="line"><span class="cl"><span class="c1"># show kubelet, not exist docker</span>
</span></span><span class="line"><span class="cl">ps -ef <span class="p">|</span> grep kubelet
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># show addons list</span>
</span></span><span class="line"><span class="cl">minikube addons list
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">minikube dashboard
</span></span><span class="line"><span class="cl"><span class="c1"># 🎉  Opening http://127.0.0.1:50733/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...</span>
</span></span></code></pre></div><img width="600" alt="image" src="https://user-images.githubusercontent.com/9289792/219294564-12655273-044c-4d56-a99a-d6f6e7cf7525.png">
<h2 id="11-yaml">11 YAML</h2>
<p>YAML 在语法上是 JSON 的超集，常被用作 Kubernetes 的配置文件格式。</p>
<p>其次，我们需要理解两种思维模式：Shell 脚本和 Dockerfile 是典型的 <strong>“命令式”</strong>，它告诉机器“第一步做什么，第二步做什么”；而 Kubernetes 推崇 <strong>“声明式”</strong>，它只告诉机器“我想要什么样的结果”，具体的步骤由系统自己完成。</p>
<p>最后，Kubernetes 的核心组件 apiserver 借鉴了 HTTP 协议中“资源”的概念，使用了标准的 RESTful 风格。在 K8s 的体系里，所有被管理的内容（如 Pod, Service）都被抽象成了 <strong>“API 对象”</strong> 来进行操作。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 查看 kubectl api-service 支持的所有对象</span>
</span></span><span class="line"><span class="cl">kubectl api-resources
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示出详细的命令执行过程</span>
</span></span><span class="line"><span class="cl">kubectl get pod --v<span class="o">=</span><span class="m">9</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 自带的 API 文档 https://kubernetes.io/docs/reference/kubernetes-api/</span>
</span></span><span class="line"><span class="cl">kubectl explain pod
</span></span><span class="line"><span class="cl">kubectl explain pod.metadata
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 命令式</span>
</span></span><span class="line"><span class="cl">kubectl run ngx --image<span class="o">=</span>nginx:alpine
</span></span><span class="line"><span class="cl"><span class="c1"># 转换为 YAML 声名式</span>
</span></span><span class="line"><span class="cl">kubectl run ngx --image<span class="o">=</span>nginx:alpine --dry-run<span class="o">=</span>client -o yaml &gt; ngx-pod.yml
</span></span><span class="line"><span class="cl">kubectl apply -f ngx-pod.yml
</span></span><span class="line"><span class="cl">kubectl delete -f ngx-pod.yml
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dnsPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">ClusterFirst</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">restartPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">Always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span></code></pre></div><h2 id="12-pod">12 Pod</h2>
<p>既然 Docker 容器提倡“一个容器只跑一个进程”，那么当我们需要多个紧密协作的进程（多应用联合运行）共同工作时，我们不能破坏单容器单进程的隔离原则（不破坏容器隔离），所以我们需要在容器之上引入一个更高层级的抽象对象（收纳舱/Pod），让这些容器在共享网络和存储的同时，依然保持镜像层面的独立。</p>
<img width="600" alt="image" src="https://user-images.githubusercontent.com/9289792/219309091-7436a44b-c0e4-4e8f-8dc1-7a208122ae7d.png">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f ngx-pod.yml
</span></span><span class="line"><span class="cl">kubectl logs ngx
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl">kubectl describe pod ngx
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># cp file to pod</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;aaa&#39;</span> &gt; a.txt
</span></span><span class="line"><span class="cl">kubectl cp a.txt ngx:/tmp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># exec need --</span>
</span></span><span class="line"><span class="cl">kubectl <span class="nb">exec</span> -it ngx -- sh
</span></span><span class="line"><span class="cl">cat /tmp/a.txt
</span></span></code></pre></div><h2 id="13-job-cronjob-离线业务">13 Job CronJob 离线业务</h2>
<p>单一职责 意味着每个容器应当只运行一个核心进程。避免“大而全”的巨型容器，保持容器功能的单一和纯粹，才能让它们像乐高积木一样被灵活复用和独立升级。</p>
<p>组合优于继承 意味着应该通过“组合”多个独立的容器（Pod 模式）来构建复杂的应用，而不是通过“继承”基础镜像并在其上层层叠加所有功能（巨型镜像）。这种松耦合的方式，允许应用在运行时通过 Sidecar 等模式灵活协作，而不是被固化在同一个镜像文件里。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl create job echo-job --image<span class="o">=</span>busybox --dry-run<span class="o">=</span>client -o yaml &gt; echo-job.yml
</span></span><span class="line"><span class="cl"><span class="c1"># 告诉 k8s 我想创建一个名为 echo-job 的任务（Job）。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># --dry-run=client 告诉 kubectl，“你假装执行一下创建命令，检查一下语法，但是千万不要真的把请求发给 Kubernetes API Server，也不要真的在集群里创建这个 Job”</span>
</span></span><span class="line"><span class="cl">vim echo-job.yml
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">batch/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Job</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">echo-job</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">busybox</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">echo-job</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># 补充输出</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;echo&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;hello world&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># -----</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">restartPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">Never</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f echo-job.yml
</span></span><span class="line"><span class="cl">kubectl get job
</span></span><span class="line"><span class="cl"><span class="c1"># NAME       COMPLETIONS   DURATION   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># echo-job   1/1           16s        33s</span>
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl"><span class="c1"># NAME             READY   STATUS      RESTARTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># echo-job-59sgn   0/1     Completed   0          68s</span>
</span></span><span class="line"><span class="cl">kubectl logs echo-job-59sgn
</span></span><span class="line"><span class="cl"><span class="c1"># hello world</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">batch/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Job</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sleep-job</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># 设置 Pod 运行的超时时间</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">activeDeadlineSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">60</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># 设置 Pod 的失败重试次数</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">backoffLimit</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Job 完成需要运行多少个 Pod，默认是 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">completions</span><span class="p">:</span><span class="w"> </span><span class="m">4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># 它与 completions 相关，表示允许并发运行的 Pod 数量，避免过多占用资源</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">parallelism</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">busybox</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">echo-job</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="c"># 随机休眠</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">command</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">sh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- -<span class="l">c</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">sleep $(($RANDOM % 10 + 1)) &amp;&amp; echo done</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">restartPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">Never</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f sleep-job.yaml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl get pod -w
</span></span><span class="line"><span class="cl"><span class="c1"># NAME              READY   STATUS      RESTARTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># sleep-job-92m4d   0/1     Completed   0          30s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># sleep-job-g8pmj   0/1     Completed   0          15s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># sleep-job-tsncl   0/1     Completed   0          30s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># sleep-job-x4qlp   0/1     Completed   0          15s</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># cronjob</span>
</span></span><span class="line"><span class="cl">kubectl create cj echo-cj --image<span class="o">=</span>busybox --schedule<span class="o">=</span><span class="s2">&#34;*/1 * * * *&#34;</span> --dry-run<span class="o">=</span>client -o yaml &gt; echo-cj.yaml
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">batch/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CronJob</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">echo-cj</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">jobTemplate</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">echo-cj</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">busybox</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">echo-cj</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="c"># 补充输出</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;/bin/echo&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">args</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;hello&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;world&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">restartPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">OnFailure</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">schedule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;*/1 * * * *&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f echo-cj.yaml
</span></span><span class="line"><span class="cl">kubectl get cj
</span></span><span class="line"><span class="cl"><span class="c1"># NAME      SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># echo-cj   */1 * * * *   False     1        5s              8s</span>
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl"><span class="c1"># NAME                     READY   STATUS      RESTARTS   AGE</span>
</span></span><span class="line"><span class="cl"><span class="c1"># echo-cj-27942326-qng4j   0/1     Completed   0          93s</span>
</span></span><span class="line"><span class="cl"><span class="c1"># echo-cj-27942327-vlrvs   0/1     Completed   0          33s</span>
</span></span></code></pre></div><h2 id="14-configmap-secret-管理配置信息">14 ConfigMap Secret 管理配置信息</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl create cm info --from-literal<span class="o">=</span><span class="nv">name</span><span class="o">=</span>zhao --dry-run<span class="o">=</span>client -o yaml &gt; cm.yml
</span></span><span class="line"><span class="cl">kubectl apply -f cm.yml
</span></span><span class="line"><span class="cl">kubectl get cm
</span></span><span class="line"><span class="cl">kubectl describe cm info
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl create secret generic user --from-literal<span class="o">=</span><span class="nv">name</span><span class="o">=</span>root --dry-run<span class="o">=</span>client -o yaml &gt; secret.yml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># -n 去掉字符串里隐含的换行符</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -n <span class="s2">&#34;root&#34;</span> <span class="p">|</span> base64
</span></span><span class="line"><span class="cl">kubectl apply -f secret.yml
</span></span><span class="line"><span class="cl">kubectl get secret
</span></span><span class="line"><span class="cl">kubectl describe secret user
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl explain pod.spec.containers.env.valueFrom
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">creationTimestamp</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">NAME</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">valueFrom</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">configMapKeyRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">info</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">SNAME</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">valueFrom</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">secretKeyRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">user</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dnsPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">ClusterFirst</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">restartPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">Always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f env-pod.yml
</span></span><span class="line"><span class="cl">kubectl <span class="nb">exec</span> -it ngx -- sh
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="nv">$NAME</span> <span class="nv">$SNAME</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># 以 Volume 的方式使用 ConfigMap/Secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vol-pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Volume 属于 Pod 与 containers 同级</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">cm-vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">configMap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">info</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sec-vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">secret</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">secretName</span><span class="p">:</span><span class="w"> </span><span class="l">user</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># 挂载到容器里的某个路径下</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">volumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp/cm-items</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">cm-vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp/sec-items</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sec-vol</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dnsPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">ClusterFirst</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">restartPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">Always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">status</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim vol-pod.yml
</span></span><span class="line"><span class="cl">kubectl apply -f vol-pod.yml
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl">kubectl <span class="nb">exec</span> -it vol-pod -- sh
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">cat /tmp/cm-items/name
</span></span><span class="line"><span class="cl">cat /tmp/sec-items/name
</span></span><span class="line"><span class="cl"><span class="c1"># ConfigMap 和 Secret 都变成了目录的形式，而它们里面的 Key-Value 变成了一个个的文件，而文件名就是 Key。</span>
</span></span></code></pre></div><h2 id="15-玩转-kubernetes">15 玩转 Kubernetes</h2>
<img width="600" alt="image" src="https://user-images.githubusercontent.com/9289792/219537942-31e778eb-888d-4331-8d65-03ae4095b003.png">
<p>搭建 WordPress 环境：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim mariadb-pod.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ConfigMap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-cm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">DATABASE</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;db&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">USER</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;wp&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;123&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ROOT_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;123&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">role</span><span class="p">:</span><span class="w"> </span><span class="l">database</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">mariadb:10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">imagePullPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">IfNotPresent</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">envFrom</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">prefix</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;MARIADB_&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">configMapRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-cm</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f mariadb-pod.yml
</span></span><span class="line"><span class="cl"><span class="c1"># 获取 IP 地址需要加上参数 -o wide</span>
</span></span><span class="line"><span class="cl">kubectl get pod -o wide
</span></span><span class="line"><span class="cl"><span class="c1"># NAME        READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES</span>
</span></span><span class="line"><span class="cl"><span class="c1"># maria-pod   1/1     Running   0          64s   172.17.0.5   minikube   &lt;none&gt;           &lt;none&gt;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># vim wp-pod.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ConfigMap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-cm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># MariaDB Pod 的 IP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">HOST</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;172.17.0.5&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">USER</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;wp&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;123&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">NAME</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;db&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">role</span><span class="p">:</span><span class="w"> </span><span class="l">website</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress:5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">imagePullPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">IfNotPresent</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">envFrom</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">prefix</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;WORDPRESS_DB_&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">configMapRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-cm</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f wp-pod.yml
</span></span><span class="line"><span class="cl">kubectl get pod -o wide
</span></span><span class="line"><span class="cl"><span class="c1"># NAME        READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES</span>
</span></span><span class="line"><span class="cl"><span class="c1"># maria-pod   1/1     Running   0          160m    172.17.0.5   minikube   &lt;none&gt;           &lt;none&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wp-pod      1/1     Running   0          2m47s   172.17.0.6   minikube   &lt;none&gt;           &lt;none&gt;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 本地的 “8080” 映射到 WordPress Pod 的“80”</span>
</span></span><span class="line"><span class="cl">kubectl port-forward wp-pod 8080:80 <span class="p">&amp;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Forwarding from 127.0.0.1:8080 -&gt; 80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Forwarding from [::1]:8080 -&gt; 80</span>
</span></span><span class="line"><span class="cl"><span class="c1"># fg</span>
</span></span></code></pre></div><h2 id="16-初级篇总结">16 初级篇总结</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">minikube version
</span></span><span class="line"><span class="cl">minikube status
</span></span><span class="line"><span class="cl">minikube start --kubernetes-version<span class="o">=</span>v1.23.3
</span></span><span class="line"><span class="cl">minikube node list
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl version
</span></span><span class="line"><span class="cl">kubectl run ngx --image<span class="o">=</span>nginx:alpine
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># apiserver 等核心组件是在 kube-system 名字空间</span>
</span></span><span class="line"><span class="cl">kubectl get pod -n kube-system
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl api-resources
</span></span><span class="line"><span class="cl">kubectl explain pod.metadata
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">out</span><span class="o">=</span><span class="s2">&#34;--dry-run=client -o yaml&#34;</span>
</span></span><span class="line"><span class="cl">kubectl run ngx --image<span class="o">=</span>nginx:alpine <span class="nv">$out</span> &gt; pod.yml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl apply -f ngx-pod.yml
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl">kubectl logs ngx-pod
</span></span><span class="line"><span class="cl">kubectl <span class="nb">exec</span> -it ngx-pod -- sh
</span></span><span class="line"><span class="cl">kubectl delete -f ngx-pod
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl create job echo-job --image<span class="o">=</span>busybox <span class="nv">$out</span>
</span></span><span class="line"><span class="cl">kubectl apply -f job.yml
</span></span><span class="line"><span class="cl">kubectl get job
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl">kubectl logs echo-job-l52l7
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl create cj echo-cj --image<span class="o">=</span>busybox --schedule<span class="o">=</span><span class="s2">&#34;* * * * *&#34;</span> <span class="nv">$out</span>
</span></span><span class="line"><span class="cl">kubectl apply -f cronjob.yml
</span></span><span class="line"><span class="cl">kubectl get cj
</span></span><span class="line"><span class="cl">kubectl get pod
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl create cm info --from-literal<span class="o">=</span><span class="nv">k</span><span class="o">=</span>v <span class="nv">$out</span>
</span></span><span class="line"><span class="cl">kubectl get cm
</span></span><span class="line"><span class="cl">kubectl describe cm info
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl create secret generic user --from-literal<span class="o">=</span><span class="nv">name</span><span class="o">=</span>root <span class="nv">$out</span>
</span></span><span class="line"><span class="cl">kubectl get secret
</span></span><span class="line"><span class="cl">kubectl describe secret user
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="nv">cm9vdA</span><span class="o">==</span> <span class="p">|</span> base64 -d
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/chronolaw/k8s_study">chronolaw/k8s_study | GitHub</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Go 语言第一课</title>
      <link>https://zyf.im/2023/02/01/go-language-lesson-one/</link>
      <pubDate>Wed, 01 Feb 2023 11:44:27 +0000</pubDate>
      <guid>https://zyf.im/2023/02/01/go-language-lesson-one/</guid>
      <description>&lt;h2 id=&#34;02-设计哲学&#34;&gt;02 设计哲学&lt;/h2&gt;
&lt;p&gt;设计哲学之于编程语言，就好比一个人的价值观之于这个人的行为。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;简单：Go 生产力的源泉。&lt;/li&gt;
&lt;li&gt;显式：Go 希望开发人员 明确知道自己在做什么；显式的基于值比较的错误处理方案。&lt;/li&gt;
&lt;li&gt;组合：类型嵌入(Type Embedding)。&lt;/li&gt;
&lt;li&gt;并发：面向多核、原生支持并发、用户层轻量级线程 goroutine。&lt;/li&gt;
&lt;li&gt;面向工程：将解决工程问题作为 Go 的 设计原则之一，这些问题包括:程序构建慢、依赖管理失控、代码难于理 解、跨语言构建难等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;03-配好环境&#34;&gt;03 配好环境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://go.dev/doc/devel/release&#34;&gt;https://go.dev/doc/devel/release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://golang.google.cn/dl/&#34;&gt;https://golang.google.cn/dl/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;安装多个-go-版本&#34;&gt;安装多个 Go 版本&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go get golang.org/dl/go1.15.13
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go1.15.13 download
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go1.15.13 version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;配置-go&#34;&gt;配置 Go&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go env
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt; environment
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;04-go-程序的结构&#34;&gt;04 Go 程序的结构&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;import &amp;quot;fmt&amp;quot;&lt;/code&gt; 一行中 &lt;code&gt;fmt&lt;/code&gt; 代表的是包的导入路径(Import)，它表示的是标准库下的 fmt 目录，整个 import 声明语句的含义是导入标准库 fmt 目录下的包&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fmt.Println&lt;/code&gt; 函数调用一行中的 &lt;code&gt;fmt&lt;/code&gt; 代表的则是包名。&lt;/li&gt;
&lt;li&gt;通常导入路径的最后一个分段名与包名是相同的，这也很容易让人误解 import 声明语句中的 &lt;code&gt;fmt&lt;/code&gt; 指的是包名，其实并不是这样的。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;gofmt&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;go&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;go-module&#34;&gt;Go module&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go mod init
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go mod tidy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;05-go-项目的布局标准&#34;&gt;05 Go 项目的布局标准&lt;/h2&gt;
&lt;p&gt;loccount 工具&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="02-设计哲学">02 设计哲学</h2>
<p>设计哲学之于编程语言，就好比一个人的价值观之于这个人的行为。</p>
<ul>
<li>简单：Go 生产力的源泉。</li>
<li>显式：Go 希望开发人员 明确知道自己在做什么；显式的基于值比较的错误处理方案。</li>
<li>组合：类型嵌入(Type Embedding)。</li>
<li>并发：面向多核、原生支持并发、用户层轻量级线程 goroutine。</li>
<li>面向工程：将解决工程问题作为 Go 的 设计原则之一，这些问题包括:程序构建慢、依赖管理失控、代码难于理 解、跨语言构建难等。</li>
</ul>
<h2 id="03-配好环境">03 配好环境</h2>
<ul>
<li><a href="https://go.dev/doc/devel/release">https://go.dev/doc/devel/release</a></li>
<li><a href="https://golang.google.cn/dl/">https://golang.google.cn/dl/</a></li>
</ul>
<h3 id="安装多个-go-版本">安装多个 Go 版本</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go get golang.org/dl/go1.15.13
</span></span><span class="line"><span class="cl">go1.15.13 download
</span></span><span class="line"><span class="cl">go1.15.13 version
</span></span></code></pre></div><h3 id="配置-go">配置 Go</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go env
</span></span><span class="line"><span class="cl">go <span class="nb">help</span> environment
</span></span></code></pre></div><h2 id="04-go-程序的结构">04 Go 程序的结构</h2>
<ul>
<li><code>import &quot;fmt&quot;</code> 一行中 <code>fmt</code> 代表的是包的导入路径(Import)，它表示的是标准库下的 fmt 目录，整个 import 声明语句的含义是导入标准库 fmt 目录下的包</li>
<li><code>fmt.Println</code> 函数调用一行中的 <code>fmt</code> 代表的则是包名。</li>
<li>通常导入路径的最后一个分段名与包名是相同的，这也很容易让人误解 import 声明语句中的 <code>fmt</code> 指的是包名，其实并不是这样的。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">gofmt</span> <span class="nx">main</span><span class="o">.</span><span class="nx">go</span>
</span></span></code></pre></div><h3 id="go-module">Go module</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go mod init
</span></span><span class="line"><span class="cl">go mod tidy
</span></span></code></pre></div><h2 id="05-go-项目的布局标准">05 Go 项目的布局标准</h2>
<p>loccount 工具</p>
<p><a href="https://github.com/golang/go">https://github.com/golang/go</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">tree -LF <span class="m">1</span> .
</span></span></code></pre></div><h2 id="06-解决包依赖管理">06 解决包依赖管理</h2>
<p>GOPATH -&gt; Vendor -&gt; Go Module</p>
<h3 id="gopath">GOPATH</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go env
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">GOPATH</span><span class="o">=</span><span class="s2">&#34;/Users/v_yfanzhao/go&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">go get github.com/sirupsen/logrus
</span></span></code></pre></div><h3 id="vendor">vendor</h3>
<ul>
<li>Go 项目必须放在 GOPATH 环境变量配置的路径下，庞大的 vendor 目录需要提交到代码仓库，不仅占用代码仓库空间，减慢仓库下载和更新的速度， 而且还会干扰代码评审，对实施代码统计等开发者效能工具也有比较大影响。</li>
<li>你还需要手工管理 vendor 下面的 Go 依赖包，包括项目依赖包的分析、版本的记 录、依赖包获取和存放，等等，最让开发者头疼的就是这一点。</li>
</ul>
<h3 id="go-module-1">Go Module</h3>
<p>Go Module 与 go.mod 是一一对应的。go.mod 文件所在的顶层目录也被称为 module 的根目录，module 根目录以及它子目录 下的所有 Go 包均归属于这个 Go Module，这个 module 也被称为 main module。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="s">&#34;github.com/sirupsen/logrus&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">logrus</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;hello, go module mode&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go mod init
</span></span><span class="line"><span class="cl">go mod tidy
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">module</span><span class="w"> </span><span class="k">go</span><span class="o">-</span><span class="nx">lesson</span><span class="o">-</span><span class="nx">one</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">go</span><span class="w"> </span><span class="mf">1.17</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">require</span><span class="w"> </span><span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">sirupsen</span><span class="o">/</span><span class="nx">logrus</span><span class="w"> </span><span class="nx">v1</span><span class="mf">.9.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">require</span><span class="w"> </span><span class="nx">golang</span><span class="p">.</span><span class="nx">org</span><span class="o">/</span><span class="nx">x</span><span class="o">/</span><span class="nx">sys</span><span class="w"> </span><span class="nx">v0</span><span class="mf">.0.0</span><span class="o">-</span><span class="mi">20220715151400</span><span class="o">-</span><span class="nx">c0bba94af5f8</span><span class="w"> </span><span class="c1">// indirect</span><span class="w">
</span></span></span></code></pre></div><p>major.minor.patch</p>
<p>Go 的语义导入版本机制：将包主版本号引入到包导入路径中。v0、v1 时不加入路径。</p>
<p>因此甚至可以同时依赖一个包的两个不兼容版本：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;github.com/sirupsen/logrus&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">logv2</span><span class="w"> </span><span class="s">&#34;github.com/sirupsen/logrus/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>Go 会在该项目依赖项的所有版本中，选出符合项目整体要求的“最小版本”。这与 PHP Composer 最新最大 (Latest Greatest) 版本 相反。</p>
<h2 id="07-go-module-操作">07 Go Module 操作</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go list -m all
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">go list -m -versions github.com/sirupsen/logrus
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 指定版本 升降级</span>
</span></span><span class="line"><span class="cl">go get github.com/sirupsen/logrus@v1.7.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 指定版本 升降级</span>
</span></span><span class="line"><span class="cl">go mod edit -require<span class="o">=</span>github.com/sirupsen/logrus@v1.7.0
</span></span><span class="line"><span class="cl">go mod tidy
</span></span></code></pre></div><h3 id="使用-vendor-机制">使用 vendor 机制</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go mod vendor
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">go build -mod<span class="o">=</span>verdor
</span></span><span class="line"><span class="cl"><span class="c1"># 顶层目录下存在 vendor 目录，那么 go build 默认也会优先基于 vendor 构建，除非：</span>
</span></span><span class="line"><span class="cl">go build -mod<span class="o">=</span>mod
</span></span></code></pre></div><h2 id="08-go-程序的执行次序">08 Go 程序的执行次序</h2>
<p>可执行程序的 main 包必须定义 main 函数，否则 Go 编译器会报错。</p>
<p>除了 main 包外，其他包也可以拥有自己的名为 main 的函数 或方法。</p>
<h3 id="init-函数">init 函数</h3>
<p>除了前面讲过的 main.main 函数之外，Go 语言还有一个特殊函数，它就是用于进行包初始化的 init 函数了。main 函数之前，常量和变量初 始化之后。每个 init 函数在整个 Go 程序生命周期内仅会被执行一次。Go 包可以拥有不止一个 init 函数。</p>
<p>Go 在进行包初始化的过程中，会采用“深度优先”的原则，递归初始化各个包的 依赖包。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">package main
</span></span><span class="line"><span class="cl"><span class="p">|</span>- import pkg1
</span></span><span class="line"><span class="cl">    <span class="p">|</span>- import pkg2
</span></span><span class="line"><span class="cl">        <span class="p">|</span>- const
</span></span><span class="line"><span class="cl">        <span class="p">|</span>- var
</span></span><span class="line"><span class="cl">        <span class="p">|</span>- init<span class="o">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">|</span>- const
</span></span><span class="line"><span class="cl">    <span class="p">|</span>- var
</span></span><span class="line"><span class="cl">    <span class="p">|</span>- init<span class="o">()</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>- const
</span></span><span class="line"><span class="cl"><span class="p">|</span>- var
</span></span><span class="line"><span class="cl"><span class="p">|</span>- init<span class="o">()</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>- main<span class="o">()</span>
</span></span></code></pre></div><h3 id="init-函数的用途">init 函数的用途</h3>
<ul>
<li>重置包级变量值。被用于检查包级变量的初始状态。</li>
<li>实现对包级变量的复杂初始化。</li>
<li>在 init 函数中实现“注册模式”。通过在 init 函数中注册自己的实现的模式，就有效降低了 Go 包对外的直接 暴露，尤其是包级变量的暴露，从而避免了外部通过包级变量对包状态的改动。</li>
</ul>
<h2 id="09-构建一个-web-服务">09 构建一个 Web 服务</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">&#34;net/http&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="w"> </span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">w</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;Hello World&#34;</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:8888&#34;</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl localhost:8888
</span></span><span class="line"><span class="cl">Hello World
</span></span></code></pre></div><blockquote>
<p><a href="https://github.com/imzyf/go-bookstore">https://github.com/imzyf/go-bookstore</a></p>
</blockquote>
<h2 id="10-变量声明">10 变量声明</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 将变量名放在了类型的前面</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 修饰关键字 变量名 类型 初值</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 省略类型信息的声明</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">b</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">12</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 显式赋予变量初值</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">b</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">int32</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 声明多个</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;A&#39;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;hello&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 短变量声明</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">a</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">12</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">b</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="sc">&#39;A&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">c</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;hello&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 声明多个</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;A&#39;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;hello&#34;</span><span class="w">
</span></span></span></code></pre></div><h3 id="go-语言的两类变量">Go 语言的两类变量</h3>
<ul>
<li>包级变量 (package varible)</li>
<li>局部变量 (local varible)</li>
</ul>
<h3 id="包级变量的声明形式">包级变量的声明形式</h3>
<p>包级变量只能使用带有 var 关键字的变量声明形式，不能使用短变量声明形式，但在形式细节上可以有一定灵活度。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">b</span><span class="w"> </span><span class="kt">int32</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">17</span><span class="w"> </span><span class="c1">// 显式指定类型</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">f</span><span class="w"> </span><span class="kt">float32</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mf">3.14</span><span class="w"> </span><span class="c1">// 显式指定类型</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">13</span><span class="w"> </span><span class="c1">// 使用默认类型</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">b</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">int32</span><span class="p">(</span><span class="mi">17</span><span class="p">)</span><span class="w"> </span><span class="c1">// 显式指定类型</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">f</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">float32</span><span class="p">(</span><span class="mf">3.14</span><span class="p">)</span><span class="w"> </span><span class="c1">// 显式指定类型</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="kt">int32</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">f</span><span class="w"> </span><span class="kt">float64</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 声明聚类</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">netGo</span><span class="w"> </span><span class="kt">bool</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">netCgo</span><span class="w"> </span><span class="kt">bool</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">aLongTimeAgo</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nf">Unix</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">noDeadline</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">noCancel</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kd">struct</span><span class="p">{})(</span><span class="kc">nil</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 就近原则</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 尽可能在靠近第一次使用变量的位置声明这个变量</span><span class="w">
</span></span></span></code></pre></div><h3 id="局部变量的声明形式">局部变量的声明形式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 延迟初始化的局部变量</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="kt">error</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 显式初始化的局部变量</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">a</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">17</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">f</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">float32</span><span class="p">(</span><span class="mf">3.14</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">s</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;hello, gopher!&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 尽量在分支控制时使用短变量声明形式</span><span class="w">
</span></span></span></code></pre></div><h2 id="11-代码块-block-与作用域-scope">11 代码块 Block 与作用域 Scope</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 变量遮蔽</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">11</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="nx">n</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">a</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">a</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="nx">n</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;a =&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">a</span><span class="p">)</span><span class="w"> </span><span class="c1">// 11</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nf">foo</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;after calling foo, a =&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">a</span><span class="p">)</span><span class="w"> </span><span class="c1">// 11</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>宇宙代码块(Universe Block)</li>
<li>包代码块(Package Block)</li>
<li>文件代码块(File Block)</li>
<li>分支控制语句隐式代码块</li>
<li>switch/select 的子句隐式代码块</li>
</ul>
<p>一个标识符的作用域就是指：这个标识符在被声明后可以被有效使用的源码区域。</p>
<p>导出标识符：</p>
<ul>
<li>声明在包代码块中</li>
<li>它名字第一个字符是一个大写的 Unicode 字符</li>
</ul>
<blockquote>
<p><a href="https://github.com/imzyf/go-lesson-one/blob/main/cmd/chapter11/main.go">https://github.com/imzyf/go-lesson-one/blob/main/cmd/chapter11/main.go</a></p>
</blockquote>
<h2 id="12-数值类型">12 数值类型</h2>
<h3 id="整型">整型</h3>
<p>Go 采用补码(2&rsquo;s complement)作为整型的比特位编码方法。Go 的补码是通过原码逐位取反后再加 1 得到的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">unit8  <span class="m">1</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="nv">1</span> <span class="o">=</span> <span class="m">129</span>
</span></span><span class="line"><span class="cl">int8   <span class="m">1</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="nv">1</span> <span class="o">=</span> -127
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">0</span> <span class="m">1</span> <span class="m">1</span> <span class="m">1</span> <span class="m">1</span> <span class="m">1</span> <span class="m">1</span> <span class="m">1</span>     <span class="m">127</span>
</span></span><span class="line"><span class="cl"><span class="m">1</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span>     取反
</span></span><span class="line"><span class="cl"><span class="m">1</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span> <span class="m">1</span>     +1    -127
</span></span></code></pre></div><h3 id="整型的溢出问题">整型的溢出问题</h3>
<blockquote>
<p><a href="https://github.com/imzyf/go-lesson-one/blob/main/cmd/chapter12/main.go">https://github.com/imzyf/go-lesson-one/blob/main/cmd/chapter12/main.go</a></p>
</blockquote>
<p>这个问题最容易发生在循环语句的结束条件判断中，因为这也是经常使用整型变量的地方。</p>
<h3 id="浮点型">浮点型</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">IEEE <span class="m">754</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">符号位Sign  阶码Exponent  尾数Maintissa
</span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>\bit 位\</th>
          <th>符号位</th>
          <th>阶码</th>
          <th>阶码偏移值</th>
          <th>尾数</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>单精度 float32</td>
          <td>1</td>
          <td>8</td>
          <td>127</td>
          <td>23</td>
      </tr>
      <tr>
          <td>双精度 float64</td>
          <td>1</td>
          <td>11</td>
          <td>1023</td>
          <td>52</td>
      </tr>
  </tbody>
</table>
<p>eg:129.8125</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">步骤一:我们要把这个浮点数值的整数部分和小数部分，分别转换为二进制形式<span class="o">(</span>后缀 d 表示十进制数，后缀 b 表示二进制数<span class="o">)</span>:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">整数部分:139d <span class="o">=</span>&gt; 10001011b<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">小数部分:0.8125d <span class="o">=</span>&gt; 0.1101b<span class="o">(</span>十进制小数转换为二进制可采用“乘 <span class="m">2</span> 取整”的竖式计算<span class="o">)</span>。
</span></span><span class="line"><span class="cl">0.8125 * <span class="nv">2</span> <span class="o">=</span> 1.625 …… <span class="m">1</span>
</span></span><span class="line"><span class="cl">0.625 * <span class="nv">2</span> <span class="o">=</span> 1.25   …… <span class="m">1</span>
</span></span><span class="line"><span class="cl">0.25 * <span class="nv">2</span> <span class="o">=</span> 0.5     …… <span class="m">0</span>
</span></span><span class="line"><span class="cl">0.5 * <span class="nv">2</span> <span class="o">=</span> <span class="m">1</span>        …… <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">139.8125d -&gt; 10001011.1101b
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">步骤二:移动小数点，直到整数部分仅有一个 1。
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">10001011.1101b -&gt; 1.00010111101b
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">小数点向左移了 <span class="m">7</span> 位，这样 指数就为 <span class="sb">`</span>7<span class="sb">`</span>，尾数为 <span class="sb">`</span>00010111101b<span class="sb">`</span>。
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">步骤三:计算阶码。对于 float32 的单精度浮点数而言：
</span></span><span class="line"><span class="cl"><span class="nv">阶码</span> <span class="o">=</span> 指数 + 偏移值
</span></span><span class="line"><span class="cl">偏移值的计算公式为 2^<span class="o">(</span>e-1<span class="o">)</span>-1，其中 e 为阶码部分的 bit 位数，这里为 8，于是单精度浮点数的阶码偏移 值就为 2^<span class="o">(</span>8-1<span class="o">)</span>-1 <span class="o">=</span> 127。
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">阶码</span> <span class="o">=</span> <span class="sb">`</span>7<span class="sb">`</span> + <span class="nv">127</span> <span class="o">=</span> <span class="nv">134d</span> <span class="o">=</span> <span class="sb">`</span>10000110b<span class="sb">`</span>。
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">步骤四:将符号位、阶码和尾数填到各自位置，得到最终浮点数的二进制表示
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">符号位 <span class="m">0</span>
</span></span><span class="line"><span class="cl">阶码 <span class="m">10000110</span>
</span></span><span class="line"><span class="cl">尾数 <span class="m">00010111101</span> 不足 <span class="m">23</span> 位补零 <span class="sb">`</span>0_0010111101_00_0000000000<span class="sb">`</span>
</span></span></code></pre></div><p><code>139.8125</code> -&gt; <code>0_10000110_00010111101_000000000000</code></p>
<ul>
<li><a href="https://stackoverflow.com/questions/75357159/go-float32bit-result-not-expected">Go Float32bit() result not expected | stackoverflow</a></li>
</ul>
<h3 id="复数型">复数型</h3>
<p>矢量计算。</p>
<h3 id="创建自定义的数值类型">创建自定义的数值类型</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">MyInt</span><span class="w"> </span><span class="kt">int32</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">m</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="kt">int32</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">6</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">MyInt</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">m</span><span class="w"> </span><span class="c1">// error</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">MyInt</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="c1">// error</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nf">MyInt</span><span class="p">(</span><span class="nx">m</span><span class="p">)</span><span class="w"> </span><span class="c1">// ok</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nf">MyInt</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span><span class="w"> </span><span class="c1">// ok</span><span class="w">
</span></span></span></code></pre></div><p>MyInt 类型的底层类型是 int32，所以它的数值性质与 int32 完全相同，但它 们仍然是完全不同的两种类型。</p>
<h3 id="类型别名type-alias">类型别名(Type Alias)</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">MyInt</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kt">int32</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="kt">int32</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">6</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">MyInt</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">n</span><span class="w">
</span></span></span></code></pre></div><p>通过类型别名语法定义的新类型与原类型别无二致，可以完全相互替代。</p>
<h2 id="13-字符串类型">13 字符串类型</h2>
<p>why-what-how</p>
<p>非原生字符串：</p>
<ul>
<li>不是原生类型，编译器不会对它进行类型校验，导致类型安全性差;</li>
<li>字符串操作时要时刻考虑结尾的 <code>\0</code>，防止缓冲区溢出;</li>
<li>以字符数组形式定义的“字符串”，它的值是可变的，在并发场景中需要考虑同步问题;</li>
<li>获取一个字符串的长度代价较大，通常是 O(n) 时间复杂度;</li>
<li>C 语言没有内置对非 ASCII 字符(如中文字符)的支持。</li>
</ul>
<p>string 类型的数据是不可变的，提高了字符串的并发安全性和存储利用率（同一个字符串值分配同一块存储）。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">&#34;hello&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">s</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="sc">&#39;k&#39;</span><span class="w"> </span><span class="c1">// cannot assign to s[0] (value of type byte)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">s</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">&#34;gopher&#34;</span><span class="w"> </span><span class="c1">// ok</span><span class="w">
</span></span></span></code></pre></div><p>没有结尾 <code>\0</code>，而且获取长度的时间复杂度是常数时间，消除了获取字符串长度的开销。</p>
<p>反引号原生支持“所见即所得”的原始字符串，大大降低构造多行字符串时的心智负担。</p>
<p>对非 ASCII 字符提供原生支持，消除了源码在不同环境下显示乱码的可能。Unicode 字符是以 UTF-8 编码格式存储在内存。</p>
<p>通过单引号括起的字符字面值：</p>
<p><a href="https://github.com/imzyf/go-lesson-one/blob/main/cmd/chapter13/main.go">https://github.com/imzyf/go-lesson-one/blob/main/cmd/chapter13/main.go</a></p>
<p>UTF-8 编码解决的是 Unicode 码点值在计算机中如何存储和表示(位模式)的问题。UTF-8 方案使用变长度字节，从 1 个到 4 个不等。</p>
<p>一个 rune 存储一个 unicode 码点或 utf-32 的四字节编码；从字节视角，string 对应的底层存储存放的是 utf8 编码。</p>
<p>Go 字符串类型的内部标示</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// StringHeader 是一个 string 的运行时表示</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// string 类型其实是一个“描述符”</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">StringHeader</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c1">// 一个指向底层存储的指针</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">Data</span><span class="w"> </span><span class="kt">uintptr</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c1">// 字符串的长度</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">Len</span><span class="w"> </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>直接将 string 类型通过函数或方法参数传入也不会带来太多的开销。因为传入的仅仅是一个“描述符”，而不是真正的字符串数据。</p>
<h3 id="go-字符串类型的常见操作">Go 字符串类型的常见操作</h3>
<p>下标操作；下标操作，我们获取的是字符串中特定下标上的字节，而不是字符。</p>
<p>字符迭代：</p>
<ul>
<li>or 迭代，字节视角的迭代</li>
<li>字符串中 Unicode 字符的码点值，以及该字符在字符串中的偏移值（字节视角）</li>
</ul>
<p>字符串连接；<code>+</code> <code>+=</code> <code>strings.Builder</code> <code>strings.Join</code> <code>fmt.Sprintf</code>。</p>
<p>字符串比较；= =、!= 、&gt;=、&lt;=、&gt; 和 &lt;。</p>
<p>字符串转换；string -&gt; <code>[]rune</code> <code>[]byte</code></p>
<h2 id="14-常量">14 常量</h2>
<ul>
<li>支持无类型常量</li>
<li>支持隐式自动转型</li>
<li>可用于实现枚举</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">myInt</span><span class="w"> </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 无类型常量(Untyped Constant)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">const</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">13</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">var</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">myInt</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c1">// 隐式转型</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">n</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 无类型常量 + 隐式转型：使得在 Go 这样的具有强类型系统的语言，在处理表达式混合数据类型运算的时候具有比较大的灵活性，代码编写也得到了一定程度的简化。</span><span class="w">
</span></span></span></code></pre></div><p>Go 的 const 语法提供了“隐式重复前一个非空表达式”的机制。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">const</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">Apple</span><span class="p">,</span><span class="w"> </span><span class="nx">Banana</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">iota</span><span class="p">,</span><span class="w"> </span><span class="kc">iota</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="c1">// 0, 10 (iota = 0)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">Strawberry</span><span class="p">,</span><span class="w"> </span><span class="nx">Grape</span><span class="w"> </span><span class="c1">// 1, 11 (iota = 1)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">Pear</span><span class="p">,</span><span class="w"> </span><span class="nx">Watermelon</span><span class="w"> </span><span class="c1">// 2, 12 (iota = 2)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">const</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">_</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">iota</span><span class="w"> </span><span class="c1">// 略过 iota = 0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">IPV6_V6ONLY</span><span class="w"> </span><span class="c1">// 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">SOMAXCONN</span><span class="w"> </span><span class="c1">// 2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">SO_ERROR</span><span class="w"> </span><span class="c1">// 3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><h2 id="15-数组与切片">15 数组与切片</h2>
<p>数组是一个固定长度的、由同构类型元素组成的连续序列。不仅是逻辑上的连续序列，而且在实际内存分配时也占据着一整块内存。</p>
<p>切片不定长同构数据类型。切片可以看成是数组的“描述符”（句柄），为数组打开了一个访问与修改的“窗口”。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// 切片</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">slice</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">array</span><span class="w"> </span><span class="nx">unsafe</span><span class="p">.</span><span class="nx">Pointer</span><span class="w"> </span><span class="c1">// 指向底层数组的指针</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">len</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="c1">// 切片的长度，即切片中当前元素的个数</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">cap</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="c1">// 底层数组的长度，也是切片的最大容量，cap 值永远大于等于 len 值</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">sl1</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="w"> </span><span class="c1">// 是声明，未初始化，是nil值，底层没有分配内存空间</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">sl2</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="p">{}</span><span class="w"> </span><span class="c1">// 初始化了，不是nil值，底层分配了内存空间，有地址。</span><span class="w">
</span></span></span></code></pre></div><h2 id="16-map-类型">16 map 类型</h2>
<p>一组无序的键值对。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">map</span><span class="p">[</span><span class="nx">key_type</span><span class="p">]</span><span class="nx">value_type</span><span class="w">
</span></span></span></code></pre></div><p>key 的类型必须支持“==”和“!=”两种比较操作符。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://go.dev/play/">The Go Playground</a></li>
</ul>
<p>&ndash; EOF &mdash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>回顾 2022</title>
      <link>https://zyf.im/2022/12/31/review-2022/</link>
      <pubDate>Sat, 31 Dec 2022 20:16:41 +0000</pubDate>
      <guid>https://zyf.im/2022/12/31/review-2022/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;犯错的人不会沉浸在错误里，而是继续战斗。 —— 管泽元 S12 总决赛 DRX vs SKT 2:2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p>犯错的人不会沉浸在错误里，而是继续战斗。 —— 管泽元 S12 总决赛 DRX vs SKT 2:2</p>
</blockquote>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP Migrating 8.0 to 8.1</title>
      <link>https://zyf.im/2022/12/21/php-migrating-80-to-81/</link>
      <pubDate>Wed, 21 Dec 2022 18:25:49 +0000</pubDate>
      <guid>https://zyf.im/2022/12/21/php-migrating-80-to-81/</guid>
      <description>&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;PHP 8.1 引入了许多重要的新特性，包括枚举（Enums）、只读属性（Readonly Properties）、一等可调用语法（First-class Callable Syntax）、Fibers、交叉类型（Intersection Types）等。本文将介绍主要变化和如何从 PHP 8.0 迁移到 PHP 8.1。&lt;/p&gt;
&lt;h3 id=&#34;参考资源&#34;&gt;参考资源&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在线测试环境：&lt;a href=&#34;https://onlinephp.io/&#34;&gt;https://onlinephp.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;官方发布说明：&lt;a href=&#34;https://www.php.net/releases/8.1/en.php&#34;&gt;https://www.php.net/releases/8.1/en.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;迁移指南：&lt;a href=&#34;https://www.php.net/manual/en/migration81.php&#34;&gt;https://www.php.net/manual/en/migration81.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;更新详情：&lt;a href=&#34;https://php.watch/versions/8.1&#34;&gt;https://php.watch/versions/8.1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;php-81-新特性&#34;&gt;PHP 8.1 新特性&lt;/h2&gt;
&lt;h3 id=&#34;枚举enums&#34;&gt;枚举（Enums）&lt;/h3&gt;
&lt;p&gt;PHP 8.1 原生支持枚举类型，取代了之前使用类常量的方式：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 纯枚举（Pure Enum）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Draft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Published&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Archived&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 回退枚举（Backed Enum）- 带有标量值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Draft&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;draft&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Published&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;published&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Archived&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;archived&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 使用枚举作为类型提示
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;BlogPost&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Status&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Draft&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;BlogPost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// &amp;#39;draft&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 枚举可以有方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Red&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;#FF0000&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Green&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;#00FF00&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Blue&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;#0000FF&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;label&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Red&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;红色&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Green&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;绿色&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Blue&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;蓝色&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Red&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;label&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// &amp;#39;红色&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 从值获取枚举
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;draft&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// Status::Draft
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;tryFrom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;invalid&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// null（不会抛出异常）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;枚举的特性：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="简介">简介</h2>
<p>PHP 8.1 引入了许多重要的新特性，包括枚举（Enums）、只读属性（Readonly Properties）、一等可调用语法（First-class Callable Syntax）、Fibers、交叉类型（Intersection Types）等。本文将介绍主要变化和如何从 PHP 8.0 迁移到 PHP 8.1。</p>
<h3 id="参考资源">参考资源</h3>
<ul>
<li>在线测试环境：<a href="https://onlinephp.io/">https://onlinephp.io/</a></li>
<li>官方发布说明：<a href="https://www.php.net/releases/8.1/en.php">https://www.php.net/releases/8.1/en.php</a></li>
<li>迁移指南：<a href="https://www.php.net/manual/en/migration81.php">https://www.php.net/manual/en/migration81.php</a></li>
<li>更新详情：<a href="https://php.watch/versions/8.1">https://php.watch/versions/8.1</a></li>
</ul>
<h2 id="php-81-新特性">PHP 8.1 新特性</h2>
<h3 id="枚举enums">枚举（Enums）</h3>
<p>PHP 8.1 原生支持枚举类型，取代了之前使用类常量的方式：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 纯枚举（Pure Enum）
</span></span></span><span class="line"><span class="cl"><span class="nx">enum</span> <span class="nx">Status</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Draft</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Published</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Archived</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 回退枚举（Backed Enum）- 带有标量值
</span></span></span><span class="line"><span class="cl"><span class="nx">enum</span> <span class="nx">Status</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Draft</span> <span class="o">=</span> <span class="s1">&#39;draft&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Published</span> <span class="o">=</span> <span class="s1">&#39;published&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Archived</span> <span class="o">=</span> <span class="s1">&#39;archived&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用枚举作为类型提示
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">BlogPost</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">Status</span> <span class="nv">$status</span> <span class="o">=</span> <span class="nx">Status</span><span class="o">::</span><span class="na">Draft</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$post</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BlogPost</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$post</span><span class="o">-&gt;</span><span class="na">status</span><span class="o">-&gt;</span><span class="na">value</span><span class="p">;</span> <span class="c1">// &#39;draft&#39;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 枚举可以有方法
</span></span></span><span class="line"><span class="cl"><span class="nx">enum</span> <span class="nx">Color</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Red</span> <span class="o">=</span> <span class="s1">&#39;#FF0000&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Green</span> <span class="o">=</span> <span class="s1">&#39;#00FF00&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Blue</span> <span class="o">=</span> <span class="s1">&#39;#0000FF&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">label</span><span class="p">()</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">match</span><span class="p">(</span><span class="nv">$this</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">self</span><span class="o">::</span><span class="na">Red</span> <span class="o">=&gt;</span> <span class="s1">&#39;红色&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">self</span><span class="o">::</span><span class="na">Green</span> <span class="o">=&gt;</span> <span class="s1">&#39;绿色&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">self</span><span class="o">::</span><span class="na">Blue</span> <span class="o">=&gt;</span> <span class="s1">&#39;蓝色&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">Color</span><span class="o">::</span><span class="na">Red</span><span class="o">-&gt;</span><span class="na">label</span><span class="p">();</span> <span class="c1">// &#39;红色&#39;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 从值获取枚举
</span></span></span><span class="line"><span class="cl"><span class="nv">$status</span> <span class="o">=</span> <span class="nx">Status</span><span class="o">::</span><span class="na">from</span><span class="p">(</span><span class="s1">&#39;draft&#39;</span><span class="p">);</span>     <span class="c1">// Status::Draft
</span></span></span><span class="line"><span class="cl"><span class="nv">$status</span> <span class="o">=</span> <span class="nx">Status</span><span class="o">::</span><span class="na">tryFrom</span><span class="p">(</span><span class="s1">&#39;invalid&#39;</span><span class="p">);</span> <span class="c1">// null（不会抛出异常）
</span></span></span></code></pre></div><p>枚举的特性：</p>
<ul>
<li>枚举可以实现接口</li>
<li>枚举可以使用 trait</li>
<li>枚举不能被实例化（<code>new Status()</code> 是非法的）</li>
<li>枚举 case 是单例</li>
</ul>
<h3 id="只读属性readonly-properties">只读属性（Readonly Properties）</h3>
<p>属性可以标记为 <code>readonly</code>，只能初始化一次：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">readonly</span> <span class="nx">string</span> <span class="nv">$id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">readonly</span> <span class="nx">string</span> <span class="nv">$name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$id</span><span class="p">,</span> <span class="nx">string</span> <span class="nv">$name</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">id</span> <span class="o">=</span> <span class="nv">$id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">name</span> <span class="o">=</span> <span class="nv">$name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="s1">&#39;123&#39;</span><span class="p">,</span> <span class="s1">&#39;John&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">;</span> <span class="c1">// &#39;123&#39;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">id</span> <span class="o">=</span> <span class="s1">&#39;456&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Fatal error: Cannot modify readonly property User::$id
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 结合构造函数提升使用
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Product</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">readonly</span> <span class="nx">string</span> <span class="nv">$sku</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">readonly</span> <span class="nx">float</span> <span class="nv">$price</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>只读属性的特性：</p>
<ul>
<li>只能初始化一次，初始化后不可修改</li>
<li>必须有类型声明</li>
<li>不能有默认值（除非在构造函数中设置）</li>
<li>可以在构造函数或声明时初始化</li>
</ul>
<h3 id="一等可调用语法first-class-callable-syntax">一等可调用语法（First-class Callable Syntax）</h3>
<p>使用 <code>...</code> 语法创建闭包，替代 <code>Closure::fromCallable()</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 函数转闭包
</span></span></span><span class="line"><span class="cl"><span class="nv">$strlen</span> <span class="o">=</span> <span class="nx">strlen</span><span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$strlen</span><span class="p">(</span><span class="s1">&#39;hello&#39;</span><span class="p">);</span> <span class="c1">// 5
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 等同于
</span></span></span><span class="line"><span class="cl"><span class="nv">$strlen</span> <span class="o">=</span> <span class="nx">Closure</span><span class="o">::</span><span class="na">fromCallable</span><span class="p">(</span><span class="s1">&#39;strlen&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 方法转闭包
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Calculator</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">add</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$a</span><span class="p">,</span> <span class="nx">int</span> <span class="nv">$b</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$a</span> <span class="o">+</span> <span class="nv">$b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">multiply</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$a</span><span class="p">,</span> <span class="nx">int</span> <span class="nv">$b</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$a</span> <span class="o">*</span> <span class="nv">$b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__invoke</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$x</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$calc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Calculator</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 实例方法
</span></span></span><span class="line"><span class="cl"><span class="nv">$addFn</span> <span class="o">=</span> <span class="nv">$calc</span><span class="o">-&gt;</span><span class="na">add</span><span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$addFn</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span> <span class="c1">// 8
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 静态方法
</span></span></span><span class="line"><span class="cl"><span class="nv">$multiplyFn</span> <span class="o">=</span> <span class="nx">Calculator</span><span class="o">::</span><span class="na">multiply</span><span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$multiplyFn</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> <span class="c1">// 12
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// __invoke 方法
</span></span></span><span class="line"><span class="cl"><span class="nv">$invokeFn</span> <span class="o">=</span> <span class="nv">$calc</span><span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$invokeFn</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span> <span class="c1">// 20
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 在数组函数中使用
</span></span></span><span class="line"><span class="cl"><span class="nv">$words</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;apple&#39;</span><span class="p">,</span> <span class="s1">&#39;banana&#39;</span><span class="p">,</span> <span class="s1">&#39;cherry&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$lengths</span> <span class="o">=</span> <span class="nx">array_map</span><span class="p">(</span><span class="nx">strlen</span><span class="p">(</span><span class="o">...</span><span class="p">),</span> <span class="nv">$words</span><span class="p">);</span> <span class="c1">// [5, 6, 6]
</span></span></span></code></pre></div><h3 id="fibers纤程">Fibers（纤程）</h3>
<p>Fibers 是轻量级的协程，允许暂停和恢复代码执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$fiber</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Fiber</span><span class="p">(</span><span class="k">function</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">echo</span> <span class="s2">&#34;1. Fiber 开始</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$value</span> <span class="o">=</span> <span class="nx">Fiber</span><span class="o">::</span><span class="na">suspend</span><span class="p">(</span><span class="s1">&#39;暂停值&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">echo</span> <span class="s2">&#34;3. Fiber 恢复，收到: </span><span class="si">$value\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;0. 主程序开始</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$suspended</span> <span class="o">=</span> <span class="nv">$fiber</span><span class="o">-&gt;</span><span class="na">start</span><span class="p">();</span>  <span class="c1">// 启动 Fiber
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;2. 主程序收到: </span><span class="si">$suspended\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$fiber</span><span class="o">-&gt;</span><span class="na">resume</span><span class="p">(</span><span class="s1">&#39;恢复值&#39;</span><span class="p">);</span>      <span class="c1">// 恢复 Fiber
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;4. 主程序结束</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 输出:
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0. 主程序开始
</span></span></span><span class="line"><span class="cl"><span class="c1">// 1. Fiber 开始
</span></span></span><span class="line"><span class="cl"><span class="c1">// 2. 主程序收到: 暂停值
</span></span></span><span class="line"><span class="cl"><span class="c1">// 3. Fiber 恢复，收到: 恢复值
</span></span></span><span class="line"><span class="cl"><span class="c1">// 4. 主程序结束
</span></span></span></code></pre></div><p>Fibers 主要用于异步框架（如 ReactPHP、Amp），普通应用开发较少直接使用。</p>
<h3 id="交叉类型intersection-types">交叉类型（Intersection Types）</h3>
<p>使用 <code>&amp;</code> 运算符要求参数同时满足多个类型：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 参数必须同时实现 Iterator 和 Countable
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">processItems</span><span class="p">(</span><span class="nx">Iterator</span><span class="o">&amp;</span><span class="nx">Countable</span> <span class="nv">$items</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="nv">$items</span> <span class="k">as</span> <span class="nv">$item</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 处理项目
</span></span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">count</span><span class="p">(</span><span class="nv">$items</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ArrayIterator 同时实现了这两个接口
</span></span></span><span class="line"><span class="cl"><span class="nv">$items</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ArrayIterator</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">processItems</span><span class="p">(</span><span class="nv">$items</span><span class="p">);</span> <span class="c1">// 3
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 更复杂的示例
</span></span></span><span class="line"><span class="cl"><span class="k">interface</span> <span class="nx">Loggable</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">log</span><span class="p">()</span><span class="o">:</span> <span class="nx">void</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">interface</span> <span class="nx">Serializable</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">serialize</span><span class="p">()</span><span class="o">:</span> <span class="nx">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">store</span><span class="p">(</span><span class="nx">Loggable</span><span class="o">&amp;</span><span class="nx">Serializable</span> <span class="nv">$object</span><span class="p">)</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$object</span><span class="o">-&gt;</span><span class="na">log</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$data</span> <span class="o">=</span> <span class="nv">$object</span><span class="o">-&gt;</span><span class="na">serialize</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 存储数据
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="never-返回类型">never 返回类型</h3>
<p>表示函数永远不会返回（总是抛出异常或终止程序）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">redirect</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$url</span><span class="p">)</span><span class="o">:</span> <span class="nx">never</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">header</span><span class="p">(</span><span class="s2">&#34;Location: </span><span class="si">$url</span><span class="s2">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">exit</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">throwError</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$message</span><span class="p">)</span><span class="o">:</span> <span class="nx">never</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">RuntimeException</span><span class="p">(</span><span class="nv">$message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 静态分析工具可以检测 never 之后的死代码
</span></span></span><span class="line"><span class="cl"><span class="nx">redirect</span><span class="p">(</span><span class="s1">&#39;/home&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;这行代码永远不会执行&#34;</span><span class="p">;</span> <span class="c1">// IDE 会标记为死代码
</span></span></span></code></pre></div><h3 id="final-类常量">final 类常量</h3>
<p>类常量可以标记为 <code>final</code>，防止子类覆盖：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ParentClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">final</span> <span class="k">public</span> <span class="k">const</span> <span class="no">VERSION</span> <span class="o">=</span> <span class="s1">&#39;1.0&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">NAME</span> <span class="o">=</span> <span class="s1">&#39;Parent&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ChildClass</span> <span class="k">extends</span> <span class="nx">ParentClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ❌ Fatal error: Cannot override final constant
</span></span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">VERSION</span> <span class="o">=</span> <span class="s1">&#39;2.0&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// ✅ 可以覆盖非 final 常量
</span></span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">NAME</span> <span class="o">=</span> <span class="s1">&#39;Child&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="初始化器中的-new-表达式">初始化器中的 new 表达式</h3>
<p>可以在默认参数、属性默认值和常量中使用 <code>new</code> 表达式：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 默认参数
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Logger</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">private</span> <span class="nx">Formatter</span> <span class="nv">$formatter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DefaultFormatter</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 属性默认值
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">DateTimeImmutable</span> <span class="nv">$createdAt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DateTimeImmutable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 属性（PHP 8.0 的属性也支持）
</span></span></span><span class="line"><span class="cl"><span class="c1">#[Attribute]
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Route</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="nx">string</span> <span class="nv">$path</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">public</span> <span class="k">array</span> <span class="nv">$methods</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;GET&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Controller</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#[Route(&#39;/users&#39;, methods: [&#39;GET&#39;, &#39;POST&#39;])]
</span></span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">users</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="字符串键数组解包">字符串键数组解包</h3>
<p>现在可以对字符串键数组使用扩展运算符：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$array1</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;a&#39;</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$array2</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;c&#39;</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;d&#39;</span> <span class="o">=&gt;</span> <span class="mi">4</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.1 之前：只能用于数字键数组
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.1：支持字符串键
</span></span></span><span class="line"><span class="cl"><span class="nv">$merged</span> <span class="o">=</span> <span class="p">[</span><span class="o">...</span><span class="nv">$array1</span><span class="p">,</span> <span class="o">...</span><span class="nv">$array2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [&#39;a&#39; =&gt; 1, &#39;b&#39; =&gt; 2, &#39;c&#39; =&gt; 3, &#39;d&#39; =&gt; 4]
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 后面的值会覆盖前面的
</span></span></span><span class="line"><span class="cl"><span class="nv">$array3</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;a&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;override&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="p">[</span><span class="o">...</span><span class="nv">$array1</span><span class="p">,</span> <span class="o">...</span><span class="nv">$array3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [&#39;a&#39; =&gt; &#39;override&#39;, &#39;b&#39; =&gt; 2]
</span></span></span></code></pre></div><h3 id="显式八进制表示法">显式八进制表示法</h3>
<p>新增 <code>0o</code> 前缀表示八进制数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 传统八进制表示法（仍然有效）
</span></span></span><span class="line"><span class="cl"><span class="nv">$permissions</span> <span class="o">=</span> <span class="mo">0755</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.1 新的显式表示法
</span></span></span><span class="line"><span class="cl"><span class="nv">$permissions</span> <span class="o">=</span> <span class="mi">0</span><span class="nx">o755</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 两者等价
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">0</span><span class="nx">o16</span><span class="p">);</span>  <span class="c1">// int(14)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mo">016</span><span class="p">);</span>   <span class="c1">// int(14)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 0o 更清晰，避免与数字 0 开头的误解
</span></span></span></code></pre></div><h2 id="php-81-新增函数">PHP 8.1 新增函数</h2>
<h3 id="array_is_list">array_is_list()</h3>
<p>检查数组是否为列表（连续的从 0 开始的整数键）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 是列表
</span></span></span><span class="line"><span class="cl"><span class="nx">array_is_list</span><span class="p">([]);</span>                    <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="nx">array_is_list</span><span class="p">([</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">]);</span>       <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="nx">array_is_list</span><span class="p">([</span><span class="mi">0</span> <span class="o">=&gt;</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="mi">1</span> <span class="o">=&gt;</span> <span class="s1">&#39;b&#39;</span><span class="p">]);</span>  <span class="c1">// true
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 不是列表
</span></span></span><span class="line"><span class="cl"><span class="nx">array_is_list</span><span class="p">([</span><span class="mi">1</span> <span class="o">=&gt;</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="mi">2</span> <span class="o">=&gt;</span> <span class="s1">&#39;b&#39;</span><span class="p">]);</span>  <span class="c1">// false（不从 0 开始）
</span></span></span><span class="line"><span class="cl"><span class="nx">array_is_list</span><span class="p">([</span><span class="s1">&#39;a&#39;</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">]);</span>  <span class="c1">// false（字符串键）
</span></span></span><span class="line"><span class="cl"><span class="nx">array_is_list</span><span class="p">([</span><span class="mi">0</span> <span class="o">=&gt;</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="mi">2</span> <span class="o">=&gt;</span> <span class="s1">&#39;b&#39;</span><span class="p">]);</span>  <span class="c1">// false（不连续）
</span></span></span></code></pre></div><h3 id="fsync-和-fdatasync">fsync() 和 fdatasync()</h3>
<p>强制将文件数据写入磁盘：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$file</span> <span class="o">=</span> <span class="nx">fopen</span><span class="p">(</span><span class="s1">&#39;data.txt&#39;</span><span class="p">,</span> <span class="s1">&#39;w&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">fwrite</span><span class="p">(</span><span class="nv">$file</span><span class="p">,</span> <span class="s1">&#39;important data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// fsync: 同步文件数据和元数据
</span></span></span><span class="line"><span class="cl"><span class="nx">fsync</span><span class="p">(</span><span class="nv">$file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// fdatasync: 只同步文件数据（更快，不包括元数据）
</span></span></span><span class="line"><span class="cl"><span class="nx">fdatasync</span><span class="p">(</span><span class="nv">$file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">fclose</span><span class="p">(</span><span class="nv">$file</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="sodium-xchacha20-函数">Sodium XChaCha20 函数</h3>
<p>新增 XChaCha20 加密函数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 生成密钥
</span></span></span><span class="line"><span class="cl"><span class="nv">$key</span> <span class="o">=</span> <span class="nx">sodium_crypto_stream_xchacha20_keygen</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// XChaCha20 加密
</span></span></span><span class="line"><span class="cl"><span class="nv">$nonce</span> <span class="o">=</span> <span class="nx">random_bytes</span><span class="p">(</span><span class="nx">SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$encrypted</span> <span class="o">=</span> <span class="nx">sodium_crypto_stream_xchacha20_xor</span><span class="p">(</span><span class="nv">$message</span><span class="p">,</span> <span class="nv">$nonce</span><span class="p">,</span> <span class="nv">$key</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="新的哈希算法">新的哈希算法</h3>
<p>支持 xxHash 和 MurmurHash3：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// xxHash（非常快的非加密哈希）
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">hash</span><span class="p">(</span><span class="s1">&#39;xxh3&#39;</span><span class="p">,</span> <span class="s1">&#39;data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">hash</span><span class="p">(</span><span class="s1">&#39;xxh64&#39;</span><span class="p">,</span> <span class="s1">&#39;data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">hash</span><span class="p">(</span><span class="s1">&#39;xxh128&#39;</span><span class="p">,</span> <span class="s1">&#39;data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// MurmurHash3
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">hash</span><span class="p">(</span><span class="s1">&#39;murmur3a&#39;</span><span class="p">,</span> <span class="s1">&#39;data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">hash</span><span class="p">(</span><span class="s1">&#39;murmur3c&#39;</span><span class="p">,</span> <span class="s1">&#39;data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">hash</span><span class="p">(</span><span class="s1">&#39;murmur3f&#39;</span><span class="p">,</span> <span class="s1">&#39;data&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="枚举相关函数">枚举相关函数</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">enum</span> <span class="nx">Status</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Active</span> <span class="o">=</span> <span class="s1">&#39;active&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nx">Inactive</span> <span class="o">=</span> <span class="s1">&#39;inactive&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取所有 case
</span></span></span><span class="line"><span class="cl"><span class="nv">$cases</span> <span class="o">=</span> <span class="nx">Status</span><span class="o">::</span><span class="na">cases</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [Status::Active, Status::Inactive]
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 回退枚举的额外方法
</span></span></span><span class="line"><span class="cl"><span class="nv">$status</span> <span class="o">=</span> <span class="nx">Status</span><span class="o">::</span><span class="na">from</span><span class="p">(</span><span class="s1">&#39;active&#39;</span><span class="p">);</span>      <span class="c1">// Status::Active
</span></span></span><span class="line"><span class="cl"><span class="nv">$status</span> <span class="o">=</span> <span class="nx">Status</span><span class="o">::</span><span class="na">tryFrom</span><span class="p">(</span><span class="s1">&#39;unknown&#39;</span><span class="p">);</span>  <span class="c1">// null
</span></span></span></code></pre></div><h2 id="弃用特性">弃用特性</h2>
<h3 id="向非可空参数传递-null">向非可空参数传递 null</h3>
<p>向内置函数的非可空参数传递 null 已被弃用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ PHP 8.1 弃用警告
</span></span></span><span class="line"><span class="cl"><span class="nx">strlen</span><span class="p">(</span><span class="k">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">str_contains</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="s1">&#39;test&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">trim</span><span class="p">(</span><span class="k">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 修复方法
</span></span></span><span class="line"><span class="cl"><span class="nx">strlen</span><span class="p">(</span><span class="nv">$value</span> <span class="o">??</span> <span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">str_contains</span><span class="p">(</span><span class="nv">$value</span> <span class="o">??</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="s1">&#39;test&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">trim</span><span class="p">(</span><span class="nv">$value</span> <span class="o">??</span> <span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="serializable-接口弃用">Serializable 接口弃用</h3>
<p><code>Serializable</code> 接口已被弃用，使用 <code>__serialize()</code> 和 <code>__unserialize()</code> 替代：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">OldWay</span> <span class="k">implements</span> <span class="nx">Serializable</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">serialize</span><span class="p">()</span><span class="o">:</span> <span class="nx">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">serialize</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">unserialize</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$data</span><span class="p">)</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">data</span> <span class="o">=</span> <span class="nx">unserialize</span><span class="p">(</span><span class="nv">$data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 推荐方式
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">NewWay</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">__serialize</span><span class="p">()</span><span class="o">:</span> <span class="k">array</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">[</span><span class="s1">&#39;data&#39;</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">data</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">__unserialize</span><span class="p">(</span><span class="k">array</span> <span class="nv">$data</span><span class="p">)</span><span class="o">:</span> <span class="nx">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">data</span> <span class="o">=</span> <span class="nv">$data</span><span class="p">[</span><span class="s1">&#39;data&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="隐式浮点数转整数弃用">隐式浮点数转整数弃用</h3>
<p>非整数浮点数隐式转换为整数时会产生弃用警告：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 弃用警告
</span></span></span><span class="line"><span class="cl"><span class="nv">$arr</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$arr</span><span class="p">[</span><span class="mf">3.14</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;value&#39;</span><span class="p">;</span> <span class="c1">// 3.14 会被转为 3
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 显式转换
</span></span></span><span class="line"><span class="cl"><span class="nv">$arr</span><span class="p">[(</span><span class="nx">int</span><span class="p">)</span><span class="mf">3.14</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;value&#39;</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="日期函数弃用">日期函数弃用</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nx">strftime</span><span class="p">(</span><span class="s1">&#39;%Y-%m-%d&#39;</span><span class="p">,</span> <span class="nx">time</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="nx">gmstrftime</span><span class="p">(</span><span class="s1">&#39;%Y-%m-%d&#39;</span><span class="p">,</span> <span class="nx">time</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="nx">strptime</span><span class="p">(</span><span class="s1">&#39;2024-01-01&#39;</span><span class="p">,</span> <span class="s1">&#39;%Y-%m-%d&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">date_sunrise</span><span class="p">(</span><span class="nv">$timestamp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">date_sunset</span><span class="p">(</span><span class="nv">$timestamp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 替代方案
</span></span></span><span class="line"><span class="cl"><span class="c1">// 使用 IntlDateFormatter
</span></span></span><span class="line"><span class="cl"><span class="nv">$formatter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">IntlDateFormatter</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;zh_CN&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">IntlDateFormatter</span><span class="o">::</span><span class="na">FULL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">IntlDateFormatter</span><span class="o">::</span><span class="na">NONE</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$formatter</span><span class="o">-&gt;</span><span class="na">format</span><span class="p">(</span><span class="nx">time</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 或使用 DateTime
</span></span></span><span class="line"><span class="cl"><span class="nv">$date</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DateTime</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$date</span><span class="o">-&gt;</span><span class="na">format</span><span class="p">(</span><span class="s1">&#39;Y-m-d&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="mhash-函数弃用">mhash*() 函数弃用</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nx">mhash</span><span class="p">(</span><span class="nx">MHASH_MD5</span><span class="p">,</span> <span class="s1">&#39;data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">mhash_keygen_s2k</span><span class="p">(</span><span class="nx">MHASH_SHA1</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="s1">&#39;salt&#39;</span><span class="p">,</span> <span class="mi">16</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用 hash() 函数
</span></span></span><span class="line"><span class="cl"><span class="nx">hash</span><span class="p">(</span><span class="s1">&#39;md5&#39;</span><span class="p">,</span> <span class="s1">&#39;data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">hash_pbkdf2</span><span class="p">(</span><span class="s1">&#39;sha1&#39;</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="s1">&#39;salt&#39;</span><span class="p">,</span> <span class="mi">1000</span><span class="p">,</span> <span class="mi">16</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="pdofetch_serialize-弃用">PDO::FETCH_SERIALIZE 弃用</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nv">$stmt</span><span class="o">-&gt;</span><span class="na">setFetchMode</span><span class="p">(</span><span class="nx">PDO</span><span class="o">::</span><span class="na">FETCH_CLASS</span> <span class="o">|</span> <span class="nx">PDO</span><span class="o">::</span><span class="na">FETCH_SERIALIZE</span><span class="p">,</span> <span class="nx">MyClass</span><span class="o">::</span><span class="na">class</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 使用 __serialize()/__unserialize() 魔术方法
</span></span></span></code></pre></div><h3 id="auto_detect_line_endings-ini-弃用">auto_detect_line_endings INI 弃用</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// ❌ 已弃用
</span></span></span><span class="line"><span class="cl"><span class="nx">ini_set</span><span class="p">(</span><span class="s1">&#39;auto_detect_line_endings&#39;</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 手动处理行结束符
</span></span></span><span class="line"><span class="cl"><span class="nv">$content</span> <span class="o">=</span> <span class="nx">str_replace</span><span class="p">([</span><span class="s2">&#34;</span><span class="se">\r\n</span><span class="s2">&#34;</span><span class="p">,</span> <span class="s2">&#34;</span><span class="se">\r</span><span class="s2">&#34;</span><span class="p">],</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">,</span> <span class="nv">$content</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="兼容性变化">兼容性变化</h2>
<h3 id="资源类型迁移为对象">资源类型迁移为对象</h3>
<p>多个扩展的资源类型被迁移为对象：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// finfo
</span></span></span><span class="line"><span class="cl"><span class="nv">$finfo</span> <span class="o">=</span> <span class="nx">finfo_open</span><span class="p">(</span><span class="nx">FILEINFO_MIME</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$finfo</span><span class="p">);</span> <span class="c1">// PHP 8.0: resource, PHP 8.1: finfo object
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// FTP
</span></span></span><span class="line"><span class="cl"><span class="nv">$ftp</span> <span class="o">=</span> <span class="nx">ftp_connect</span><span class="p">(</span><span class="s1">&#39;ftp.example.com&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$ftp</span><span class="p">);</span> <span class="c1">// FTP\Connection object
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// IMAP
</span></span></span><span class="line"><span class="cl"><span class="nv">$imap</span> <span class="o">=</span> <span class="nx">imap_open</span><span class="p">(</span><span class="s1">&#39;{imap.example.com:993/imap/ssl}&#39;</span><span class="p">,</span> <span class="s1">&#39;user&#39;</span><span class="p">,</span> <span class="s1">&#39;pass&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$imap</span><span class="p">);</span> <span class="c1">// IMAP\Connection object
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// LDAP
</span></span></span><span class="line"><span class="cl"><span class="nv">$ldap</span> <span class="o">=</span> <span class="nx">ldap_connect</span><span class="p">(</span><span class="s1">&#39;ldap.example.com&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$ldap</span><span class="p">);</span> <span class="c1">// LDAP\Connection object
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PostgreSQL
</span></span></span><span class="line"><span class="cl"><span class="nv">$pg</span> <span class="o">=</span> <span class="nx">pg_connect</span><span class="p">(</span><span class="s1">&#39;host=localhost dbname=test&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$pg</span><span class="p">);</span> <span class="c1">// PgSql\Connection object
</span></span></span></code></pre></div><h3 id="继承方法中的静态变量">继承方法中的静态变量</h3>
<p>继承方法中的静态变量现在与父类共享：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">counter</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">static</span> <span class="nv">$i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">++</span><span class="nv">$i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">B</span> <span class="k">extends</span> <span class="nx">A</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">A</span><span class="o">::</span><span class="na">counter</span><span class="p">());</span> <span class="c1">// int(1)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">A</span><span class="o">::</span><span class="na">counter</span><span class="p">());</span> <span class="c1">// int(2)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">B</span><span class="o">::</span><span class="na">counter</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.0: int(1)（B 有自己的计数器）
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.1: int(3)（B 共享 A 的计数器）
</span></span></span></code></pre></div><h3 id="html-实体函数默认处理单引号">HTML 实体函数默认处理单引号</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// PHP 8.0: 默认 ENT_COMPAT，不转义单引号
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.1: 默认 ENT_QUOTES | ENT_SUBSTITUTE
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$str</span> <span class="o">=</span> <span class="s2">&#34;It&#39;s a </span><span class="se">\&#34;</span><span class="s2">test</span><span class="se">\&#34;</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">htmlspecialchars</span><span class="p">(</span><span class="nv">$str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.0: It&#39;s a &amp;quot;test&amp;quot;
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP 8.1: It&amp;#039;s a &amp;quot;test&amp;quot;
</span></span></span></code></pre></div><h2 id="性能改进">性能改进</h2>
<p>PHP 8.1 带来了显著的性能提升：</p>
<ul>
<li><strong>Symfony Demo</strong>: 提升约 23%</li>
<li><strong>WordPress</strong>: 提升约 3.5%</li>
<li>继承缓存优化</li>
<li>快速类名解析</li>
<li>内置函数优化</li>
<li>ARM64 JIT 后端支持</li>
</ul>
<h2 id="总结">总结</h2>
<p>PHP 8.1 带来了许多实用的新特性和改进：</p>
<p><strong>主要新特性：</strong></p>
<ul>
<li>枚举（Enums）</li>
<li>只读属性（Readonly Properties）</li>
<li>一等可调用语法</li>
<li>Fibers（纤程）</li>
<li>交叉类型（Intersection Types）</li>
<li>never 返回类型</li>
<li>final 类常量</li>
<li>初始化器中的 new 表达式</li>
<li>字符串键数组解包</li>
</ul>
<p><strong>需要注意的变化：</strong></p>
<ul>
<li>向非可空参数传递 null 已弃用</li>
<li>Serializable 接口已弃用</li>
<li>隐式浮点数转整数已弃用</li>
<li>多个资源类型迁移为对象</li>
<li>继承方法中的静态变量行为变化</li>
<li>日期相关函数弃用</li>
</ul>
<p>通过了解这些变化并遵循迁移建议，可以顺利地从 PHP 8.0 迁移到 PHP 8.1。</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP Migrating 7.4 to 8.0</title>
      <link>https://zyf.im/2022/12/14/php-migrating-74-to-80/</link>
      <pubDate>Wed, 14 Dec 2022 16:40:03 +0000</pubDate>
      <guid>https://zyf.im/2022/12/14/php-migrating-74-to-80/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;实验环境：&lt;a href=&#34;https://onlinephp.io/&#34;&gt;https://onlinephp.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.php.net/releases/8.0/en.php&#34;&gt;https://www.php.net/releases/8.0/en.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.php.net/manual/en/migration80.php&#34;&gt;https://www.php.net/manual/en/migration80.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://php.watch/versions/8.0&#34;&gt;https://php.watch/versions/8.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/symfony/polyfill/tree/1.x/src/Php80&#34;&gt;https://github.com/symfony/polyfill/tree/1.x/src/Php80&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;PHP 8.0 contains many new features and optimizations including named arguments, union types, attributes, constructor property promotion, match expression, nullsafe operator, JIT, and improvements in the type system, error handling, and consistency.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;new-features-80&#34;&gt;New Features 8.0&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Named Parameters 命名参数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// https://www.php.net/manual/zh/functions.arguments.php#functions.named-arguments
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// array_fill(int $start_index, int $count, mixed $value): array
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;array_fill&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;start_index&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP80 [50, 50, 50]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 注解（Attributes）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// https://www.php.net/manual/zh/language.attributes.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 定义注解 -&amp;gt; 使用注解 -&amp;gt; （通过反射）提取注释
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 注意以后用词：Attributes 注解，Properties 属性
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#[\Attribute]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;JsonSerialize&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fieldName&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;JsonSerialize:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;$fieldName&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;VersionedObject&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#[JsonSerialize(&amp;#39;json-foobar&amp;#39;)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$myValue&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;VersionedObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$reflection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ReflectionObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$reflection&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getProperties&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$reflectionProp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$reflectionProp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getAttributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;JsonSerialize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$jsonSerializeAttr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$jsonSerializeAttr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$jsonSerializeAttr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getArguments&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$jsonSerializeAttr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getTarget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$jsonSerializeAttr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;isRepeated&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$jsonSerializeAttr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;newInstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(13) &amp;#34;JsonSerialize&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// array(1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [0]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   string(11) &amp;#34;json-foobar&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(8) Attribute::TARGET_PROPERTY
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(25) &amp;#34;JsonSerialize:json-foobar&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// object(JsonSerialize)#5 (1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;fieldName&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   string(11) &amp;#34;json-foobar&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 构造器属性提升 Constructor Property Promotion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 当构造器参数带访问控制（visibility modifier）时，PHP 会同时把它当作对象属性和构造器参数，并赋值到属性
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Point&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$y&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Point&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// object(Point)#1 (2) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;x&amp;#34;:protected]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   int(2)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;y&amp;#34;:protected]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   int(4)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Point2&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$y&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$y&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 此类中已经定义了具有相同名称的字段
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Fatal error: Cannot redeclare Point::$x
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Union types 联合类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ?T is same T|null
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// - 类型只能出现一次 int|string|INT 否则报错
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// - 使用 mixed 会报错
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// - bool 与 false or true 不能混用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// - object 与 class 不能混用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// - iterable 与 array Traversable 不能混用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// - 使用 self、parent 或 static 都会导致错误
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;parse_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// match 表达式
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// https://www.php.net/manual/en/control-structures.match.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// match 表达式必须是详尽，则会抛出 UnhandledMatchError。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;8.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;8.0&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Oh no!&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mf&#34;&gt;8.0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;This matches&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// This matches
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Nullsafe Operator
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$repository&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Is equivalent to the following code block:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;is_null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$repository&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$repository&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;is_null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Argument Unpacking
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;str_replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;replace&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Iron Man&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;search&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Inevitable&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;subject&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;I am Inevitable&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Named Parameters with Variadic Parameters
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;june&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;september&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;january&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;01&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// array(3) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//     [&amp;#34;june&amp;#34;]=&amp;gt; int(6)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//     [&amp;#34;september&amp;#34;]=&amp;gt; int(15)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//     [&amp;#34;january&amp;#34;]=&amp;gt; string(2) &amp;#34;01&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// JIT Just-In-Time Compilation
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// php -i | grep -i opcache
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// php -d opcache.enable_cli=1 -d opcache.jit_buffer_size=256M -d opcache.jit=tracing -i | grep -i jit
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// opcache.enable=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// opcache.enable_cli=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// opcache.jit_buffer_size=256M
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;fibonacci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fibonacci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fibonacci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;microtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;fibonacci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;42&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;microtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Time taken: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34; seconds&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// php -d opcache.enable_cli=1 -d opcache.jit_buffer_size=256M -d opcache.jit=tracing test.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// The WeakMap class has been added, accepts objects as keys, similar SplObjectStorage
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// https://www.php.net/manual/en/class.weakmap.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// https://www.php.net/manual/en/class.splobjectstorage.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// final class WeakMap implements ArrayAccess, Countable, IteratorAggregate
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$wm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;WeakMap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;StdClass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__destruct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Dead!&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$wm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$wm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Dead!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 销毁
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$wm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 对比 SplObjectStorage
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$wm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SplObjectStorage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;StdClass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__destruct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Dead!&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$wm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$wm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 未销毁
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$wm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 只要类型兼容，现在可以将任意数量的函数参数替换为可变参数 variadic argument
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$many&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$parameters&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$here&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;B&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$everything&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// OK
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;C&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$everything&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Fatal error: Declaration of C::method(int ...$everything)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// static 后期静态绑定，现在可以用作返回类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Test&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// get_class($obj) === $obj::class
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Test&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get_class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// new instanceof 可以与任意表达式一起使用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// new (expression)(...$args)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// $obj instanceof (expression)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;B&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;A&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;B&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 新增 Stringable 接口。如果 class 定义了 __toString()，则自动实现了此接口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Traits can define abstract private methods
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;trait&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Throw 可以作为一个表达式 as an expression
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;path&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;??&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;path required&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$fn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Exception in arrow function&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 参数列表中现在允许使用可选的尾随逗号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;functionWithLongSignature&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;Type1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$parameter1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;Type2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$parameter2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// &amp;lt;-- This comma is now allowed.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 允许 catch (Exception) 无需存储到变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 在父类上声明的私有方法不再对子类的方法强制执行任何继承规则（私有构造函数除外）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ParentClass&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ChildClass&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ParentClass&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// OK
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// New string functions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;str_contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Foobar&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Foo&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;str_starts_with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;PHP 8&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;PHP&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;str_ends_with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Version8&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;8&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;backward-incompatible-changes-80&#34;&gt;Backward Incompatible Changes 8.0&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 字符串与数字比较
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 0 == &amp;#34;not-a-number&amp;#34; is false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 将数字转换为字符串，并使用字符串比较
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;42&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;42foo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP80           PHP74
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)     true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)     true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)     true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;a&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;ab&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP80           PHP74
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(true)      false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(true)      false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)     false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)     false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(true)      false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// is_callable() 在检查具有 classname 的 non-static 方法时将 false（必须检查对象实例）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Test&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;method1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;is_callable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Test&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;method1&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;is_callable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;method1&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP80           PHP74
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)     true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(true)      true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// __autoload() function has been removed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 删除了对 object 使用 array_key_exists() 的能力。isset() or property_exists() may be used instead
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// isset($arr[&amp;#39;key&amp;#39;]) → false（当值为 null）- 只能在 null 安全时检查，无法区分 null 与 不存在
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// array_key_exists(&amp;#39;key&amp;#39;, $arr) → true（即使值为 null）- 可以区分存在但值为 null 的情况
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<ul>
<li>实验环境：<a href="https://onlinephp.io/">https://onlinephp.io/</a></li>
<li><a href="https://www.php.net/releases/8.0/en.php">https://www.php.net/releases/8.0/en.php</a></li>
<li><a href="https://www.php.net/manual/en/migration80.php">https://www.php.net/manual/en/migration80.php</a></li>
<li><a href="https://php.watch/versions/8.0">https://php.watch/versions/8.0</a></li>
<li><a href="https://github.com/symfony/polyfill/tree/1.x/src/Php80">https://github.com/symfony/polyfill/tree/1.x/src/Php80</a></li>
</ul>
<blockquote>
<p>PHP 8.0 contains many new features and optimizations including named arguments, union types, attributes, constructor property promotion, match expression, nullsafe operator, JIT, and improvements in the type system, error handling, and consistency.</p>
</blockquote>
<h2 id="new-features-80">New Features 8.0</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Named Parameters 命名参数
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://www.php.net/manual/zh/functions.arguments.php#functions.named-arguments
</span></span></span><span class="line"><span class="cl"><span class="c1">// array_fill(int $start_index, int $count, mixed $value): array
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">array_fill</span><span class="p">(</span><span class="nx">value</span><span class="o">:</span> <span class="mi">50</span><span class="p">,</span> <span class="nx">count</span><span class="o">:</span> <span class="mi">3</span><span class="p">,</span> <span class="nx">start_index</span><span class="o">:</span> <span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP80 [50, 50, 50]
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 注解（Attributes）
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://www.php.net/manual/zh/language.attributes.php
</span></span></span><span class="line"><span class="cl"><span class="c1">// 定义注解 -&gt; 使用注解 -&gt; （通过反射）提取注释
</span></span></span><span class="line"><span class="cl"><span class="c1">// 注意以后用词：Attributes 注解，Properties 属性
</span></span></span><span class="line"><span class="cl"><span class="c1">#[\Attribute]
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">JsonSerialize</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span><span class="k">public</span> <span class="o">?</span><span class="nx">string</span> <span class="nv">$fieldName</span> <span class="o">=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">var_dump</span><span class="p">(</span><span class="s2">&#34;JsonSerialize:</span><span class="si">$fieldName</span><span class="s2">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">VersionedObject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#[JsonSerialize(&#39;json-foobar&#39;)]
</span></span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="nx">string</span> <span class="nv">$myValue</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nv">$object</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VersionedObject</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$reflection</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ReflectionObject</span><span class="p">(</span><span class="nv">$object</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$reflection</span><span class="o">-&gt;</span><span class="na">getProperties</span><span class="p">()</span> <span class="k">as</span> <span class="nv">$reflectionProp</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="nv">$reflectionProp</span><span class="o">-&gt;</span><span class="na">getAttributes</span><span class="p">(</span><span class="nx">JsonSerialize</span><span class="o">::</span><span class="na">class</span><span class="p">)</span> <span class="k">as</span> <span class="nv">$jsonSerializeAttr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$jsonSerializeAttr</span><span class="o">-&gt;</span><span class="na">getName</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$jsonSerializeAttr</span><span class="o">-&gt;</span><span class="na">getArguments</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$jsonSerializeAttr</span><span class="o">-&gt;</span><span class="na">getTarget</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$jsonSerializeAttr</span><span class="o">-&gt;</span><span class="na">isRepeated</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$jsonSerializeAttr</span><span class="o">-&gt;</span><span class="na">newInstance</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(13) &#34;JsonSerialize&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [0]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(11) &#34;json-foobar&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// int(8) Attribute::TARGET_PROPERTY
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// string(25) &#34;JsonSerialize:json-foobar&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// object(JsonSerialize)#5 (1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;fieldName&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(11) &#34;json-foobar&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 构造器属性提升 Constructor Property Promotion
</span></span></span><span class="line"><span class="cl"><span class="c1">// 当构造器参数带访问控制（visibility modifier）时，PHP 会同时把它当作对象属性和构造器参数，并赋值到属性
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Point</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span><span class="k">protected</span> <span class="nx">int</span> <span class="nv">$x</span><span class="p">,</span> <span class="k">protected</span> <span class="nx">int</span> <span class="nv">$y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="k">new</span> <span class="nx">Point</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// object(Point)#1 (2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;x&#34;:protected]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(2)
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;y&#34;:protected]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(4)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Point2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="nx">int</span> <span class="nv">$x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="nx">int</span> <span class="nv">$y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span><span class="k">protected</span> <span class="nx">int</span> <span class="nv">$x</span><span class="p">,</span> <span class="k">protected</span> <span class="nx">int</span> <span class="nv">$y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 此类中已经定义了具有相同名称的字段
</span></span></span><span class="line"><span class="cl"><span class="c1">// Fatal error: Cannot redeclare Point::$x
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Union types 联合类型
</span></span></span><span class="line"><span class="cl"><span class="c1">// ?T is same T|null
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// - 类型只能出现一次 int|string|INT 否则报错
</span></span></span><span class="line"><span class="cl"><span class="c1">// - 使用 mixed 会报错
</span></span></span><span class="line"><span class="cl"><span class="c1">// - bool 与 false or true 不能混用
</span></span></span><span class="line"><span class="cl"><span class="c1">// - object 与 class 不能混用
</span></span></span><span class="line"><span class="cl"><span class="c1">// - iterable 与 array Traversable 不能混用
</span></span></span><span class="line"><span class="cl"><span class="c1">// - 使用 self、parent 或 static 都会导致错误
</span></span></span><span class="line"><span class="cl"><span class="k">public</span> <span class="k">function</span> <span class="nf">parse_value</span><span class="p">(</span><span class="nx">string</span><span class="o">|</span><span class="nx">int</span><span class="o">|</span><span class="nx">float</span><span class="p">)</span><span class="o">:</span> <span class="nx">string</span><span class="o">|</span><span class="k">null</span> <span class="p">{}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// match 表达式
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://www.php.net/manual/en/control-structures.match.php
</span></span></span><span class="line"><span class="cl"><span class="c1">// match 表达式必须是详尽，则会抛出 UnhandledMatchError。
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">match</span> <span class="p">(</span><span class="mf">8.0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;8.0&#39;</span> <span class="o">=&gt;</span> <span class="s2">&#34;Oh no!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mf">8.0</span> <span class="o">=&gt;</span> <span class="s2">&#34;This matches&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="c1">// This matches
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Nullsafe Operator
</span></span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nv">$repository</span><span class="o">?-&gt;</span><span class="na">getUser</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">?-&gt;</span><span class="na">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Is equivalent to the following code block:
</span></span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">is_null</span><span class="p">(</span><span class="nv">$repository</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$result</span> <span class="o">=</span> <span class="k">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$user</span> <span class="o">=</span> <span class="nv">$repository</span><span class="o">-&gt;</span><span class="na">getUser</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">is_null</span><span class="p">(</span><span class="nv">$user</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$result</span> <span class="o">=</span> <span class="k">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$result</span> <span class="o">=</span> <span class="nv">$user</span><span class="o">-&gt;</span><span class="na">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Argument Unpacking
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">str_replace</span><span class="p">(</span><span class="o">...</span><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;replace&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;Iron Man&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;search&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;Inevitable&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;subject&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;I am Inevitable&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">]);</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Named Parameters with Variadic Parameters
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">test</span><span class="p">(</span><span class="nv">$a</span><span class="p">,</span> <span class="nv">$b</span><span class="p">,</span> <span class="o">...</span><span class="nv">$args</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">test</span><span class="p">(</span><span class="mi">28</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="nx">june</span><span class="o">:</span> <span class="mi">6</span><span class="p">,</span> <span class="nx">september</span><span class="o">:</span> <span class="mi">15</span><span class="p">,</span> <span class="nx">january</span><span class="o">:</span> <span class="s2">&#34;01&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// array(3) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//     [&#34;june&#34;]=&gt; int(6)
</span></span></span><span class="line"><span class="cl"><span class="c1">//     [&#34;september&#34;]=&gt; int(15)
</span></span></span><span class="line"><span class="cl"><span class="c1">//     [&#34;january&#34;]=&gt; string(2) &#34;01&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// JIT Just-In-Time Compilation
</span></span></span><span class="line"><span class="cl"><span class="c1">// php -i | grep -i opcache
</span></span></span><span class="line"><span class="cl"><span class="c1">// php -d opcache.enable_cli=1 -d opcache.jit_buffer_size=256M -d opcache.jit=tracing -i | grep -i jit
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// opcache.enable=1
</span></span></span><span class="line"><span class="cl"><span class="c1">// opcache.enable_cli=1
</span></span></span><span class="line"><span class="cl"><span class="c1">// opcache.jit_buffer_size=256M
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="nv">$n</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nv">$n</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="k">return</span> <span class="nv">$n</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">fibonacci</span><span class="p">(</span><span class="nv">$n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="nx">fibonacci</span><span class="p">(</span><span class="nv">$n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$start</span> <span class="o">=</span> <span class="nx">microtime</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">fibonacci</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$end</span> <span class="o">=</span> <span class="nx">microtime</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;Time taken: &#34;</span> <span class="o">.</span> <span class="p">(</span><span class="nv">$end</span> <span class="o">-</span> <span class="nv">$start</span><span class="p">)</span> <span class="o">.</span> <span class="s2">&#34; seconds</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// php -d opcache.enable_cli=1 -d opcache.jit_buffer_size=256M -d opcache.jit=tracing test.php
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// The WeakMap class has been added, accepts objects as keys, similar SplObjectStorage
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://www.php.net/manual/en/class.weakmap.php
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://www.php.net/manual/en/class.splobjectstorage.php
</span></span></span><span class="line"><span class="cl"><span class="c1">// final class WeakMap implements ArrayAccess, Countable, IteratorAggregate
</span></span></span><span class="line"><span class="cl"><span class="nv">$wm</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WeakMap</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$o</span> <span class="o">=</span> <span class="k">new</span> <span class="k">StdClass</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__destruct</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">echo</span> <span class="s2">&#34;Dead!</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nv">$wm</span><span class="p">[</span><span class="nv">$o</span><span class="p">]</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">A</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">count</span><span class="p">(</span><span class="nv">$wm</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// int(1)
</span></span></span><span class="line"><span class="cl"><span class="nx">unset</span><span class="p">(</span><span class="nv">$o</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Dead!
</span></span></span><span class="line"><span class="cl"><span class="c1">// 销毁
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">count</span><span class="p">(</span><span class="nv">$wm</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// int(0)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 对比 SplObjectStorage
</span></span></span><span class="line"><span class="cl"><span class="nv">$wm</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">SplObjectStorage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$o</span> <span class="o">=</span> <span class="k">new</span> <span class="k">StdClass</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__destruct</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">echo</span> <span class="s2">&#34;Dead!</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nv">$wm</span><span class="p">[</span><span class="nv">$o</span><span class="p">]</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">A</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">count</span><span class="p">(</span><span class="nv">$wm</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// int(1)
</span></span></span><span class="line"><span class="cl"><span class="nx">unset</span><span class="p">(</span><span class="nv">$o</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 未销毁
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">count</span><span class="p">(</span><span class="nv">$wm</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// int(1)
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 只要类型兼容，现在可以将任意数量的函数参数替换为可变参数 variadic argument
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">method</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$many</span><span class="p">,</span> <span class="nx">string</span> <span class="nv">$parameters</span><span class="p">,</span> <span class="nv">$here</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">B</span> <span class="k">extends</span> <span class="nx">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">method</span><span class="p">(</span><span class="o">...</span><span class="nv">$everything</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// OK
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">C</span> <span class="k">extends</span> <span class="nx">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">method</span><span class="p">(</span><span class="nx">int</span> <span class="o">...</span><span class="nv">$everything</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Fatal error: Declaration of C::method(int ...$everything)
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// static 后期静态绑定，现在可以用作返回类型
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Test</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">create</span><span class="p">()</span><span class="o">:</span> <span class="k">static</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="k">static</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// get_class($obj) === $obj::class
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Test</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Test</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$obj</span><span class="o">::</span><span class="na">class</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">get_class</span><span class="p">(</span><span class="nv">$obj</span><span class="p">));</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// new instanceof 可以与任意表达式一起使用
</span></span></span><span class="line"><span class="cl"><span class="c1">// new (expression)(...$args)
</span></span></span><span class="line"><span class="cl"><span class="c1">// $obj instanceof (expression)
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span><span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">B</span><span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="p">(</span><span class="mi">1</span><span class="o">&gt;</span><span class="mi">2</span> <span class="o">?</span> <span class="s2">&#34;A&#34;</span> <span class="o">:</span> <span class="s2">&#34;B&#34;</span><span class="p">)();</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 新增 Stringable 接口。如果 class 定义了 __toString()，则自动实现了此接口
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Traits can define abstract private methods
</span></span></span><span class="line"><span class="cl"><span class="k">trait</span> <span class="nx">T</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">abstract</span> <span class="k">private</span> <span class="k">function</span> <span class="nf">a</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Throw 可以作为一个表达式 as an expression
</span></span></span><span class="line"><span class="cl"><span class="nv">$value</span> <span class="o">=</span> <span class="nv">$config</span><span class="p">[</span><span class="s1">&#39;path&#39;</span><span class="p">]</span> <span class="o">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">InvalidArgumentException</span><span class="p">(</span><span class="s1">&#39;path required&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$fn</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">Exception</span><span class="p">(</span><span class="s1">&#39;Exception in arrow function&#39;</span><span class="p">);</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 参数列表中现在允许使用可选的尾随逗号
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">functionWithLongSignature</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Type1</span> <span class="nv">$parameter1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Type2</span> <span class="nv">$parameter2</span><span class="p">,</span> <span class="c1">// &lt;-- This comma is now allowed.
</span></span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="p">{}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 允许 catch (Exception) 无需存储到变量
</span></span></span><span class="line"><span class="cl"><span class="k">try</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="k">catch</span> <span class="p">(</span><span class="nx">Exception</span><span class="p">)</span> <span class="p">{}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 在父类上声明的私有方法不再对子类的方法强制执行任何继承规则（私有构造函数除外）
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ParentClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">function</span> <span class="nf">method1</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">function</span> <span class="nf">method2</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">method3</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">abstract</span> <span class="k">class</span> <span class="nc">ChildClass</span> <span class="k">extends</span> <span class="nx">ParentClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">abstract</span> <span class="k">function</span> <span class="nf">method1</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">method2</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">method3</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// OK
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// New string functions
</span></span></span><span class="line"><span class="cl"><span class="nx">str_contains</span><span class="p">(</span><span class="s1">&#39;Foobar&#39;</span><span class="p">,</span> <span class="s1">&#39;Foo&#39;</span><span class="p">);</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="nx">str_starts_with</span><span class="p">(</span><span class="s1">&#39;PHP 8&#39;</span><span class="p">,</span> <span class="s1">&#39;PHP&#39;</span><span class="p">);</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="nx">str_ends_with</span><span class="p">(</span><span class="s1">&#39;Version8&#39;</span><span class="p">,</span> <span class="s1">&#39;8&#39;</span><span class="p">);</span> <span class="c1">// true
</span></span></span></code></pre></div><h2 id="backward-incompatible-changes-80">Backward Incompatible Changes 8.0</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 字符串与数字比较
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0 == &#34;not-a-number&#34; is false
</span></span></span><span class="line"><span class="cl"><span class="c1">// 将数字转换为字符串，并使用字符串比较
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">0</span> <span class="o">==</span> <span class="s2">&#34;foo&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">42</span> <span class="o">==</span> <span class="s2">&#34;42foo&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">0</span> <span class="o">==</span> <span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP80           PHP74
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)     true
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)     true
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)     true
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="s2">&#34;&#34;</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="s2">&#34;&#34;</span> <span class="o">&lt;</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="s2">&#34;a&#34;</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="s2">&#34;b&#34;</span> <span class="o">&lt;</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="s2">&#34;ab&#34;</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP80           PHP74
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(true)      false
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(true)      false
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)     false
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)     false
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(true)      false
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// is_callable() 在检查具有 classname 的 non-static 方法时将 false（必须检查对象实例）
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Test</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">method1</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">is_callable</span><span class="p">([</span><span class="nx">Test</span><span class="o">::</span><span class="na">class</span><span class="p">,</span> <span class="s1">&#39;method1&#39;</span><span class="p">]));</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">is_callable</span><span class="p">([</span><span class="k">new</span> <span class="nx">Test</span><span class="p">,</span> <span class="s1">&#39;method1&#39;</span><span class="p">]));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP80           PHP74
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)     true
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(true)      true
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// __autoload() function has been removed
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 删除了对 object 使用 array_key_exists() 的能力。isset() or property_exists() may be used instead
</span></span></span><span class="line"><span class="cl"><span class="c1">// isset($arr[&#39;key&#39;]) → false（当值为 null）- 只能在 null 安全时检查，无法区分 null 与 不存在
</span></span></span><span class="line"><span class="cl"><span class="c1">// array_key_exists(&#39;key&#39;, $arr) → true（即使值为 null）- 可以区分存在但值为 null 的情况
</span></span></span></code></pre></div><p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP Migrating to 7.2 7.3 7.4</title>
      <link>https://zyf.im/2022/12/12/php-migrating-to-72-73-74/</link>
      <pubDate>Mon, 12 Dec 2022 20:51:51 +0000</pubDate>
      <guid>https://zyf.im/2022/12/12/php-migrating-to-72-73-74/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;实验环境：&lt;a href=&#34;https://onlinephp.io/&#34;&gt;https://onlinephp.io/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;php71-to-php72&#34;&gt;PHP7.1 to PHP7.2&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.php.net/manual/en/migration72.php&#34;&gt;https://www.php.net/manual/en/migration72.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://php.watch/versions/7.2&#34;&gt;https://php.watch/versions/7.2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Argon2 password hashing support, class constant visibility, object type, and many more.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;new-features-72&#34;&gt;New Features 7.2&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 新的 object 类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 可用于逆变（contravariant）参数输入和协变（covariant）返回任何对象类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// https://www.php.net/manual/zh/language.oop5.variance.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 协变使子类比父类方法能返回更具体的类型；逆变使子类比父类方法参数类型能接受更模糊的类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;object&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SplQueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;StdClass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 抽象类可以重写被继承的抽象类的抽象方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;A&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;B&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// overridden - 仍然保持参数的逆变和返回的逆变
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 重写方法和接口实现的参数类型可以省略
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 仍然是符合LSP，这种参数类型是逆变
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;B&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// type omitted for $input
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;B&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72 int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71 Fatal error: Declaration of B::test($input) must be compatible with A::test(array $input)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;backward-incompatible-changes-72&#34;&gt;Backward incompatible changes 7.2&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 防止 number_format() 返回负零
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;number_format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.01&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72 string(1) &amp;#34;0&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71 string(2) &amp;#34;-0&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 转换对象和数组中的数字键
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 新写法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 新写法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72 object(stdClass)#1 (1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;0&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71 object(stdClass)#1 (1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [0]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Notice: Undefined property: stdClass::$0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 整数 或者 字符串整数 含义相同
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72 array(1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [1]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   string(2) &amp;#34;my&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71 无法取整型字符串 key
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// array(1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;1&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   string(2) &amp;#34;my&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Notice: Undefined offset: 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Notice: Undefined offset: 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Notice: Undefined offset: 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;v1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1.4&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;v2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ALL array key float 会被转换为 int
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// array(1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [1]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   string(2) &amp;#34;v2&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// get_class() 函数不再接受 null
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get_class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Warning: get_class() expects parameter 1 to be object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Warning: get_class() called without object from outside a class
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP80
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Fatal error: Uncaught TypeError: get_class(): Argument #1 ($object) must be of type object, null given
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 计算非可数类型（non-countable）时发出警告
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// NULL is not countable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// integers are not countable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;abc&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// strings are not countable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;stdclass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// objects not implementing the Countable interface are not countable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Warning: count(): Parameter must be an array or an object that implements Countable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 无 Warning
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP80
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 调用未定义的常量，现在会抛出一个 E_WARNING 错误（之前版本中为 E_NOTICE)）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP8 将不会转化成他们自身的字符串，同时抛出 Error 异常
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;MY_CONST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Warning: Use of undefined constant MY_CONST - assumed &amp;#39;MY_CONST&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(8) &amp;#34;MY_CONST&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Notice: Use of undefined constant MY_CONST
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(8) &amp;#34;MY_CONST&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP80
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Fatal error: Uncaught Error: Undefined constant &amp;#34;MY_CONST&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bcmod 任意精度数字取模，添加新增参数 scale
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bcmod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;3.5&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(1) &amp;#34;0&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(1) &amp;#34;1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bcmod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;3.5&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(3) &amp;#34;0.5&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Warning: bcmod() expects exactly 2 parameters, 3 given
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// json_decode associative 允许为 null
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 当为 true 时，JSON 对象将返回关联 array；当为 false 时，JSON 对象将返回 object。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 当为 null 时，JSON 对象将返回关联 array 或 object，这取决于是否在 flags 中设置 JSON_OBJECT_AS_ARRAY
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// https://www.php.net/manual/zh/function.json-decode.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$json&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;a&amp;#34;:1,&amp;#34;b&amp;#34;:2}&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json_decode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;512&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;JSON_OBJECT_AS_ARRAY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP72
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// array(2) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;a&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;b&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   int(2)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// object(stdClass)#1 (2) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;a&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;b&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   int(2)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;php72-to-php73&#34;&gt;PHP7.2 to PHP7.3&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.php.net/manual/en/migration72.php&#34;&gt;https://www.php.net/manual/en/migration72.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://php.watch/versions/7.3&#34;&gt;https://php.watch/versions/7.3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Heredoc/nowdoc syntax improvements and a bunch of legacy code deprecations.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<ul>
<li>实验环境：<a href="https://onlinephp.io/">https://onlinephp.io/</a></li>
</ul>
<h2 id="php71-to-php72">PHP7.1 to PHP7.2</h2>
<ul>
<li><a href="https://www.php.net/manual/en/migration72.php">https://www.php.net/manual/en/migration72.php</a></li>
<li><a href="https://php.watch/versions/7.2">https://php.watch/versions/7.2</a></li>
</ul>
<blockquote>
<p>Argon2 password hashing support, class constant visibility, object type, and many more.</p>
</blockquote>
<h3 id="new-features-72">New Features 7.2</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 新的 object 类型
</span></span></span><span class="line"><span class="cl"><span class="c1">// 可用于逆变（contravariant）参数输入和协变（covariant）返回任何对象类型
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://www.php.net/manual/zh/language.oop5.variance.php
</span></span></span><span class="line"><span class="cl"><span class="c1">// 协变使子类比父类方法能返回更具体的类型；逆变使子类比父类方法参数类型能接受更模糊的类型
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">test</span><span class="p">(</span><span class="nx">object</span> <span class="nv">$obj</span><span class="p">)</span><span class="o">:</span> <span class="nx">object</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">new</span> <span class="nx">SplQueue</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">test</span><span class="p">(</span><span class="k">new</span> <span class="k">StdClass</span><span class="p">());</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 抽象类可以重写被继承的抽象类的抽象方法
</span></span></span><span class="line"><span class="cl"><span class="k">abstract</span> <span class="k">class</span> <span class="nc">A</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">abstract</span> <span class="k">function</span> <span class="nf">test</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$s</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">abstract</span> <span class="k">class</span> <span class="nc">B</span> <span class="k">extends</span> <span class="nx">A</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// overridden - 仍然保持参数的逆变和返回的逆变
</span></span></span><span class="line"><span class="cl">    <span class="k">abstract</span> <span class="k">function</span> <span class="nf">test</span><span class="p">(</span><span class="nv">$s</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 重写方法和接口实现的参数类型可以省略
</span></span></span><span class="line"><span class="cl"><span class="c1">// 仍然是符合LSP，这种参数类型是逆变
</span></span></span><span class="line"><span class="cl"><span class="k">interface</span> <span class="nx">A</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">test</span><span class="p">(</span><span class="k">array</span> <span class="nv">$input</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">B</span> <span class="k">implements</span> <span class="nx">A</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// type omitted for $input
</span></span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">test</span><span class="p">(</span><span class="nv">$input</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$input</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">((</span><span class="k">new</span> <span class="nx">B</span><span class="p">())</span><span class="o">-&gt;</span><span class="na">test</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72 int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71 Fatal error: Declaration of B::test($input) must be compatible with A::test(array $input)
</span></span></span></code></pre></div><h3 id="backward-incompatible-changes-72">Backward incompatible changes 7.2</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 防止 number_format() 返回负零
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">number_format</span><span class="p">(</span><span class="o">-</span><span class="mf">0.01</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72 string(1) &#34;0&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71 string(2) &#34;-0&#34;
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 转换对象和数组中的数字键
</span></span></span><span class="line"><span class="cl"><span class="nv">$arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$obj</span> <span class="o">=</span> <span class="p">(</span><span class="nx">object</span><span class="p">)</span> <span class="nv">$arr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$obj</span><span class="o">-&gt;</span><span class="p">{</span><span class="s1">&#39;0&#39;</span><span class="p">},</span> <span class="c1">// 新写法
</span></span></span><span class="line"><span class="cl">    <span class="nv">$obj</span><span class="o">-&gt;</span><span class="p">{</span><span class="mi">0</span><span class="p">}</span> <span class="c1">// 新写法
</span></span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72 object(stdClass)#1 (1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;0&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1">// int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">// int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71 object(stdClass)#1 (1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [0]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1">// Notice: Undefined property: stdClass::$0
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="k">class</span> <span class="err">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="p">{</span><span class="mi">1</span><span class="p">}</span> <span class="o">=</span> <span class="s2">&#34;value&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="nv">$arr</span> <span class="o">=</span> <span class="p">(</span><span class="k">array</span><span class="p">)</span> <span class="nv">$obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$arr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$arr</span><span class="p">[</span><span class="s2">&#34;1&#34;</span><span class="p">]);</span> <span class="c1">// 整数 或者 字符串整数 含义相同
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$arr</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span> <span class="c1">// PHP71
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$arr</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72 array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [1]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(2) &#34;my&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71 无法取整型字符串 key
</span></span></span><span class="line"><span class="cl"><span class="c1">// array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;1&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(2) &#34;my&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1">// Notice: Undefined offset: 1
</span></span></span><span class="line"><span class="cl"><span class="c1">// Notice: Undefined offset: 1
</span></span></span><span class="line"><span class="cl"><span class="c1">// Notice: Undefined offset: 0
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$a</span> <span class="o">=</span> <span class="p">[</span><span class="mf">1.3</span> <span class="o">=&gt;</span> <span class="s2">&#34;v1&#34;</span><span class="p">,</span> <span class="mf">1.4</span> <span class="o">=&gt;</span> <span class="s2">&#34;v2&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ALL array key float 会被转换为 int
</span></span></span><span class="line"><span class="cl"><span class="c1">// array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [1]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(2) &#34;v2&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// get_class() 函数不再接受 null
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">get_class</span><span class="p">(</span><span class="k">null</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72
</span></span></span><span class="line"><span class="cl"><span class="c1">// Warning: get_class() expects parameter 1 to be object
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71
</span></span></span><span class="line"><span class="cl"><span class="c1">// Warning: get_class() called without object from outside a class
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP80
</span></span></span><span class="line"><span class="cl"><span class="c1">// Fatal error: Uncaught TypeError: get_class(): Argument #1 ($object) must be of type object, null given
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 计算非可数类型（non-countable）时发出警告
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nx">count</span><span class="p">(</span><span class="k">null</span><span class="p">),</span> <span class="c1">// NULL is not countable
</span></span></span><span class="line"><span class="cl">    <span class="nx">count</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="c1">// integers are not countable
</span></span></span><span class="line"><span class="cl">    <span class="nx">count</span><span class="p">(</span><span class="s1">&#39;abc&#39;</span><span class="p">),</span> <span class="c1">// strings are not countable
</span></span></span><span class="line"><span class="cl">    <span class="nx">count</span><span class="p">(</span><span class="k">new</span> <span class="k">stdclass</span><span class="p">)</span> <span class="c1">// objects not implementing the Countable interface are not countable
</span></span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72
</span></span></span><span class="line"><span class="cl"><span class="c1">// Warning: count(): Parameter must be an array or an object that implements Countable
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71
</span></span></span><span class="line"><span class="cl"><span class="c1">// 无 Warning
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP80
</span></span></span><span class="line"><span class="cl"><span class="c1">// Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 调用未定义的常量，现在会抛出一个 E_WARNING 错误（之前版本中为 E_NOTICE)）
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP8 将不会转化成他们自身的字符串，同时抛出 Error 异常
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">MY_CONST</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72
</span></span></span><span class="line"><span class="cl"><span class="c1">// Warning: Use of undefined constant MY_CONST - assumed &#39;MY_CONST&#39;
</span></span></span><span class="line"><span class="cl"><span class="c1">// string(8) &#34;MY_CONST&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71
</span></span></span><span class="line"><span class="cl"><span class="c1">// Notice: Use of undefined constant MY_CONST
</span></span></span><span class="line"><span class="cl"><span class="c1">// string(8) &#34;MY_CONST&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP80
</span></span></span><span class="line"><span class="cl"><span class="c1">// Fatal error: Uncaught Error: Undefined constant &#34;MY_CONST&#34;
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// bcmod 任意精度数字取模，添加新增参数 scale
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">bcmod</span><span class="p">(</span><span class="s2">&#34;4&#34;</span><span class="p">,</span> <span class="s2">&#34;3.5&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72
</span></span></span><span class="line"><span class="cl"><span class="c1">// string(1) &#34;0&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71
</span></span></span><span class="line"><span class="cl"><span class="c1">// string(1) &#34;1&#34;
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">bcmod</span><span class="p">(</span><span class="s2">&#34;4&#34;</span><span class="p">,</span> <span class="s2">&#34;3.5&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72
</span></span></span><span class="line"><span class="cl"><span class="c1">// string(3) &#34;0.5&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71
</span></span></span><span class="line"><span class="cl"><span class="c1">// Warning: bcmod() expects exactly 2 parameters, 3 given
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// json_decode associative 允许为 null
</span></span></span><span class="line"><span class="cl"><span class="c1">// 当为 true 时，JSON 对象将返回关联 array；当为 false 时，JSON 对象将返回 object。
</span></span></span><span class="line"><span class="cl"><span class="c1">// 当为 null 时，JSON 对象将返回关联 array 或 object，这取决于是否在 flags 中设置 JSON_OBJECT_AS_ARRAY
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://www.php.net/manual/zh/function.json-decode.php
</span></span></span><span class="line"><span class="cl"><span class="nv">$json</span> <span class="o">=</span> <span class="s1">&#39;{&#34;a&#34;:1,&#34;b&#34;:2}&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">json_decode</span><span class="p">(</span><span class="nv">$json</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="mi">512</span><span class="p">,</span> <span class="nx">JSON_OBJECT_AS_ARRAY</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP72
</span></span></span><span class="line"><span class="cl"><span class="c1">// array(2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;a&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;b&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(2)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP71
</span></span></span><span class="line"><span class="cl"><span class="c1">// object(stdClass)#1 (2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;a&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;b&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(2)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><h2 id="php72-to-php73">PHP7.2 to PHP7.3</h2>
<ul>
<li><a href="https://www.php.net/manual/en/migration72.php">https://www.php.net/manual/en/migration72.php</a></li>
<li><a href="https://php.watch/versions/7.3">https://php.watch/versions/7.3</a></li>
</ul>
<blockquote>
<p>Heredoc/nowdoc syntax improvements and a bunch of legacy code deprecations.</p>
</blockquote>
<h3 id="new-features-73">New Features 7.3</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Heredoc Nowdoc 不再需要后跟分号或换行符
</span></span></span><span class="line"><span class="cl"><span class="c1">// 结束标记可以缩进，结束时所引用的标识符必须在该行的第一列
</span></span></span><span class="line"><span class="cl"><span class="nv">$values</span> <span class="o">=</span> <span class="p">[</span><span class="s">&lt;&lt;&lt;</span><span class="dl">END</span><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">a
</span></span></span><span class="line"><span class="cl"><span class="s">  b
</span></span></span><span class="line"><span class="cl"><span class="s">    c
</span></span></span><span class="line"><span class="cl"><span class="s">END, &#39;d e f&#39;];
</span></span></span><span class="line"><span class="cl"><span class="s">var_dump($values);
</span></span></span><span class="line"><span class="cl"><span class="s">// PHP73 array(2) {
</span></span></span><span class="line"><span class="cl"><span class="s">//   [0]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">//   string(13) &#34;a
</span></span></span><span class="line"><span class="cl"><span class="s">//   b
</span></span></span><span class="line"><span class="cl"><span class="s">//     c&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">//   [1]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">//   string(5) &#34;d e f&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">// }
</span></span></span><span class="line"><span class="cl"><span class="s">//
</span></span></span><span class="line"><span class="cl"><span class="s">// PHP72 Parse error: syntax error, unexpected end of file
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">echo &lt;&lt;&lt;END
</span></span></span><span class="line"><span class="cl"><span class="s">      a
</span></span></span><span class="line"><span class="cl"><span class="s">     b
</span></span></span><span class="line"><span class="cl"><span class="s">    c
</span></span></span><span class="line"><span class="cl"><span class="s">    </span><span class="dl">END</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP73
</span></span></span><span class="line"><span class="cl"><span class="c1">//   a
</span></span></span><span class="line"><span class="cl"><span class="c1">//  b
</span></span></span><span class="line"><span class="cl"><span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP72 Parse error: syntax error, unexpected end of file, expecting variable (T_VARIABLE) or heredoc end (T_END_HEREDOC) or ${ (T_DOLLAR_OPEN_CURLY_BRACES) or {$ (T_CURLY_OPEN)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s">&lt;&lt;&lt;</span><span class="dl">END</span><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">  a
</span></span></span><span class="line"><span class="cl"><span class="s"> b
</span></span></span><span class="line"><span class="cl"><span class="s">c
</span></span></span><span class="line"><span class="cl"><span class="s">   </span><span class="dl">END</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP73 Parse error: Invalid body indentation level
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 数组解构支持引用赋值
</span></span></span><span class="line"><span class="cl"><span class="nv">$arr</span> <span class="o">=</span> <span class="p">[[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">]];</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="o">&amp;</span><span class="nv">$a</span><span class="p">,</span> <span class="nv">$b</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$arr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$a</span><span class="p">[]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$b</span><span class="p">[]</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$arr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP73 array(2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [0]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   &amp;array(2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//     [0]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//     int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">//     [1]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//     int(2)
</span></span></span><span class="line"><span class="cl"><span class="c1">//   }
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [1]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//     [0]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//     int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">//   }
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP72 Fatal error: [] and list() assignments cannot be by reference
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 允许将 literals 作为第一个操作数，always false
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="k">true</span> <span class="nx">instanceof</span> <span class="k">stdClass</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP73 bool(false)
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP72 Fatal error: instanceof expects an object instance, constant given
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 调用中允许尾随逗号
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">my</span><span class="p">(</span><span class="nv">$v</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">echo</span> <span class="s2">&#34;php-</span><span class="si">{</span><span class="nv">$v</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">my</span><span class="p">(</span><span class="mi">73</span><span class="p">,);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP73 php-73
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP72 Parse error: syntax error, unexpected &#39;)&#39;
</span></span></span></code></pre></div><h3 id="new-functions-73">New Functions 7.3</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Gets the first key of an array
</span></span></span><span class="line"><span class="cl"><span class="nx">array_key_first</span><span class="p">(</span><span class="k">array</span> <span class="nv">$array</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span><span class="o">|</span><span class="nx">string</span><span class="o">|</span><span class="k">null</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Gets the last key of an array
</span></span></span><span class="line"><span class="cl"><span class="nx">array_key_last</span><span class="p">(</span><span class="k">array</span> <span class="nv">$array</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span><span class="o">|</span><span class="nx">string</span><span class="o">|</span><span class="k">null</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Get the system&#39;s high resolution time
</span></span></span><span class="line"><span class="cl"><span class="c1">// [seconds, nanoseconds] int（64 位平台）或 float（32 位平台）
</span></span></span><span class="line"><span class="cl"><span class="nx">hrtime</span><span class="p">(</span><span class="nx">bool</span> <span class="nv">$as_number</span> <span class="o">=</span> <span class="k">false</span><span class="p">)</span><span class="o">:</span> <span class="k">array</span><span class="o">|</span><span class="nx">int</span><span class="o">|</span><span class="nx">float</span><span class="o">|</span><span class="k">false</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 验证变量的内容是否为 countable 值
</span></span></span><span class="line"><span class="cl"><span class="c1">// return is_array($value) || $value instanceof Countable || $value instanceof ResourceBundle || $value instanceof SimpleXmlElement;
</span></span></span><span class="line"><span class="cl"><span class="nx">is_countable</span><span class="p">(</span><span class="nx">mixed</span> <span class="nv">$value</span><span class="p">)</span><span class="o">:</span> <span class="nx">bool</span>
</span></span></code></pre></div><h3 id="backward-incompatible-changes-73">Backward Incompatible Changes 7.3</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Continue Targeting Switch 问题警告
</span></span></span><span class="line"><span class="cl"><span class="nv">$foo</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">(</span><span class="nv">$foo</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="nv">$foo</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="s2">&#34;baz&#34;</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">         <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">   <span class="p">}</span>
</span></span><span class="line"><span class="cl">   <span class="nv">$foo</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP73 Warning: &#34;continue&#34; targeting switch is equivalent to &#34;break&#34;. Did you mean to use &#34;continue 2&#34;?
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP72 ok
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// $obj[&#34;123&#34;] 类型的数组访问，其中 $obj 实现 ArrayAccess 且 &#34;123&#34; 是整数字符串文字将不再导致隐式转换为整数
</span></span></span><span class="line"><span class="cl"><span class="c1">// 数组的行为不会受到任何影响，它们继续将整数字符串键隐式转换为整数
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="k">implements</span> <span class="nx">\ArrayAccess</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">offsetExists</span><span class="p">(</span><span class="nv">$offset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">offsetGet</span><span class="p">(</span><span class="nv">$offset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$offset</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">offsetSet</span><span class="p">(</span><span class="nv">$offset</span><span class="p">,</span> <span class="nv">$value</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">offsetUnset</span><span class="p">(</span><span class="nv">$offset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nv">$a</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">A</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$a</span><span class="p">[</span><span class="s2">&#34;123&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP73 string(3) &#34;123&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP72 int(123)
</span></span></span></code></pre></div><h2 id="php73-to-php74">PHP7.3 to PHP7.4</h2>
<ul>
<li><a href="https://www.php.net/manual/en/migration74.php">https://www.php.net/manual/en/migration74.php</a></li>
<li><a href="https://php.watch/versions/7.4">https://php.watch/versions/7.4</a></li>
</ul>
<blockquote>
<p>PHP 7.4, the final release in the PHP 7.x series. PHP 7.4 brings typed properties, underscore numeric separator, and other minor improvements to PHP.</p>
</blockquote>
<h3 id="new-features-74">New Features 7.4</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Typed properties
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nx">int</span> <span class="nv">$id</span><span class="p">;</span> <span class="c1">// 会强制要求 $user-&gt;id 只能为 int 类型，访问前必须进行处理化，?int 也要进行初始化
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 箭头函数 Arrow functions
</span></span></span><span class="line"><span class="cl"><span class="nv">$factor</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$nums</span> <span class="o">=</span> <span class="nx">array_map</span><span class="p">(</span><span class="nx">fn</span><span class="p">(</span><span class="nv">$n</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nv">$n</span> <span class="o">*</span> <span class="nv">$factor</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]);</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 有限的 Limited 返回类型协变和参数类型逆变
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">B</span> <span class="k">extends</span> <span class="nx">A</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Producer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">method</span><span class="p">()</span><span class="o">:</span> <span class="nx">A</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ChildProducer</span> <span class="k">extends</span> <span class="nx">Producer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">method</span><span class="p">()</span><span class="o">:</span> <span class="nx">B</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP74 ok
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP73 Fatal error: Declaration of ChildProducer::method(): B must be compatible with Producer::method(): A
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 空合并赋值运算符
</span></span></span><span class="line"><span class="cl"><span class="nv">$array</span><span class="p">[</span><span class="s1">&#39;key1&#39;</span><span class="p">]</span> <span class="o">??=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// is roughly equivalent to
</span></span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isset</span><span class="p">(</span><span class="nv">$array</span><span class="p">[</span><span class="s1">&#39;key2&#39;</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$array</span><span class="p">[</span><span class="s1">&#39;key2&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$array</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// array(2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;key1&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;key2&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   int(2)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Unpacking inside arrays
</span></span></span><span class="line"><span class="cl"><span class="c1">// 可以平替 array_merge
</span></span></span><span class="line"><span class="cl"><span class="nv">$parts</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;apple&#39;</span><span class="p">,</span> <span class="s1">&#39;pear&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$fruits</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;orange&#39;</span><span class="p">,</span> <span class="o">...</span><span class="nv">$parts</span><span class="p">,</span> <span class="s1">&#39;watermelon&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$fruits</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// array(4) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [0]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(6) &#34;orange&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [1]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(5) &#34;apple&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [2]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(4) &#34;pear&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [3]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(10) &#34;watermelon&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 数字文字分隔符
</span></span></span><span class="line"><span class="cl"><span class="mf">6.674</span><span class="nx">_083e</span><span class="o">-</span><span class="mi">11</span><span class="p">;</span> <span class="c1">// float
</span></span></span><span class="line"><span class="cl"><span class="mi">299_792_458</span><span class="p">;</span>   <span class="c1">// decimal
</span></span></span><span class="line"><span class="cl"><span class="mh">0xCAFE_F00D</span><span class="p">;</span>   <span class="c1">// hexadecimal
</span></span></span><span class="line"><span class="cl"><span class="mi">0</span><span class="nx">b0101_1111</span><span class="p">;</span>   <span class="c1">// binary
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">((</span><span class="nx">int</span><span class="p">)</span><span class="s2">&#34;1_123&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// int(1)
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// WeakReference 类
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://www.php.net/manual/zh/class.weakreference.php
</span></span></span><span class="line"><span class="cl"><span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="k">stdClass</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$weakref</span> <span class="o">=</span> <span class="nx">WeakReference</span><span class="o">::</span><span class="na">create</span><span class="p">(</span><span class="nv">$obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$weakref</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="nx">unset</span><span class="p">(</span><span class="nv">$obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$weakref</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="c1">// object(stdClass)#1 (0) {
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1">// NULL
</span></span></span></code></pre></div><h3 id="backward-incompatible-changes-74">Backward Incompatible Changes 7.4</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 以数组形式访问非数组，将会抛出 notic
</span></span></span><span class="line"><span class="cl"><span class="c1">// null, bool, int, float or resource
</span></span></span><span class="line"><span class="cl"><span class="nv">$i</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$i</span><span class="p">[</span><span class="s2">&#34;a&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP74 Notice: Trying to access array offset on value of type int
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP73 ok
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP80 Warning: Trying to access array offset on value of type int
</span></span></span></code></pre></div><h3 id="deprecated-features-74">Deprecated Features 7.4</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 嵌套的三元运算必须明确地使用括号来指示运算的顺序
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">1</span> <span class="o">?</span> <span class="mi">2</span> <span class="o">:</span> <span class="mi">3</span> <span class="o">?</span> <span class="mi">4</span> <span class="o">:</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP74 Deprecated: Unparenthesized `a ? b : c ? d : e` is deprecated. Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)`
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP73 int(4)
</span></span></span></code></pre></div><p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP Migrating to 7.0 7.1</title>
      <link>https://zyf.im/2022/11/28/php-migrating-to-70-71/</link>
      <pubDate>Mon, 28 Nov 2022 10:36:34 +0000</pubDate>
      <guid>https://zyf.im/2022/11/28/php-migrating-to-70-71/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;实验环境：&lt;a href=&#34;https://onlinephp.io/&#34;&gt;https://onlinephp.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Polyfill &lt;a href=&#34;https://github.com/symfony/polyfill/tree/main/src&#34;&gt;https://github.com/symfony/polyfill/tree/main/src&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;php70-to-php71&#34;&gt;PHP7.0 to PHP7.1&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://www.php.net/manual/en/migration71.php&#34;&gt;https://www.php.net/manual/en/migration71.php&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;71-new-features&#34;&gt;7.1 New Features&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Nullable types 可为空类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Void 函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;swap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$left&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$right&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;void&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取一个 void 方法的返回值会得到 null，并且不会产生任何警告
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 对称数组解构
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Tom&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Fred&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 赋值部分
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$name1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// logic here with $id and $name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;swap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;void&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// list 支持键名
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Tom&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Fred&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// logic here with $id and $name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 类常量支持可见性
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ConstDemo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;PUBLIC_CONST_A&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// iterable 伪类（与 callable 类似）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Traversable&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;iterable&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 这个接口没有任何方法，它的作用仅仅是作为所有可遍历类的基本接口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Traversable as part of either Iterator or IteratorAggregate，不能直接实现
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;iterator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;iterable&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$iter&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 多异常捕获处理，可以通过管道字符(|)来实现多个异常的捕获
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// some code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;FirstException&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SecondException&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// handle first and second exceptions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 支持为负的字符串偏移量，一个负数的偏移量会被理解为一个从字符串结尾开始的偏移量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 所有支持偏移量的字符串操作函数，都支持接受负数作为偏移量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 中文操作要小心
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;abcdef&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// e
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;strpos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;aabbcc&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;strpos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;aabbcc&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 新增 Closure::fromCallable()，将 callable 转为一个 Closure 对象
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// public static Closure::fromCallable(callable $callback): Closure
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;exposeFunction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Closure&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;fromCallable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;privateFunction&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;privateFunction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$param&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$param&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$privFunc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;exposeFunction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$privFunc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;some value&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;some value&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;71-new-functions&#34;&gt;7.1 New Functions&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 否为可迭代
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;is_iterable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;71-backward-incompatible-changes&#34;&gt;7.1 Backward Incompatible Changes&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 当传递参数过少时将抛出错误
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$param&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;){}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;Fatal error: Uncaught ArgumentCountError: Too few arguments to function test()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;PHP70
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;Warning: Missing argument 1 for test()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 禁止动态调用作用域自检函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// $func() or array_map(&amp;#39;extract&amp;#39;, ...)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// function array_map($callback, array $array, array ...$arrays): array
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$func&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;func_num_args&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;})();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;Warning: Cannot call func_num_args() dynamically
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 以下名称不能用于命名 class, interface, trait
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// - void
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// - iterable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 数字字符串转换 遵循科学记数法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;intval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;1e5&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;is_numeric&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;1e5&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;is_numeric&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71             PHP70
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(100000)       int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(true)        bool(true)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// bool(false)       bool(false)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// call_user_func() 不再支持对传址的函数的调用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;error_reporting&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;E_ALL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;increment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$var&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;call_user_func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;increment&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// ...expected to be a reference, value given
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;call_user_func_array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;increment&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$increment&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;increment&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$increment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 字符串不再支持空索引运算符
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;abc&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71 Fatal error: Uncaught Error: [] operator not supported for strings
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP70 array(1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [0]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   string(3) &amp;#34;abc&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71 string(1) &amp;#34;h&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP70 array(1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [0]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   string(11) &amp;#34;hello world&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 通过空字符串上的字符串索引访问赋值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71 string(11) &amp;#34;          f&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP70 array(1) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [10]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   string(3) &amp;#34;foo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 当通过引用赋值引用它们自动创建这些元素时，数组中元素的顺序已更改。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71 array(2) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;b&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   &amp;amp;int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;a&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   &amp;amp;int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP70 array(2) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;a&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   &amp;amp;int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   [&amp;#34;b&amp;#34;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   &amp;amp;int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 词法绑定变量不能重用名称，以下都将 fatal error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$f&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_SERVER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{};&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// any superglobals
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$f&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$f&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$param&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$param&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 禁止 &amp;#34;return;&amp;#34; 对于已经在编译时键入的返回值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP Compile Error A function with return type must return a value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;1b&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;something&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP71
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Notice: A non well formed numeric value encountered
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Warning: A non-numeric value encountered
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP70 int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;php56-to-php70&#34;&gt;PHP5.6 to PHP7.0&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.php.net/manual/en/migration70.php&#34;&gt;https://www.php.net/manual/en/migration70.php&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;backward-incompatible-changes&#34;&gt;Backward incompatible changes&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 错误和异常处理相关的变更
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// set_exception_handler() 不再保证收到的一定是 Exception 对象
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Exception&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;set_exception_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;handler&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 兼容 PHP 5 和 7
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 仅支持 PHP 7
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 当内部构造器失败的时候，总是抛出异常
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 间接调用的表达式的新旧解析顺序
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 现在严格遵循从左到右的顺序来解析
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 表达式               PHP 5 的解析方式           PHP 7 的解析方式
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$$foo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;    &lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$foo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]}&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$$foo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$foo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$bar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;      &lt;span class=&#34;nv&#34;&gt;$foo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$bar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]}&lt;/span&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$foo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$bar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// foreach 通过值遍历时，操作的值为数组的副本
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// foreach通过引用遍历时，有更好的迭代特性
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP 7.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP 5.6
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// call_user_method() and call_user_method_array() 被移除。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 应该使用 call_user_func() 和 call_user_func_array()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 在函数中检视参数值会返回 当前 的值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;foo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;func_get_arg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;foo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(2) PHP 7.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// int(1) PHP 5.6
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;70-new-features&#34;&gt;7.0 New Features&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 标量类型声明
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 返回值类型声明
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;arraysSum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arrays&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arrays&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// null 合并运算符，对 isset() 的简化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$username&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_GET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;??&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;nobody&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 太空船操作符（组合比较符）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// -1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 通过 define() 定义 常量数组
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;define&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ANIMALS&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;dog&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;cat&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;bird&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 匿名类 用来替代一些“用后即焚”的完整类定义
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Application&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setLogger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Logger&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Unicode codepoint 转译语法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 这接受一个以 16 进制形式的 Unicode codepoint
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;\u{9999}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 香
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Closure::call() 新方法，简化绑定一个方法到对象上闭包并调用它
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP 7 之前版本的代码
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$getXCB&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// function bindTo($newThis, $newScope = &amp;#39;static&amp;#39;) { }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// $newScope 将闭包关联到的类作用域
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$getX&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$getXCB&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;bindTo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 中间层闭包
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$getX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP 7+ 及更高版本的代码
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$getX&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$getX&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 生成器委托 yield from
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;gen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;gen2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;gen2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PHP_EOL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 柯里化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// int 25
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;deprecated-features-in-php-70x&#34;&gt;Deprecated features in PHP 7.0.x&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 构造函数（方法名和类名一样）将被弃用，并在将来移除
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Foo&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;foo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;I am the constructor&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP 7.0: Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PHP 5.6: OK
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;other-changes&#34;&gt;Other Changes&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 放宽了保留词限制，以前不能用 &amp;#39;new&amp;#39;、&amp;#39;private&amp;#39; 和 &amp;#39;for&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Project&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Project Name&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;purpose here&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;username here&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// class 关键词不能用于常量名，否则会和类名解析语法冲突（ClassName::class）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;new-global-constants&#34;&gt;New Global Constants&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;PHP_INT_MIN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<ul>
<li>实验环境：<a href="https://onlinephp.io/">https://onlinephp.io/</a></li>
<li>Polyfill <a href="https://github.com/symfony/polyfill/tree/main/src">https://github.com/symfony/polyfill/tree/main/src</a></li>
</ul>
<h2 id="php70-to-php71">PHP7.0 to PHP7.1</h2>
<blockquote>
<p><a href="https://www.php.net/manual/en/migration71.php">https://www.php.net/manual/en/migration71.php</a></p>
</blockquote>
<h3 id="71-new-features">7.1 New Features</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Nullable types 可为空类型
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">test</span><span class="p">(</span><span class="o">?</span><span class="nx">string</span> <span class="nv">$name</span><span class="p">)</span><span class="o">:</span> <span class="o">?</span><span class="nx">string</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Void 函数
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">swap</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">$left</span><span class="p">,</span> <span class="o">&amp;</span><span class="nv">$right</span><span class="p">)</span> <span class="o">:</span> <span class="nx">void</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 获取一个 void 方法的返回值会得到 null，并且不会产生任何警告
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 对称数组解构
</span></span></span><span class="line"><span class="cl"><span class="nv">$data</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;Tom&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;Fred&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nv">$id</span><span class="p">,</span> <span class="nv">$name</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$data</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 赋值部分
</span></span></span><span class="line"><span class="cl"><span class="p">[,</span> <span class="nv">$name1</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$data</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$data</span> <span class="k">as</span> <span class="p">[</span><span class="nv">$id</span><span class="p">,</span> <span class="nv">$name</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// logic here with $id and $name
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">swap</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">$a</span><span class="p">,</span> <span class="o">&amp;</span><span class="nv">$b</span><span class="p">)</span><span class="o">:</span> <span class="nx">void</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="nv">$a</span><span class="p">,</span> <span class="nv">$b</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="nv">$b</span><span class="p">,</span> <span class="nv">$a</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// list 支持键名
</span></span></span><span class="line"><span class="cl"><span class="nv">$data</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="s2">&#34;id&#34;</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="s1">&#39;Tom&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="s2">&#34;id&#34;</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">,</span> <span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="s1">&#39;Fred&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$data</span> <span class="k">as</span> <span class="p">[</span><span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="nv">$name</span><span class="p">,</span> <span class="s2">&#34;id&#34;</span> <span class="o">=&gt;</span> <span class="nv">$id</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// logic here with $id and $name
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 类常量支持可见性
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ConstDemo</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">const</span> <span class="no">PUBLIC_CONST_A</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// iterable 伪类（与 callable 类似）
</span></span></span><span class="line"><span class="cl"><span class="k">interface</span> <span class="nx">Traversable</span> <span class="k">extends</span> <span class="nx">iterable</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 这个接口没有任何方法，它的作用仅仅是作为所有可遍历类的基本接口
</span></span></span><span class="line"><span class="cl">    <span class="c1">// Traversable as part of either Iterator or IteratorAggregate，不能直接实现
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">iterator</span><span class="p">(</span><span class="nx">iterable</span> <span class="nv">$iter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="nv">$iter</span> <span class="k">as</span> <span class="nv">$val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 多异常捕获处理，可以通过管道字符(|)来实现多个异常的捕获
</span></span></span><span class="line"><span class="cl"><span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// some code
</span></span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">FirstException</span> <span class="o">|</span> <span class="nx">SecondException</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// handle first and second exceptions
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 支持为负的字符串偏移量，一个负数的偏移量会被理解为一个从字符串结尾开始的偏移量
</span></span></span><span class="line"><span class="cl"><span class="c1">// 所有支持偏移量的字符串操作函数，都支持接受负数作为偏移量
</span></span></span><span class="line"><span class="cl"><span class="c1">// 中文操作要小心
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="s2">&#34;abcdef&#34;</span><span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">]);</span> <span class="c1">// e
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">strpos</span><span class="p">(</span><span class="s2">&#34;aabbcc&#34;</span><span class="p">,</span> <span class="s2">&#34;b&#34;</span><span class="p">));</span> <span class="c1">// 2
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">strpos</span><span class="p">(</span><span class="s2">&#34;aabbcc&#34;</span><span class="p">,</span> <span class="s2">&#34;b&#34;</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">));</span> <span class="c1">// 3
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 新增 Closure::fromCallable()，将 callable 转为一个 Closure 对象
</span></span></span><span class="line"><span class="cl"><span class="c1">// public static Closure::fromCallable(callable $callback): Closure
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Test</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">exposeFunction</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">Closure</span><span class="o">::</span><span class="na">fromCallable</span><span class="p">([</span><span class="nv">$this</span><span class="p">,</span> <span class="s1">&#39;privateFunction&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">function</span> <span class="nf">privateFunction</span><span class="p">(</span><span class="nv">$param</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$param</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$privFunc</span> <span class="o">=</span> <span class="p">(</span><span class="k">new</span> <span class="nx">Test</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">exposeFunction</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$privFunc</span><span class="p">(</span><span class="s1">&#39;some value&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;some value&#34;
</span></span></span></code></pre></div><h3 id="71-new-functions">7.1 New Functions</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 否为可迭代
</span></span></span><span class="line"><span class="cl"><span class="nx">is_iterable</span><span class="p">()</span>
</span></span></code></pre></div><h3 id="71-backward-incompatible-changes">7.1 Backward Incompatible Changes</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 当传递参数过少时将抛出错误
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">test</span><span class="p">(</span><span class="nv">$param</span><span class="p">){}</span>
</span></span><span class="line"><span class="cl"><span class="nx">test</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">PHP71
</span></span></span><span class="line"><span class="cl"><span class="cm">Fatal error: Uncaught ArgumentCountError: Too few arguments to function test()
</span></span></span><span class="line"><span class="cl"><span class="cm">
</span></span></span><span class="line"><span class="cl"><span class="cm">PHP70
</span></span></span><span class="line"><span class="cl"><span class="cm">Warning: Missing argument 1 for test()
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 禁止动态调用作用域自检函数
</span></span></span><span class="line"><span class="cl"><span class="c1">// $func() or array_map(&#39;extract&#39;, ...)
</span></span></span><span class="line"><span class="cl"><span class="c1">// function array_map($callback, array $array, array ...$arrays): array
</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$func</span> <span class="o">=</span> <span class="s1">&#39;func_num_args&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$func</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">})();</span>
</span></span><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">PHP71
</span></span></span><span class="line"><span class="cl"><span class="cm">Warning: Cannot call func_num_args() dynamically
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 以下名称不能用于命名 class, interface, trait
</span></span></span><span class="line"><span class="cl"><span class="c1">// - void
</span></span></span><span class="line"><span class="cl"><span class="c1">// - iterable
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 数字字符串转换 遵循科学记数法
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">intval</span><span class="p">(</span><span class="s1">&#39;1e5&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">is_numeric</span><span class="p">(</span><span class="s1">&#39;1e5&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">is_numeric</span><span class="p">(</span><span class="s1">&#39;e&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP71             PHP70
</span></span></span><span class="line"><span class="cl"><span class="c1">// int(100000)       int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(true)        bool(true)
</span></span></span><span class="line"><span class="cl"><span class="c1">// bool(false)       bool(false)
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// call_user_func() 不再支持对传址的函数的调用
</span></span></span><span class="line"><span class="cl"><span class="nx">error_reporting</span><span class="p">(</span><span class="k">E_ALL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">increment</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">$var</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$var</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">call_user_func</span><span class="p">(</span><span class="s1">&#39;increment&#39;</span><span class="p">,</span> <span class="nv">$a</span><span class="p">);</span> <span class="c1">// ...expected to be a reference, value given
</span></span></span><span class="line"><span class="cl"><span class="nx">call_user_func_array</span><span class="p">(</span><span class="s1">&#39;increment&#39;</span><span class="p">,</span> <span class="p">[</span><span class="o">&amp;</span><span class="nv">$a</span><span class="p">]);</span> <span class="c1">// 1
</span></span></span><span class="line"><span class="cl"><span class="nv">$increment</span> <span class="o">=</span> <span class="s1">&#39;increment&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$increment</span><span class="p">(</span><span class="nv">$a</span><span class="p">);</span> <span class="c1">// 2
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 字符串不再支持空索引运算符
</span></span></span><span class="line"><span class="cl"><span class="nv">$str</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$str</span><span class="p">[]</span> <span class="o">=</span> <span class="s2">&#34;abc&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP71 Fatal error: Uncaught Error: [] operator not supported for strings
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP70 array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [0]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(3) &#34;abc&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$a</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$a</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;hello world&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP71 string(1) &#34;h&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP70 array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [0]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(11) &#34;hello world&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 通过空字符串上的字符串索引访问赋值
</span></span></span><span class="line"><span class="cl"><span class="nv">$str</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$str</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;foo&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$str</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP71 string(11) &#34;          f&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP70 array(1) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [10]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   string(3) &#34;foo&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 当通过引用赋值引用它们自动创建这些元素时，数组中元素的顺序已更改。
</span></span></span><span class="line"><span class="cl"><span class="nv">$array</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$array</span><span class="p">[</span><span class="s1">&#39;a&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="o">&amp;</span><span class="nv">$array</span><span class="p">[</span><span class="s1">&#39;b&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nv">$array</span><span class="p">[</span><span class="s1">&#39;b&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$array</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP71 array(2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;b&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   &amp;int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;a&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   &amp;int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP70 array(2) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;a&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   &amp;int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">//   [&#34;b&#34;]=&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">//   &amp;int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 词法绑定变量不能重用名称，以下都将 fatal error
</span></span></span><span class="line"><span class="cl"><span class="nv">$f</span> <span class="o">=</span> <span class="k">function</span> <span class="p">()</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$_SERVER</span><span class="p">)</span> <span class="p">{};</span> <span class="c1">// any superglobals
</span></span></span><span class="line"><span class="cl"><span class="nv">$f</span> <span class="o">=</span> <span class="k">function</span> <span class="p">()</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$this</span><span class="p">)</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="nv">$f</span> <span class="o">=</span> <span class="k">function</span> <span class="p">(</span><span class="nv">$param</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$param</span><span class="p">)</span> <span class="p">{};</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 禁止 &#34;return;&#34; 对于已经在编译时键入的返回值
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">test</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$i</span><span class="p">)</span><span class="o">:</span> <span class="k">array</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$i</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP Compile Error A function with return type must return a value
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="s1">&#39;1b&#39;</span> <span class="o">+</span> <span class="s1">&#39;something&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP71
</span></span></span><span class="line"><span class="cl"><span class="c1">// Notice: A non well formed numeric value encountered
</span></span></span><span class="line"><span class="cl"><span class="c1">// Warning: A non-numeric value encountered
</span></span></span><span class="line"><span class="cl"><span class="c1">// int(1)
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP70 int(1)
</span></span></span></code></pre></div><h2 id="php56-to-php70">PHP5.6 to PHP7.0</h2>
<ul>
<li><a href="https://www.php.net/manual/en/migration70.php">https://www.php.net/manual/en/migration70.php</a></li>
</ul>
<h3 id="backward-incompatible-changes">Backward incompatible changes</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 错误和异常处理相关的变更
</span></span></span><span class="line"><span class="cl"><span class="c1">// set_exception_handler() 不再保证收到的一定是 Exception 对象
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">handler</span><span class="p">(</span><span class="nx">Exception</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">set_exception_handler</span><span class="p">(</span><span class="s1">&#39;handler&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 兼容 PHP 5 和 7
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">handler</span><span class="p">(</span><span class="nv">$e</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 仅支持 PHP 7
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">handler</span><span class="p">(</span><span class="nx">Throwable</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 当内部构造器失败的时候，总是抛出异常
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 间接调用的表达式的新旧解析顺序
</span></span></span><span class="line"><span class="cl"><span class="c1">// 现在严格遵循从左到右的顺序来解析
</span></span></span><span class="line"><span class="cl"><span class="c1">// 表达式               PHP 5 的解析方式           PHP 7 的解析方式
</span></span></span><span class="line"><span class="cl"><span class="nv">$$foo</span><span class="p">[</span><span class="s1">&#39;bar&#39;</span><span class="p">][</span><span class="s1">&#39;baz&#39;</span><span class="p">]</span>    <span class="err">$</span><span class="p">{</span><span class="nv">$foo</span><span class="p">[</span><span class="s1">&#39;bar&#39;</span><span class="p">][</span><span class="s1">&#39;baz&#39;</span><span class="p">]}</span>    <span class="p">(</span><span class="nv">$$foo</span><span class="p">)[</span><span class="s1">&#39;bar&#39;</span><span class="p">][</span><span class="s1">&#39;baz&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">$foo</span><span class="o">-&gt;</span><span class="nv">$bar</span><span class="p">[</span><span class="s1">&#39;baz&#39;</span><span class="p">]</span>      <span class="nv">$foo</span><span class="o">-&gt;</span><span class="p">{</span><span class="nv">$bar</span><span class="p">[</span><span class="s1">&#39;baz&#39;</span><span class="p">]}</span>      <span class="p">(</span><span class="nv">$foo</span><span class="o">-&gt;</span><span class="nv">$bar</span><span class="p">)[</span><span class="s1">&#39;baz&#39;</span><span class="p">]</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// foreach 通过值遍历时，操作的值为数组的副本
</span></span></span><span class="line"><span class="cl"><span class="nv">$array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$array</span> <span class="k">as</span> <span class="nv">$val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// int(0)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// foreach通过引用遍历时，有更好的迭代特性
</span></span></span><span class="line"><span class="cl"><span class="nv">$array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$array</span> <span class="k">as</span> <span class="o">&amp;</span><span class="nv">$val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 7.0
</span></span></span><span class="line"><span class="cl"><span class="c1">// int(0)
</span></span></span><span class="line"><span class="cl"><span class="c1">// int(1)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 5.6
</span></span></span><span class="line"><span class="cl"><span class="c1">// int(0)
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// call_user_method() and call_user_method_array() 被移除。
</span></span></span><span class="line"><span class="cl"><span class="c1">// 应该使用 call_user_func() 和 call_user_func_array()
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 在函数中检视参数值会返回 当前 的值
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">foo</span><span class="p">(</span><span class="nv">$x</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$x</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">var_dump</span><span class="p">(</span><span class="nx">func_get_arg</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">foo</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// int(2) PHP 7.0
</span></span></span><span class="line"><span class="cl"><span class="c1">// int(1) PHP 5.6
</span></span></span></code></pre></div><h3 id="70-new-features">7.0 New Features</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 标量类型声明
</span></span></span><span class="line"><span class="cl"><span class="c1">// 返回值类型声明
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">arraysSum</span><span class="p">(</span><span class="k">array</span> <span class="o">...</span><span class="nv">$arrays</span><span class="p">)</span><span class="o">:</span> <span class="k">array</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">array_map</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="k">array</span> <span class="nv">$array</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">array_sum</span><span class="p">(</span><span class="nv">$array</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span> <span class="nv">$arrays</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// null 合并运算符，对 isset() 的简化
</span></span></span><span class="line"><span class="cl"><span class="nv">$username</span> <span class="o">=</span> <span class="nv">$_GET</span><span class="p">[</span><span class="s1">&#39;user&#39;</span><span class="p">]</span> <span class="o">??</span> <span class="s1">&#39;nobody&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 太空船操作符（组合比较符）
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="mi">1</span> <span class="o">&lt;=&gt;</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// 0
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="mi">1</span> <span class="o">&lt;=&gt;</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// -1
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="mi">2</span> <span class="o">&lt;=&gt;</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// 1
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 通过 define() 定义 常量数组
</span></span></span><span class="line"><span class="cl"><span class="nx">define</span><span class="p">(</span><span class="s1">&#39;ANIMALS&#39;</span><span class="p">,</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;dog&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;cat&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;bird&#39;</span>
</span></span><span class="line"><span class="cl"><span class="p">]);</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 匿名类 用来替代一些“用后即焚”的完整类定义
</span></span></span><span class="line"><span class="cl"><span class="nv">$app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Application</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$app</span><span class="o">-&gt;</span><span class="na">setLogger</span><span class="p">(</span><span class="k">new</span> <span class="k">class</span> <span class="nc">implements</span> <span class="nx">Logger</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">log</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$msg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">echo</span> <span class="nv">$msg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Unicode codepoint 转译语法
</span></span></span><span class="line"><span class="cl"><span class="c1">// 这接受一个以 16 进制形式的 Unicode codepoint
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;\u{9999}&#34;</span><span class="p">;</span> <span class="c1">// 香
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Closure::call() 新方法，简化绑定一个方法到对象上闭包并调用它
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP 7 之前版本的代码
</span></span></span><span class="line"><span class="cl"><span class="nv">$getXCB</span> <span class="o">=</span> <span class="k">function</span><span class="p">()</span> <span class="p">{</span><span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">x</span><span class="p">;};</span>
</span></span><span class="line"><span class="cl"><span class="c1">// function bindTo($newThis, $newScope = &#39;static&#39;) { }
</span></span></span><span class="line"><span class="cl"><span class="c1">// $newScope 将闭包关联到的类作用域
</span></span></span><span class="line"><span class="cl"><span class="nv">$getX</span> <span class="o">=</span> <span class="nv">$getXCB</span><span class="o">-&gt;</span><span class="na">bindTo</span><span class="p">(</span><span class="k">new</span> <span class="nx">A</span><span class="p">,</span> <span class="nx">A</span><span class="o">::</span><span class="na">class</span><span class="p">);</span> <span class="c1">// 中间层闭包
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$getX</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 7+ 及更高版本的代码
</span></span></span><span class="line"><span class="cl"><span class="nv">$getX</span> <span class="o">=</span> <span class="k">function</span><span class="p">()</span> <span class="p">{</span><span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">x</span><span class="p">;};</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$getX</span><span class="o">-&gt;</span><span class="na">call</span><span class="p">(</span><span class="k">new</span> <span class="nx">A</span><span class="p">);</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 生成器委托 yield from
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">gen</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="nx">from</span> <span class="nx">gen2</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">gen2</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nx">gen</span><span class="p">()</span> <span class="k">as</span> <span class="nv">$val</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">echo</span> <span class="nv">$val</span><span class="p">,</span> <span class="nx">PHP_EOL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1
</span></span></span><span class="line"><span class="cl"><span class="c1">// 2
</span></span></span><span class="line"><span class="cl"><span class="c1">// 3
</span></span></span><span class="line"><span class="cl"><span class="c1">// 4
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 柯里化
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">add</span><span class="p">(</span><span class="nv">$a</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="k">function</span><span class="p">(</span><span class="nv">$b</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$a</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$a</span> <span class="o">+</span> <span class="nv">$b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">10</span><span class="p">)(</span><span class="mi">15</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$result</span><span class="p">);</span> <span class="c1">// int 25
</span></span></span></code></pre></div><h3 id="deprecated-features-in-php-70x">Deprecated features in PHP 7.0.x</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 构造函数（方法名和类名一样）将被弃用，并在将来移除
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">function</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">echo</span> <span class="s1">&#39;I am the constructor&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// PHP 7.0: Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP
</span></span></span><span class="line"><span class="cl"><span class="c1">// PHP 5.6: OK
</span></span></span></code></pre></div><h3 id="other-changes">Other Changes</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 放宽了保留词限制，以前不能用 &#39;new&#39;、&#39;private&#39; 和 &#39;for&#39;
</span></span></span><span class="line"><span class="cl"><span class="nx">Project</span><span class="o">::</span><span class="na">new</span><span class="p">(</span><span class="s1">&#39;Project Name&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">private</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">for</span><span class="p">(</span><span class="s1">&#39;purpose here&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">with</span><span class="p">(</span><span class="s1">&#39;username here&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// class 关键词不能用于常量名，否则会和类名解析语法冲突（ClassName::class）
</span></span></span></code></pre></div><h3 id="new-global-constants">New Global Constants</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">PHP_INT_MIN</span>
</span></span></code></pre></div><p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Docker Code Snippet</title>
      <link>https://zyf.im/2022/11/17/docker-code-snippet/</link>
      <pubDate>Thu, 17 Nov 2022 10:09:26 +0000</pubDate>
      <guid>https://zyf.im/2022/11/17/docker-code-snippet/</guid>
      <description>&lt;h2 id=&#34;dockerfile&#34;&gt;Dockerfile&lt;/h2&gt;
&lt;h3 id=&#34;arg-构建参数&#34;&gt;ARG 构建参数&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ARG &amp;lt;参数名&amp;gt;[=&amp;lt;默认值&amp;gt;]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;该默认值可以在构建命令 docker build 中用 &amp;ndash;build-arg &amp;lt;参数名&amp;gt;=&amp;lt;值&amp;gt; 来覆盖。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ARG&lt;/span&gt; LARADOCK_PHP_VERSION&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ARG&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;BASE_IMAGE_TAG_PREFIX&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;latest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;laradock/workspace:${BASE_IMAGE_TAG_PREFIX}-${LARADOCK_PHP_VERSION&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;LABEL&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;maintainer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Mahmoud Zalt &amp;lt;mahmoud@zalt.me&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# ARG 指令有生效范围，如果在 FROM 指令之前指定，那么只能用于 FROM 指令中。&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 在 FROM 之后须再次指定 ARG&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ARG&lt;/span&gt; LARADOCK_PHP_VERSION&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;copy-vs-add&#34;&gt;COPY vs. ADD&lt;/h3&gt;
&lt;p&gt;能用 COPY 就用 COPY，只有在 ADD 独有功能确实需要时才使用 ADD。&lt;/p&gt;
&lt;p&gt;只有在确实需要 ADD 的额外功能（自动解压本地归档文件、从远程 URL 获取资源）时才使用 ADD。&lt;/p&gt;
&lt;h3 id=&#34;run-vs-cmd-vs-entrypoint&#34;&gt;RUN vs. CMD vs. ENTRYPOINT&lt;/h3&gt;
&lt;p&gt;RUN 在 镜像构建时执行 并生成新层；CMD/ENTRYPOINT 在 容器启动时执行。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RUN 安装依赖、编译源码。&lt;/li&gt;
&lt;li&gt;ENTRYPOINT 用来定义 &lt;strong&gt;始终要执行&lt;/strong&gt; 的主命令。&lt;/li&gt;
&lt;li&gt;CMD 用来提供 &lt;strong&gt;默认参数&lt;/strong&gt;，也可在缺少 ENTRYPOINT 时充当默认命令。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ENTRYPOINT&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/scripts/app/entrypoint.sh&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CMD&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/scripts/fpm/start-php-fpm.sh&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;ENTRYPOINT 以 &lt;em&gt;exec 形式 (JSON 数组)&lt;/em&gt; 指定：容器启动时一定会先执行 &lt;code&gt;/scripts/app/entrypoint.sh&lt;/code&gt;，它就是 PID 1。可被 docker run 的 &amp;ndash;entrypoint 覆盖。&lt;/li&gt;
&lt;li&gt;CMD 以 &lt;em&gt;shell 形式 (单字符串)&lt;/em&gt; 指定默认命令：缺省情况下，Docker 会把它当作 &lt;code&gt;/bin/sh -c &amp;quot;/scripts/fpm/start-php-fpm.sh&amp;quot;&lt;/code&gt; 并把这一整串 &lt;strong&gt;作为参数&lt;/strong&gt; 传给 ENTRYPOINT。可被 docker run 后面参数覆盖。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/scripts/app/entrypoint.sh /bin/sh -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;/scripts/fpm/start-php-fpm.sh&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;label-vs-arg-vs-env&#34;&gt;LABEL vs. ARG vs. ENV&lt;/h3&gt;
&lt;p&gt;LABEL 写入镜像 &lt;strong&gt;元数据&lt;/strong&gt;。ARG 仅在 &lt;strong&gt;构建阶段&lt;/strong&gt; 可用；ENV 在 &lt;strong&gt;构建后续层&lt;/strong&gt; 和 &lt;strong&gt;运行时&lt;/strong&gt; 都可用，并会出现在最终镜像中。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="dockerfile">Dockerfile</h2>
<h3 id="arg-构建参数">ARG 构建参数</h3>
<p><code>ARG &lt;参数名&gt;[=&lt;默认值&gt;]</code></p>
<p>该默认值可以在构建命令 docker build 中用 &ndash;build-arg &lt;参数名&gt;=&lt;值&gt; 来覆盖。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ARG</span> LARADOCK_PHP_VERSION<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">BASE_IMAGE_TAG_PREFIX</span><span class="o">=</span>latest
</span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">laradock/workspace:${BASE_IMAGE_TAG_PREFIX}-${LARADOCK_PHP_VERSION</span><span class="o">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">LABEL</span> <span class="nv">maintainer</span><span class="o">=</span><span class="s2">&#34;Mahmoud Zalt &lt;mahmoud@zalt.me&gt;&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># ARG 指令有生效范围，如果在 FROM 指令之前指定，那么只能用于 FROM 指令中。</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># 在 FROM 之后须再次指定 ARG</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ARG</span> LARADOCK_PHP_VERSION<span class="err">
</span></span></span></code></pre></div><h3 id="copy-vs-add">COPY vs. ADD</h3>
<p>能用 COPY 就用 COPY，只有在 ADD 独有功能确实需要时才使用 ADD。</p>
<p>只有在确实需要 ADD 的额外功能（自动解压本地归档文件、从远程 URL 获取资源）时才使用 ADD。</p>
<h3 id="run-vs-cmd-vs-entrypoint">RUN vs. CMD vs. ENTRYPOINT</h3>
<p>RUN 在 镜像构建时执行 并生成新层；CMD/ENTRYPOINT 在 容器启动时执行。</p>
<ul>
<li>RUN 安装依赖、编译源码。</li>
<li>ENTRYPOINT 用来定义 <strong>始终要执行</strong> 的主命令。</li>
<li>CMD 用来提供 <strong>默认参数</strong>，也可在缺少 ENTRYPOINT 时充当默认命令。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl">...<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ENTRYPOINT</span> <span class="p">[</span> <span class="s2">&#34;/scripts/app/entrypoint.sh&#34;</span> <span class="p">]</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">CMD</span> <span class="s2">&#34;/scripts/fpm/start-php-fpm.sh&#34;</span><span class="err">
</span></span></span></code></pre></div><ul>
<li>ENTRYPOINT 以 <em>exec 形式 (JSON 数组)</em> 指定：容器启动时一定会先执行 <code>/scripts/app/entrypoint.sh</code>，它就是 PID 1。可被 docker run 的 &ndash;entrypoint 覆盖。</li>
<li>CMD 以 <em>shell 形式 (单字符串)</em> 指定默认命令：缺省情况下，Docker 会把它当作 <code>/bin/sh -c &quot;/scripts/fpm/start-php-fpm.sh&quot;</code> 并把这一整串 <strong>作为参数</strong> 传给 ENTRYPOINT。可被 docker run 后面参数覆盖。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/scripts/app/entrypoint.sh /bin/sh -c <span class="s2">&#34;/scripts/fpm/start-php-fpm.sh&#34;</span>
</span></span></code></pre></div><h3 id="label-vs-arg-vs-env">LABEL vs. ARG vs. ENV</h3>
<p>LABEL 写入镜像 <strong>元数据</strong>。ARG 仅在 <strong>构建阶段</strong> 可用；ENV 在 <strong>构建后续层</strong> 和 <strong>运行时</strong> 都可用，并会出现在最终镜像中。</p>
<ul>
<li>用 LABEL 记录作者、版本、VCS 地址等信息。</li>
<li>用 ARG 传版本号、密钥等只在构建时需要的值。</li>
<li>用 ENV 传配置、端口等运行期仍需的变量。</li>
</ul>
<h2 id="config-setting">Config Setting</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;registry-mirrors&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;https://mirror.ccs.tencentyun.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;https://reg-mirror.qiniu.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;https://hub-mirror.c.163.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;https://mirror.baidubce.com&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>Laradock 学习笔记</title>
      <link>https://zyf.im/2022/11/09/explore-laradock/</link>
      <pubDate>Wed, 09 Nov 2022 22:24:01 +0000</pubDate>
      <guid>https://zyf.im/2022/11/09/explore-laradock/</guid>
      <description>&lt;h2 id=&#34;laradock-简介&#34;&gt;Laradock 简介&lt;/h2&gt;
&lt;p&gt;Laradock 是一个基于 Docker 的完整 PHP 开发环境，专为 PHP 开发者（特别是 Laravel 开发者）设计。它提供了一套预配置的 Docker 容器，包含开发中常用的服务，如 Nginx、MySQL、Redis、PHP-FPM 等。&lt;/p&gt;
&lt;h3 id=&#34;主要优势&#34;&gt;主要优势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;开箱即用&lt;/strong&gt;：预配置的开发环境，无需繁琐设置&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一致性&lt;/strong&gt;：确保开发、测试和生产环境的一致性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;灵活性&lt;/strong&gt;：支持多种 PHP 版本和扩展&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模块化&lt;/strong&gt;：可以根据需要启用或禁用特定服务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨平台&lt;/strong&gt;：在 Windows、macOS 和 Linux 上表现一致&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;基本架构&#34;&gt;基本架构&lt;/h3&gt;
&lt;p&gt;Laradock 采用模块化设计，每个服务都运行在独立的容器中，通过 Docker 网络连接：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Workspace&lt;/strong&gt;：包含开发工具的容器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PHP-FPM&lt;/strong&gt;：PHP 解释器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx/Apache&lt;/strong&gt;：Web 服务器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL/PostgreSQL/MongoDB&lt;/strong&gt;：数据库&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redis/Memcached&lt;/strong&gt;：缓存&lt;/li&gt;
&lt;li&gt;其他辅助服务（邮件服务器、消息队列等）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;dockerfile&#34;&gt;Dockerfile&lt;/h2&gt;
&lt;h3 id=&#34;debian_frontend&#34;&gt;DEBIAN_FRONTEND&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ENV&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;DEBIAN_FRONTEND&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;noninteractive
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;DEBIAN_FRONTEND 环境变量用于告知操作系统应该从哪儿获得用户输入。&lt;/p&gt;
&lt;p&gt;设置为 &amp;ldquo;noninteractive&amp;rdquo; 可以实现自动安装而无需用户交互，对于 CI/CD 流程和 Docker 构建特别有用。这在运行 apt-get 命令时尤为重要，因为它会自动选择默认选项并以最快速度完成构建。&lt;/p&gt;
&lt;p&gt;最佳实践是在 RUN 命令中设置该变量，而不是使用 ENV 进行全局设置，因为全局设置会影响容器运行时的交互体验：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;DEBIAN_FRONTEND&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;noninteractive apt-get update &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get install -y ...&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;基于角色的用户配置&#34;&gt;基于角色的用户配置&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 创建非 root 用户防止文件在宿主机上创建时具有 root 权限&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ARG&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PUID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ENV&lt;/span&gt; PUID &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PUID&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ARG&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PGID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ENV&lt;/span&gt; PGID &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PGID&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; -xe&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    groupadd -g &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PGID&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt; laradock &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    useradd -l -u &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PUID&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt; -g laradock -m laradock -G docker_env &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    usermod -p &lt;span class=&#34;s2&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt; laradock -s /bin/bash&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这段配置创建了一个非 root 用户 &lt;code&gt;laradock&lt;/code&gt;，解决了容器和宿主机之间文件权限问题：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="laradock-简介">Laradock 简介</h2>
<p>Laradock 是一个基于 Docker 的完整 PHP 开发环境，专为 PHP 开发者（特别是 Laravel 开发者）设计。它提供了一套预配置的 Docker 容器，包含开发中常用的服务，如 Nginx、MySQL、Redis、PHP-FPM 等。</p>
<h3 id="主要优势">主要优势</h3>
<ul>
<li><strong>开箱即用</strong>：预配置的开发环境，无需繁琐设置</li>
<li><strong>一致性</strong>：确保开发、测试和生产环境的一致性</li>
<li><strong>灵活性</strong>：支持多种 PHP 版本和扩展</li>
<li><strong>模块化</strong>：可以根据需要启用或禁用特定服务</li>
<li><strong>跨平台</strong>：在 Windows、macOS 和 Linux 上表现一致</li>
</ul>
<h3 id="基本架构">基本架构</h3>
<p>Laradock 采用模块化设计，每个服务都运行在独立的容器中，通过 Docker 网络连接：</p>
<ul>
<li><strong>Workspace</strong>：包含开发工具的容器</li>
<li><strong>PHP-FPM</strong>：PHP 解释器</li>
<li><strong>Nginx/Apache</strong>：Web 服务器</li>
<li><strong>MySQL/PostgreSQL/MongoDB</strong>：数据库</li>
<li><strong>Redis/Memcached</strong>：缓存</li>
<li>其他辅助服务（邮件服务器、消息队列等）</li>
</ul>
<h2 id="dockerfile">Dockerfile</h2>
<h3 id="debian_frontend">DEBIAN_FRONTEND</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ENV</span> <span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span>noninteractive
</span></span></code></pre></div><p>DEBIAN_FRONTEND 环境变量用于告知操作系统应该从哪儿获得用户输入。</p>
<p>设置为 &ldquo;noninteractive&rdquo; 可以实现自动安装而无需用户交互，对于 CI/CD 流程和 Docker 构建特别有用。这在运行 apt-get 命令时尤为重要，因为它会自动选择默认选项并以最快速度完成构建。</p>
<p>最佳实践是在 RUN 命令中设置该变量，而不是使用 ENV 进行全局设置，因为全局设置会影响容器运行时的交互体验：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">RUN</span> <span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span>noninteractive apt-get update <span class="o">&amp;&amp;</span> apt-get install -y ...<span class="err">
</span></span></span></code></pre></div><h3 id="基于角色的用户配置">基于角色的用户配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="c"># 创建非 root 用户防止文件在宿主机上创建时具有 root 权限</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">PUID</span><span class="o">=</span><span class="m">1000</span>
</span></span><span class="line"><span class="cl"><span class="k">ENV</span> PUID <span class="si">${</span><span class="nv">PUID</span><span class="si">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">PGID</span><span class="o">=</span><span class="m">1000</span>
</span></span><span class="line"><span class="cl"><span class="k">ENV</span> PGID <span class="si">${</span><span class="nv">PGID</span><span class="si">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> <span class="nb">set</span> -xe<span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    groupadd -g <span class="si">${</span><span class="nv">PGID</span><span class="si">}</span> laradock <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    useradd -l -u <span class="si">${</span><span class="nv">PUID</span><span class="si">}</span> -g laradock -m laradock -G docker_env <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    usermod -p <span class="s2">&#34;*&#34;</span> laradock -s /bin/bash<span class="err">
</span></span></span></code></pre></div><p>这段配置创建了一个非 root 用户 <code>laradock</code>，解决了容器和宿主机之间文件权限问题：</p>
<ul>
<li><strong>可配置的 UID/GID</strong>：通过 ARG 和 ENV 指令使用户 ID 和组 ID 可配置</li>
<li><strong>权限一致性</strong>：将容器内用户的 UID/GID 与宿主机用户匹配，确保文件权限一致</li>
<li><strong>改善开发体验</strong>：设置 bash 作为默认 shell，并且不需要密码</li>
</ul>
<p>实际使用中，可以通过 <code>.env</code> 文件配置这些值：</p>
<pre tabindex="0"><code class="language-dotenv" data-lang="dotenv">PUID=1000
PGID=1000
</code></pre><p>通过 <code>id -u</code> 和 <code>id -g</code> 命令可以获取当前用户的 UID 和 GID。</p>
<h3 id="php-扩展安装示例">PHP 扩展安装示例</h3>
<h4 id="swoole-安装">Swoole 安装</h4>
<p><a href="https://wiki.swoole.com/">Swoole</a> 是一个高性能的 PHP 异步网络通信引擎。安装 Swoole 时，可以使用 PECL：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="c"># php7.1</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;&#39;</span> <span class="p">|</span> pecl -q install --configureoptions <span class="s1">&#39;enable-openssl=&#34;yes&#34; enable-mysqlnd=&#34;yes&#34; enable-swoole-curl=&#34;yes&#34;&#39;</span> swoole-4.5.11<span class="p">;</span><span class="err">
</span></span></span></code></pre></div><p>这个命令通过 PECL 安装 Swoole 4.5.11，并启用了 OpenSSL、MySQL Native Driver 和 cURL 支持。安装后可以通过以下命令验证 OpenSSL 支持：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php -r <span class="s2">&#34;new swoole_server(&#39;0.0.0.0&#39;, 443, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);&#34;</span>
</span></span></code></pre></div><p>如果没有错误输出，说明 OpenSSL 支持已正确启用。</p>
<h4 id="php-版本智能检测">PHP 版本智能检测</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">RUN</span> <span class="k">if</span> <span class="o">[</span> <span class="k">$(</span>php -r <span class="s2">&#34;echo PHP_MAJOR_VERSION;&#34;</span><span class="k">)</span> <span class="o">=</span> <span class="s2">&#34;8&#34;</span> <span class="o">]</span> <span class="o">||</span> <span class="o">{</span> <span class="o">[</span> <span class="k">$(</span>php -r <span class="s2">&#34;echo PHP_MAJOR_VERSION;&#34;</span><span class="k">)</span> <span class="o">=</span> <span class="s2">&#34;7&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">{</span> <span class="o">[</span> <span class="k">$(</span>php -r <span class="s2">&#34;echo PHP_MINOR_VERSION;&#34;</span><span class="k">)</span> <span class="o">=</span> <span class="s2">&#34;4&#34;</span> <span class="o">]</span> <span class="o">||</span> <span class="o">[</span> <span class="k">$(</span>php -r <span class="s2">&#34;echo PHP_MINOR_VERSION;&#34;</span><span class="k">)</span> <span class="o">=</span> <span class="s2">&#34;3&#34;</span> <span class="o">]</span> <span class="p">;</span><span class="o">}</span> <span class="p">;</span><span class="o">}</span><span class="p">;</span> <span class="k">then</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="c1"># PHP 8.x 或 PHP 7.3/7.4 的配置</span><span class="err">
</span></span></span><span class="line"><span class="cl">    pecl install xdebug-3.1.6<span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="k">else</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="k">$(</span>php -r <span class="s2">&#34;echo PHP_MAJOR_VERSION;&#34;</span><span class="k">)</span> <span class="o">=</span> <span class="s2">&#34;5&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        <span class="c1"># PHP 5.x 的配置</span><span class="err">
</span></span></span><span class="line"><span class="cl">        pecl install xdebug-2.5.5<span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        <span class="c1"># 其他 PHP 7.x 版本的配置</span><span class="err">
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">[</span> <span class="k">$(</span>php -r <span class="s2">&#34;echo PHP_MINOR_VERSION;&#34;</span><span class="k">)</span> <span class="o">=</span> <span class="s2">&#34;0&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">            pecl install xdebug-2.9.0<span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">            pecl install xdebug-2.9.8<span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        <span class="k">fi</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="k">fi</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="k">fi</span><span class="err">
</span></span></span></code></pre></div><p>Laradock 的 Dockerfile 中大量使用 PHP 版本检测，确保安装与当前 PHP 版本兼容的扩展和工具：</p>
<ol>
<li>使用 <code>php -r</code> 内联执行 PHP 命令获取版本信息</li>
<li>通过嵌套条件语句处理不同的 PHP 版本需求</li>
<li>支持多种 PHP 版本（从 5.x 到 8.x）使用同一个 Dockerfile</li>
</ol>
<p>这种方法使得一个 Dockerfile 可以适应多个 PHP 版本，极大简化了维护工作。</p>
<h4 id="composer-双版本安装">Composer 双版本安装</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="c"># 更新 composer</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">COMPOSER_VERSION</span><span class="o">=</span><span class="m">2</span>
</span></span><span class="line"><span class="cl"><span class="k">ENV</span> COMPOSER_VERSION <span class="si">${</span><span class="nv">COMPOSER_VERSION</span><span class="si">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> <span class="nb">set</span> -eux<span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$COMPOSER_VERSION</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;1&#34;</span> <span class="o">]</span> <span class="o">||</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$COMPOSER_VERSION</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;2&#34;</span> <span class="o">]</span> <span class="o">||</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$COMPOSER_VERSION</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;2.2&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        composer self-update --<span class="si">${</span><span class="nv">COMPOSER_VERSION</span><span class="si">}</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        composer self-update <span class="si">${</span><span class="nv">COMPOSER_VERSION</span><span class="si">}</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="k">fi</span><span class="err">
</span></span></span></code></pre></div><p>Laradock 支持灵活选择 Composer 版本：</p>
<ul>
<li>允许通过环境变量切换 Composer 1.x、2.x 或特定版本</li>
<li>适应不同项目的兼容性需求</li>
<li>方便在旧项目和新项目之间切换</li>
</ul>
<p>实用场景：某些旧项目可能与 Composer 2.x 不兼容，而新项目需要更新的 Composer 功能。</p>
<h2 id="docker-composeyml-配置技巧">docker-compose.yml 配置技巧</h2>
<h3 id="主机名解析">主机名解析</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">extra_hosts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="s2">&#34;dockerhost:${DOCKER_HOST_IP}&#34;</span><span class="w">
</span></span></span></code></pre></div><p>这个配置会在容器的 <code>/etc/hosts</code> 文件中添加一条记录：</p>
<pre tabindex="0"><code>10.0.75.1 dockerhost
</code></pre><p>这样，容器内的应用可以通过 <code>dockerhost</code> 域名访问宿主机，无需使用动态 IP 地址。</p>
<h3 id="模块化服务架构">模块化服务架构</h3>
<p>Laradock 的 docker-compose.yml 文件采用高度模块化的设计：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">workspace</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">./workspace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">args</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">PHP_VERSION=${PHP_VERSION}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">frontend</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">backend</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">php-fpm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">./php-fpm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">args</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">PHP_VERSION=${PHP_VERSION}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">depends_on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">workspace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">backend</span><span class="w">
</span></span></span></code></pre></div><p>主要特点：</p>
<ul>
<li><strong>每个服务独立配置</strong>：可以只启动需要的服务</li>
<li><strong>明确的依赖关系</strong>：使用 <code>depends_on</code> 确保正确的启动顺序</li>
<li><strong>统一的环境变量</strong>：从 <code>.env</code> 文件加载配置</li>
</ul>
<h3 id="网络配置">网络配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">networks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">frontend</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l">bridge</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">backend</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l">bridge</span><span class="w">
</span></span></span></code></pre></div><p>Laradock 采用前后端分离的网络设计：</p>
<ul>
<li><strong>前端网络</strong>：连接 Web 服务器和负载均衡器</li>
<li><strong>后端网络</strong>：连接应用服务（PHP-FPM）和数据库</li>
<li><strong>安全隔离</strong>：数据库等服务不直接暴露给前端</li>
</ul>
<p>这种设计增强了安全性，防止外部直接访问敏感服务。</p>
<h3 id="卷管理策略">卷管理策略</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">mysql</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l">${VOLUMES_DRIVER}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">redis</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l">${VOLUMES_DRIVER}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">postgres</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l">${VOLUMES_DRIVER}</span><span class="w">
</span></span></span></code></pre></div><p>Laradock 使用命名卷而非绑定挂载来提高性能和可移植性：</p>
<ul>
<li><strong>集中式数据路径</strong>：使用 <code>${DATA_PATH_HOST}</code> 管理所有持久化数据</li>
<li><strong>可配置驱动</strong>：支持本地存储或网络存储</li>
<li><strong>性能优化</strong>：命名卷在某些平台（特别是 Windows 和 macOS）上性能更好</li>
</ul>
<p>实际使用示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 查看卷列表</span>
</span></span><span class="line"><span class="cl">docker volume ls
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 备份数据卷</span>
</span></span><span class="line"><span class="cl">docker run --rm -v laradock_mysql:/source -v <span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>:/backup alpine tar -czvf /backup/mysql_backup.tar.gz -C /source .
</span></span></code></pre></div><h3 id="资源限制配置">资源限制配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">php-fpm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ulimits</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">nproc</span><span class="p">:</span><span class="w"> </span><span class="m">65535</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">nofile</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">soft</span><span class="p">:</span><span class="w"> </span><span class="m">20000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">hard</span><span class="p">:</span><span class="w"> </span><span class="m">40000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">mem_limit</span><span class="p">:</span><span class="w"> </span><span class="l">1024m</span><span class="w">
</span></span></span></code></pre></div><p>Laradock 支持为服务配置资源限制：</p>
<ul>
<li><strong>进程数限制</strong>：控制可创建的进程数</li>
<li><strong>文件描述符限制</strong>：影响并发连接能力</li>
<li><strong>内存限制</strong>：防止单个容器消耗过多资源</li>
</ul>
<p>这些设置对于生产环境特别重要，可以防止单个服务耗尽系统资源。</p>
<h3 id="端口映射策略">端口映射策略</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">mysql</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="s2">&#34;${MYSQL_PORT}:3306&#34;</span><span class="w">
</span></span></span></code></pre></div><p>Laradock 采用可配置的端口映射策略：</p>
<ul>
<li><strong>所有端口都可配置</strong>：通过环境变量定义外部端口</li>
<li><strong>避免默认暴露端口</strong>：增强安全性</li>
<li><strong>灵活处理端口冲突</strong>：可以在不同项目使用不同端口</li>
</ul>
<p>端口冲突是多项目开发中常见的问题，这种设计可以轻松避免。</p>
<h2 id="php-扩展管理">PHP 扩展管理</h2>
<h3 id="常用开发扩展">常用开发扩展</h3>
<h4 id="xdebug">Xdebug</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">INSTALL_XDEBUG</span><span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">RUN</span> <span class="k">if</span> <span class="o">[</span> <span class="si">${</span><span class="nv">INSTALL_XDEBUG</span><span class="si">}</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="c1"># 安装 Xdebug 并配置</span><span class="err">
</span></span></span><span class="line"><span class="cl">  pecl install xdebug <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  docker-php-ext-enable xdebug <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="k">fi</span><span class="err">
</span></span></span></code></pre></div><p><a href="https://xdebug.org/">Xdebug</a> 是 PHP 的调试和分析工具。在 Laradock 中，可以通过环境变量控制是否安装：</p>
<pre tabindex="0"><code class="language-dotenv" data-lang="dotenv">WORKSPACE_INSTALL_XDEBUG=true
PHP_FPM_INSTALL_XDEBUG=true
</code></pre><p>实际使用配置（php.ini）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[xdebug]</span>
</span></span><span class="line"><span class="cl"><span class="na">xdebug.mode</span><span class="o">=</span><span class="s">debug</span>
</span></span><span class="line"><span class="cl"><span class="na">xdebug.client_host</span><span class="o">=</span><span class="s">host.docker.internal</span>
</span></span><span class="line"><span class="cl"><span class="na">xdebug.client_port</span><span class="o">=</span><span class="s">9003</span>
</span></span><span class="line"><span class="cl"><span class="na">xdebug.start_with_request</span><span class="o">=</span><span class="s">yes</span>
</span></span></code></pre></div><h4 id="pcov">PCOV</h4>
<p><a href="https://github.com/krakjoe/pcov">PCOV</a> 是一个轻量级代码覆盖率驱动，比 Xdebug 更快：</p>
<ul>
<li>专注于代码覆盖率分析</li>
<li>与 PHPUnit 完全兼容</li>
<li>性能影响比 Xdebug 小</li>
</ul>
<p>在 Laravel 项目中使用示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php -dpcov.enabled<span class="o">=</span><span class="m">1</span> vendor/bin/phpunit --coverage-html coverage
</span></span></code></pre></div><h4 id="taint">Taint</h4>
<p><a href="https://github.com/laruence/taint">Taint</a> 是由 PHP 核心开发者鸟哥（惠新宸）开发的 PHP 扩展，用于检测 XSS 代码：</p>
<ul>
<li>自动检测可能的 XSS 漏洞</li>
<li>低运行时开销</li>
<li>适合在开发环境中使用</li>
</ul>
<h3 id="数据库相关扩展">数据库相关扩展</h3>
<h4 id="oci8">OCI8</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">INSTALL_OCI8</span><span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">ORACLE_INSTANT_CLIENT_MIRROR</span><span class="o">=</span>https://github.com/the-paulus/oracle-instantclient/raw/master/<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">ORACLE_INSTANT_CLIENT_ARCH</span><span class="o">=</span>x86_64
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">ORACLE_INSTANT_CLIENT_MAJOR</span><span class="o">=</span><span class="m">18</span>
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">ORACLE_INSTANT_CLIENT_MINOR</span><span class="o">=</span><span class="m">3</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">RUN</span> <span class="k">if</span> <span class="o">[</span> <span class="si">${</span><span class="nv">INSTALL_OCI8</span><span class="si">}</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="c1"># 安装 Oracle Instantclient 和 PHP OCI8 扩展</span><span class="err">
</span></span></span><span class="line"><span class="cl">    ...<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">fi</span><span class="err">
</span></span></span></code></pre></div><p><a href="https://www.php.net/manual/en/book.oci8.php">OCI8</a> 用于连接 Oracle 数据库，通过 Oracle Call Interface 提供完整的功能：</p>
<ul>
<li>支持 Oracle 的所有数据类型</li>
<li>处理大型对象（LOB）</li>
<li>支持绑定变量和存储过程</li>
</ul>
<p>示例代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$conn</span> <span class="o">=</span> <span class="nx">oci_connect</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="s1">&#39;localhost/XE&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$stmt</span> <span class="o">=</span> <span class="nx">oci_parse</span><span class="p">(</span><span class="nv">$conn</span><span class="p">,</span> <span class="s1">&#39;SELECT * FROM employees&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">oci_execute</span><span class="p">(</span><span class="nv">$stmt</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">(</span><span class="nv">$row</span> <span class="o">=</span> <span class="nx">oci_fetch_array</span><span class="p">(</span><span class="nv">$stmt</span><span class="p">,</span> <span class="nx">OCI_ASSOC</span><span class="o">+</span><span class="nx">OCI_RETURN_NULLS</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$row</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h4 id="redis">Redis</h4>
<p>Laradock 支持两种 Redis 扩展：</p>
<ol>
<li><strong>PhpRedis</strong>：C 扩展，性能更好</li>
<li><strong>Predis</strong>：纯 PHP 实现，更灵活</li>
</ol>
<p>PhpRedis 安装示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">INSTALL_PHPREDIS</span><span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">RUN</span> <span class="k">if</span> <span class="o">[</span> <span class="si">${</span><span class="nv">INSTALL_PHPREDIS</span><span class="si">}</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    pecl install redis <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    docker-php-ext-enable redis <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="k">fi</span><span class="err">
</span></span></span></code></pre></div><h3 id="性能优化扩展">性能优化扩展</h3>
<h4 id="opcache">OPcache</h4>
<p>OPcache 通过将 PHP 脚本预编译的字节码存储在共享内存中来提高性能，减少加载和解析的开销：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[opcache]</span>
</span></span><span class="line"><span class="cl"><span class="na">opcache.enable</span><span class="o">=</span><span class="s">1</span>
</span></span><span class="line"><span class="cl"><span class="na">opcache.memory_consumption</span><span class="o">=</span><span class="s">128</span>
</span></span><span class="line"><span class="cl"><span class="na">opcache.interned_strings_buffer</span><span class="o">=</span><span class="s">8</span>
</span></span><span class="line"><span class="cl"><span class="na">opcache.max_accelerated_files</span><span class="o">=</span><span class="s">4000</span>
</span></span><span class="line"><span class="cl"><span class="na">opcache.validate_timestamps</span><span class="o">=</span><span class="s">0</span>
</span></span><span class="line"><span class="cl"><span class="na">opcache.save_comments</span><span class="o">=</span><span class="s">1</span>
</span></span><span class="line"><span class="cl"><span class="na">opcache.fast_shutdown</span><span class="o">=</span><span class="s">0</span>
</span></span></code></pre></div><p>在生产环境中，可以设置 <code>validate_timestamps=0</code> 来防止 OPcache 检查文件变化，进一步提高性能。</p>
<h4 id="apcu">APCu</h4>
<p>APCu 是一个用户数据缓存系统，可以在 PHP 进程中存储 key-value 数据：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">apcu_store</span><span class="p">(</span><span class="s1">&#39;my_key&#39;</span><span class="p">,</span> <span class="s1">&#39;my_value&#39;</span><span class="p">,</span> <span class="mi">3600</span><span class="p">);</span> <span class="c1">// 存储数据，过期时间为 1 小时
</span></span></span><span class="line"><span class="cl"><span class="nv">$value</span> <span class="o">=</span> <span class="nx">apcu_fetch</span><span class="p">(</span><span class="s1">&#39;my_key&#39;</span><span class="p">);</span> <span class="c1">// 读取数据
</span></span></span></code></pre></div><p>与 OPcache 不同，APCu 专注于用户数据缓存，而非代码缓存。</p>
<h2 id="实用工具与配置">实用工具与配置</h2>
<h3 id="软件源和镜像">软件源和镜像</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="c"># 将应用源从 deb.debian.org 更改为阿里云源</span><span class="err">
</span></span></span><span class="line"><span class="cl">sed -i <span class="s1">&#39;s/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/&#39;</span> /etc/apt/sources.list <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">sed -i <span class="s1">&#39;s/security.debian.org/mirrors.tuna.tsinghua.edu.cn/&#39;</span> /etc/apt/sources.list <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">sed -i <span class="s1">&#39;s/security-cdn.debian.org/mirrors.tuna.tsinghua.edu.cn/&#39;</span> /etc/apt/sources.list <span class="err">\
</span></span></span></code></pre></div><p>在国内环境使用 Laradock 时，配置国内镜像源可以大幅提升构建速度：</p>
<ol>
<li><strong>系统源</strong>：使用 TUNA 或阿里云源</li>
<li><strong>Composer</strong>：配置 Packagist 中国镜像</li>
<li><strong>NPM</strong>：配置淘宝 NPM 镜像</li>
</ol>
<p>可以在 <code>.env</code> 文件中设置：</p>
<pre tabindex="0"><code class="language-dotenv" data-lang="dotenv">WORKSPACE_NPM_REGISTRY=https://registry.npm.taobao.org/
WORKSPACE_COMPOSER_REPO_PACKAGIST=https://mirrors.aliyun.com/composer/
</code></pre><h3 id="nodejs-配置">Node.js 配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">INSTALL_NODE</span><span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">NODE_VERSION</span><span class="o">=</span>node
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">INSTALL_NPM_GULP</span><span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">INSTALL_NPM_BOWER</span><span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">INSTALL_NPM_VUE_CLI</span><span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">INSTALL_NPM_ANGULAR_CLI</span><span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> NPM_REGISTRY<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> <span class="k">if</span> <span class="o">[</span> <span class="si">${</span><span class="nv">INSTALL_NODE</span><span class="si">}</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="c1"># 安装 nvm 和 Node.js</span><span class="err">
</span></span></span><span class="line"><span class="cl">    ...<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">fi</span><span class="err">
</span></span></span></code></pre></div><p>Laradock 的 Node.js 配置非常灵活：</p>
<ul>
<li>使用 NVM 管理 Node.js 版本</li>
<li>支持配置 NPM 镜像源</li>
<li>可选安装常用前端工具（Vue CLI、Angular CLI 等）</li>
<li>支持 Yarn 和 PNPM 等现代包管理器</li>
</ul>
<p>使用案例：</p>
<pre tabindex="0"><code class="language-dotenv" data-lang="dotenv">WORKSPACE_INSTALL_NODE=true
WORKSPACE_NODE_VERSION=16
WORKSPACE_NPM_REGISTRY=https://registry.npm.taobao.org/
WORKSPACE_INSTALL_NPM_VUE_CLI=true
</code></pre><h3 id="常用命令工具">常用命令工具</h3>
<h4 id="update-alternatives">update-alternatives</h4>
<p><code>update-alternatives</code> 用于管理 Linux 系统中的命令替代版本：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 设置默认 PHP 版本</span>
</span></span><span class="line"><span class="cl">update-alternatives --set php /usr/bin/php7.4
</span></span></code></pre></div><p>Laradock 使用这个命令管理多个 PHP 版本。</p>
<h4 id="docker-php-ext-disable">docker-php-ext-disable</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 禁用不需要的扩展（不需要重新编译）</span>
</span></span><span class="line"><span class="cl">docker-php-ext-disable xdebug pcov
</span></span></code></pre></div><p>Laradock 特有的实用脚本，允许：</p>
<ul>
<li>在生产环境中禁用性能密集型扩展</li>
<li>保留扩展文件但不加载，避免重新编译</li>
<li>需要时快速重新启用扩展</li>
</ul>
<p>这对于开发/生产环境切换非常有用，可以保持相同的镜像但有不同的扩展启用状态。</p>
<h2 id="常用操作指南">常用操作指南</h2>
<h3 id="基本使用流程">基本使用流程</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 克隆 Laradock 仓库</span>
</span></span><span class="line"><span class="cl">git clone https://github.com/laradock/laradock.git
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 复制环境配置文件</span>
</span></span><span class="line"><span class="cl">cp .env.example .env
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 编辑配置文件，设置项目路径等</span>
</span></span><span class="line"><span class="cl">vim .env
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 构建并启动容器</span>
</span></span><span class="line"><span class="cl">docker compose up -d nginx mysql redis
</span></span></code></pre></div><h3 id="常用维护命令">常用维护命令</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 构建特定服务</span>
</span></span><span class="line"><span class="cl">docker compose build workspace php-fpm
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 进入容器</span>
</span></span><span class="line"><span class="cl">docker compose <span class="nb">exec</span> workspace bash
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看日志</span>
</span></span><span class="line"><span class="cl">docker compose logs nginx
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 停止所有容器</span>
</span></span><span class="line"><span class="cl">docker compose down
</span></span></code></pre></div><h3 id="故障排除">故障排除</h3>
<ol>
<li>
<p><strong>端口冲突</strong>：修改 <code>.env</code> 文件中的端口映射</p>
<pre tabindex="0"><code>NGINX_HOST_HTTP_PORT=8080
MYSQL_PORT=33060
</code></pre></li>
<li>
<p><strong>权限问题</strong>：确保 PUID/PGID 设置正确</p>
<pre tabindex="0"><code>PUID=$(id -u)
PGID=$(id -g)
</code></pre></li>
<li>
<p><strong>内存不足</strong>：增加 Docker 分配的资源，或减少启用的服务</p>
<pre tabindex="0"><code>docker compose up -d nginx php-fpm mysql
</code></pre></li>
</ol>
<h2 id="高级功能">高级功能</h2>
<h3 id="多项目环境">多项目环境</h3>
<p>Laradock 支持同时运行多个项目：</p>
<pre tabindex="0"><code>.
├── laradock/
│   ├── docker-compose.yml
│   └── .env
├── project1/
├── project2/
└── project3/
</code></pre><p>配置示例：</p>
<pre tabindex="0"><code class="language-dotenv" data-lang="dotenv">APP_CODE_PATH_HOST=../:/var/www
NGINX_SITES_PATH=./nginx/sites/
</code></pre><p>然后为每个项目创建 Nginx 配置文件：</p>
<pre tabindex="0"><code># ./nginx/sites/project1.conf
server {
    server_name project1.test;
    root /var/www/project1/public;
    # ...
}
</code></pre><h3 id="多架构支持">多架构支持</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="c"># 支持 ARM 和 x86 架构</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">--platform=${TARGETPLATFORM</span><span class="o">}</span> php:<span class="si">${</span><span class="nv">PHP_VERSION</span><span class="si">}</span>-fpm-alpine<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ARG</span> TARGETPLATFORM<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">ARG</span> BUILDPLATFORM<span class="err">
</span></span></span></code></pre></div><p>Laradock 现代版本支持：</p>
<ul>
<li>使用 Docker BuildKit 进行多架构构建</li>
<li>在 ARM 设备（M1/M2 Mac、树莓派）上本地开发</li>
<li>通过 GitHub Actions 实现跨平台自动构建</li>
</ul>
<p>使用多架构镜像的命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 显示支持的架构</span>
</span></span><span class="line"><span class="cl">docker manifest inspect laradock/workspace:latest
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 构建多架构镜像</span>
</span></span><span class="line"><span class="cl">docker buildx build --platform linux/amd64,linux/arm64 -t myname/myapp:latest .
</span></span></code></pre></div><h3 id="cicd-集成">CI/CD 集成</h3>
<p>Laradock 可以与各种 CI/CD 工具集成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># .gitlab-ci.yml 示例</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">stages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">deploy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">cd laradock</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">docker compose build php-fpm workspace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">cd laradock</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">docker compose up -d php-fpm mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">docker compose exec workspace php artisan test</span><span class="w">
</span></span></span></code></pre></div><p>可以使用 Docker Compose 的 <code>--profile</code> 参数为不同环境创建配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 开发环境</span>
</span></span><span class="line"><span class="cl">docker compose --profile dev up -d
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 测试环境</span>
</span></span><span class="line"><span class="cl">docker compose --profile <span class="nb">test</span> up -d
</span></span></code></pre></div><h2 id="最佳实践">最佳实践</h2>
<h3 id="优化构建速度">优化构建速度</h3>
<ol>
<li>
<p><strong>使用 BuildKit</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">DOCKER_BUILDKIT</span><span class="o">=</span><span class="m">1</span> docker compose build
</span></span></code></pre></div></li>
<li>
<p><strong>创建层缓存</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="c"># 先复制依赖文件，安装依赖，然后再复制应用代码</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">COPY</span> composer.json composer.lock ./<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> composer install --no-scripts --no-autoloader<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">COPY</span> . .<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> composer dump-autoload --optimize<span class="err">
</span></span></span></code></pre></div></li>
<li>
<p><strong>多阶段构建</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">php:7.4-fpm</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">builder</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># 安装构建依赖...</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">php:7.4-fpm</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># 只复制需要的文件</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">COPY</span> --from<span class="o">=</span>builder /var/www/vendor /var/www/vendor<span class="err">
</span></span></span></code></pre></div></li>
</ol>
<h3 id="安全最佳实践">安全最佳实践</h3>
<ol>
<li>
<p><strong>避免使用 root 用户</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">USER</span><span class="w"> </span><span class="s">laradock</span><span class="err">
</span></span></span></code></pre></div></li>
<li>
<p><strong>限制容器能力</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">security_opt</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="kc">no</span>-<span class="l">new-privileges:true</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p><strong>使用健康检查</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">healthcheck</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;CMD&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;php&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;-v&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">interval</span><span class="p">:</span><span class="w"> </span><span class="l">30s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">timeout</span><span class="p">:</span><span class="w"> </span><span class="l">10s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">retries</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p><strong>定期更新基础镜像</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose pull
</span></span><span class="line"><span class="cl">docker compose build --pull
</span></span></code></pre></div></li>
</ol>
<h2 id="references">References</h2>
<ul>
<li><a href="https://laradock.io/documentation/">Laradock 官方文档</a></li>
<li><a href="https://github.com/laradock/laradock">Laradock GitHub 仓库</a></li>
<li><a href="https://docs.docker.com/">Docker 官方文档</a></li>
<li><a href="https://www.php.net/manual/zh/">PHP 官方文档</a></li>
<li><a href="https://dev.to/dendihandian/laradock-a-php-developer-s-best-friend-33ef">Laradock - A PHP Developer&rsquo;s best friend. | dev.to</a></li>
<li><a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/">如何优化 Docker 镜像构建</a></li>
<li><a href="https://docs.docker.com/compose/reference/">Docker Compose 参考</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Git Code Snippet</title>
      <link>https://zyf.im/2022/10/01/git-code-snippet/</link>
      <pubDate>Sat, 01 Oct 2022 10:00:00 +0000</pubDate>
      <guid>https://zyf.im/2022/10/01/git-code-snippet/</guid>
      <description>&lt;h2 id=&#34;建立独立的分支&#34;&gt;建立独立的分支&lt;/h2&gt;
&lt;p&gt;当想在项目中使用一个独立分支进行项目文档的管理时，或者当我们想要发布一个软件的开源版本但又不希望将软件的版本历史暴露给外界时，都可以使用以下的方法建立一个独立分支：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt; checkout
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# git checkout --orphan &amp;lt;new-branch&amp;gt; &amp;lt;start-point&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;修改提交时间&#34;&gt;修改提交时间&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 指定本次提交时间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;fix...&amp;#34;&lt;/span&gt; --date&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;date -R&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;fix...&amp;#34;&lt;/span&gt; --date&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date -R&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;fix...&amp;#34;&lt;/span&gt; --date&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Tue, 11 Jun 2019 17:50:50 +0800&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 修改上次提交时间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit --amend --date&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;date -R&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 风险操作。会修改 history hash.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;忽略文件权限修改&#34;&gt;忽略文件权限修改&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Git 默认会记录文件权限的修改，可关闭&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config core.filemode &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;git-status-中文文件名乱码&#34;&gt;git status 中文文件名乱码&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global core.quotepath &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当 &lt;code&gt;core.quotepath&lt;/code&gt; 设置为 &lt;code&gt;false&lt;/code&gt; 时，Git 将不会对路径名进行 quoting，这意味着路径名中的特殊字符将不被转义。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;http://xstarcd.github.io/wiki/shell/git_chinese.html&#34;&gt;Git 中文显示问题解决&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;工作区中删除未跟踪的文件&#34;&gt;工作区中删除未跟踪的文件&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看文档&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt; clean
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clean -h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# git clean [-d] [-f] [-i] [-n] [-q] [-e &amp;lt;pattern&amp;gt;] [-x | -X] [--] [&amp;lt;pathspec&amp;gt;...]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 【查看】有哪些文件将被删除&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clean -n
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 【查看】删除 Git【忽略】的文件与文件夹&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clean -Xn
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 【查看】删除【src 路径下】下的【未跟踪】文件以及文件夹&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clean -d -- src
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 如果想执行删除 -n 替换为 -f，注意风险。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;http://glgjing.github.io/blog/2015/01/09/git-qing-chu-wei-gen-zong-wen-jian/&#34;&gt;Git 清除未跟踪文件 | github.io&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="建立独立的分支">建立独立的分支</h2>
<p>当想在项目中使用一个独立分支进行项目文档的管理时，或者当我们想要发布一个软件的开源版本但又不希望将软件的版本历史暴露给外界时，都可以使用以下的方法建立一个独立分支：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git <span class="nb">help</span> checkout
</span></span><span class="line"><span class="cl"><span class="c1"># git checkout --orphan &lt;new-branch&gt; &lt;start-point&gt;</span>
</span></span></code></pre></div><h2 id="修改提交时间">修改提交时间</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 指定本次提交时间</span>
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;fix...&#34;</span> --date<span class="o">=</span><span class="sb">`</span>date -R<span class="sb">`</span>
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;fix...&#34;</span> --date<span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>date -R<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;fix...&#34;</span> --date<span class="o">=</span><span class="s2">&#34;Tue, 11 Jun 2019 17:50:50 +0800&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 修改上次提交时间</span>
</span></span><span class="line"><span class="cl">git commit --amend --date<span class="o">=</span><span class="sb">`</span>date -R<span class="sb">`</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 风险操作。会修改 history hash.</span>
</span></span></code></pre></div><h2 id="忽略文件权限修改">忽略文件权限修改</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Git 默认会记录文件权限的修改，可关闭</span>
</span></span><span class="line"><span class="cl">git config core.filemode <span class="nb">false</span>
</span></span></code></pre></div><h2 id="git-status-中文文件名乱码">git status 中文文件名乱码</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global core.quotepath <span class="nb">false</span>
</span></span></code></pre></div><p>当 <code>core.quotepath</code> 设置为 <code>false</code> 时，Git 将不会对路径名进行 quoting，这意味着路径名中的特殊字符将不被转义。</p>
<blockquote>
<p><a href="http://xstarcd.github.io/wiki/shell/git_chinese.html">Git 中文显示问题解决</a></p>
</blockquote>
<h2 id="工作区中删除未跟踪的文件">工作区中删除未跟踪的文件</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 查看文档</span>
</span></span><span class="line"><span class="cl">git <span class="nb">help</span> clean
</span></span><span class="line"><span class="cl">git clean -h
</span></span><span class="line"><span class="cl"><span class="c1"># git clean [-d] [-f] [-i] [-n] [-q] [-e &lt;pattern&gt;] [-x | -X] [--] [&lt;pathspec&gt;...]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 【查看】有哪些文件将被删除</span>
</span></span><span class="line"><span class="cl">git clean -n
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 【查看】删除 Git【忽略】的文件与文件夹</span>
</span></span><span class="line"><span class="cl">git clean -Xn
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 【查看】删除【src 路径下】下的【未跟踪】文件以及文件夹</span>
</span></span><span class="line"><span class="cl">git clean -d -- src
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 如果想执行删除 -n 替换为 -f，注意风险。</span>
</span></span></code></pre></div><blockquote>
<p><a href="http://glgjing.github.io/blog/2015/01/09/git-qing-chu-wei-gen-zong-wen-jian/">Git 清除未跟踪文件 | github.io</a></p>
</blockquote>
<h2 id="rebase">rebase</h2>
<ul>
<li><a href="https://git-rebase.io/">git rebase in depth</a></li>
</ul>
<h2 id="gitflow-workflow">Gitflow workflow</h2>
<ul>
<li><a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow">Gitflow workflow</a></li>
<li><a href="https://nvie.com/posts/a-successful-git-branching-model/">A successful Git branching model</a></li>
</ul>
<h2 id="相关站点">相关站点</h2>
<ul>
<li><a href="https://www.liaoxuefeng.com/wiki/896043488029600">Git教程 | 廖雪峰的官方网站</a></li>
<li><a href="https://rogerdudler.github.io/git-guide/index.zh.html">git - 简明指南</a></li>
<li><a href="https://semver.org/lang/zh-CN/">语义化版本 2.0.0 | semver.org</a></li>
<li><a href="https://jwiegley.github.io/git-from-the-bottom-up/">Git from the Bottom Up</a></li>
<li><a href="https://www.git-tower.com/learn/">Tower&rsquo;s Learning Resources</a></li>
<li><a href="https://nvie.com/posts/a-successful-git-branching-model/">A successful Git branching model</a></li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="http://graphicalgit.blogspot.com/2012/07/git-cola-git-gui-1.html">圖解 Git 版本控制: Cola Git GUI (1)</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>MySQL 实战 45 讲 Part1</title>
      <link>https://zyf.im/2022/08/14/mysql-course-45-part1/</link>
      <pubDate>Sun, 14 Aug 2022 15:53:16 +0000</pubDate>
      <guid>https://zyf.im/2022/08/14/mysql-course-45-part1/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;http://gk.link/a/11AmM&#34;&gt;MySQL 实战 45 讲 | 林晓斌&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;01--基础架构一条-sql-查询语句是如何执行的&#34;&gt;01 | 基础架构：一条 SQL 查询语句是如何执行的？&lt;/h2&gt;
&lt;img alt=&#34;image&#34; src=&#34;https://user-images.githubusercontent.com/9289792/184530380-826038a0-335a-45f7-a56e-cff7b336b4a4.png&#34; style=&#34;width: 480px&#34; /&gt;
&lt;p&gt;连接器：跟客户端建立连接、获取权限、维持和管理连接。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SHOW&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PROCESSLIST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;137462239&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;135&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;41261&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Query&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;starting&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;show&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;processlist&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 一个空闲连接
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;137462293&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;130&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;128&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;47174&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Sleep&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 客户端如果太长时间没动静，连接器就会自动将它断开
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wait_timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;show&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;variables&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;like&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;wait_timeout&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 28800 8h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过执行 &lt;code&gt;mysql_reset_connection&lt;/code&gt; 来重新初始化连接资源，这是个接口函数，不是一个 SQL 语句。&lt;/p&gt;
&lt;p&gt;查询缓存：不建议使用。在一个表上有更新的时候，跟这个表有关的查询缓存都会失效。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;query_cache_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- OFF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_query_cache_type&#34;&gt;sysvar_query_cache_type | mysql&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;分析器：词法分析、语法分析。进行分词和验证语法规则。解析器和预处理器。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解析器处理语法和解析查询，生成一课对应的解析树。&lt;/li&gt;
&lt;li&gt;预处理器进一步检查解析树的合法。比如: 数据表和数据列是否存在，别名是否有歧义等。如果通过则生成新的解析树，再提交给优化器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优化器：决定使用哪个索引，决定各个表的连接顺序。&lt;/p&gt;
&lt;p&gt;执行器：有没有执行查询的权限，操作引擎，返回结果。执行器调用的次数（rows_examined）与引擎总共扫描行数可能是不等的，后文有例子。&lt;/p&gt;
&lt;p&gt;读写、存取数据在 engine 引擎层，连接、鉴权、计算在 server 服务层。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><a href="http://gk.link/a/11AmM">MySQL 实战 45 讲 | 林晓斌</a></p>
</blockquote>
<h2 id="01--基础架构一条-sql-查询语句是如何执行的">01 | 基础架构：一条 SQL 查询语句是如何执行的？</h2>
<img alt="image" src="https://user-images.githubusercontent.com/9289792/184530380-826038a0-335a-45f7-a56e-cff7b336b4a4.png" style="width: 480px" />
<p>连接器：跟客户端建立连接、获取权限、维持和管理连接。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">PROCESSLIST</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="mi">137462239</span><span class="w">  </span><span class="n">db</span><span class="w">  </span><span class="mi">10</span><span class="p">.</span><span class="mi">135</span><span class="p">.</span><span class="mi">4</span><span class="p">.</span><span class="mi">7</span><span class="p">:</span><span class="mi">41261</span><span class="w">  </span><span class="n">db</span><span class="w">  </span><span class="n">Query</span><span class="w">  </span><span class="mi">0</span><span class="w">  </span><span class="n">starting</span><span class="w">  </span><span class="k">show</span><span class="w"> </span><span class="n">processlist</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 一个空闲连接
</span></span></span><span class="line"><span class="cl"><span class="mi">137462293</span><span class="w">  </span><span class="n">db</span><span class="w">  </span><span class="mi">10</span><span class="p">.</span><span class="mi">130</span><span class="p">.</span><span class="mi">128</span><span class="p">.</span><span class="mi">1</span><span class="p">:</span><span class="mi">47174</span><span class="w">  </span><span class="n">db</span><span class="w">  </span><span class="n">Sleep</span><span class="w">  </span><span class="mi">24</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 客户端如果太长时间没动静，连接器就会自动将它断开
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">@@</span><span class="n">wait_timeout</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">show</span><span class="w"> </span><span class="n">variables</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="s1">&#39;wait_timeout&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 28800 8h
</span></span></span></code></pre></div><p>通过执行 <code>mysql_reset_connection</code> 来重新初始化连接资源，这是个接口函数，不是一个 SQL 语句。</p>
<p>查询缓存：不建议使用。在一个表上有更新的时候，跟这个表有关的查询缓存都会失效。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">@@</span><span class="n">query_cache_type</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- OFF
</span></span></span></code></pre></div><ul>
<li><a href="https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_query_cache_type">sysvar_query_cache_type | mysql</a></li>
</ul>
<p>分析器：词法分析、语法分析。进行分词和验证语法规则。解析器和预处理器。</p>
<ul>
<li>解析器处理语法和解析查询，生成一课对应的解析树。</li>
<li>预处理器进一步检查解析树的合法。比如: 数据表和数据列是否存在，别名是否有歧义等。如果通过则生成新的解析树，再提交给优化器。</li>
</ul>
<p>优化器：决定使用哪个索引，决定各个表的连接顺序。</p>
<p>执行器：有没有执行查询的权限，操作引擎，返回结果。执行器调用的次数（rows_examined）与引擎总共扫描行数可能是不等的，后文有例子。</p>
<p>读写、存取数据在 engine 引擎层，连接、鉴权、计算在 server 服务层。</p>
<p>连接的长短是由客户端来决定的，MySQL 服务端不会主动断开连接，除非到了 waiting_timeout 所设置的时间。</p>
<p>查询一个没用 k 列的表 <code>1054 - Unknown column 'k' in 'field list'</code> 是在哪阶段报错的？</p>
<ul>
<li>答案是：分析器。分析器在词法分析阶段，需要知道 SQL 中的每个字段代表什么意思，所以在这个阶段就可以判断表中存不存在 k 这一列。</li>
<li>不是执行器的原因：有人说在执行器时才打开表获取数据，但是表的字段不是数据，是事先定义好的，所以可以直接读取的，不需要打开表。</li>
</ul>
<h2 id="02--日志系统一条-sql-更新语句是如何执行的">02 | 日志系统：一条 SQL 更新语句是如何执行的？</h2>
<p>WAL 的全称是 Write-Ahead Logging，先写日志，再写磁盘。</p>
<ol>
<li>redo log 是 InnoDB 引擎特有的；binlog 是 MySQL 的 Server 层实现的，所有引擎都可以使用</li>
<li>redo log 是物理日志，记录的是“在某个数据页上做了什么修改”；binlog 是逻辑日志，记录的是这个语句的原始逻辑，比如“给 ID=2 这一行的 c 字段加 1”。</li>
<li>redo log 是循环写的，空间固定会用完；binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个，并不会覆盖以前的日志。</li>
</ol>
<ul>
<li>物理：InnoDB 存储引擎提供接口给执行器调用（操作数据），数据库的数据是存在磁盘上的（或者说硬盘上），那么 redo log 记录存储引擎修改硬盘上的数据的操作就叫做物理操作；物理日志就只有“我”自己能用，别人没有共享我的“物理格式”。</li>
<li>逻辑：binlog 归档日志，有两种模式 1 statement 记录 SQL；2 row 格式记录两条数据，数据修改前的样子，数据修改后的样子。记录的是一种逻辑上的变化 。逻辑日志可以给别的数据库，别的引擎使用，已经大家都讲得通这个“逻辑”。</li>
</ul>
<img alt="image" src="https://user-images.githubusercontent.com/9289792/184621469-ce7bea97-685a-421f-a582-0d571badc226.png" style="width: 480px" />
<p>把 binlog 夹在 redo log 中间，就是为了保证如果 redo 提交前的任何失败，都会带来回滚，binlog 的写入也应该不会成功，只有这样，才能保证两个一致。</p>
<ul>
<li>如果提交了 binlog，提交事务接口崩溃了，恢复时 redo log 有日志记录，binlog 有日志记录，一致，直接自动提交事务，事务完成确认数据修改成功。</li>
<li>如果提交 binlog 前就崩了，redo log 是 prepare 阶段，binlog 没有记录不一致，事务回滚，事务执行失败。</li>
</ul>
<p>redo log 负责事务、crash-safe；binlog 负责归档恢复。redo log 是物理的，binlog 是逻辑的。</p>
<p>redo log 是顺序写，数据文件是随机写。</p>
<p>MySQL 的记录是以“页”为单位存取的，默认大小 16K。也就是说，你要访问磁盘中一个记录，不会只读这个记录，而会把它所在的 16K 数据一起读入内存。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 每个 binlog 文件的大小
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">@@</span><span class="n">max_binlog_size</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">1024</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">1024</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 256.00000000
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 一页数据大小
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">@@</span><span class="n">innodb_page_size</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">1024</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 16.0000
</span></span></span></code></pre></div><p>redo log 和 binlog 都可以用于表示事务的提交状态，而两阶段提交就是让这两个状态保持逻辑上的一致。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 保证 crash-safe 能力；每次事务的 redo log 都直接持久化到磁盘
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">@@</span><span class="n">innodb_flush_log_at_trx_commit</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 2
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 每次事务的 binlog 都持久化到磁盘
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">@@</span><span class="n">sync_binlog</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 0
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">@@</span><span class="n">binlog_format</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- STATEMENT 是记 SQL 语句，但是有风险比如时间函数
</span></span></span><span class="line"><span class="cl"><span class="c1">-- ROW 记录行的内容，记两条，更新前和更新后都有
</span></span></span></code></pre></div><ul>
<li><a href="https://segmentfault.com/a/1190000042041728">彻底搞懂三大 MySQL 日志，Redo Log、Undo Log、Bin Log</a></li>
<li><a href="https://blog.51cto.com/u_3664660/3212550">彻底搞懂 mysql 日志系统 binlog,redolog,undolog</a></li>
</ul>
<h2 id="03--事务隔离为什么你改了我还看不见">03 | 事务隔离：为什么你改了我还看不见？</h2>
<p>脏读（dirty read）读到其他事务未提交的数据，仅发生在读未提交的的隔离级别下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- session1 当数据库中一个事务A正在修改一个数据但是还未提交或者回滚
</span></span></span><span class="line"><span class="cl"><span class="k">BEGIN</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1
</span></span></span><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">k</span><span class="o">=</span><span class="mi">2</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- session2
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="k">SESSION</span><span class="w"> </span><span class="k">TRANSACTION</span><span class="w"> </span><span class="k">ISOLATION</span><span class="w"> </span><span class="k">LEVEL</span><span class="w"> </span><span class="k">READ</span><span class="w"> </span><span class="k">UNCOMMITTED</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 2 session2 出现脏读
</span></span></span></code></pre></div><p>不可重复读（non-repeatable read）前后读取的记录内容不一致，发生在读未提交、读提交的隔离级别：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- session1 在一个事务A中多次操作数据，在事务操作过程中(未最终提交)
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="k">SESSION</span><span class="w"> </span><span class="k">TRANSACTION</span><span class="w"> </span><span class="k">ISOLATION</span><span class="w"> </span><span class="k">LEVEL</span><span class="w"> </span><span class="k">READ</span><span class="w"> </span><span class="k">COMMITTED</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">BEGIN</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- session2 修改改值
</span></span></span><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">k</span><span class="o">=</span><span class="mi">2</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- session1
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 2 session1 出现不可重复读
</span></span></span></code></pre></div><p>幻读（phantom read）前后读取的记录数量不一致，发生在读未提交、读提交的隔离级别，InnoDB RR 不发生幻读：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- session1 获取当前行数量
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="k">SESSION</span><span class="w"> </span><span class="k">TRANSACTION</span><span class="w"> </span><span class="k">ISOLATION</span><span class="w"> </span><span class="k">LEVEL</span><span class="w"> </span><span class="k">READ</span><span class="w"> </span><span class="k">COMMITTED</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">BEGIN</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">k</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- session2 插入一行数
</span></span></span><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">1</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- session1
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">count</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 2
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- session1 表数据
</span></span></span><span class="line"><span class="cl"><span class="k">BEGIN</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- id,k 1,1 只有一行数据
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- session2 更新 id 主键
</span></span></span><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">5</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- session1
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- id,k 1,1
</span></span></span><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="n">id</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- ok
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 竟然看到了两行 1,1 6,1
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1,1 MVCC 保证了 1,1 的存在
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 因为主键的 UPDATE 在 MySQL 里是以 insert+delete 方式执行的。这个 6 和 1 在 MySQL 看来已经不是同一行数据了，1 的 delete version 是在事务 1 的可见范围，所以才能看得到。如果是非主键就只用一行，就是下面的例子.
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 这个例子不能被归类为幻读，只不过是当前最新读带来的问题。
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- session1 获取 id=1 的 k
</span></span></span><span class="line"><span class="cl"><span class="k">BEGIN</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1 一行数据
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- session2 更新
</span></span></span><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">k</span><span class="o">=</span><span class="mi">2</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- session1
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1
</span></span></span><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">k</span><span class="o">=</span><span class="n">k</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- ok
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 3
</span></span></span></code></pre></div><ul>
<li><a href="https://www.zhihu.com/question/47007926">关于幻读，可重复读的真实用例是什么？| zhihu</a></li>
</ul>
<p>不可重复读和幻读区别：由于在 InnoDB RR 下模拟不出幻读的场景，退回到 RC 隔离级别的话就容易把幻读和不可重复读搞混淆。理论上 RR 级别是无法解决幻读的问题, 但是由于 InnoDB 引擎的 RR 级别还使用了 MVCC，所以也就避免了幻读的出现。</p>
<p>后文有讲到 FOR UPDATE 下的幻读。</p>
<p>事务隔离级别：</p>
<ul>
<li>读未提交（READ UNCOMMITTED）一个事务还没提交时，它做的变更就能被别的事务看到。直接返回记录上的最新值，没有视图概念。</li>
<li>读提交（READ COMMITTED）一个事务提交之后，它做的变更才会被其他事务看到。这个视图是在每个 SQL 语句开始执行的时候创建的。</li>
<li>可重复读（REPEATABLE READ）一个事务执行过程中看到的数据，总是跟这个事务在启动时看到的数据是一致的 read-view。当然在可重复读隔离级别下，未提交变更对其他事务也是不可见的。这个视图是在事务启动时（第一个 SQL 执行时）创建的，整个事务存在期间都用这个视图。</li>
<li>串行化（SERIALIZABLE）对于同一行记录，“写”会加“写锁”，“读”会加“读锁”。当出现读写锁冲突的时候，后访问的事务必须等前一个事务执行完成，才能继续执行。用加锁的方式来避免并行访问。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">show</span><span class="w"> </span><span class="n">variables</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="s1">&#39;%tx_isolation%&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">show</span><span class="w"> </span><span class="n">variables</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="s1">&#39;transaction_isolation&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- tx_isolation  REPEATABLE-READ
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">@@</span><span class="n">tx_isolation</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- REPEATABLE-READ
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="k">SESSION</span><span class="w"> </span><span class="k">TRANSACTION</span><span class="w"> </span><span class="k">ISOLATION</span><span class="w"> </span><span class="k">LEVEL</span><span class="w"> </span><span class="k">READ</span><span class="w"> </span><span class="k">UNCOMMITTED</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 修改 session 事务隔离级别
</span></span></span></code></pre></div><blockquote>
<p><a href="https://dev.mysql.com/doc/refman/8.0/en/set-transaction.html">SET TRANSACTION Statement | mysql</a></p>
</blockquote>
<p>多版本并发控制（MVCC）</p>
<p><code>set autocommit=0</code>，这个命令会将这个线程的自动提交关掉。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 持续时间超过 60s 的事务
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">information_schema</span><span class="p">.</span><span class="n">innodb_trx</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">TIME_TO_SEC</span><span class="p">(</span><span class="n">timediff</span><span class="p">(</span><span class="n">now</span><span class="p">(),</span><span class="n">trx_started</span><span class="p">))</span><span class="o">&gt;</span><span class="mi">60</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">commit</span><span class="w"> </span><span class="k">work</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="k">chain</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 等价于 commit; begin;
</span></span></span></code></pre></div><p>如何避免长事务对业务的影响？</p>
<ul>
<li>确认是否使用了 set autocommit=0，如果没有，则可以使用 set autocommit=1 来避免长事务对业务的影响。</li>
<li>确认是否有不必要的只读事务。有些框架会习惯不管什么语句先用 begin/commit 框起来。</li>
<li>业务连接数据库的时候，根据业务本身的预估，通过 SET MAX_EXECUTION_TIME 命令，来控制每个语句执行的最长时间，避免单个语句意外执行太长时间。</li>
<li>监控 information_schema.Innodb_trx 表，设置长事务阈值。</li>
<li>开发测试阶段输出所用 general_log，分析日志提前发现问题。</li>
</ul>
<h2 id="04--深入浅出索引上">04 | 深入浅出索引（上）</h2>
<ul>
<li>哈希表：这种结构适用于只有等值查询的场景。</li>
<li>有序数组：有序数组在等值查询和范围查询场景中的性能就都非常优秀。但有序数组索引只适用于静态存储引擎</li>
<li>搜索树：平衡二叉树是 O(log(N)) 的查询复杂度；N 叉树，以 InnoDB 整数字段索引为例，这个 N 差不多是 1200。</li>
</ul>
<blockquote>
<p>MySQL 默认一个节点的长度为 16K，一个整数（bigint）字段索引的长度为 8B，另外每个索引还跟着 6B 的指向其子树的指针；所以 16K/14B ≈ 1170</p>
</blockquote>
<p>innodb B+树主键索引的叶子节点存的是什么：</p>
<p>回复 1：</p>
<blockquote>
<p>InnoDB 磁盘管理的最小单位就是“页”，也就是说无论是叶子节点、非叶子节点和行数据，都是存放在页当中。页组成结构有头部数据、主体数据和尾部数据。头部数据主要存的是页相关数据，例如上一页、下一页、当前页号等。是一个双向链表结构。主体数据主要关注索引和数据的存储，也就是我们常说的索引和数据的存储位置。主体数据当中有一个“User Records”的概念，用来存储索引和数据，是一个单链表结构。
User Records 根据节点的不同，User Records 又分为四种不同类型：主键索引树叶子节点和非叶子节点，二级索引树叶子节点和非叶子节点。
有了页和 User Records 的认识，其实说叶子节点存的是页是一种笼统的回答，基于我的理解，我认为叶子节点（主键索引树叶子节点）存放的是行数据更为贴切。</p>
</blockquote>
<p>回复 2：</p>
<blockquote>
<p>B+树的叶子节点是 page（页），一个页里面可以存多个行。 B+树的结点跟 innoDB 的“页”都属于一种抽象逻辑概念。如果你要问“存”的是什么？我觉得回答行数据没毛病。因为存的不可能是“页”。这一逻辑概念，只能说这个叶结点大小等于 innoDB 里设置的页大小，或者说这个叶结点其实就是“页”。但存的是什么？那当然是数据，什么数据？当然是表中的行数据。</p>
</blockquote>
<p>索引类型分为主键索引和非主键索引。</p>
<ul>
<li>主键索引也被称为聚簇索引（clustered index）</li>
<li>非主键索引也被称为二级索引（secondary index）</li>
</ul>
<p>基于非主键索引的查询需要多扫描一棵索引树，也就是主键索引树，也就是回表操作。</p>
<p>自增主键的意义：</p>
<ul>
<li>性能方面：每次插入一条新记录，都是追加操作，都不涉及到挪动其他记录，也不会触发叶子节点的分裂（页分裂）。</li>
<li>存储空间方面：主键长度越小，普通索引的叶子节点就越小，普通索引占用的空间也就越小。</li>
</ul>
<p>有没有什么场景适合用业务字段直接做主键的呢？</p>
<ul>
<li>只有一个索引</li>
<li>该索引必须是唯一索引</li>
</ul>
<p>即典型的 KV 场景。</p>
<p>非聚集索引上为啥叶子节点的 value 为什么不是地址，这样可以直接定位到整条数据，而不用再次对整棵树进行查询？</p>
<p>这个叫作“堆组织表”，MyISAM 就是这样的，各有利弊。你想一下如果修改了数据的位置的情况，InnoDB 这种模式是不是就方便些。主键索引页分裂的场景，就可能会导致主键记录的地址发生变化，这时候需要更新每一个索引上面对主键记录地址的引用。</p>
<h2 id="05--深入浅出索引下">05 | 深入浅出索引（下）</h2>
<p>覆盖索引：在查询里索引已经覆盖了查询需要的列。覆盖索引可以减少树的搜索次数，显著提升查询性能，是一个常用的性能优化手段。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">T</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="k">between</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="mi">5</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>表中 k 的值是：1，2，3，5，6，7。引擎内部使用覆盖索引在索引 k 上其实读了三个记录（第一次 3，第二次 3 的下一个 5，第三次 5 的下一个 6 不满足 ），但是对于 MySQL 的 Server 层来说，它只是找引擎拿到了两条记录，因此 MySQL 认为扫描行数是 2。这就是引擎实际扫描条数不等于 MySQL explain 语句中的 rows 字段的原因。</p>
<p>最左前缀原则：B+树这种索引结构，可以利用索引的“最左前缀”，来定位记录。</p>
<p>第一原则是，如果通过调整顺序，可以少维护一个索引，那么这个顺序往往就是需要优先考虑采用的。</p>
<p>如果需要常查人的名字与年龄。可以建立索引 (name,age) 与 (age)，而不是 (age,name) 与 (age)，考虑的原则就是空间。</p>
<p>索引下推优化（index condition pushdown)：</p>
<p>联合索引（name,age）为例，名字第一个字是张，而且年龄是 10 岁的所有男孩：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">tuser</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="s1">&#39;张%&#39;</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="n">age</span><span class="o">=</span><span class="mi">10</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="n">ismale</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>这个语句在搜索索引树的时候，只能用 “张”。</p>
<ul>
<li>在 MySQL 5.6 之前，只能从符合 &lsquo;张%&rsquo; 开始一个个回表。到主键索引上找出数据行，再对比字段值。</li>
<li>而 MySQL 5.6 引入的索引下推优化（index condition pushdown)，可以在索引遍历过程中，对索引中包含的字段先做判断，直接过滤掉不满足条件的记录，减少回表次数。</li>
</ul>
<img alt="image" src="https://user-images.githubusercontent.com/9289792/185831784-b6f62f71-a0f7-49c8-afe4-dda0f2dc335a.png" style="width: 420px" />
<p>InnoDB 在（name,age）索引内部就判断了 age 是否等于 10，对于不等于 10 的记录，直接判断并跳过。</p>
<p>联合索引中是按索引列的顺序，来排序的。（name,age）先排 name 列的值，再排 age 列的值。</p>
<p>在满足语句需求的情况下，尽量少地访问资源是数据库设计的重要原则之一。</p>
<h2 id="06--全局锁和表锁给表加个字段怎么有这么多阻碍">06 | 全局锁和表锁：给表加个字段怎么有这么多阻碍？</h2>
<p>全局锁：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 加全局读锁 FTWRL，整个库处于只读状态；做全库逻辑备份
</span></span></span><span class="line"><span class="cl"><span class="n">FLUSH</span><span class="w"> </span><span class="n">TABLES</span><span class="w"> </span><span class="k">WITH</span><span class="w"> </span><span class="k">READ</span><span class="w"> </span><span class="k">LOCK</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 插入一行数据
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">PROCESSLIST</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- Waiting for global read lock
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 释放锁
</span></span></span><span class="line"><span class="cl"><span class="n">UNLOCK</span><span class="w"> </span><span class="n">TABLES</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>但是让整库都只读，听上去就很危险：</p>
<ul>
<li>如果你在主库上备份，那么在备份期间都不能执行更新，业务基本上就得停摆。</li>
<li>如果你在从库上备份，那么备份期间从库不能执行主库同步过来的 binlog，会导致主从延迟。</li>
</ul>
<p>在可重复读隔离级别下开启一个事务，来确保拿到一致性视图。mysqldump 使用参数 <code>-single-transaction</code> 就是如此执行的。而由于 MVCC 的支持，这个过程中数据是可以正常更新的。</p>
<p>有了可重复读的事务隔离级别却还需要 FTWRL 的原因是：引擎不都支持这个事务隔离级别。</p>
<p>既然要全库只读，为什么不使用 <code>set global readonly=true</code> 的方式</p>
<ul>
<li>在有些系统中，readonly 的值会被用来做其他逻辑，比如用来判断一个库是主库还是备库。修改 global 变量的方式影响面更大。</li>
<li>在异常处理机制上有差异。如果执行 FTWRL 命令之后由于客户端发生异常断开，那么 MySQL 会自动释放这个全局锁，整个库回到可以正常更新的状态。而将
整个库设置为 readonly 之后，如果客户端发生异常，则数据库就会一直保持 readonly 状态，这样会导致整个库长时间处于不可写状态，风险较高。</li>
</ul>
<p>表级锁：</p>
<p>第一类表锁：<a href="https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html">lock-tables | mysql</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">LOCK</span><span class="w"> </span><span class="n">TABLES</span><span class="w"> </span><span class="n">T1</span><span class="w"> </span><span class="k">READ</span><span class="p">,</span><span class="w"> </span><span class="n">T2</span><span class="w"> </span><span class="k">WRITE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 其他线程 写 T1、读写 T2 的语句都会被阻塞
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 当前线程也只能执行 读 T1、读写 T2 的操作，其他表都不能访问
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 当前线程读 T2
</span></span></span><span class="line"><span class="cl"><span class="c1">-- Waiting for table metadata lock
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 当前线程读 T3
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1100 - Table &#39;T3&#39; was not locked with LOCK TABLES
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 查看表锁
</span></span></span><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="k">OPEN</span><span class="w"> </span><span class="n">TABLES</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">In_use</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>第二类 MDL（metadata lock)：</p>
<p>MDL 不需要显式使用：</p>
<ul>
<li>当对一个表做增删改查操作的时候，加 MDL 读锁。读锁之间不互斥，因此你可以有多个线程同时对一张表增删改查。共享锁。</li>
<li>当要对表做结构变更操作的时候，加 MDL 写锁。读写锁之间、写锁之间是互斥的，用来保证变更表结构操作的安全性。如果有两个线程要同时给一个表加字段，其中一个要等另一个执行完才能开始执行。排他锁。</li>
</ul>
<p>给一个小表加个字段，导致整个库挂了：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- session1
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">autocommit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 关闭自动提交
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">BEGIN</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 开启使用
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 查询 T 表，一切 ok
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 获取 MDL 读锁 ok，因为在事务中，查询结束后没有释放读锁。
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 事务中的 MDL 锁，在语句执行开始时申请，但是语句结束后并不会马上释放，而会等到整个事务提交后再释放。
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">information_schema</span><span class="p">.</span><span class="n">innodb_trx</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 查询事务有个 RUNNING
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- session2
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 查询 T 表，一切 ok
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 获取 MDL 读锁 ok，查询结束后释放读锁。
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- session3
</span></span></span><span class="line"><span class="cl"><span class="k">ALTER</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="o">`</span><span class="n">T</span><span class="o">`</span><span class="w"> </span><span class="k">ADD</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="o">`</span><span class="n">c2</span><span class="o">`</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span><span class="w"> </span><span class="k">NULL</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 修改字段，卡住
</span></span></span><span class="line"><span class="cl"><span class="c1">-- blocked 原因：是因为 session1 的 MDL 读锁还没有释放，而 session3 需要 MDL 写锁，因此只能被阻塞。
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="k">OPEN</span><span class="w"> </span><span class="n">TABLES</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">In_use</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- In_use 1
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- session4
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">T</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 查询 T 表，卡住
</span></span></span><span class="line"><span class="cl"><span class="c1">-- blocked 原因：对表的增删改查操作都需要先申请 MDL 读锁，都被 session3 阻塞。
</span></span></span></code></pre></div><img alt="image" src="https://user-images.githubusercontent.com/9289792/185884836-f36feff9-1668-4f37-ad06-1fff9d9c18ef.png" style="width: 420px" />
<p>这里自己想了一个问题：session4 卡住后，session1 COMMIT 后，session4 的 SELECT 执行结果是否有 session3 的新列呢？</p>
<p>从结果上看是没有的。开始以为是事务级别，但是调整为 READ UNCOMMITTED，session4 仍然没有新列。后来了解到 online DDL 意识到应该有关系。测试后得出 session3 在获取写锁后，在做 DDL 前会释放写锁加读锁，这时 session4 就可以执行了，但是这时 DDL 还没有执行，所以 session4 的执行结果没有新列。</p>
<p>如果 session3 执行语句是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">ALTER</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="o">`</span><span class="n">T</span><span class="o">`</span><span class="w"> </span><span class="k">ADD</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="o">`</span><span class="n">c2</span><span class="o">`</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span><span class="w"> </span><span class="k">LOCK</span><span class="o">=</span><span class="k">EXCLUSIVE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- NONE：允许并发查询和DML操作；
</span></span></span><span class="line"><span class="cl"><span class="c1">-- SHARED：允许并发查询，但不允许DML操作；
</span></span></span><span class="line"><span class="cl"><span class="c1">-- DEFAULT：允许尽可能多的并发查询或DML操作(或两者都允许)，没指定LOCK选项默认就为DEFAULT；
</span></span></span><span class="line"><span class="cl"><span class="c1">-- EXCLUSIVE：不允许并发查询和DML操作。
</span></span></span></code></pre></div><p>EXCLUSIVE 时写锁会一直持有，一直等到 DDL 完毕 session4 才开始执行，所以 session4 的执行结果有新列。</p>
<p>衍生问题：session3 如果没有 <code>LOCK=EXCLUSIVE</code>，session4 查询前 <code>BEGIN;</code> 开启事务那结果是如何呢？实验结果：session1 提交后，session3 session4 依然阻塞。</p>
<p>衍生问题：session3 添加 <code>LOCK=EXCLUSIVE</code>，session4 查询前 <code>BEGIN;</code> 开启事务那结果是如何呢？实验结果：ssession1 提交后，session3 session4 正常结束。</p>
<p>分析猜测 session3 阻塞的原因是：session1 提交后释放了读锁，session3 session4 拿到读锁，session3 DDL 操作完成后又要拿写锁，但是 session4 的读锁未释放，所以 session3 阻塞。那 session4 为啥阻塞呢？session4 可以获取读锁就应该可以执行完毕，遗留问题。</p>
<p>如何安全地给小表加字段？</p>
<ul>
<li>解决长事务，事务不提交，就会一直占着 MDL 锁。</li>
<li>在 ALTER TABLE 语句里面设定等待时间，如果在这个指定的等待时间里面能够拿到 MDL 写锁最好，拿不到也不要阻塞后面的业务语句，先放弃。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">ALTER</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">tbl_name</span><span class="w"> </span><span class="n">NOWAIT</span><span class="w"> </span><span class="k">add</span><span class="w"> </span><span class="k">column</span><span class="w"> </span><span class="p">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">ALTER</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">tbl_name</span><span class="w"> </span><span class="n">WAIT</span><span class="w"> </span><span class="n">N</span><span class="w"> </span><span class="k">add</span><span class="w"> </span><span class="k">column</span><span class="w"> </span><span class="p">...</span><span class="w">
</span></span></span></code></pre></div><p>实测：MySQL 5.7.30 不支持。腾讯云 MySQL 5.7.18-txsql-log 支持。</p>
<ul>
<li><a href="https://cloud.tencent.com/document/product/236/48838">支持 NOWAIT 语法 | cloud.tencent</a></li>
</ul>
<h2 id="07--行锁功过怎么减少行锁对性能的影响">07 | 行锁功过：怎么减少行锁对性能的影响？</h2>
<p>在 InnoDB 事务中，行锁是在需要的时候才加上的，但并不是不需要了就立刻释放，而是要等到事务结束时才释放。这个就是两阶段锁协议。</p>
<p>如果事务中需要锁多个行，要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。</p>
<p>举例：</p>
<ol>
<li>从顾客 A 账户余额中扣除电影票价；UPDATE</li>
<li>给影院 B 的账户余额增加这张电影票价；UPDATE 最容易发送锁等待的地方</li>
<li>记录一条交易日志。INSERT</li>
</ol>
<p>以 3、1、2 顺序执行可以最大程度地减少了事务之间的锁等待，提升了并发度。</p>
<p>当并发系统中不同线程出现循环资源依赖，涉及的线程都在等待别的线程释放资源时，就会导致这几个线程都进入无限等待的状态，称为死锁。</p>
<img alt="image" src="https://user-images.githubusercontent.com/9289792/186377931-53a1b737-3149-4985-820f-87ffd1319158.png" style="width: 420px" />
<p>上图 session1 在等 session2 id=2 的锁，session2 在等 session1 id=1 的锁，进入死锁状态。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 所等待超时时间 s
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">@@</span><span class="n">innodb_lock_wait_timeout</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 7200 2h
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 死锁检测，发现死锁后，主动回滚死锁链条中的某一个事务
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">@@</span><span class="n">innodb_deadlock_detect</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1213 - Deadlock found when trying to get lock; try restarting transaction
</span></span></span></code></pre></div><p>主动死锁检测在发生死锁的时候，是能够快速发现并进行处理的，但是它也是有额外负担的：</p>
<p>每当一个事务被锁的时候，就要看看它所依赖的线程有没有被别人锁住，如此循环，最后判断是否出现了循环等待，也就是死锁。</p>
<p>每个新来的被堵住的线程，都要判断会不会由于自己的加入导致了死锁，这是一个时间复杂度是 O(n)的操作。假设有 1000 个并发线程要同时更新同一行，那么死锁检测操作就是 100 万这个量级的。这期间要消耗大量的 CPU 资源。即：热点行更新导致的性能问题。</p>
<p>可以通过尝试将热点数据拆分多行进行处理，提高并发处理。</p>
<h2 id="08--事务到底是隔离的还是不隔离的">08 | 事务到底是隔离的还是不隔离的？</h2>
<p><code>begin/start transaction</code> 命令并不是一个事务的起点，在执行到它们之后的第一个操作 InnoDB 表的语句，事务才真正启动。如果你想要马上启动一个事务：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">START</span><span class="w"> </span><span class="k">TRANSACTION</span><span class="w"> </span><span class="k">WITH</span><span class="w"> </span><span class="n">CONSISTENT</span><span class="w"> </span><span class="n">SNAPSHOT</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>有两个“视图”的概念：</p>
<ul>
<li>一个是 view。它是一个用查询语句定义的虚拟表，在调用的时候执行查询语句并生成结果。创建视图的语法是 create view &hellip;，而它的查询方法与表一样。</li>
<li>另一个是 InnoDB 在实现 MVCC 时用到的一致性读视图，即 consistent read view，用于支持 RC（Read Committed，读提交）和 RR（Repeatable Read，可重复读）隔离级别的实现。</li>
</ul>
<p>在可重复读隔离级别下，事务在启动的时候就“拍了个快照”。注意，这个快照是基于整库的。</p>
<p>InnoDB 里面每个事务有一个唯一的事务 ID，叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的，是按申请顺序严格递增的。</p>
<hr>
]]></content:encoded>
    </item>
    <item>
      <title>Shell Code Snippet</title>
      <link>https://zyf.im/2022/07/28/shell-code-snippet/</link>
      <pubDate>Thu, 28 Jul 2022 11:51:46 +0000</pubDate>
      <guid>https://zyf.im/2022/07/28/shell-code-snippet/</guid>
      <description>&lt;h2 id=&#34;package-management&#34;&gt;Package Management&lt;/h2&gt;
&lt;h3 id=&#34;find-packages-with-pkgsorg&#34;&gt;Find Packages with pkgs.org&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://pkgs.org/&#34;&gt;pkgs.org&lt;/a&gt; is a Linux package search website that helps you find software packages for various Linux distributions.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Check installed package versions (Red Hat/CentOS)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rpm -qa &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep docker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;process-management&#34;&gt;Process Management&lt;/h2&gt;
&lt;h3 id=&#34;run-commands-in-background&#34;&gt;Run Commands in Background&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Run in background (will stop when terminal closes)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cmd &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Run in background (continues after terminal closes)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nohup cmd &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;control-background-jobs&#34;&gt;Control Background Jobs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Z&lt;/code&gt; - Pause the current foreground job and move it to background&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jobs&lt;/code&gt; - List all background jobs (&lt;code&gt;jobs -l&lt;/code&gt; shows PIDs)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fg&lt;/code&gt; - Bring the most recent background job to foreground&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fg %N&lt;/code&gt; - Bring job number N to foreground&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bg&lt;/code&gt; - Resume the most recent job in the background&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bg %N&lt;/code&gt; - Resume job number N in the background&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;using-fg-command&#34;&gt;Using fg Command&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Restore the most recent background job&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;fg&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Restore a specific job by number&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;fg&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Restore using % syntax&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;fg&lt;/span&gt; %2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Restore by process name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;fg&lt;/span&gt; %vim
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# List all background jobs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;jobs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;dns-operations&#34;&gt;DNS Operations&lt;/h2&gt;
&lt;h3 id=&#34;lookup-cname-records&#34;&gt;Lookup CNAME Records&lt;/h3&gt;
&lt;p&gt;CNAME (Canonical Name) records map one domain name to another, commonly used for subdomains or third-party hosted services.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="package-management">Package Management</h2>
<h3 id="find-packages-with-pkgsorg">Find Packages with pkgs.org</h3>
<p><a href="https://pkgs.org/">pkgs.org</a> is a Linux package search website that helps you find software packages for various Linux distributions.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Check installed package versions (Red Hat/CentOS)</span>
</span></span><span class="line"><span class="cl">rpm -qa <span class="p">|</span> grep docker
</span></span></code></pre></div><h2 id="process-management">Process Management</h2>
<h3 id="run-commands-in-background">Run Commands in Background</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Run in background (will stop when terminal closes)</span>
</span></span><span class="line"><span class="cl">cmd <span class="p">&amp;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Run in background (continues after terminal closes)</span>
</span></span><span class="line"><span class="cl">nohup cmd <span class="p">&amp;</span>
</span></span></code></pre></div><h3 id="control-background-jobs">Control Background Jobs</h3>
<ul>
<li><code>Ctrl + Z</code> - Pause the current foreground job and move it to background</li>
<li><code>jobs</code> - List all background jobs (<code>jobs -l</code> shows PIDs)</li>
<li><code>fg</code> - Bring the most recent background job to foreground</li>
<li><code>fg %N</code> - Bring job number N to foreground</li>
<li><code>bg</code> - Resume the most recent job in the background</li>
<li><code>bg %N</code> - Resume job number N in the background</li>
</ul>
<h3 id="using-fg-command">Using fg Command</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Restore the most recent background job</span>
</span></span><span class="line"><span class="cl"><span class="nb">fg</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Restore a specific job by number</span>
</span></span><span class="line"><span class="cl"><span class="nb">fg</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Restore using % syntax</span>
</span></span><span class="line"><span class="cl"><span class="nb">fg</span> %2
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Restore by process name</span>
</span></span><span class="line"><span class="cl"><span class="nb">fg</span> %vim
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># List all background jobs</span>
</span></span><span class="line"><span class="cl"><span class="nb">jobs</span>
</span></span></code></pre></div><h2 id="dns-operations">DNS Operations</h2>
<h3 id="lookup-cname-records">Lookup CNAME Records</h3>
<p>CNAME (Canonical Name) records map one domain name to another, commonly used for subdomains or third-party hosted services.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Using dig</span>
</span></span><span class="line"><span class="cl">dig example.com cname
</span></span><span class="line"><span class="cl"><span class="c1"># Output: example.com. 300 IN CNAME target-domain.com.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Using host command</span>
</span></span><span class="line"><span class="cl">host -t CNAME example.com
</span></span><span class="line"><span class="cl"><span class="c1"># Output: example.com is an alias for target-domain.com.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Using nslookup</span>
</span></span><span class="line"><span class="cl">nslookup -type<span class="o">=</span>CNAME example.com
</span></span><span class="line"><span class="cl"><span class="c1"># Output: example.com canonical name = target-domain.com.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check multiple domains at once</span>
</span></span><span class="line"><span class="cl">dig +short CNAME www.example.com blog.example.com
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># See full DNS lookup path</span>
</span></span><span class="line"><span class="cl">dig +trace example.com
</span></span></code></pre></div><h2 id="file-system-operations">File System Operations</h2>
<h3 id="find-large-files">Find Large Files</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Find and display the 3 largest files/folders in current directory</span>
</span></span><span class="line"><span class="cl">du -sh ./* <span class="p">|</span> sort -rh <span class="p">|</span> head -3
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Find files larger than 100MB</span>
</span></span><span class="line"><span class="cl">find . -type f -size +100M
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Find large files and show human-readable sizes</span>
</span></span><span class="line"><span class="cl">find . -type f -size +50M -exec du -h <span class="o">{}</span> <span class="se">\;</span> <span class="p">|</span> sort -rh
</span></span></code></pre></div><h3 id="dealing-with-deleted-files-that-still-use-space">Dealing with Deleted Files That Still Use Space</h3>
<p>When you delete a file but the disk space isn&rsquo;t freed, it&rsquo;s usually because a process is still using the file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Find deleted files still in use</span>
</span></span><span class="line"><span class="cl">lsof <span class="p">|</span> grep deleted
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Clear file content without deleting the file</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;&#34;</span> &gt; /var/log/large_logfile.log
</span></span><span class="line"><span class="cl"><span class="c1"># Or more safely:</span>
</span></span><span class="line"><span class="cl">truncate -s <span class="m">0</span> /var/log/large_logfile.log
</span></span></code></pre></div><h3 id="search-text-in-files-recursively">Search Text in Files Recursively</h3>
<blockquote>
<ul>
<li><a href="https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html">The Set Builtin</a></li>
<li><a href="https://www.ruanyifeng.com/blog/2017/11/bash-set.html">Bash 脚本 set 命令教程</a></li>
</ul>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Search for text in all files in current directory and subdirectories</span>
</span></span><span class="line"><span class="cl">grep -r <span class="s2">&#34;search text&#34;</span> .
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Limit search to specific file types</span>
</span></span><span class="line"><span class="cl">grep -r <span class="s2">&#34;search text&#34;</span> --include<span class="o">=</span><span class="s2">&#34;*.php&#34;</span> .
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Show line numbers in results</span>
</span></span><span class="line"><span class="cl">grep -rn <span class="s2">&#34;search text&#34;</span> .
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Case-insensitive search</span>
</span></span><span class="line"><span class="cl">grep -ri <span class="s2">&#34;search text&#34;</span> .
</span></span></code></pre></div><h3 id="鉴别自己是否真的使用了代理来登陆服务器">鉴别自己是否真的使用了代理来登陆服务器</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">root@ubuntu:~# who
</span></span><span class="line"><span class="cl">root     pts/2        2017-05-13 18:13 <span class="o">(</span>xxx.xxx.xxx.xxx<span class="o">)</span>
</span></span></code></pre></div><h2 id="script-safety-with-set">Script Safety with set</h2>
<p>The <code>set</code> command controls shell behavior and can make scripts safer.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Stop script if any command fails</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -o errexit  <span class="c1"># Same as set -e</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Treat unset variables as errors</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -o nounset  <span class="c1"># Same as set -u</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Make pipeline fail if any command in it fails</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -o pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Print commands before execution (useful for debugging)</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -o xtrace   <span class="c1"># Same as set -x</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Common combined safety settings</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -euxo pipefail
</span></span></code></pre></div><h3 id="error-handling-patterns">Error Handling Patterns</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Stop if command fails</span>
</span></span><span class="line"><span class="cl"><span class="nb">command</span> <span class="o">||</span> <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># With error message</span>
</span></span><span class="line"><span class="cl"><span class="nb">command</span> <span class="o">||</span> <span class="o">{</span> <span class="nb">echo</span> <span class="s2">&#34;Command failed&#34;</span><span class="p">;</span> <span class="nb">exit</span> 1<span class="p">;</span> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Using if statement</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! command<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;Command failed&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Other way</span>
</span></span><span class="line"><span class="cl"><span class="nb">command</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$?</span><span class="s2">&#34;</span> -ne <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="nb">echo</span> <span class="s2">&#34;command failed&#34;</span><span class="p">;</span> <span class="nb">exit</span> 1<span class="p">;</span> <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Execute second command only if first succeeds</span>
</span></span><span class="line"><span class="cl">command1 <span class="o">&amp;&amp;</span> command2
</span></span></code></pre></div><h2 id="navigation-shortcuts">Navigation Shortcuts</h2>
<h3 id="using-cdpath">Using CDPATH</h3>
<p>CDPATH creates shortcuts for the <code>cd</code> command by checking multiple locations.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Add directories to CDPATH</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CDPATH</span><span class="o">=</span>.:/etc:~/projects
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Now you can directly cd to subdirectories of those paths</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> project1    <span class="c1"># Changes to ~/projects/project1</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> mail        <span class="c1"># Changes to /etc/mail</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Unset CDPATH (useful in scripts for predictability)</span>
</span></span><span class="line"><span class="cl"><span class="nb">unset</span> CDPATH
</span></span></code></pre></div><h2 id="system-configuration">System Configuration</h2>
<h3 id="set-timezone-in-ubuntu">Set Timezone in Ubuntu</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Interactive timezone selection</span>
</span></span><span class="line"><span class="cl">sudo dpkg-reconfigure tzdata
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Set timezone directly</span>
</span></span><span class="line"><span class="cl">sudo timedatectl set-timezone Asia/Shanghai
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Restart logging service (may be needed for cron)</span>
</span></span><span class="line"><span class="cl">sudo systemctl restart rsyslog
</span></span><span class="line"><span class="cl"><span class="c1"># Or</span>
</span></span><span class="line"><span class="cl">/etc/init.d/rsyslog restart
</span></span></code></pre></div><h3 id="open-pdf-files-from-terminal">Open PDF Files from Terminal</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Open PDF without loading file explorer</span>
</span></span><span class="line"><span class="cl">evince document.pdf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Alternative PDF viewers</span>
</span></span><span class="line"><span class="cl">xpdf document.pdf
</span></span><span class="line"><span class="cl">okular document.pdf
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="http://askubuntu.com/questions/323131/setting-timezone-from-terminal">setting timezone from terminal - Ask Ubuntu</a></li>
<li><a href="https://askubuntu.com/questions/54364/how-do-you-set-the-timezone-for-crontab">cron - How do you set the timezone for crontab? - Ask Ubuntu</a></li>
<li><a href="http://blog.csdn.net/bigmarco/article/details/6555582">Ubuntu 命令行打开 PDF 文件 - bigmarco 的专栏</a></li>
<li><a href="https://www.cnblogs.com/xd502djj/p/6668632.html">Linux 文件删除，但是 df 之后磁盘空间没有释放 | cnblogs</a></li>
<li><a href="https://www.nslookup.io/cname-lookup/">How to lookup CNAME records | nslookup.io</a></li>
<li><a href="https://juejin.cn/post/6985727600320593951">Linux 命令后台运行</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>回顾 2021</title>
      <link>https://zyf.im/2021/12/31/review-2021/</link>
      <pubDate>Fri, 31 Dec 2021 20:16:41 +0000</pubDate>
      <guid>https://zyf.im/2021/12/31/review-2021/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://music.163.com/#/song?id=25638375&#34;&gt;直到对的人来 · 追星族乐队&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我想跟他说：那家餐厅太美了，我一定要和你去一次。但是那个菜其实很一般的，那个老板娘戴的耳环特别漂亮。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://music.163.com/#/song?id=25638375">直到对的人来 · 追星族乐队</a></p>
<blockquote>
<p>我想跟他说：那家餐厅太美了，我一定要和你去一次。但是那个菜其实很一般的，那个老板娘戴的耳环特别漂亮。</p>
</blockquote>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP 安装 gRPC</title>
      <link>https://zyf.im/2021/10/13/php-install-grpc/</link>
      <pubDate>Wed, 13 Oct 2021 15:48:41 +0000</pubDate>
      <guid>https://zyf.im/2021/10/13/php-install-grpc/</guid>
      <description>&lt;h2 id=&#34;env&#34;&gt;ENV&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /etc/redhat-release
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;CentOS Linux release 7.2 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Final&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uname -a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Linux xxx-xxx 3.10.107-1-tlinux2_kvm_guest-0052 &lt;span class=&#34;c1&#34;&gt;#1 SMP Wed Jan 15 18:42:19 CST 2020 x86_64 x86_64 x86_64 GNU/Linux&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;pecl&#34;&gt;PECL&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pecl install grpc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果遇到：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Connection to &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;ssl://pecl.php.net:443′ failed:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;参考：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://zyf.im/2020/08/07/deploy-swoft-framework/&#34;&gt;PHP Swoft 框架环境配置 | ZYF.IM&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;build&#34;&gt;Build&lt;/h2&gt;
&lt;h3 id=&#34;通用手动安装-pecl-扩展&#34;&gt;通用手动安装 PECL 扩展&lt;/h3&gt;
&lt;p&gt;For this to work, you’ll need to have root access to your server, and you’ll most probably need developer tools installed as well.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://pecl.php.net/&#34;&gt;https://pecl.php.net/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建临时目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir /tmp/download/ &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /tmp/download/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 下载&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget http://pecl.php.net/get/&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;extension&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 解压&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -xvf &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;extension&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;extension&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;phpize
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --help
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置参考 以实际情况为准&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --enable-&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;extension&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 编译&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make install
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看 ini 路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php --ini
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 开启&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vi &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;php-ini&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;.ini
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;extension&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=[&lt;/span&gt;extension&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;.so
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;gcc-is-currently-not-installed&#34;&gt;gcc is currently not installed&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yum install gcc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...already installed and latest version...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...gcc is currently not installed.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yum reinstall gcc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 重新安装后解决&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;grpclbtokenandclientstatsattributetostring-const&#34;&gt;GrpcLb::TokenAndClientStatsAttribute::ToString() const&lt;/h3&gt;
&lt;p&gt;原因：gcc 版本过低。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="env">ENV</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat /etc/redhat-release
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">CentOS Linux release 7.2 <span class="o">(</span>Final<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">uname -a
</span></span><span class="line"><span class="cl">Linux xxx-xxx 3.10.107-1-tlinux2_kvm_guest-0052 <span class="c1">#1 SMP Wed Jan 15 18:42:19 CST 2020 x86_64 x86_64 x86_64 GNU/Linux</span>
</span></span></code></pre></div><h2 id="pecl">PECL</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pecl install grpc
</span></span></code></pre></div><p>如果遇到：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Connection to <span class="sb">`</span>ssl://pecl.php.net:443′ failed:
</span></span></code></pre></div><p>参考：</p>
<p><a href="/2020/08/07/deploy-swoft-framework/">PHP Swoft 框架环境配置 | ZYF.IM</a></p>
<h2 id="build">Build</h2>
<h3 id="通用手动安装-pecl-扩展">通用手动安装 PECL 扩展</h3>
<p>For this to work, you’ll need to have root access to your server, and you’ll most probably need developer tools installed as well.</p>
<blockquote>
<p><a href="https://pecl.php.net/">https://pecl.php.net/</a></p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 创建临时目录</span>
</span></span><span class="line"><span class="cl">mkdir /tmp/download/ <span class="o">&amp;&amp;</span> <span class="nb">cd</span> /tmp/download/
</span></span><span class="line"><span class="cl"><span class="c1"># 下载</span>
</span></span><span class="line"><span class="cl">wget http://pecl.php.net/get/<span class="o">[</span>extension<span class="o">]</span>.tgz
</span></span><span class="line"><span class="cl"><span class="c1"># 解压</span>
</span></span><span class="line"><span class="cl">tar -xvf <span class="o">[</span>extension<span class="o">]</span>.tgz
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">[</span>extension<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 配置</span>
</span></span><span class="line"><span class="cl">phpize
</span></span><span class="line"><span class="cl"><span class="c1"># 查看参数</span>
</span></span><span class="line"><span class="cl">./configure --help
</span></span><span class="line"><span class="cl"><span class="c1"># 设置参考 以实际情况为准</span>
</span></span><span class="line"><span class="cl">./configure --enable-<span class="o">[</span>extension<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 编译</span>
</span></span><span class="line"><span class="cl">make <span class="o">&amp;&amp;</span> make <span class="nb">test</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 安装</span>
</span></span><span class="line"><span class="cl">make install
</span></span><span class="line"><span class="cl"><span class="c1"># 查看 ini 路径</span>
</span></span><span class="line"><span class="cl">php --ini
</span></span><span class="line"><span class="cl"><span class="c1"># 开启</span>
</span></span><span class="line"><span class="cl">vi <span class="o">[</span>php-ini<span class="o">]</span>.ini
</span></span><span class="line"><span class="cl"><span class="nv">extension</span><span class="o">=[</span>extension<span class="o">]</span>.so
</span></span></code></pre></div><h3 id="gcc-is-currently-not-installed">gcc is currently not installed</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yum install gcc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...already installed and latest version...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">gcc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...gcc is currently not installed.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">yum reinstall gcc
</span></span><span class="line"><span class="cl"><span class="c1"># 重新安装后解决</span>
</span></span></code></pre></div><h3 id="grpclbtokenandclientstatsattributetostring-const">GrpcLb::TokenAndClientStatsAttribute::ToString() const</h3>
<p>原因：gcc 版本过低。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yum install centos-release-scl
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">yum install devtoolset-8-gcc*
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">scl <span class="nb">enable</span> devtoolset-8 bash
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">source</span> /opt/rh/devtoolset-8/enable
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://cloud.google.com/php/grpc">安装适用于 PHP 的 gRPC | cloud.google</a></li>
<li><a href="https://www.jianshu.com/p/387b7a46d9fd">PHP 安装 grpc 扩展报错 | jianshu</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP __invoke 使用</title>
      <link>https://zyf.im/2021/07/20/php-invoke-is-anybody-using-it/</link>
      <pubDate>Tue, 20 Jul 2021 14:58:33 +0000</pubDate>
      <guid>https://zyf.im/2021/07/20/php-invoke-is-anybody-using-it/</guid>
      <description>&lt;p&gt;why they are magic? Because they are magically called by PHP when specific actions happen.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;__invoke()&lt;/code&gt; method is called when a script tries to call an object as a function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;CallableClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__invoke&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CallableClass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;is_callable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;int&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;5&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bool&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;使用明显的操作方法初始化&#34;&gt;使用明显的操作方法初始化&lt;/h2&gt;
&lt;p&gt;例如，当我们有一个提供者时，就会发生这种情况。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/aws/aws-sdk-php/blob/master/src/Endpoint/PatternEndpointProvider.php&#34;&gt;aws-sdk-php/src/Endpoint/PatternEndpointProvider.php&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__invoke&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$args&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$service&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;service&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;service&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$region&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;region&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;region&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$keys&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$region&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$service&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$region&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;*/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$service&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;*/*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$keys&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;patterns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;expand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;patterns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;isset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;scheme&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;scheme&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;https&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$service&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$region&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它使用 invoke 使用一些参数提供端点。我们如何使用这个类？&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>why they are magic? Because they are magically called by PHP when specific actions happen.</p>
<p>The <code>__invoke()</code> method is called when a script tries to call an object as a function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">CallableClass</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__invoke</span><span class="p">(</span><span class="nv">$x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">var_dump</span><span class="p">(</span><span class="nv">$x</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">CallableClass</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$obj</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">is_callable</span><span class="p">(</span><span class="nv">$obj</span><span class="p">));</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">int<span class="o">(</span>5<span class="o">)</span>
</span></span><span class="line"><span class="cl">bool<span class="o">(</span><span class="nb">true</span><span class="o">)</span>
</span></span></code></pre></div><h2 id="使用明显的操作方法初始化">使用明显的操作方法初始化</h2>
<p>例如，当我们有一个提供者时，就会发生这种情况。</p>
<p><a href="https://github.com/aws/aws-sdk-php/blob/master/src/Endpoint/PatternEndpointProvider.php">aws-sdk-php/src/Endpoint/PatternEndpointProvider.php</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">public</span> <span class="k">function</span> <span class="fm">__invoke</span><span class="p">(</span><span class="k">array</span> <span class="nv">$args</span> <span class="o">=</span> <span class="p">[])</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$service</span> <span class="o">=</span> <span class="nx">isset</span><span class="p">(</span><span class="nv">$args</span><span class="p">[</span><span class="s1">&#39;service&#39;</span><span class="p">])</span> <span class="o">?</span> <span class="nv">$args</span><span class="p">[</span><span class="s1">&#39;service&#39;</span><span class="p">]</span> <span class="o">:</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$region</span> <span class="o">=</span> <span class="nx">isset</span><span class="p">(</span><span class="nv">$args</span><span class="p">[</span><span class="s1">&#39;region&#39;</span><span class="p">])</span> <span class="o">?</span> <span class="nv">$args</span><span class="p">[</span><span class="s1">&#39;region&#39;</span><span class="p">]</span> <span class="o">:</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$keys</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;</span><span class="si">{</span><span class="nv">$region</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="nv">$service</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="s2">&#34;</span><span class="si">{</span><span class="nv">$region</span><span class="si">}</span><span class="s2">/*&#34;</span><span class="p">,</span> <span class="s2">&#34;*/</span><span class="si">{</span><span class="nv">$service</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="s2">&#34;*/*&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="nv">$keys</span> <span class="k">as</span> <span class="nv">$key</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">isset</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">patterns</span><span class="p">[</span><span class="nv">$key</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">expand</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">patterns</span><span class="p">[</span><span class="nv">$key</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="nx">isset</span><span class="p">(</span><span class="nv">$args</span><span class="p">[</span><span class="s1">&#39;scheme&#39;</span><span class="p">])</span> <span class="o">?</span> <span class="nv">$args</span><span class="p">[</span><span class="s1">&#39;scheme&#39;</span><span class="p">]</span> <span class="o">:</span> <span class="s1">&#39;https&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nv">$service</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nv">$region</span>
</span></span><span class="line"><span class="cl">            <span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>它使用 invoke 使用一些参数提供端点。我们如何使用这个类？</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">public</span> <span class="k">function</span> <span class="nf">testReturnsNullWhenUnresolved</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$e</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PatternEndpointProvider</span><span class="p">([</span><span class="s1">&#39;foo&#39;</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">&#39;rules&#39;</span> <span class="o">=&gt;</span> <span class="p">[]]]);</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">assertNull</span><span class="p">(</span><span class="nv">$e</span><span class="p">([</span><span class="s1">&#39;service&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;foo&#39;</span><span class="p">,</span> <span class="s1">&#39;region&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;bar&#39;</span><span class="p">]));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="尝试使用单动作控制器">尝试使用单动作控制器？</h2>
<p>控制器应该大而广泛？他们不应该。我们应该有瘦控制器和胖服务。</p>
<p>在这里，invoke 可以帮助我们，因为我们可以定义一个只处理单个动作的控制器，并在其上放置单个 invoke 方法。</p>
<p>这也有助于我们实现单一职责原则，即 SOLID 中的 S，这是前五个面向对象设计 (OOD) 原则的首字母缩写词。</p>
<blockquote>
<p>A class should have one and only one reason to change, meaning that a class should have only one job.</p>
</blockquote>
<p>在 Laravel 中的例子：<a href="https://laravel.com/docs/5.7/controllers#single-action-controllers">Single Action Controllers | laravel</a>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nx">App\Http\Controllers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">App\User</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">App\Http\Controllers\Controller</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ShowProfile</span> <span class="k">extends</span> <span class="nx">Controller</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * Show the profile for the given user.
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param  int  $id
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return View
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__invoke</span><span class="p">(</span><span class="nv">$id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">view</span><span class="p">(</span><span class="s1">&#39;user.profile&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;user&#39;</span> <span class="o">=&gt;</span> <span class="nx">User</span><span class="o">::</span><span class="na">findOrFail</span><span class="p">(</span><span class="nv">$id</span><span class="p">)]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>然后，在注册路由时，我们不需要指定方法名称。只有类名。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="nx">Route</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">&#39;user/{id}&#39;</span><span class="p">,</span> <span class="s1">&#39;ShowProfile&#39;</span><span class="p">);</span>
</span></span></code></pre></div><p>This way we can have Single Action Controllers.</p>
<h2 id="实现回调函数-by-gpt">实现回调函数 by GPT</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Sorter</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__invoke</span><span class="p">(</span><span class="nv">$a</span><span class="p">,</span> <span class="nv">$b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$a</span> <span class="o">&lt;=&gt;</span> <span class="nv">$b</span><span class="p">;</span> <span class="c1">// 比较函数
</span></span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$numbers</span> <span class="o">=</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">5</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nx">usort</span><span class="p">(</span><span class="nv">$numbers</span><span class="p">,</span> <span class="k">new</span> <span class="nx">Sorter</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="nx">print_r</span><span class="p">(</span><span class="nv">$numbers</span><span class="p">);</span> <span class="c1">// 输出排序后的数组
</span></span></span></code></pre></div><h2 id="实现函数式编程-by-gpt">实现函数式编程 by GPT</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Adder</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__construct</span><span class="p">(</span><span class="nv">$value</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">value</span> <span class="o">=</span> <span class="nv">$value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="fm">__invoke</span><span class="p">(</span><span class="nv">$x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">value</span> <span class="o">+</span> <span class="nv">$x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$add5</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Adder</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$result</span> <span class="o">=</span> <span class="nv">$add5</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> <span class="c1">// 结果为 8
</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://luis-barros-nobrega.medium.com/php-invoke-is-anybody-using-it-1933c64d17f1">PHP invoke: is anybody using it? | medium</a></li>
<li><a href="https://www.php.net/manual/en/language.oop5.magic.php#object.invoke">__invoke() | php</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP strict_types 严格模式</title>
      <link>https://zyf.im/2021/07/19/php-strict-types/</link>
      <pubDate>Mon, 19 Jul 2021 19:58:26 +0000</pubDate>
      <guid>https://zyf.im/2021/07/19/php-strict-types/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;严格模式的声明 &lt;em&gt;必须&lt;/em&gt; 放在文件的顶部。&lt;/li&gt;
&lt;li&gt;严格模式不仅作用于函数参数的类型声明，也作用于函数的返回值类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;声明 PHP 文件作为严格模式的一个好事是，实际上只适用于当前文件。这确保了这个文件是严格类型，但是他没有影响到整个项目中的其他文件。这允许你一步一步的迁移非严格模式的代码。&lt;/p&gt;
&lt;p&gt;使用提示类型没有 strict_types 可能导致微妙的错误。&lt;/p&gt;
&lt;p&gt;严格类型之前，&lt;code&gt;int $x&lt;/code&gt; 意味着 &lt;code&gt;$x must have a value coercible to an int&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a float (example: 13.1459 -&amp;gt; 13)&lt;/li&gt;
&lt;li&gt;a bool (example: true -&amp;gt; 1)&lt;/li&gt;
&lt;li&gt;a null (example: null -&amp;gt; 0)&lt;/li&gt;
&lt;li&gt;a string with leading digits (example: “15 Trees” -&amp;gt; 15)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;设置严格模式后，you tell the engine that &lt;code&gt;int $x&lt;/code&gt; means &lt;code&gt;$x must only be an int proper, no type coercion allowed&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;谁给更关心 &lt;code&gt;strict_type&lt;/code&gt; 这行？is more for the reader than for the writer. Why? Bacause it will explicitly tell the reader:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<ul>
<li>严格模式的声明 <em>必须</em> 放在文件的顶部。</li>
<li>严格模式不仅作用于函数参数的类型声明，也作用于函数的返回值类型。</li>
</ul>
<p>声明 PHP 文件作为严格模式的一个好事是，实际上只适用于当前文件。这确保了这个文件是严格类型，但是他没有影响到整个项目中的其他文件。这允许你一步一步的迁移非严格模式的代码。</p>
<p>使用提示类型没有 strict_types 可能导致微妙的错误。</p>
<p>严格类型之前，<code>int $x</code> 意味着 <code>$x must have a value coercible to an int</code>。</p>
<ul>
<li>a float (example: 13.1459 -&gt; 13)</li>
<li>a bool (example: true -&gt; 1)</li>
<li>a null (example: null -&gt; 0)</li>
<li>a string with leading digits (example: “15 Trees” -&gt; 15)</li>
</ul>
<p>设置严格模式后，you tell the engine that <code>int $x</code> means <code>$x must only be an int proper, no type coercion allowed</code>。</p>
<p>谁给更关心 <code>strict_type</code> 这行？is more for the reader than for the writer. Why? Bacause it will explicitly tell the reader:</p>
<p>The types in this current scope are treated strictly.</p>
<h2 id="对比">对比</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">add</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$a</span><span class="p">,</span> <span class="nx">int</span> <span class="nv">$b</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nv">$a</span> <span class="o">+</span> <span class="nv">$b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">add</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">));</span>
</span></span></code></pre></div><p>运行输出 <code>int(3)</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="k">declare</span><span class="p">(</span><span class="nx">strict_types</span><span class="o">=</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">add</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$a</span><span class="p">,</span> <span class="nx">int</span> <span class="nv">$b</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nv">$a</span> <span class="o">+</span> <span class="nv">$b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">add</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">));</span>
</span></span></code></pre></div><p>运行输出：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">PHP Fatal error:  Uncaught TypeError: Argument <span class="m">1</span> passed to add<span class="o">()</span> must be of the <span class="nb">type</span> int, float given, ...
</span></span></code></pre></div><h2 id="声明位置">声明位置</h2>
<p>必须在脚本最前。不能写在脚本的中间，如下写法是错误的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">add</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$a</span><span class="p">,</span> <span class="nx">int</span> <span class="nv">$b</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nv">$a</span> <span class="o">+</span> <span class="nv">$b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">declare</span><span class="p">(</span><span class="nx">strict_types</span><span class="o">=</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">add</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">));</span>
</span></span></code></pre></div><p>运行后报错：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">PHP Fatal error:  strict_types declaration must be the very first statement in the script in ...
</span></span></code></pre></div><p>不得使用 block mode 进行声明：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="k">declare</span><span class="p">(</span><span class="nx">strict_types</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">var_dump</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>运行后报错：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">PHP Fatal error:  strict_types declaration must not use block mode in ...
</span></span></code></pre></div><h2 id="多文件场景">多文件场景</h2>
<h3 id="例子-1">例子 1</h3>
<p><code>A.php</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 严格模式
</span></span></span><span class="line"><span class="cl"><span class="k">declare</span><span class="p">(</span><span class="nx">strict_types</span><span class="o">=</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">add</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$a</span><span class="p">,</span> <span class="nx">int</span> <span class="nv">$b</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nv">$a</span> <span class="o">+</span> <span class="nv">$b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>B.php</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 非严格模式
</span></span></span><span class="line"><span class="cl"><span class="k">require</span> <span class="s1">&#39;A.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 违反了 A 的定义
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">add</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">));</span>
</span></span></code></pre></div><p>运行</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php B.php
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">int<span class="o">(</span>3<span class="o">)</span>
</span></span></code></pre></div><h3 id="例子-2">例子 2</h3>
<p><code>A.php</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 非严格模式
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">add</span><span class="p">(</span><span class="nx">int</span> <span class="nv">$a</span><span class="p">,</span> <span class="nx">int</span> <span class="nv">$b</span><span class="p">)</span><span class="o">:</span> <span class="nx">int</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nv">$a</span> <span class="o">+</span> <span class="nv">$b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>B.php</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 严格模式
</span></span></span><span class="line"><span class="cl"><span class="k">declare</span><span class="p">(</span><span class="nx">strict_types</span><span class="o">=</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">require</span> <span class="s1">&#39;A.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 违反了 A 的定义
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">add</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">));</span>
</span></span></code></pre></div><p>运行</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php B.php
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">PHP Fatal error:  Uncaught TypeError: Argument <span class="m">1</span> passed to add<span class="o">()</span> must be of the <span class="nb">type</span> int, float given, called in ...
</span></span></code></pre></div><h3 id="小结">小结</h3>
<ul>
<li>函数定义时的严格模式，行为并不会出现什么不同。</li>
<li>函数执行时的，严格模式会出现差异。</li>
<li><code>declare(strict_types=1);</code> 的声明本身在 A.php 文件中完成。被 B.php 文件 require，而 B.php 并没有定义严格模式，那么执行 require 的 B.php 文件不会变成严格模式。</li>
</ul>
<h2 id="总结">总结</h2>
<p>只有在写 declare 的文件的执行部分才会执行严格模式，该文件中调用的其它函数（其它文件中的函数）也会被影响。</p>
<p>若果想完全使用严格模式，比较简单的方法是在所有 php 文件都写上 <code>declare(strict_types=1);</code>。</p>
<h2 id="工具">工具</h2>
<p>推荐自动格式化工具：<a href="https://github.com/symplify/easy-coding-standard">symplify/easy-coding-standard</a>。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://segmentfault.com/a/1190000018389227">关于 declare(strict_types=1) 的有效范围 | segmentfault</a></li>
<li><a href="https://chemaclass.medium.com/strict-types-in-php-d4166bd25394">Strict Types in PHP | medium</a></li>
<li><a href="https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.scalar-type-declarations">Scalar type declarations | php</a></li>
<li><a href="https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.strict">Strict typing | php</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Git and GitHub Secrets</title>
      <link>https://zyf.im/2021/07/12/git-and-github-secrets/</link>
      <pubDate>Mon, 12 Jul 2021 10:40:15 +0000</pubDate>
      <guid>https://zyf.im/2021/07/12/git-and-github-secrets/</guid>
      <description>&lt;h2 id=&#34;记住密码&#34;&gt;记住密码&lt;/h2&gt;
&lt;p&gt;Git 记住密码配置后，不用每次 pull、push 都需要输入密码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global credential.helper store
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会在 &lt;code&gt;cat ~/.gitconfig&lt;/code&gt; 看到：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;credential&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;helper&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; store
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;快速检出上一个分支&#34;&gt;快速检出上一个分支&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git checkout -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;提交空改动&#34;&gt;提交空改动&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;empty commit&amp;#34;&lt;/span&gt; --allow-empty
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在如下几种情况下是有意义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;标记一批工作或一个新功能的开始。&lt;/li&gt;
&lt;li&gt;记录你对项目进行了跟代码无关的改动。&lt;/li&gt;
&lt;li&gt;跟使用你仓库的其他人交流。&lt;/li&gt;
&lt;li&gt;作为仓库的第一次提交，因为第一次提交日后是不能被 rebase 的：&lt;code&gt;git commit -m &amp;quot;init repo&amp;quot; --allow-empty&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;更直观的-status&#34;&gt;更直观的 status&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git status -sb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;更直观的-log&#34;&gt;更直观的 log&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git log --all --graph --pretty&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;format:&lt;span class=&#34;s1&#34;&gt;&amp;#39;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&amp;lt;%an %ae&amp;gt;%Creset&amp;#39;&lt;/span&gt; --abbrev-commit --date&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;relative
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;提交信息查询&#34;&gt;提交信息查询&lt;/h2&gt;
&lt;p&gt;找到其中和搜索条件相匹配的最近的一条。query （区别大小写）是你想要搜索的词语。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git show :/query
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;分支合并&#34;&gt;分支合并&lt;/h2&gt;
&lt;p&gt;显示所有已经合并到你当前分支的分支列表：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git branch --merged
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;相反地：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="记住密码">记住密码</h2>
<p>Git 记住密码配置后，不用每次 pull、push 都需要输入密码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global credential.helper store
</span></span></code></pre></div><p>会在 <code>cat ~/.gitconfig</code> 看到：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>credential<span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="nv">helper</span> <span class="o">=</span> store
</span></span></code></pre></div><h2 id="快速检出上一个分支">快速检出上一个分支</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout -
</span></span></code></pre></div><h2 id="提交空改动">提交空改动</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit -m <span class="s2">&#34;empty commit&#34;</span> --allow-empty
</span></span></code></pre></div><p>在如下几种情况下是有意义：</p>
<ul>
<li>标记一批工作或一个新功能的开始。</li>
<li>记录你对项目进行了跟代码无关的改动。</li>
<li>跟使用你仓库的其他人交流。</li>
<li>作为仓库的第一次提交，因为第一次提交日后是不能被 rebase 的：<code>git commit -m &quot;init repo&quot; --allow-empty</code>。</li>
</ul>
<h2 id="更直观的-status">更直观的 status</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git status -sb
</span></span></code></pre></div><h2 id="更直观的-log">更直观的 log</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log --all --graph --pretty<span class="o">=</span>format:<span class="s1">&#39;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an %ae&gt;%Creset&#39;</span> --abbrev-commit --date<span class="o">=</span>relative
</span></span></code></pre></div><h2 id="提交信息查询">提交信息查询</h2>
<p>找到其中和搜索条件相匹配的最近的一条。query （区别大小写）是你想要搜索的词语。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git show :/query
</span></span></code></pre></div><h2 id="分支合并">分支合并</h2>
<p>显示所有已经合并到你当前分支的分支列表：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git branch --merged
</span></span></code></pre></div><p>相反地：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git branch --no-merged
</span></span></code></pre></div><h2 id="gitconfig">.gitconfig</h2>
<p>打开编辑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim ~/.gitconfig
</span></span></code></pre></div><p>命令修改：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global alias.co <span class="s1">&#39;checkout&#39;</span>
</span></span><span class="line"><span class="cl">git config --global alias.ac <span class="s1">&#39;add -A . &amp;&amp; commit&#39;</span>
</span></span><span class="line"><span class="cl">git config --global alias.lg <span class="s2">&#34;log --color --graph --pretty=format:&#39;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an %ae&gt;%Creset&#39; --abbrev-commit&#34;</span>
</span></span></code></pre></div><h2 id="github">GitHub</h2>
<h3 id="整行高亮">整行高亮</h3>
<p>多行高亮也可以，比如用 #L53-L60 选择范围，或者按住 shift 键，然后再点击选择的两行。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">https://github.com/rails/rails/blob/master/activemodel/lib/active_model.rb#L53-L60
</span></span></code></pre></div><h3 id="用-commit-信息关闭-issue">用 commit 信息关闭 issue</h3>
<p>如果某个提交修复了一个 Issue，当提交到 master 分支时，提交信息里可以使用 fix/fixes/fixed , close/closes/closed 或者 resolve/resolves/resolved 等关键词，后面再跟上 Issue 号，这样就会关闭这个 Issue。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit -m <span class="s2">&#34;Fix screwup, fixes #12&#34;</span>
</span></span></code></pre></div><h3 id="链接其他仓库的-issue">链接其他仓库的 Issue</h3>
<p>如果你想引用到同一个仓库中的一个 Issue，只需使用井号 # 加上 Issue 号，这样就会自动创建到此 Issue 的链接。</p>
<p>要链接到其他仓库的 Issue，就使用 <code>user_name/repo_name#ISSUE_NUMBER</code> 的方式，例如 <code>tiimgreen/toc#12</code>。</p>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://speakerdeck.com/holman/git-and-github-secrets">Git and GitHub Secrets | speakerdeck</a></li>
<li><a href="https://speakerdeck.com/holman/more-git-and-github-secrets">More Git and GitHub Secrets | speakerdeck</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>设计模式之美 Part2</title>
      <link>https://zyf.im/2021/02/06/the-beauty-of-design-patterns-part2/</link>
      <pubDate>Sat, 06 Feb 2021 11:00:41 +0000</pubDate>
      <guid>https://zyf.im/2021/02/06/the-beauty-of-design-patterns-part2/</guid>
      <description>&lt;h2 id=&#34;11&#34;&gt;11&lt;/h2&gt;
&lt;p&gt;领域驱动设计（Domain Driven Design，简称 DDD）。&lt;/p&gt;
&lt;h3 id=&#34;什么是基于贫血模型的传统开发模式&#34;&gt;什么是基于贫血模型的传统开发模式？&lt;/h3&gt;
&lt;p&gt;UserEntity 和 UserRepository 组成了数据访问层，UserBo 和 UserService 组成了业务逻辑层，UserVo 和 UserController 在这里属于接口层。&lt;/p&gt;
&lt;p&gt;Service 层的数据和业务逻辑，被分割为 BO 和 Service 两个类中。像 UserBo 这样，只包含数据，不包含业务逻辑的类，就叫作贫血模型（Anemic Domain Model）。&lt;/p&gt;
&lt;p&gt;这种贫血模型将数据与操作分离，破坏了面向对象的封装特性，是一种典型的面向过程的编程风格。&lt;/p&gt;
&lt;h3 id=&#34;什么是基于充血模型的-ddd-开发模式&#34;&gt;什么是基于充血模型的 DDD 开发模式？&lt;/h3&gt;
&lt;p&gt;领域驱动设计，即 DDD，主要是用来指导如何解耦业务系统，划分业务模块，定义业务领域模型及其交互。&lt;/p&gt;
&lt;p&gt;基于贫血模型的传统的开发模式，重 Service 轻 BO；基于充血模型的 DDD 开发模式，轻 Service 重 Domain。&lt;/p&gt;
&lt;h3 id=&#34;为什么基于贫血模型的传统开发模式如此受欢迎&#34;&gt;为什么基于贫血模型的传统开发模式如此受欢迎？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;系统业务可能都比较简单，简单到就是基于 SQL 的 CRUD 操作&lt;/li&gt;
&lt;li&gt;充血模型的设计要比贫血模型更加有难度。我们从一开始就要设计好针对数据要暴露哪些操作，定义哪些业务逻辑。而不是像贫血模型那样，我们只需要定义数据，之后有什么功能开发需求，我们就在 Service 层定义什么操作，不需要事先做太多设计。&lt;/li&gt;
&lt;li&gt;思维已固化，转型有成本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;什么项目应该考虑使用基于充血模型的-ddd-开发模式&#34;&gt;什么项目应该考虑使用基于充血模型的 DDD 开发模式？&lt;/h3&gt;
&lt;p&gt;适合业务复杂的系统开发。比如，包含各种利息计算模型、还款模型等复杂业务的金融系统。&lt;/p&gt;
&lt;p&gt;两种不同的开发模式会导致不同的开发流程。基于充血模型的 DDD 开发模式的开发流程，在应对复杂业务系统的开发的时候更加有优势。&lt;/p&gt;
&lt;p&gt;DDD 这种开发模式下，我们需要事先理清楚所有的业务，定义领域模型所包含的属性和方法。领域模型相当于可复用的业务中间层。新功能需求的开发，都基于之前定义好的这些领域模型来完成。&lt;/p&gt;
&lt;h2 id=&#34;12&#34;&gt;12&lt;/h2&gt;
&lt;h3 id=&#34;一个虚拟钱包系统&#34;&gt;一个虚拟钱包系统&lt;/h3&gt;
&lt;p&gt;充值、提现、支付、查询余额、查询交易流水。甚至还有冻结、透支、转赠等。&lt;/p&gt;
&lt;p&gt;整个钱包系统一部分单纯跟应用内的虚拟钱包账户打交道，另一部分单纯跟银行账户打交道。我们基于这样一个业务划分，给系统解耦，将整个钱包系统拆分为两个子系统：虚拟钱包系统和三方支付系统。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;image&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/107135323-ba50b780-6934-11eb-8b85-1738440f42bc.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;不保证数据的强一致性，只实现数据的最终一致性。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;VirtualWalletService&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 通过构造函数或者 IOC 框架注入&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWalletRepository&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWalletTransactionRepository&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWallet&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getVirtualWallet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWalletEntity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletEntity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getWalletEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWallet&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wallet&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;convert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wallet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BigDecimal&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getBalance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;virtualWalletRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getBalance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;debit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BigDecimal&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWalletEntity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletEntity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getWalletEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 贫血型&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// BigDecimal balance = walletEntity.getBalance();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// if (balance.compareTo(amount) &amp;lt; 0) {&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//   throw new NoSufficientBalanceException(...);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// }&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// walletRepo.updateBalance(walletId, balance.subtract(amount));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// DDD&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWallet&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wallet&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;convert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wallet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;debit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;updateBalance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wallet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;balance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;credit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BigDecimal&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWalletEntity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletEntity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getWalletEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 贫血型&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// BigDecimal balance = walletEntity.getBalance();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// walletRepo.updateBalance(walletId, balance.add(amount));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// DDD&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWallet&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wallet&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;convert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wallet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;credit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;updateBalance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;walletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wallet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;balance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;transfer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fromWalletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;toWalletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BigDecimal&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWalletTransactionEntity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionEntity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VirtualWalletTransactionEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setAmount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setCreateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setFromWalletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fromWalletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setToWalletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;toWalletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;TO_BE_EXECUTED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionId&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;saveTransaction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionEntity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;debit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fromWalletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;credit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;toWalletId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;InsufficientBalanceException&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;updateStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;CLOSED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ...rethrow exception e...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;updateStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;FAILED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ...rethrow exception e...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionRepo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;updateStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transactionId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;EXECUTED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;领域模型 VirtualWallet 类很单薄，包含的业务逻辑很简单。相对于原来的贫血模型的设计思路，这种充血模型的设计思路，貌似并没有太大优势。这也是大部分业务系统都使用基于贫血模型开发的原因。不过，如果虚拟钱包系统需要支持更复杂的业务逻辑，那充血模型的优势就显现出来了。比如，我们要支持透支一定额度和冻结部分余额的功能。这个时候，我们重新来看一下 VirtualWallet 类的实现代码。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="11">11</h2>
<p>领域驱动设计（Domain Driven Design，简称 DDD）。</p>
<h3 id="什么是基于贫血模型的传统开发模式">什么是基于贫血模型的传统开发模式？</h3>
<p>UserEntity 和 UserRepository 组成了数据访问层，UserBo 和 UserService 组成了业务逻辑层，UserVo 和 UserController 在这里属于接口层。</p>
<p>Service 层的数据和业务逻辑，被分割为 BO 和 Service 两个类中。像 UserBo 这样，只包含数据，不包含业务逻辑的类，就叫作贫血模型（Anemic Domain Model）。</p>
<p>这种贫血模型将数据与操作分离，破坏了面向对象的封装特性，是一种典型的面向过程的编程风格。</p>
<h3 id="什么是基于充血模型的-ddd-开发模式">什么是基于充血模型的 DDD 开发模式？</h3>
<p>领域驱动设计，即 DDD，主要是用来指导如何解耦业务系统，划分业务模块，定义业务领域模型及其交互。</p>
<p>基于贫血模型的传统的开发模式，重 Service 轻 BO；基于充血模型的 DDD 开发模式，轻 Service 重 Domain。</p>
<h3 id="为什么基于贫血模型的传统开发模式如此受欢迎">为什么基于贫血模型的传统开发模式如此受欢迎？</h3>
<ul>
<li>系统业务可能都比较简单，简单到就是基于 SQL 的 CRUD 操作</li>
<li>充血模型的设计要比贫血模型更加有难度。我们从一开始就要设计好针对数据要暴露哪些操作，定义哪些业务逻辑。而不是像贫血模型那样，我们只需要定义数据，之后有什么功能开发需求，我们就在 Service 层定义什么操作，不需要事先做太多设计。</li>
<li>思维已固化，转型有成本。</li>
</ul>
<h3 id="什么项目应该考虑使用基于充血模型的-ddd-开发模式">什么项目应该考虑使用基于充血模型的 DDD 开发模式？</h3>
<p>适合业务复杂的系统开发。比如，包含各种利息计算模型、还款模型等复杂业务的金融系统。</p>
<p>两种不同的开发模式会导致不同的开发流程。基于充血模型的 DDD 开发模式的开发流程，在应对复杂业务系统的开发的时候更加有优势。</p>
<p>DDD 这种开发模式下，我们需要事先理清楚所有的业务，定义领域模型所包含的属性和方法。领域模型相当于可复用的业务中间层。新功能需求的开发，都基于之前定义好的这些领域模型来完成。</p>
<h2 id="12">12</h2>
<h3 id="一个虚拟钱包系统">一个虚拟钱包系统</h3>
<p>充值、提现、支付、查询余额、查询交易流水。甚至还有冻结、透支、转赠等。</p>
<p>整个钱包系统一部分单纯跟应用内的虚拟钱包账户打交道，另一部分单纯跟银行账户打交道。我们基于这样一个业务划分，给系统解耦，将整个钱包系统拆分为两个子系统：虚拟钱包系统和三方支付系统。</p>
<p><img alt="image" loading="lazy" src="https://user-images.githubusercontent.com/9289792/107135323-ba50b780-6934-11eb-8b85-1738440f42bc.png"></p>
<p>不保证数据的强一致性，只实现数据的最终一致性。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">VirtualWalletService</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c1">// 通过构造函数或者 IOC 框架注入</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">VirtualWalletRepository</span><span class="w"> </span><span class="n">walletRepo</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">VirtualWalletTransactionRepository</span><span class="w"> </span><span class="n">transactionRepo</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="n">VirtualWallet</span><span class="w"> </span><span class="nf">getVirtualWallet</span><span class="p">(</span><span class="n">Long</span><span class="w"> </span><span class="n">walletId</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">VirtualWalletEntity</span><span class="w"> </span><span class="n">walletEntity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">walletRepo</span><span class="p">.</span><span class="na">getWalletEntity</span><span class="p">(</span><span class="n">walletId</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">VirtualWallet</span><span class="w"> </span><span class="n">wallet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">convert</span><span class="p">(</span><span class="n">walletEntity</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">wallet</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">getBalance</span><span class="p">(</span><span class="n">Long</span><span class="w"> </span><span class="n">walletId</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">virtualWalletRepo</span><span class="p">.</span><span class="na">getBalance</span><span class="p">(</span><span class="n">walletId</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">debit</span><span class="p">(</span><span class="n">Long</span><span class="w"> </span><span class="n">walletId</span><span class="p">,</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">VirtualWalletEntity</span><span class="w"> </span><span class="n">walletEntity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">walletRepo</span><span class="p">.</span><span class="na">getWalletEntity</span><span class="p">(</span><span class="n">walletId</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 贫血型</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// BigDecimal balance = walletEntity.getBalance();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// if (balance.compareTo(amount) &lt; 0) {</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">//   throw new NoSufficientBalanceException(...);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// }</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// walletRepo.updateBalance(walletId, balance.subtract(amount));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// DDD</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">VirtualWallet</span><span class="w"> </span><span class="n">wallet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">convert</span><span class="p">(</span><span class="n">walletEntity</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">wallet</span><span class="p">.</span><span class="na">debit</span><span class="p">(</span><span class="n">amount</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">walletRepo</span><span class="p">.</span><span class="na">updateBalance</span><span class="p">(</span><span class="n">walletId</span><span class="p">,</span><span class="w"> </span><span class="n">wallet</span><span class="p">.</span><span class="na">balance</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">credit</span><span class="p">(</span><span class="n">Long</span><span class="w"> </span><span class="n">walletId</span><span class="p">,</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">VirtualWalletEntity</span><span class="w"> </span><span class="n">walletEntity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">walletRepo</span><span class="p">.</span><span class="na">getWalletEntity</span><span class="p">(</span><span class="n">walletId</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 贫血型</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// BigDecimal balance = walletEntity.getBalance();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// walletRepo.updateBalance(walletId, balance.add(amount));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// DDD</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">VirtualWallet</span><span class="w"> </span><span class="n">wallet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">convert</span><span class="p">(</span><span class="n">walletEntity</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">wallet</span><span class="p">.</span><span class="na">credit</span><span class="p">(</span><span class="n">amount</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">walletRepo</span><span class="p">.</span><span class="na">updateBalance</span><span class="p">(</span><span class="n">walletId</span><span class="p">,</span><span class="w"> </span><span class="n">wallet</span><span class="p">.</span><span class="na">balance</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">transfer</span><span class="p">(</span><span class="n">Long</span><span class="w"> </span><span class="n">fromWalletId</span><span class="p">,</span><span class="w"> </span><span class="n">Long</span><span class="w"> </span><span class="n">toWalletId</span><span class="p">,</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">VirtualWalletTransactionEntity</span><span class="w"> </span><span class="n">transactionEntity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">VirtualWalletTransactionEntity</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">transactionEntity</span><span class="p">.</span><span class="na">setAmount</span><span class="p">(</span><span class="n">amount</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">transactionEntity</span><span class="p">.</span><span class="na">setCreateTime</span><span class="p">(</span><span class="n">System</span><span class="p">.</span><span class="na">currentTimeMillis</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">transactionEntity</span><span class="p">.</span><span class="na">setFromWalletId</span><span class="p">(</span><span class="n">fromWalletId</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">transactionEntity</span><span class="p">.</span><span class="na">setToWalletId</span><span class="p">(</span><span class="n">toWalletId</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">transactionEntity</span><span class="p">.</span><span class="na">setStatus</span><span class="p">(</span><span class="n">Status</span><span class="p">.</span><span class="na">TO_BE_EXECUTED</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Long</span><span class="w"> </span><span class="n">transactionId</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">transactionRepo</span><span class="p">.</span><span class="na">saveTransaction</span><span class="p">(</span><span class="n">transactionEntity</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">debit</span><span class="p">(</span><span class="n">fromWalletId</span><span class="p">,</span><span class="w"> </span><span class="n">amount</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">credit</span><span class="p">(</span><span class="n">toWalletId</span><span class="p">,</span><span class="w"> </span><span class="n">amount</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">InsufficientBalanceException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">transactionRepo</span><span class="p">.</span><span class="na">updateStatus</span><span class="p">(</span><span class="n">transactionId</span><span class="p">,</span><span class="w"> </span><span class="n">Status</span><span class="p">.</span><span class="na">CLOSED</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c1">// ...rethrow exception e...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">Exception</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">transactionRepo</span><span class="p">.</span><span class="na">updateStatus</span><span class="p">(</span><span class="n">transactionId</span><span class="p">,</span><span class="w"> </span><span class="n">Status</span><span class="p">.</span><span class="na">FAILED</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c1">// ...rethrow exception e...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">transactionRepo</span><span class="p">.</span><span class="na">updateStatus</span><span class="p">(</span><span class="n">transactionId</span><span class="p">,</span><span class="w"> </span><span class="n">Status</span><span class="p">.</span><span class="na">EXECUTED</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>领域模型 VirtualWallet 类很单薄，包含的业务逻辑很简单。相对于原来的贫血模型的设计思路，这种充血模型的设计思路，貌似并没有太大优势。这也是大部分业务系统都使用基于贫血模型开发的原因。不过，如果虚拟钱包系统需要支持更复杂的业务逻辑，那充血模型的优势就显现出来了。比如，我们要支持透支一定额度和冻结部分余额的功能。这个时候，我们重新来看一下 VirtualWallet 类的实现代码。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">VirtualWallet</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">Long</span><span class="w"> </span><span class="n">id</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">Long</span><span class="w"> </span><span class="n">createTime</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">currentTimeMillis</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">balance</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">isAllowedOverdraft</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">overdraftAmount</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">frozenAmount</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="nf">VirtualWallet</span><span class="p">(</span><span class="n">Long</span><span class="w"> </span><span class="n">preAllocatedId</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">this</span><span class="p">.</span><span class="na">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">preAllocatedId</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">balance</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">balance</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">getAvaliableBalance</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">totalAvaliableBalance</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">balance</span><span class="p">.</span><span class="na">subtract</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">frozenAmount</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">isAllowedOverdraft</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">totalAvaliableBalance</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">overdraftAmount</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">totalAvaliableBalance</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">debit</span><span class="p">(</span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">credit</span><span class="p">(</span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">freeze</span><span class="p">(</span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">unfreeze</span><span class="p">(</span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">increaseOverdraftAmount</span><span class="p">(</span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">decreaseOverdraftAmount</span><span class="p">(</span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">closeOverdraft</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">openOverdraft</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>如果功能继续演进，我们可以增加更加细化的冻结策略、透支策略、支持钱包账号（VirtualWallet id 字段）自动生成的逻辑（不是通过构造函数经外部传入 ID，而是通过分布式 ID 生成算法来自动生成 ID）等等。</p>
<h3 id="辩证思考与灵活应用">辩证思考与灵活应用</h3>
<p>两种代码设计与实现中，并没有完全将 Service 类去掉，这是为什么？或者说，Service 类在这种情况下担当的职责是什么？哪些功能逻辑会放到 Service 类中？</p>
<ol>
<li>Service 类负责与 Repository 交流。获取数据库中的数据，转化成领域模型 VirtualWallet，然后由领域模型 VirtualWallet 来完成业务逻辑，最后调用 Repository 类的方法，将数据存回数据库。之所以让 VirtualWalletService 类与 Repository 打交道，而不是让领域模型 VirtualWallet 与 Repository 打交道，那是因为我们想保持领域模型的独立性，不与任何其他层的代码（Repository 层的代码）或开发框架（比如 Spring、MyBatis）耦合在一起，将流程性的代码逻辑（比如从 DB 中取数据、映射数据）与领域模型的业务逻辑解耦，让领域模型更加可复用。</li>
<li>Service 类负责跨领域模型的业务聚合功能。VirtualWalletService 类中的 transfer() 转账函数会涉及两个钱包的操作，因此这部分业务逻辑无法放到 VirtualWallet 类中，所以，我们暂且把转账业务放到 VirtualWalletService 类中了。当然，虽然功能演进，使得转账业务变得复杂起来之后，我们也可以将转账业务抽取出来，设计成一个独立的领域模型。</li>
<li>Service 类负责一些非功能性及与三方系统交互的工作。比如幂等、事务、发邮件、发消息、记录日志、调用其他系统的 RPC 接口等，都可以放到 Service 类中。</li>
</ol>
<h3 id="小结">小结</h3>
<p>基于充血模型的 DDD 开发模式跟基于贫血模型的传统开发模式相比，主要区别在 Service 层。在基于充血模型的开发模式下，我们将部分原来在 Service 类中的业务逻辑移动到了一个充血的 Domain 领域模型中，让 Service 类的实现依赖这个 Domain 类。</p>
<p>在基于充血模型的 DDD 开发模式下，Service 类并不会完全移除，而是负责一些不适合放在 Domain 类中的功能。比如，负责与 Repository 层打交道、跨领域模型的业务聚合功能、幂等事务等非功能性的工作。</p>
<p>基于充血模型的 DDD 开发模式跟基于贫血模型的传统开发模式相比，Controller 层和 Repository 层的代码基本上相同。这是因为，Repository 层的 Entity 生命周期有限，Controller 层的 VO 只是单纯作为一种 DTO。两部分的业务逻辑都不会太复杂。业务逻辑主要集中在 Service 层。所以，Repository 层和 Controller 层继续沿用贫血模型的设计思路是没有问题的。</p>
<p>遗留问题：Entity 与 Domain 的转换应该放在哪里？</p>
<h2 id="13-如何对接口鉴权这样一个功能开发做面向对象分析">13 如何对接口鉴权这样一个功能开发做面向对象分析？</h2>
<p>面向对象分析（OOA）、面向对象设计（OOD）、面向对象编程（OOP），是面向对象开发的三个主要环节。</p>
<p>我们需要通过沟通、挖掘、分析、假设、梳理，搞清楚具体的需求有哪些，哪些是现在要做的，哪些是未来可能要做的，哪些是不用考虑做的。</p>
<blockquote>
<p>加密之后的密码及 AppID，可能被 <strong>重放攻击</strong>。</p>
</blockquote>
<p>调用方将请求接口的 URL 跟 AppID、密码拼接在一起，然后进行加密，生成一个 token。</p>
<p><img alt="image" loading="lazy" src="https://user-images.githubusercontent.com/9289792/107141538-3ca4a000-6964-11eb-903f-404295e31ebf.png"></p>
<p>这样的设计仍然存在重放攻击的风险。</p>
<p>为了解决这个问题，我们可以进一步优化 token 生成算法，引入一个随机变量，让每次接口请求生成的 token 都不一样。</p>
<p>微服务端在收到这些数据之后，会验证当前时间戳跟传递过来的时间戳，是否在一定的时间窗口内（比如一分钟）。</p>
<p><img alt="image" loading="lazy" src="https://user-images.githubusercontent.com/9289792/107141588-ba68ab80-6964-11eb-8cc4-c84ddf91a6d4.png"></p>
<h2 id="14">14</h2>
<ol>
<li>把 URL、AppID、密码、时间戳拼接为一个字符串；</li>
<li>对字符串通过加密算法加密生成 token；</li>
<li>将 token、AppID、时间戳拼接到 URL 中，形成新的 URL；</li>
<li>解析 URL，得到 token、AppID、时间戳等信息；</li>
<li>从存储中取出 AppID 和对应的密码；</li>
<li>根据时间戳判断 token 是否过期失效；</li>
<li>验证两个 token 是否匹配；</li>
</ol>
<p>1、2、6、7 都是跟 token 有关，负责 token 的生成、验证；3、4 都是在处理 URL，负责 URL 的拼接、解析；5 是操作 AppID 和密码，负责从存储中读取 AppID 和密码。AuthToken、Url、CredentialStorage。</p>
<h2 id="15">15</h2>
]]></content:encoded>
    </item>
    <item>
      <title>设计模式之美 Part1</title>
      <link>https://zyf.im/2021/02/05/the-beauty-of-design-patterns-part1/</link>
      <pubDate>Fri, 05 Feb 2021 11:00:41 +0000</pubDate>
      <guid>https://zyf.im/2021/02/05/the-beauty-of-design-patterns-part1/</guid>
      <description>&lt;h2 id=&#34;00&#34;&gt;00&lt;/h2&gt;
&lt;p&gt;KISS 原则（Keep It Simple and Stupid），这个原则理解起来很简单，一看貌似就懂了，那我问你，怎样的代码才算是足够简单呢？怎样才算不够简单需要优化呢？&lt;/p&gt;
&lt;p&gt;“Talk is cheap, show me the code.”&lt;/p&gt;
&lt;h2 id=&#34;01&#34;&gt;01&lt;/h2&gt;
&lt;p&gt;为什么要学习设计模式：应对面试中的设计模式相关问题；告别写被人吐槽的烂代码；提高复杂代码的设计和开发能力；让读源码、学框架事半功倍；为你的职场发展做铺垫。&lt;/p&gt;
&lt;h2 id=&#34;02&#34;&gt;02&lt;/h2&gt;
&lt;p&gt;灵活性（flexibility）、可扩展性（extensibility）、可维护性（maintainability）、可读性（readability）、可理解性（understandability）、易修改性（changeability）、可复用（reusability）、可测试性（testability）、模块化（modularity）、高内聚低耦合（high cohesion loose coupling）、高效（high effciency）、高性能（highperformance）、安全性（security）、兼容性（compatibility）、易用性（usability）、整洁（clean）、清晰（clarity）、简单（simple）、直接（straightforward）、少即是多（less code is more）、文档详尽（well-documented）、分层清晰（well-layered）、正确性（correctness、bug free）、健壮性（robustness）、鲁棒性（robustness）、可用性（reliability）、可伸缩性（scalability）、稳定性（stability）、优雅（elegant）、好（good）、坏（bad）&lt;/p&gt;
&lt;p&gt;我们并不能通过单一的维度去评价一段代码写的好坏。比如，即使一段代码的可扩展性很好，但可读性很差，那我们也不能说这段代码质量高。&lt;/p&gt;
&lt;p&gt;如果用数字来量化代码的可读性的话，它应该是一个连续的区间值，而非 0、1 这样的离散值。&lt;/p&gt;
&lt;p&gt;代码质量的评价有很强的主观性。&lt;/p&gt;
&lt;p&gt;有些词语过于笼统、抽象，比较偏向对于整体的描述，比如优雅、好、坏、整洁、清晰等；有些过于细节、偏重方法论，比如模块化、高内聚低耦合、文档详尽、分层清晰等；有些可能并不仅仅局限于编码，跟架构设计等也有关系，比如可伸缩性、可用性、稳定性等。&lt;/p&gt;
&lt;h3 id=&#34;可维护性maintainability&#34;&gt;可维护性（maintainability）&lt;/h3&gt;
&lt;p&gt;破坏原有代码设计、不引入新的 bug 的情况下，能够快速地修改或者添加代码。与之相反，修改或者添加代码需要冒着极大的引入新 bug 的风险，并且需要花费很长的时间才能完成。&lt;/p&gt;
&lt;p&gt;码分层清晰、模块化好、高内聚低耦合、遵从基于接口而非实现编程的设计原则等等，那就可能意味着代码易维护。&lt;/p&gt;
&lt;h3 id=&#34;可读性readability&#34;&gt;可读性（readability）&lt;/h3&gt;
&lt;p&gt;“任何傻瓜都会编写计算机能理解的代码。好的程序员能够编写人能够理解的代码。”&lt;/p&gt;
&lt;p&gt;是否符合编码规范、命名是否达意、注释是否详尽、函数是否长短合适、模块划分是否清晰、是否符合高内聚低耦合等等。&lt;/p&gt;
&lt;p&gt;code review 是一个很好的测验代码可读性的手段。如果你的同事可以轻松地读懂你写的代码，那说明你的代码可读性很好；如果同事在读你的代码时，有很多疑问，那就说明你的代码可读性有待提高了。&lt;/p&gt;
&lt;h3 id=&#34;可扩展性extensibility&#34;&gt;可扩展性（extensibility）&lt;/h3&gt;
&lt;p&gt;我们在不修改或少量修改原有代码的情况下，通过扩展的方式添加新的功能代码。说直白点就是，代码预留了一些功能扩展点，你可以把新功能代码，直接插到扩展点上，而不需要因为要添加一个功能而大动干戈，改动大量的原始代码。&lt;/p&gt;
&lt;p&gt;“对修改关闭，对扩展开放”。&lt;/p&gt;
&lt;h3 id=&#34;灵活性flexibility&#34;&gt;灵活性（flexibility）&lt;/h3&gt;
&lt;p&gt;如果一段代码易扩展、易复用或者易用，我们都可以称这段代码写得比较灵活。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当我们添加一个新的功能代码的时候，原有的代码已经预留好了扩展点，我们不需要修改原有的代码，只要在扩展点上添加新的代码即可。这个时候，我们除了可以说代码易扩展，还可以说代码写得好灵活。&lt;/li&gt;
&lt;li&gt;当我们要实现一个功能的时候，发现原有代码中，已经抽象出了很多底层可以复用的模块、类等代码，我们可以拿来直接使用。这个时候，我们除了可以说代码易复用之外，还可以说代码写得好灵活。&lt;/li&gt;
&lt;li&gt;当我们使用某组接口的时候，如果这组接口可以应对各种使用场景，满足各种不同的需求，我们除了可以说接口易用之外，还可以说这个接口设计得好灵活或者代码写得好灵活。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;简洁性simplicity&#34;&gt;简洁性（simplicity）&lt;/h3&gt;
&lt;p&gt;尽量保持代码简单。代码简单、逻辑清晰，也就意味着易读、易维护。我们在编写代码的时候，往往也会把简单、清晰放到首位。&lt;/p&gt;
&lt;p&gt;KISS 原则，思从深而行从简，真正的高手能云淡风轻地用最简单的方法解决最复杂的问题。这也是一个编程老手跟编程新手的本质区别之一。&lt;/p&gt;
&lt;h3 id=&#34;可复用性reusability&#34;&gt;可复用性（reusability）&lt;/h3&gt;
&lt;p&gt;尽量减少重复代码的编写，复用已有的代码。&lt;/p&gt;
&lt;p&gt;当讲到面向对象特性的时候，我们会讲到继承、多态存在的目的之一，就是为了提高代码的可复用性；当讲到设计原则的时候，我们会讲到单一职责原则也跟代码的可复用性相关；当讲到重构技巧的时候，我们会讲到解耦、高内聚、模块化等都能提高代码的可复用性。可见，可复用性也是一个非常重要的代码评价标准，是很多设计原则、思想、模式等所要达到的最终效果。&lt;/p&gt;
&lt;p&gt;DRY（Don’t Repeat Yourself）设计原则。&lt;/p&gt;
&lt;h3 id=&#34;可测试性testability&#34;&gt;可测试性（testability）&lt;/h3&gt;
&lt;p&gt;代码可测试性的好坏，能从侧面上非常准确地反应代码质量的好坏。代码的可测试性差，比较难写单元测试，那基本上就能说明代码设计得有问题。&lt;/p&gt;
&lt;h2 id=&#34;03&#34;&gt;03&lt;/h2&gt;
&lt;h3 id=&#34;面向对象&#34;&gt;面向对象&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;面向对象的四大特性：封装、抽象、继承、多态面&lt;/li&gt;
&lt;li&gt;向对象编程与面向过程编程的区别和联系&lt;/li&gt;
&lt;li&gt;面向对象分析、面向对象设计、面向对象编程&lt;/li&gt;
&lt;li&gt;接口和抽象类的区别以及各自的应用场景&lt;/li&gt;
&lt;li&gt;基于接口而非实现编程的设计思想&lt;/li&gt;
&lt;li&gt;多用组合少用继承的设计思想&lt;/li&gt;
&lt;li&gt;面向过程的贫血模型和面向对象的充血模型&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;设计原则&#34;&gt;设计原则&lt;/h3&gt;
&lt;p&gt;指导我们代码设计的一些经验总结。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="00">00</h2>
<p>KISS 原则（Keep It Simple and Stupid），这个原则理解起来很简单，一看貌似就懂了，那我问你，怎样的代码才算是足够简单呢？怎样才算不够简单需要优化呢？</p>
<p>“Talk is cheap, show me the code.”</p>
<h2 id="01">01</h2>
<p>为什么要学习设计模式：应对面试中的设计模式相关问题；告别写被人吐槽的烂代码；提高复杂代码的设计和开发能力；让读源码、学框架事半功倍；为你的职场发展做铺垫。</p>
<h2 id="02">02</h2>
<p>灵活性（flexibility）、可扩展性（extensibility）、可维护性（maintainability）、可读性（readability）、可理解性（understandability）、易修改性（changeability）、可复用（reusability）、可测试性（testability）、模块化（modularity）、高内聚低耦合（high cohesion loose coupling）、高效（high effciency）、高性能（highperformance）、安全性（security）、兼容性（compatibility）、易用性（usability）、整洁（clean）、清晰（clarity）、简单（simple）、直接（straightforward）、少即是多（less code is more）、文档详尽（well-documented）、分层清晰（well-layered）、正确性（correctness、bug free）、健壮性（robustness）、鲁棒性（robustness）、可用性（reliability）、可伸缩性（scalability）、稳定性（stability）、优雅（elegant）、好（good）、坏（bad）</p>
<p>我们并不能通过单一的维度去评价一段代码写的好坏。比如，即使一段代码的可扩展性很好，但可读性很差，那我们也不能说这段代码质量高。</p>
<p>如果用数字来量化代码的可读性的话，它应该是一个连续的区间值，而非 0、1 这样的离散值。</p>
<p>代码质量的评价有很强的主观性。</p>
<p>有些词语过于笼统、抽象，比较偏向对于整体的描述，比如优雅、好、坏、整洁、清晰等；有些过于细节、偏重方法论，比如模块化、高内聚低耦合、文档详尽、分层清晰等；有些可能并不仅仅局限于编码，跟架构设计等也有关系，比如可伸缩性、可用性、稳定性等。</p>
<h3 id="可维护性maintainability">可维护性（maintainability）</h3>
<p>破坏原有代码设计、不引入新的 bug 的情况下，能够快速地修改或者添加代码。与之相反，修改或者添加代码需要冒着极大的引入新 bug 的风险，并且需要花费很长的时间才能完成。</p>
<p>码分层清晰、模块化好、高内聚低耦合、遵从基于接口而非实现编程的设计原则等等，那就可能意味着代码易维护。</p>
<h3 id="可读性readability">可读性（readability）</h3>
<p>“任何傻瓜都会编写计算机能理解的代码。好的程序员能够编写人能够理解的代码。”</p>
<p>是否符合编码规范、命名是否达意、注释是否详尽、函数是否长短合适、模块划分是否清晰、是否符合高内聚低耦合等等。</p>
<p>code review 是一个很好的测验代码可读性的手段。如果你的同事可以轻松地读懂你写的代码，那说明你的代码可读性很好；如果同事在读你的代码时，有很多疑问，那就说明你的代码可读性有待提高了。</p>
<h3 id="可扩展性extensibility">可扩展性（extensibility）</h3>
<p>我们在不修改或少量修改原有代码的情况下，通过扩展的方式添加新的功能代码。说直白点就是，代码预留了一些功能扩展点，你可以把新功能代码，直接插到扩展点上，而不需要因为要添加一个功能而大动干戈，改动大量的原始代码。</p>
<p>“对修改关闭，对扩展开放”。</p>
<h3 id="灵活性flexibility">灵活性（flexibility）</h3>
<p>如果一段代码易扩展、易复用或者易用，我们都可以称这段代码写得比较灵活。</p>
<ul>
<li>当我们添加一个新的功能代码的时候，原有的代码已经预留好了扩展点，我们不需要修改原有的代码，只要在扩展点上添加新的代码即可。这个时候，我们除了可以说代码易扩展，还可以说代码写得好灵活。</li>
<li>当我们要实现一个功能的时候，发现原有代码中，已经抽象出了很多底层可以复用的模块、类等代码，我们可以拿来直接使用。这个时候，我们除了可以说代码易复用之外，还可以说代码写得好灵活。</li>
<li>当我们使用某组接口的时候，如果这组接口可以应对各种使用场景，满足各种不同的需求，我们除了可以说接口易用之外，还可以说这个接口设计得好灵活或者代码写得好灵活。</li>
</ul>
<h3 id="简洁性simplicity">简洁性（simplicity）</h3>
<p>尽量保持代码简单。代码简单、逻辑清晰，也就意味着易读、易维护。我们在编写代码的时候，往往也会把简单、清晰放到首位。</p>
<p>KISS 原则，思从深而行从简，真正的高手能云淡风轻地用最简单的方法解决最复杂的问题。这也是一个编程老手跟编程新手的本质区别之一。</p>
<h3 id="可复用性reusability">可复用性（reusability）</h3>
<p>尽量减少重复代码的编写，复用已有的代码。</p>
<p>当讲到面向对象特性的时候，我们会讲到继承、多态存在的目的之一，就是为了提高代码的可复用性；当讲到设计原则的时候，我们会讲到单一职责原则也跟代码的可复用性相关；当讲到重构技巧的时候，我们会讲到解耦、高内聚、模块化等都能提高代码的可复用性。可见，可复用性也是一个非常重要的代码评价标准，是很多设计原则、思想、模式等所要达到的最终效果。</p>
<p>DRY（Don’t Repeat Yourself）设计原则。</p>
<h3 id="可测试性testability">可测试性（testability）</h3>
<p>代码可测试性的好坏，能从侧面上非常准确地反应代码质量的好坏。代码的可测试性差，比较难写单元测试，那基本上就能说明代码设计得有问题。</p>
<h2 id="03">03</h2>
<h3 id="面向对象">面向对象</h3>
<ul>
<li>面向对象的四大特性：封装、抽象、继承、多态面</li>
<li>向对象编程与面向过程编程的区别和联系</li>
<li>面向对象分析、面向对象设计、面向对象编程</li>
<li>接口和抽象类的区别以及各自的应用场景</li>
<li>基于接口而非实现编程的设计思想</li>
<li>多用组合少用继承的设计思想</li>
<li>面向过程的贫血模型和面向对象的充血模型</li>
</ul>
<h3 id="设计原则">设计原则</h3>
<p>指导我们代码设计的一些经验总结。</p>
<ul>
<li>SOLID 原则 SRP 单一职责原则</li>
<li>SOLID 原则 OCP 开闭原则</li>
<li>SOLID 原则 SP 里式替换原则</li>
<li>SOLID 原则 ISP 接口隔离原则</li>
<li>SOLID 原则 DIP 依赖倒置原则</li>
<li>DRY 原则、KISS 原则、YAGNI 原则、LOD 法则</li>
</ul>
<h3 id="设计模式">设计模式</h3>
<p>针对软件开发中经常遇到的一些设计问题，总结出来的一套解决方案或者设计思路。</p>
<p>大部分设计模式要解决的都是代码的可扩展性问题。</p>
<ol>
<li>创建型 常用的有：单例模式、工厂模式（工厂方法和抽象工厂）、建造者模式。不常用的有：原型模式。</li>
<li>结构型 常用的有：代理模式、桥接模式、装饰者模式、适配器模式。不常用的有：门面模式、组合模式、享元模式。</li>
<li>行为型 常用的有：观察者模式、模板模式、策略模式、职责链模式、迭代器模式、状态模式。不常用的有：访问者模式、备忘录模式、命令模式、解释器模式、中介模式。</li>
</ol>
<h3 id="编程规范">编程规范</h3>
<p>主要解决的是代码的可读性问题。</p>
<h3 id="代码重构">代码重构</h3>
<p>在软件开发中，只要软件在不停地迭代，就没有一劳永逸的设计。</p>
<p>在开发初期，除非特别必须，我们一定不要过度设计，应用复杂的设计模式。而是当代码出现问题的时候，我们再针对问题，应用原则和模式进行重构。这样就能有效避免前期的过度设计。</p>
<ul>
<li>重构的目的（why）、对象（what）、时机（when）、方法（how）；</li>
<li>保证重构不出错的技术手段：单元测试和代码的可测试性；</li>
<li>两种不同规模的重构：大重构（大规模高层次）和小重构（小规模低层次）。</li>
</ul>
<h3 id="五者联系">五者联系</h3>
<p>面向对象编程因为其具有丰富的特性（封装、抽象、继承、多态），可以实现很多复杂的设计思路，是很多设计原则、设计模式等编码实现的基础。</p>
<p>设计原则是指导我们代码设计的一些经验总结，对于某些场景下，是否应该应用某种设计模式，具有指导意义。比如，“开闭原则”是很多设计模式（策略、模板等）的指导原则。</p>
<p>设计模式是针对软件开发中经常遇到的一些设计问题，总结出来的一套解决方案或者设计思路。应用设计模式的主要目的是提高代码的可扩展性。从抽象程度上来讲，设计原则比设计模式更抽象。设计模式更加具体、更加可执行。</p>
<p>编程规范主要解决的是代码的可读性问题。编码规范相对于设计原则、设计模式，更加具体、更加偏重代码细节、更加能落地。持续的小重构依赖的理论基础主要就是编程规范。</p>
<p>重构作为保持代码质量不下降的有效手段，利用的就是面向对象、设计原则、设计模式、编码规范这些理论。</p>
<p><img alt="image" loading="lazy" src="https://user-images.githubusercontent.com/9289792/106997160-59eb3a00-67bd-11eb-8f71-db5336d3e77f.png"></p>
<h2 id="04">04</h2>
<p>面向对象编程是一种编程范式或编程风格。它以类或对象作为组织代码的基本单元，并将封装、抽象、继承、多态四个特性，作为代码设计和实现的基石。</p>
<h2 id="05">05</h2>
<h3 id="封装encapsulation">封装（Encapsulation）</h3>
<p>封装也叫作信息隐藏或者数据访问保护。类通过暴露有限的访问接口，授权外部仅能通过类提供的方式来访问内部信息或者数据。</p>
<p>封装特性存在的意义，一方面是保护数据不被随意修改，提高代码的可维护性；另一方面是仅暴露有限的必要接口，提高类的易用性。</p>
<h3 id="抽象abstraction">抽象（Abstraction）</h3>
<p>抽象可以通过接口类或者抽象类来实现，但也并不需要特殊的语法机制来支持。</p>
<p>抽象存在的意义，一方面是提高代码的可扩展性、维护性，修改实现不需要改变定义，减少代码的改动范围；另一方面，它也是处理复杂系统的有效手段，能有效地过滤掉不必要关注的信息。</p>
<p>提供“函数”这一非常基础的语法机制，就可以实现抽象特性、所以，它没有很强的“特异性”，有时候并不被看作面向对象编程的特性之一。</p>
<p>在定义（或者叫命名）类的方法的时候，也要有抽象思维，不要在方法定义中，暴露太多的实现细节，以保证在某个时间点需要改变方法的实现逻辑的时候，不用去修改其定义。getPictureUrl 好于 getAliyunPictureUrl。</p>
<h3 id="继承inheritance">继承（Inheritance）</h3>
<p>继承是用来表示类之间的 is-a 关系。继承主要是用来解决代码复用的问题。</p>
<p>多用组合少用继承。</p>
<h3 id="多态polymorphism">多态（Polymorphism）</h3>
<p>多态是指子类可以替换父类，在实际的代码运行过程中，调用子类的方法实现。</p>
<p>多态这种特性也需要编程语言提供特殊的语法机制来实现，比如继承、接口类、duck-typing。多态可以提高代码的扩展性和复用性，是很多设计模式、设计原则、编程技巧的代码实现基础。</p>
<p>只要两个类具有相同的方法，就可以实现多态，并不要求两个类之间有任何关系，这就是所谓的 duck-typing，是一些动态语言所特有的语法机制。</p>
<h2 id="06">06</h2>
<p>相较于面向对象编程以类为组织代码的基本单元，面向过程编程则是以过程（或方法）作为组织代码的基本单元。它最主要的特点就是数据和方法相分离。相较于面向对象编程语言，面向过程编程语言最大的特点就是不支持丰富的面向对象编程特性，比如继承、多态、封装。</p>
<p>面向对象编程相比面向过程编程有哪些优势？</p>
<ul>
<li>对于大规模复杂程序的开发，程序的处理流程并非单一的一条主线，而是错综复杂的网状结构。</li>
<li>面向对象编程比起面向过程编程，更能应对这种复杂类型的程序开发。面向对象编程相比面向过程编程，具有更加丰富的特性（封装、抽象、继承、多态）。利用这些特性编写出来的代码，更加易扩展、易复用、易维护。</li>
<li>从编程语言跟机器打交道的方式的演进规律中，我们可以总结出：面向对象编程语言比起面向过程编程语言，更加人性化、更加高级、更加智能。</li>
</ul>
<h2 id="07">07</h2>
<h3 id="滥用-gettersetter-方法">滥用 getter、setter 方法</h3>
<p>尽管 getter 方法相对 setter 方法要安全些，但是如果返回的是集合容器（比如例子中的 List 容器），也要防范集合内部数据被修改的危险。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">ShoppingCart</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// ... 省略其他代码...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">ShoppingCartItem</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">getItems</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">Collections</span><span class="p">.</span><span class="na">unmodifiableList</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">items</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">UnmodifiableList</span><span class="o">&lt;</span><span class="n">E</span><span class="o">&gt;</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">UnmodifiableCollection</span><span class="o">&lt;</span><span class="n">E</span><span class="o">&gt;</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">E</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">add</span><span class="p">(</span><span class="n">E</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">UnsupportedOperationException</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">clear</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">UnsupportedOperationException</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// ... 省略其他代码...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="滥用全局变量和全局方法">滥用全局变量和全局方法</h3>
<p>Constants 类、Utils 类的设计尽量能做到职责单一，定义一些细化的小类。</p>
<p>静态成员变量归属于类上的数据，被所有的实例化对象所共享，也相当于一定程度上的全局变量。</p>
<p>静态方法将方法与数据分离，破坏了封装特性，是典型的面向过程风格。</p>
<p>只包含静态方法不包含任何属性的 Utils 类，是彻彻底底的面向过程的编程风格。要尽量避免滥用，不要不加思考地随意去定义 Utils 类。</p>
<h3 id="定义数据和方法分离的类">定义数据和方法分离的类</h3>
<p>Controller 层负责暴露接口给前端调用，Service 层负责核心业务逻辑，Repository 层负责数据读写。</p>
<p>而在每一层中，我们又会定义相应的 VO（View Object）、BO（Business Object）、Entity。一般情况下，VO、BO、Entity 中只会定义数据，不会定义方法，所有操作这些数据的业务逻辑都定义在对应的 Controller 类、Service 类、Repository 类中。这就是典型的面向过程的编程风格。</p>
<blockquote>
<p>实际上，这种开发模式叫作基于 <code>贫血模型的开发模式</code>，也是我们现在非常常用的一种 Web 项目的开发模式。看到这里，你内心里应该有很多疑惑吧？既然这种开发模式明显违背面向对象的编程风格，为什么大部分 Web 项目都是基于这种开发模式来开发呢？</p>
</blockquote>
<h3 id="在面向对象编程中为什么容易写出面向过程风格的代码">在面向对象编程中，为什么容易写出面向过程风格的代码？</h3>
<p>面向过程编程风格恰恰符合人的这种流程化思维方式。而面向对象编程风格正好相反。它是一种自底向上的思考方式。</p>
<p>它不是先去按照执行流程来分解任务，而是将任务翻译成一个一个的小的模块（也就是类），设计类之间的交互，最后按照流程将类组装起来，完成整个任务。</p>
<p>在面向对象编程中，类的设计还是挺需要技巧，挺需要一定设计经验的。你要去思考如何封装合适的数据和方法到一个类里，如何设计类之间的关系，如何设计类之间的交互等等诸多设计问题。</p>
<h3 id="面向过程编程及面向过程编程语言就真的无用武之地了吗">面向过程编程及面向过程编程语言就真的无用武之地了吗？</h3>
<p>如果我们开发的是微小程序，或者是一个数据处理相关的代码，以算法为主，数据为辅，那脚本式的面向过程的编程风格就更适合一些。</p>
<h2 id="08">08</h2>
<h3 id="抽象类和接口的语法特性">抽象类和接口的语法特性</h3>
<p>抽象类不允许被实例化，只能被继承。它可以包含属性和方法。方法既可以包含代码实现，也可以不包含代码实现。不包含代码实现的方法叫作抽象方法。子类继承抽象类，必须实现抽象类中的所有抽象方法。接口不能包含属性，只能声明方法，方法不能包含代码实现。类实现接口的时候，必须实现接口中声明的所有方法。</p>
<h3 id="抽象类和接口存在的意义">抽象类和接口存在的意义</h3>
<p>抽象类是对成员变量和方法的抽象，是一种 is-a 关系，是为了解决代码复用问题。接口仅仅是对方法的抽象，是一种 has-a 关系，表示具有某一组行为特性，是为了解决解耦问题，隔离接口和具体的实现，提高代码的扩展性。</p>
<p>从类的继承层次上来看，抽象类是一种自下而上的设计思路，先有子类的代码重复，然后再抽象成上层的父类（也就是抽象类）。而接口正好相反，它是一种自上而下的设计思路。</p>
<h2 id="09">09</h2>
<p>“Program to an interface, not animplementation”。“基于抽象而非实现编程”。</p>
<p>越抽象、越顶层、越脱离具体某一实现的设计，越能提高代码的灵活性，越能应对未来的需求变化。好的代码设计，不仅能应对当下的需求，而且在将来需求发生变化的时候，仍然能够在不破坏原有代码设计的情况下灵活应对。</p>
<p>“细节是魔鬼”。</p>
<ol>
<li>函数的命名不能暴露任何实现细节。</li>
<li>封装具体的实现细节。</li>
<li>为实现类定义抽象的接口。</li>
</ol>
<p>抽象意识、封装意识、接口意识。</p>
<h2 id="10">10</h2>
<p>继承层次过深、过复杂，也会影响到代码的可维护性。</p>
<p>鸟 -&gt; 会飞、不会飞、会叫、不会叫、会下蛋、不会下蛋。</p>
<p>利用组合（composition）、接口、委托（delegation 解决。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// 接口</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">interface</span> <span class="nc">Flyable</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">void</span><span class="w"> </span><span class="nf">fly</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 提高复用性</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">FlyAbility</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Flyable</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">fly</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">//...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Sparrow</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Flyable</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c1">// 组合</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">FlyAbility</span><span class="w"> </span><span class="n">flyAbility</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">FlyAbility</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">fly</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 委托</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">flyAbility</span><span class="p">.</span><span class="na">fly</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>继承改写成组合意味着要做更细粒度的类的拆分。这也就意味着，我们要定义更多的类和接口。类和接口的增多也就或多或少地增加代码的复杂程度和维护成本。</p>
<p>组合并不完美，继承也不是一无是处。</p>
<p>如果类之间的继承结构稳定，层次比较浅，关系不复杂，我们就可以大胆地使用继承。反之，我们就尽量使用组合来替代继承。除此之外，还有一些设计模式、特殊的应用场景，会固定使用继承（模板模式（template pattern））或者组合（装饰者模式（decoratorpattern）、策略模式（strategy pattern）、组合模式（composite pattern））。</p>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>MySQL Code Snippet</title>
      <link>https://zyf.im/2021/01/29/mysql-code-snippet/</link>
      <pubDate>Fri, 29 Jan 2021 18:08:00 +0000</pubDate>
      <guid>https://zyf.im/2021/01/29/mysql-code-snippet/</guid>
      <description>&lt;h2 id=&#34;tips-sql&#34;&gt;Tips SQL&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 追踪优化器 Trace 功能
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;--  optimizer_trace_enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;--  optimizer_trace_file=optimizer_trace.log
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;optimizer_trace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;optimizer_trace&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;enabled=on&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- &amp;lt;your query&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;optimizer_trace&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;enabled=off&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;INFORMATION_SCHEMA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OPTIMIZER_TRACE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 查看优化后的 SQL
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 在联表查询时比较有效果
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;EXPLAIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;你的&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SQL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SHOW&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WARNINGS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 查看处理
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SHOW&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PROCESSLIST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 查看结构
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SHOW&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;COLUMNS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;DESCRIBE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;视图更新&#34;&gt;视图更新&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;OR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;REPLACE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VIEW&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;err&#34;&gt;视图名&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[...]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[...]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;字符集转换&#34;&gt;字符集转换&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;LEFT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;code_value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cost_type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CONVERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;USING&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utf8mb4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COLLATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utf8mb4_unicode_ci&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;function&#34;&gt;Function&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- https://www.w3schools.com/sql/func_mysql_find_in_set.asp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FIND_IN_SET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;bb&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;aa,bb,cc&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FIND_IN_SET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- COALESCE 函数用于返回参数列表中第一个非 NULL 值。如果所有参数都为 NULL，则返回 NULL
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;COALESCE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;code_value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cost_type_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;on-vs-using&#34;&gt;ON vs USING&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://stackoverflow.com/questions/11366006/mysql-on-vs-using&#34;&gt;MySQL ON vs USING? | stackoverflow&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="tips-sql">Tips SQL</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 追踪优化器 Trace 功能
</span></span></span><span class="line"><span class="cl"><span class="c1">--  optimizer_trace_enabled=1
</span></span></span><span class="line"><span class="cl"><span class="c1">--  optimizer_trace_file=optimizer_trace.log
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">@@</span><span class="n">optimizer_trace</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">optimizer_trace</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;enabled=on&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- &lt;your query&gt;;
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">optimizer_trace</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;enabled=off&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">OPTIMIZER_TRACE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 查看优化后的 SQL
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 在联表查询时比较有效果
</span></span></span><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="o">&lt;</span><span class="err">你的</span><span class="w"> </span><span class="k">SQL</span><span class="o">&gt;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">WARNINGS</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 查看处理
</span></span></span><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">PROCESSLIST</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 查看结构
</span></span></span><span class="line"><span class="cl"><span class="k">DESC</span><span class="w"> </span><span class="k">user</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">COLUMNS</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="k">user</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">DESCRIBE</span><span class="w"> </span><span class="k">user</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h2 id="视图更新">视图更新</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">OR</span><span class="w"> </span><span class="k">REPLACE</span><span class="w"> </span><span class="k">VIEW</span><span class="w"> </span><span class="err">视图名</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="k">SELECT</span><span class="p">[...]</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="p">[...]</span><span class="w">
</span></span></span></code></pre></div><h2 id="字符集转换">字符集转换</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">LEFT</span><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="n">code_value</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">cost_type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">CONVERT</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="o">`</span><span class="n">code</span><span class="o">`</span><span class="w"> </span><span class="k">USING</span><span class="w"> </span><span class="n">utf8mb4</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="k">COLLATE</span><span class="w"> </span><span class="n">utf8mb4_unicode_ci</span><span class="w">
</span></span></span></code></pre></div><h2 id="function">Function</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- https://www.w3schools.com/sql/func_mysql_find_in_set.asp
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="n">FIND_IN_SET</span><span class="p">(</span><span class="s1">&#39;bb&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;aa,bb,cc&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="n">FIND_IN_SET</span><span class="p">(</span><span class="k">null</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;0&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- COALESCE 函数用于返回参数列表中第一个非 NULL 值。如果所有参数都为 NULL，则返回 NULL
</span></span></span><span class="line"><span class="cl"><span class="n">COALESCE</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="o">`</span><span class="n">code_value</span><span class="o">`</span><span class="p">.</span><span class="o">`</span><span class="n">name</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">cost_type_name</span><span class="p">,</span><span class="w">
</span></span></span></code></pre></div><h2 id="on-vs-using">ON vs USING</h2>
<p><a href="https://stackoverflow.com/questions/11366006/mysql-on-vs-using">MySQL ON vs USING? | stackoverflow</a></p>
<h2 id="删除重复数据">删除重复数据</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">DELETE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">student</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">id</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">id</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">(</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">MIN</span><span class="p">(</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">student</span><span class="w"> </span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="o">`</span><span class="n">name</span><span class="o">`</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="n">tmp</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>要多加一层 tmp 包装，否则会遇到：<code>1093 - You can't specify target table 'student' for update in FROM clause</code></p>
<h2 id="备份表">备份表</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 创建同结构备份表
</span></span></span><span class="line"><span class="cl"><span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="n">zzz_my_table_220727</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="n">my_table</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 将需要数据写入备份表
</span></span></span><span class="line"><span class="cl"><span class="k">insert</span><span class="w"> </span><span class="k">into</span><span class="w"> </span><span class="n">zzz_my_table_220727</span><span class="w"> </span><span class="k">select</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">my_table</span><span class="w"> </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">desc</span><span class="w"> </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">1000</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 情况原表
</span></span></span><span class="line"><span class="cl"><span class="k">truncate</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="n">my_table</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h2 id="sql-and-or-执行优先级">SQL AND OR 执行优先级</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">table01</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">condition1</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">condition2</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="n">condition3</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 等价于：
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">table01</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">condition1</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">condition2</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="n">condition3</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 而非：
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">table01</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="p">(</span><span class="n">condition1</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">condition2</span><span class="p">)</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="n">condition3</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p><code>and</code> 级别高于 <code>or</code>。相当于可以把 <code>and</code> 看成 <code>乘号 *</code>，把 <code>or</code> 看成 <code>加号 +</code>。</p>
<h2 id="事务">事务</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">//</span><span class="err">对读取的记录加共享锁</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="k">lock</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="k">share</span><span class="w"> </span><span class="k">mode</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- //对读取的记录加独占锁
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="k">update</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://www.standbyside.com/2019/06/19/tips-of-coding-3/">编程小技巧（3）：查看优化后的 SQL</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP 月份加减问题</title>
      <link>https://zyf.im/2021/01/27/php-strtotime-month/</link>
      <pubDate>Wed, 27 Jan 2021 18:18:24 +0000</pubDate>
      <guid>https://zyf.im/2021/01/27/php-strtotime-month/</guid>
      <description>&lt;h2 id=&#34;看现象&#34;&gt;看现象&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;+1 month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-07-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2020-08-31&amp;#34; 符合预期
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;+1 month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-05-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2020-07-01&amp;#34; 不符合预期，预期 2020-06-30
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;-1 month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-02-29&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2020-01-29&amp;#34; 符合预期
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;-1 month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-03-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2020-03-02&amp;#34; 不符合预期，预期 2020-02-29
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Carbon\Carbon
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Carbon&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-07-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addMonth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toDateString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// &amp;#34;2020-08-31&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Carbon&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-05-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addMonth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toDateString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// &amp;#34;2020-07-01&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Carbon&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-02-29&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subMonth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toDateString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// &amp;#34;2020-01-29&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Carbon&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-03-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subMonth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toDateString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// &amp;#34;2020-03-02&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 结果与 strtotime 一致。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;原因&#34;&gt;原因&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;+1 month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-05-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2020-07-01&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;date 内部的处理逻辑：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;2020-05-31&lt;/code&gt; 做 &lt;code&gt;+1 month&lt;/code&gt; 也就是 &lt;code&gt;2020-06-31&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;再做日期规范化，因为没有 &lt;code&gt;06-31&lt;/code&gt;，所以 &lt;code&gt;06-31&lt;/code&gt; 就等于了 &lt;code&gt;07-01&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2020-06-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2017-07-01&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;next month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2017-01-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2017-03-03&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;last month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2017-03-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2017-03-03&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;解决方案&#34;&gt;解决方案&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;last day of -1 month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2017-03-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2017-02-28&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;first day of +1 month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2017-08-31&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2017-09-01&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 但要注意短语的含义：
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;last day of -1 month&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2017-03-01&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// string(10) &amp;#34;2017-02-28&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果使用 &lt;code&gt;Carbon\Carbon&lt;/code&gt; 可以用 &lt;code&gt;subMonthNoOverflow&lt;/code&gt; 与 &lt;code&gt;addMonthNoOverflow&lt;/code&gt; 防止进位：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="看现象">看现象</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;+1 month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2020-07-31&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2020-08-31&#34; 符合预期
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;+1 month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2020-05-31&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2020-07-01&#34; 不符合预期，预期 2020-06-30
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;-1 month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2020-02-29&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2020-01-29&#34; 符合预期
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;-1 month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2020-03-31&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2020-03-02&#34; 不符合预期，预期 2020-02-29
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Carbon\Carbon
</span></span></span><span class="line"><span class="cl"><span class="nx">Carbon</span><span class="o">::</span><span class="na">parse</span><span class="p">(</span><span class="s2">&#34;2020-07-31&#34;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">addMonth</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">toDateString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;2020-08-31&#34;
</span></span></span><span class="line"><span class="cl"><span class="nx">Carbon</span><span class="o">::</span><span class="na">parse</span><span class="p">(</span><span class="s2">&#34;2020-05-31&#34;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">addMonth</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">toDateString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;2020-07-01&#34;
</span></span></span><span class="line"><span class="cl"><span class="nx">Carbon</span><span class="o">::</span><span class="na">parse</span><span class="p">(</span><span class="s2">&#34;2020-02-29&#34;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">subMonth</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">toDateString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;2020-01-29&#34;
</span></span></span><span class="line"><span class="cl"><span class="nx">Carbon</span><span class="o">::</span><span class="na">parse</span><span class="p">(</span><span class="s2">&#34;2020-03-31&#34;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">subMonth</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">toDateString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;2020-03-02&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 结果与 strtotime 一致。
</span></span></span></code></pre></div><h2 id="原因">原因</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;+1 month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2020-05-31&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2020-07-01&#34;
</span></span></span></code></pre></div><p>date 内部的处理逻辑：</p>
<ol>
<li><code>2020-05-31</code> 做 <code>+1 month</code> 也就是 <code>2020-06-31</code>。</li>
<li>再做日期规范化，因为没有 <code>06-31</code>，所以 <code>06-31</code> 就等于了 <code>07-01</code>。</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2020-06-31&#34;</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2017-07-01&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;next month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2017-01-31&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2017-03-03&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;last month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2017-03-31&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2017-03-03&#34;
</span></span></span></code></pre></div><h2 id="解决方案">解决方案</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;last day of -1 month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2017-03-31&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2017-02-28&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;first day of +1 month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2017-08-31&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2017-09-01&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 但要注意短语的含义：
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">date</span><span class="p">(</span><span class="s2">&#34;Y-m-d&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;last day of -1 month&#34;</span><span class="p">,</span> <span class="nx">strtotime</span><span class="p">(</span><span class="s2">&#34;2017-03-01&#34;</span><span class="p">))));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// string(10) &#34;2017-02-28&#34;
</span></span></span></code></pre></div><p>如果使用 <code>Carbon\Carbon</code> 可以用 <code>subMonthNoOverflow</code> 与 <code>addMonthNoOverflow</code> 防止进位：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">Carbon</span><span class="o">::</span><span class="na">parse</span><span class="p">(</span><span class="s1">&#39;2020-03-31&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">subMonthNoOverflow</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">toDateString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;2020-02-29&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Carbon</span><span class="o">::</span><span class="na">parse</span><span class="p">(</span><span class="s2">&#34;2020-05-31&#34;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">addMonthNoOverflow</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">toDateString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;2020-06-30&#34;
</span></span></span></code></pre></div><h2 id="ym-类似问题">Ym 类似问题</h2>
<p>在当日是 31 号场景下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">Carbon</span><span class="o">::</span><span class="na">createFromFormat</span><span class="p">(</span><span class="s1">&#39;Ym&#39;</span><span class="p">,</span> <span class="s1">&#39;202206&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">format</span><span class="p">(</span><span class="s1">&#39;Y-m&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 结果是 2022-07 不符合本意
</span></span></span></code></pre></div><ul>
<li><a href="https://www.php.net/manual/en/datetimeimmutable.createfromformat.php">DateTimeImmutable::createFromFormat | php</a></li>
</ul>
<p><code>!</code> Resets all fields (year, month, day, hour, minute, second, fraction and timezone information) to zero-like values ( 0 for hour, minute, second and fraction, 1 for month and day, 1970 for year and UTC for timezone information)</p>
<p>Without !, all fields will be set to the current date and time.</p>
<p>如果 format 包含字符 !，则未在 format 中提供的生成日期/时间部分以及 ! 左侧的值将设置为 Unix 纪元的相应值。</p>
<p>The Unix epoch is <code>1970-01-01 00:00:00</code> UTC.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">Carbon</span><span class="o">::</span><span class="na">createFromFormat</span><span class="p">(</span><span class="s1">&#39;!Ym&#39;</span><span class="p">,</span> <span class="s1">&#39;202206&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">format</span><span class="p">(</span><span class="s1">&#39;Y-m&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 结果是 2022-06
</span></span></span><span class="line"><span class="cl"><span class="nx">Carbon</span><span class="o">::</span><span class="na">createFromFormat</span><span class="p">(</span><span class="s1">&#39;Ym|&#39;</span><span class="p">,</span> <span class="s1">&#39;202206&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">format</span><span class="p">(</span><span class="s1">&#39;Y-m&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 结果是 2022-06
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$format</span> <span class="o">=</span> <span class="s1">&#39;Y-m-!d H:i:s&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$date</span> <span class="o">=</span> <span class="nx">DateTimeImmutable</span><span class="o">::</span><span class="na">createFromFormat</span><span class="p">(</span><span class="nv">$format</span><span class="p">,</span> <span class="s1">&#39;2009-02-15 15:16:17&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s2">&#34;Format: </span><span class="si">$format</span><span class="s2">; &#34;</span> <span class="o">.</span> <span class="nv">$date</span><span class="o">-&gt;</span><span class="na">format</span><span class="p">(</span><span class="s1">&#39;Y-m-d H:i:s&#39;</span><span class="p">)</span> <span class="o">.</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Format: Y-m-!d H:i:s; 1970-01-15 15:16:17
</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/briannesbitt/Carbon/issues/428">Why does subMonth not work correctly? | github</a></li>
<li><a href="https://www.laruence.com/2018/07/31/3207.html">令人困惑的 strtotime | laruence</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>回顾 2020</title>
      <link>https://zyf.im/2020/12/31/review-2020/</link>
      <pubDate>Thu, 31 Dec 2020 20:00:00 +0000</pubDate>
      <guid>https://zyf.im/2020/12/31/review-2020/</guid>
      <description>&lt;h2 id=&#34;过年就在石家庄&#34;&gt;过年就在石家庄&lt;/h2&gt;
&lt;h2 id=&#34;lwl-走学&#34;&gt;lwl 走学&lt;/h2&gt;
&lt;h2 id=&#34;肺炎在家办公&#34;&gt;肺炎在家办公&lt;/h2&gt;
&lt;h2 id=&#34;pr-剪辑&#34;&gt;PR 剪辑&lt;/h2&gt;
&lt;h2 id=&#34;表情包制作&#34;&gt;表情包制作&lt;/h2&gt;
&lt;h2 id=&#34;自己染发剪发&#34;&gt;自己染发剪发&lt;/h2&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="过年就在石家庄">过年就在石家庄</h2>
<h2 id="lwl-走学">lwl 走学</h2>
<h2 id="肺炎在家办公">肺炎在家办公</h2>
<h2 id="pr-剪辑">PR 剪辑</h2>
<h2 id="表情包制作">表情包制作</h2>
<h2 id="自己染发剪发">自己染发剪发</h2>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>MySQL 空格问题</title>
      <link>https://zyf.im/2020/11/28/mysql-blank-space/</link>
      <pubDate>Sat, 28 Nov 2020 11:11:59 +0000</pubDate>
      <guid>https://zyf.im/2020/11/28/mysql-blank-space/</guid>
      <description>&lt;h2 id=&#34;看现象&#34;&gt;看现象&lt;/h2&gt;
&lt;p&gt;创建一个测试数据库表，插入测试数据：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blank_space&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unsigned&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uid&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;varchar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COLLATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utf8mb4_unicode_ci&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DEFAULT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;desc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;varchar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COLLATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utf8mb4_unicode_ci&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DEFAULT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UNIQUE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uniq_key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uid&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;USING&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BTREE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ENGINE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;InnoDB&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DEFAULT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CHARSET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utf8mb4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COLLATE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utf8mb4_unicode_ci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blank_space&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uid&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;desc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;abc &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;末尾1个&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blank_space&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uid&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;desc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; abc&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;开头1个&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blank_space&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uid&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;desc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;  abc&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;开头2个&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;id&lt;/th&gt;
          &lt;th&gt;uid&lt;/th&gt;
          &lt;th&gt;desc&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;abc_&lt;/td&gt;
          &lt;td&gt;末尾 1 个&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2&lt;/td&gt;
          &lt;td&gt;_abc&lt;/td&gt;
          &lt;td&gt;开头 1 个&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;3&lt;/td&gt;
          &lt;td&gt;__abc&lt;/td&gt;
          &lt;td&gt;开头 2 个&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;uid 实际上没有 &lt;code&gt;_&lt;/code&gt;，这样写是为了看到空格。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="看现象">看现象</h2>
<p>创建一个测试数据库表，插入测试数据：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space</span><span class="o">`</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="w"> </span><span class="nb">int</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="n">unsigned</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="k">COLLATE</span><span class="w"> </span><span class="n">utf8mb4_unicode_ci</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">`</span><span class="k">desc</span><span class="o">`</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="k">COLLATE</span><span class="w"> </span><span class="n">utf8mb4_unicode_ci</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="w"> </span><span class="p">(</span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">UNIQUE</span><span class="w"> </span><span class="k">KEY</span><span class="w"> </span><span class="o">`</span><span class="n">uniq_key</span><span class="o">`</span><span class="w"> </span><span class="p">(</span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="p">)</span><span class="w"> </span><span class="k">USING</span><span class="w"> </span><span class="n">BTREE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w"> </span><span class="n">ENGINE</span><span class="o">=</span><span class="n">InnoDB</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="n">CHARSET</span><span class="o">=</span><span class="n">utf8mb4</span><span class="w"> </span><span class="k">COLLATE</span><span class="o">=</span><span class="n">utf8mb4_unicode_ci</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space</span><span class="o">`</span><span class="p">(</span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="k">desc</span><span class="o">`</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;abc &#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;末尾1个&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space</span><span class="o">`</span><span class="p">(</span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="k">desc</span><span class="o">`</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39; abc&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;开头1个&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space</span><span class="o">`</span><span class="p">(</span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="k">desc</span><span class="o">`</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;  abc&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;开头2个&#39;</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>id</th>
          <th>uid</th>
          <th>desc</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td>abc_</td>
          <td>末尾 1 个</td>
      </tr>
      <tr>
          <td>2</td>
          <td>_abc</td>
          <td>开头 1 个</td>
      </tr>
      <tr>
          <td>3</td>
          <td>__abc</td>
          <td>开头 2 个</td>
      </tr>
  </tbody>
</table>
<blockquote>
<p>uid 实际上没有 <code>_</code>，这样写是为了看到空格。</p>
</blockquote>
<p>执行操作：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">blank_space</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;abc&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">blank_space</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;abc &#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">blank_space</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;abc   &#39;</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>都可以查询出：</p>
<table>
  <thead>
      <tr>
          <th>id</th>
          <th>uid</th>
          <th>desc</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td>abc_</td>
          <td>末尾 1 个</td>
      </tr>
  </tbody>
</table>
<p>执行操作：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space</span><span class="o">`</span><span class="p">(</span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="k">desc</span><span class="o">`</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;abc&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;无空格&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1062 - Duplicate entry &#39;abc&#39; for key &#39;uniq_key&#39;, Time: 0.322000s
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space</span><span class="o">`</span><span class="p">(</span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="k">desc</span><span class="o">`</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;abc  &#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;末位两个&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1062 - Duplicate entry &#39;abc&#39; for key &#39;uniq_key&#39;, Time: 0.322000s
</span></span></span></code></pre></div><h2 id="原因">原因</h2>
<p>MySQL 校对规则属于 PADSPACE，会忽略尾部空格。针对的是 varchar char text 等文本类的数据类型。此为 SQL 标准化行为。无需要设置也无法改变。</p>
<h2 id="解决方案">解决方案</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">blank_space</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">BINARY</span><span class="w"> </span><span class="s1">&#39;abc&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 0 records
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">blank_space</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">BINARY</span><span class="w"> </span><span class="s1">&#39;abc &#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1 records
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">blank_space</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">BINARY</span><span class="w"> </span><span class="s1">&#39;abc   &#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 0 records
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">blank_space</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="s1">&#39;abc&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 0 records
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">blank_space</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="s1">&#39;abc &#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 1 records
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">blank_space</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="s1">&#39;abc   &#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 0 records
</span></span></span></code></pre></div><blockquote>
<p>BINARY 不是函数，是类型转换运算符，它用来强制它后面的字符串为一个二进制字符串，可以理解成精确匹配。</p>
</blockquote>
<h2 id="约束攻击">约束攻击</h2>
<p><strong>需要在非严格模式下。</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">sql_mode</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space_attack</span><span class="o">`</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="w"> </span><span class="nb">int</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="n">unsigned</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="k">COLLATE</span><span class="w"> </span><span class="n">utf8mb4_unicode_ci</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">`</span><span class="n">pwd</span><span class="o">`</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="k">COLLATE</span><span class="w"> </span><span class="n">utf8mb4_unicode_ci</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="w"> </span><span class="p">(</span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w"> </span><span class="n">ENGINE</span><span class="o">=</span><span class="n">InnoDB</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="n">CHARSET</span><span class="o">=</span><span class="n">utf8mb4</span><span class="w"> </span><span class="k">COLLATE</span><span class="o">=</span><span class="n">utf8mb4_unicode_ci</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space_attack</span><span class="o">`</span><span class="p">(</span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="n">pwd</span><span class="o">`</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;admin&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;123&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space_attack</span><span class="o">`</span><span class="p">(</span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="n">pwd</span><span class="o">`</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;tim&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;234&#39;</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>攻击：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="o">`</span><span class="n">blank_space_attack</span><span class="o">`</span><span class="p">(</span><span class="o">`</span><span class="n">uid</span><span class="o">`</span><span class="p">,</span><span class="w"> </span><span class="o">`</span><span class="n">pwd</span><span class="o">`</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;admin      1&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;easy&#39;</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>结果：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">blank_space_attack</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">uid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">&#34;admin&#34;</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="n">pwd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">&#34;easy&#34;</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>id</th>
          <th>uid</th>
          <th>desc</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>3</td>
          <td>admin_____</td>
          <td>easy</td>
      </tr>
  </tbody>
</table>
<h3 id="限制条件">限制条件</h3>
<ul>
<li>服务端没有对用户名长度进行限制。如果服务端限制了用户名长度就不能导致数据库截断，也就没有利用条件。</li>
<li>登陆验证的 SQL 语句必须是用户名和密码一起验证。如果是验证流程是先根据用户名查找出对应的密码，然后再比对密码的话，那么也不能进行利用。因为当使用 admin 为用户名来查询密码的话，数据库此时就会返回两条记录，而一般取第一条则是目标用户的记录，那么你传输的密码肯定是和目标用户密码匹配不上的。</li>
<li>验证成功后返回的必须是用户传递进来的用户名，而不是从数据库取出的用户名。因为当我们以用户 admin 和密码 easy 登陆时，其实数据库返回的是我们自己的用户信息，而我们的用户名其实是 <code>admin_____</code>，如果此后的业务逻辑以该用户名为准，那么就不能达到越权的目的了。</li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="https://www.v0n.top/2019/08/05/SQL%E7%BA%A6%E6%9D%9F%E6%94%BB%E5%87%BB/">SQL 约束攻击 | v0n</a></li>
<li><a href="https://iluoy.com/articles/295">记一次数据库空格问题 | iluoy</a></li>
<li><a href="https://www.cnblogs.com/xjnotxj/p/9019866.html">Mysql 查询条件中字符串尾部有空格也能匹配上的问题 | xjnotxj</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Composer 文档笔记</title>
      <link>https://zyf.im/2020/10/28/composer-document-note/</link>
      <pubDate>Wed, 28 Oct 2020 11:36:27 +0000</pubDate>
      <guid>https://zyf.im/2020/10/28/composer-document-note/</guid>
      <description>&lt;h2 id=&#34;basic-usage&#34;&gt;Basic usage&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://getcomposer.org/doc/01-basic-usage.md&#34;&gt;https://getcomposer.org/doc/01-basic-usage.md&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# get a list of your locally available platform packages.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# php | ext-&amp;lt;name&amp;gt; | lib-&amp;lt;name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;composer show --platform
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;libraries&#34;&gt;Libraries&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://getcomposer.org/doc/02-libraries.md&#34;&gt;https://getcomposer.org/doc/02-libraries.md&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Light-weight distribution packages 轻量级分发包。使用 &lt;code&gt;.gitattributes&lt;/code&gt; 来防止不需要的文件使 zip 分发包膨胀。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;// .gitattributes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/demo export-ignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;phpunit.xml.dist export-ignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/.github/ export-ignore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过检查手动生成的压缩文件进行测试：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git archive branchName --format zip -o file.zip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;command-line-interface--commands&#34;&gt;Command-line interface / Commands&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://getcomposer.org/doc/03-cli.md&#34;&gt;https://getcomposer.org/doc/03-cli.md&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As Composer uses symfony/console you can call commands by short name if it&amp;rsquo;s not ambiguous.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;composer dump
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# calls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;composer dump-autoload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bash Completions&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="basic-usage">Basic usage</h2>
<blockquote>
<p><a href="https://getcomposer.org/doc/01-basic-usage.md">https://getcomposer.org/doc/01-basic-usage.md</a></p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># get a list of your locally available platform packages.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># php | ext-&lt;name&gt; | lib-&lt;name&gt;</span>
</span></span><span class="line"><span class="cl">composer show --platform
</span></span></code></pre></div><h3 id="libraries">Libraries</h3>
<blockquote>
<p><a href="https://getcomposer.org/doc/02-libraries.md">https://getcomposer.org/doc/02-libraries.md</a></p>
</blockquote>
<p>Light-weight distribution packages 轻量级分发包。使用 <code>.gitattributes</code> 来防止不需要的文件使 zip 分发包膨胀。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">// .gitattributes
</span></span><span class="line"><span class="cl">/demo export-ignore
</span></span><span class="line"><span class="cl">phpunit.xml.dist export-ignore
</span></span><span class="line"><span class="cl">/.github/ export-ignore
</span></span></code></pre></div><p>通过检查手动生成的压缩文件进行测试：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git archive branchName --format zip -o file.zip
</span></span></code></pre></div><h2 id="command-line-interface--commands">Command-line interface / Commands</h2>
<blockquote>
<p><a href="https://getcomposer.org/doc/03-cli.md">https://getcomposer.org/doc/03-cli.md</a></p>
</blockquote>
<p>As Composer uses symfony/console you can call commands by short name if it&rsquo;s not ambiguous.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">composer dump
</span></span><span class="line"><span class="cl"><span class="c1"># calls</span>
</span></span><span class="line"><span class="cl">composer dump-autoload
</span></span></code></pre></div><p>Bash Completions</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">composer completion bash &gt; completion.bash
</span></span></code></pre></div><p>Global Options:</p>
<ul>
<li><code>--profile</code> 显示计时和内存使用信息</li>
</ul>
<p>install / i:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar install
</span></span></code></pre></div><p>如果当前目录中有 <code>composer.lock</code> 文件，会使用其中的确切版本而不是重新解析。这确保了每个人使用的依赖版本相同。</p>
<p>如果没有 <code>composer.lock</code> 文件，Composer 会在解析依赖后创建一个。</p>
<p>常用选项：</p>
<ul>
<li><code>--prefer-install</code>: 指定安装方式，可选 <code>source</code> 或 <code>dist</code></li>
<li><code>--no-dev</code>: 跳过安装 <code>require-dev</code> 中列出的包</li>
<li><code>--optimize-autoloader (-o)</code>: 优化自动加载器，建议在生产环境使用</li>
<li><code>--classmap-authoritative (-a)</code>: 仅从 classmap 自动加载类，隐式启用 <code>--optimize-autoloader</code></li>
</ul>
<p>update / u / upgrade:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar update
</span></span></code></pre></div><p>更新所有依赖到最新版本（根据 composer.json 的约束）并更新 composer.lock 文件。</p>
<p>常用选项：</p>
<ul>
<li><code>--prefer-stable</code>: 优先使用稳定版本</li>
<li><code>--prefer-lowest</code>: 尝试回退到最低匹配的版本</li>
<li><code>--with-dependencies</code>: 同时更新依赖的依赖项</li>
</ul>
<p>require / r:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar require vendor/package:version
</span></span></code></pre></div><p>添加新的依赖包并更新 composer.json 文件。</p>
<p>remove / rm / uninstall:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar remove vendor/package
</span></span></code></pre></div><p>移除包并更新 composer.json 文件。</p>
<p>dump-autoload / dumpautoload:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar dump-autoload
</span></span></code></pre></div><p>更新自动加载器，不安装或更新任何包。在添加新类时很有用。</p>
<p>选项：</p>
<ul>
<li><code>-o, --optimize</code>: 生成优化的 autoloader</li>
<li><code>--classmap-authoritative (-a)</code>: 仅从 classmap 自动加载</li>
<li><code>--no-dev</code>: 不生成开发环境的 autoloader</li>
</ul>
<p>global:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar global require vendor/package
</span></span></code></pre></div><p>全局安装包，存储在 COMPOSER_HOME 目录。</p>
<p>search:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar search keyword
</span></span></code></pre></div><p>搜索包。</p>
<p>self-update / selfupdate:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar self-update
</span></span></code></pre></div><p>更新 Composer 到最新版本。</p>
<p>Composer 支持多种环境变量来控制其行为：</p>
<ul>
<li><code>COMPOSER_HOME</code>: 指定 Composer 主目录位置</li>
<li><code>COMPOSER_CACHE_DIR</code>: 更改 Composer 缓存目录</li>
<li><code>COMPOSER_MEMORY_LIMIT</code>: 设置 PHP 内存限制</li>
<li><code>COMPOSER_PROCESS_TIMEOUT</code>: 设置命令执行超时时间（默认 300 秒）</li>
<li><code>COMPOSER_NO_INTERACTION</code>: 禁用交互模式</li>
<li><code>COMPOSER_NO_DEV</code>: 等同于 <code>--no-dev</code> 选项</li>
<li><code>COMPOSER_VENDOR_DIR</code>: 更改安装依赖的目录</li>
</ul>
<h2 id="the-composerjson-schema">The composer.json schema</h2>
<blockquote>
<p><a href="https://getcomposer.org/doc/04-schema.md">https://getcomposer.org/doc/04-schema.md</a></p>
</blockquote>
<p>composer.json 文件是 Composer 项目的核心配置文件，用于定义包的基本信息和依赖关系。以下是主要字段的说明：</p>
<h3 id="基本字段">基本字段</h3>
<ul>
<li><strong>name</strong>: 包名称，格式为 <code>vendor/project</code>，例如 <code>monolog/monolog</code>。必须小写，可包含短横线、下划线或点号。</li>
<li><strong>description</strong>: 包的简短描述，通常为一行。</li>
<li><strong>version</strong>: 包的版本号，格式如 <code>1.0.0</code> 或 <code>v1.0.0</code>。一般建议省略，由 VCS 标签推断。</li>
<li><strong>type</strong>: 包类型，默认为 <code>library</code>。其他可选值包括 <code>project</code>、<code>metapackage</code> 和 <code>composer-plugin</code>。</li>
<li><strong>keywords</strong>: 关键词数组，用于搜索和过滤。</li>
<li><strong>homepage</strong>: 项目网站的 URL。</li>
<li><strong>readme</strong>: 自述文件的相对路径，默认为 <code>README.md</code>。</li>
<li><strong>time</strong>: 版本发布日期，格式为 <code>YYYY-MM-DD</code> 或 <code>YYYY-MM-DD HH:MM:SS</code>。</li>
<li><strong>license</strong>: 包的许可证，可以是字符串或字符串数组。</li>
</ul>
<h3 id="作者与支持">作者与支持</h3>
<ul>
<li><strong>authors</strong>: 包作者数组，每个作者为一个对象，包含 <code>name</code>、<code>email</code>、<code>homepage</code> 和 <code>role</code> 字段。</li>
<li><strong>support</strong>: 提供支持的各种信息，如 <code>email</code>、<code>issues</code>、<code>forum</code>、<code>wiki</code> 等。</li>
<li><strong>funding</strong>: 用于赞助包作者的 URL 数组。</li>
</ul>
<h3 id="依赖管理">依赖管理</h3>
<ul>
<li><strong>require</strong>: 运行时必需的依赖包映射表（包名到版本约束）。</li>
<li><strong>require-dev</strong>: 开发环境需要的依赖包映射表，仅对根包有效。</li>
<li><strong>conflict</strong>: 与当前包冲突的包。</li>
<li><strong>replace</strong>: 由当前包替换的包。</li>
<li><strong>provide</strong>: 当前包提供的包（接口实现等）。</li>
<li><strong>suggest</strong>: 推荐安装的包，仅起信息提示作用。</li>
</ul>
<h3 id="自动加载配置">自动加载配置</h3>
<ul>
<li><strong>autoload</strong>: 定义项目自动加载规则，支持 <code>psr-4</code>、<code>psr-0</code>、<code>classmap</code> 和 <code>files</code>。</li>
<li><strong>autoload-dev</strong>: 开发环境的额外自动加载规则，仅对根包有效。</li>
</ul>
<h3 id="其他高级配置">其他高级配置</h3>
<ul>
<li><strong>repositories</strong>: 定义额外的包来源，仅对根包有效。</li>
<li><strong>minimum-stability</strong>: 默认包稳定性过滤级别，仅对根包有效。</li>
<li><strong>prefer-stable</strong>: 是否优先使用稳定版本，仅对根包有效。</li>
<li><strong>config</strong>: 各种配置选项，仅对根包有效。</li>
<li><strong>scripts</strong>: 在安装过程中各个阶段执行的脚本钩子，仅对根包有效。</li>
<li><strong>extra</strong>: 供脚本使用的任意额外数据。</li>
<li><strong>bin</strong>: 应作为二进制文件安装的文件集合。</li>
</ul>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;monolog/monolog&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;Logging library&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;keywords&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;logging&#34;</span><span class="p">,</span> <span class="s2">&#34;psr-3&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;homepage&#34;</span><span class="p">:</span> <span class="s2">&#34;https://github.com/Seldaek/monolog&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;license&#34;</span><span class="p">:</span> <span class="s2">&#34;MIT&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;authors&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Jordi Boggiano&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;email&#34;</span><span class="p">:</span> <span class="s2">&#34;j.boggiano@seld.be&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;homepage&#34;</span><span class="p">:</span> <span class="s2">&#34;https://seld.be&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;require&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;php&#34;</span><span class="p">:</span> <span class="s2">&#34;&gt;=5.3.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;psr/log&#34;</span><span class="p">:</span> <span class="s2">&#34;~1.0&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;autoload&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;psr-4&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;Monolog\\&#34;</span><span class="p">:</span> <span class="s2">&#34;src/Monolog&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="repositories">Repositories</h2>
<blockquote>
<p><a href="https://getcomposer.org/doc/05-repositories.md">https://getcomposer.org/doc/05-repositories.md</a></p>
</blockquote>
<p>仓库是包的来源，Composer 会在所有配置的仓库中查找项目所需的包。本节介绍仓库的概念和类型。</p>
<h3 id="仓库类型">仓库类型</h3>
<h4 id="1-composer-仓库">1. Composer 仓库</h4>
<p>使用单个 <code>packages.json</code> 文件包含所有包元数据的仓库类型，这也是 Packagist 使用的类型。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;repositories&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;composer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://example.org&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h4 id="2-vcs-仓库">2. VCS 仓库</h4>
<p>版本控制系统（Version Control System）仓库，支持从 git、svn、fossil 或 hg 仓库安装包。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;repositories&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;vcs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://github.com/username/repository&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;require&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;vendor/package&#34;</span><span class="p">:</span> <span class="s2">&#34;dev-branch-name&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>使用 VCS 仓库的主要场景包括：</p>
<ul>
<li>维护第三方库的自己的分支</li>
<li>使用私有仓库</li>
<li>需要直接从版本控制系统获取代码</li>
</ul>
<h4 id="3-package-仓库">3. Package 仓库</h4>
<p>针对不支持 Composer 的项目，可以手动定义包的信息。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;repositories&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;package&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;package&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;smarty/smarty&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;3.1.7&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://www.smarty.net/files/Smarty-3.1.7.zip&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;zip&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="自托管仓库选项">自托管仓库选项</h3>
<p>当需要保持包私有或为项目创建独立生态系统时，可以选择自托管仓库：</p>
<ol>
<li>
<p><strong>Private Packagist</strong>：提供私有包托管和镜像功能的托管或自托管应用。</p>
</li>
<li>
<p><strong>Satis</strong>：轻量级的静态 Composer 仓库生成器，适合小型团队。</p>
</li>
<li>
<p><strong>Artifact 仓库</strong>：使用包含 ZIP 或 TAR 归档文件的文件夹作为包源。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;repositories&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;artifact&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;path/to/directory/with/zips/&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p><strong>Path 仓库</strong>：直接依赖本地目录，对于单体仓库特别有用。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;repositories&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;path&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;../../packages/my-package&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ol>
<h3 id="禁用-packagistorg">禁用 Packagist.org</h3>
<p>默认情况下，Composer 会使用 Packagist.org 作为包源。如需禁用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;repositories&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;packagist.org&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>或通过全局配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar config -g repo.packagist <span class="nb">false</span>
</span></span></code></pre></div><h2 id="config">Config</h2>
<blockquote>
<p><a href="https://getcomposer.org/doc/06-config.md">https://getcomposer.org/doc/06-config.md</a></p>
</blockquote>
<p>Composer 配置选项可以通过 <code>composer.json</code> 中的 <code>config</code> 字段设置。以下是一些常用的配置项：</p>
<h3 id="基本配置选项">基本配置选项</h3>
<ul>
<li>
<p><strong>process-timeout</strong>: 进程执行超时时间（秒），默认 300（5分钟）。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;config&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;process-timeout&#34;</span><span class="p">:</span> <span class="mi">900</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p><strong>allow-plugins</strong>: 控制哪些 Composer 插件可以执行代码（Composer 2.2.0+）。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;config&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;allow-plugins&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;vendor/plugin-name&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;another-vendor/*&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p><strong>preferred-install</strong>: 首选安装方式，可为 <code>source</code>、<code>dist</code> 或 <code>auto</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;config&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;preferred-install&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;vendor/*&#34;</span><span class="p">:</span> <span class="s2">&#34;source&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;*&#34;</span><span class="p">:</span> <span class="s2">&#34;dist&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="仓库与认证">仓库与认证</h3>
<ul>
<li>
<p><strong>github-protocols</strong>: 克隆 GitHub 仓库使用的协议列表，默认 <code>[&quot;https&quot;, &quot;ssh&quot;, &quot;git&quot;]</code>。</p>
</li>
<li>
<p><strong>github-oauth</strong>: GitHub OAuth 令牌列表。</p>
</li>
<li>
<p><strong>gitlab-oauth</strong>/<strong>gitlab-token</strong>: GitLab OAuth 令牌或私有令牌列表。</p>
</li>
<li>
<p><strong>http-basic</strong>: HTTP 基本认证信息。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;config&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;http-basic&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;example.org&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;username&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;password&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="目录与缓存">目录与缓存</h3>
<ul>
<li><strong>vendor-dir</strong>: 安装依赖的目录，默认为 <code>vendor</code>。</li>
<li><strong>bin-dir</strong>: 二进制文件链接目录，默认为 <code>vendor/bin</code>。</li>
<li><strong>cache-dir</strong>: 缓存目录。</li>
<li><strong>cache-files-ttl</strong>: 缓存文件的存活时间（秒），默认 15552000（6个月）。</li>
<li><strong>cache-files-maxsize</strong>: 缓存可使用的最大空间，默认 &ldquo;300MiB&rdquo;。</li>
</ul>
<h3 id="自动加载相关">自动加载相关</h3>
<ul>
<li><strong>optimize-autoloader</strong>: 始终优化自动加载器，默认 <code>false</code>。</li>
<li><strong>prepend-autoloader</strong>: 是否将 Composer 自动加载器前置到已存在的自动加载器，默认 <code>true</code>。</li>
<li><strong>classmap-authoritative</strong>: 设为 <code>true</code> 时，自动加载器只会从 classmap 加载类，默认 <code>false</code>。</li>
<li><strong>apcu-autoloader</strong>: 是否使用 APCu 缓存查找的类，默认 <code>false</code>。</li>
</ul>
<h3 id="安全与其他">安全与其他</h3>
<ul>
<li><strong>platform-check</strong>: 平台检查级别，默认 <code>php-only</code>，可设为 <code>true</code> 或 <code>false</code>。</li>
<li><strong>secure-http</strong>: 是否只允许通过 HTTPS 下载包，默认 <code>true</code>。</li>
<li><strong>discard-changes</strong>: 处理脏更新的方式，默认 <code>false</code>，可设为 <code>true</code> 或 <code>stash</code>。</li>
<li><strong>sort-packages</strong>: 添加新包时是否按名称排序，默认 <code>false</code>。</li>
</ul>
<h3 id="全局配置">全局配置</h3>
<p>某些配置可通过命令行全局设置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php composer.phar config -g repo.packagist <span class="nb">false</span>
</span></span><span class="line"><span class="cl">php composer.phar config -g process-timeout <span class="m">900</span>
</span></span></code></pre></div><h2 id="runtime-composer-utilities">Runtime Composer utilities</h2>
<blockquote>
<p><a href="https://getcomposer.org/doc/07-runtime.md">https://getcomposer.org/doc/07-runtime.md</a></p>
</blockquote>
<p>Composer 除了安装和管理依赖外，还提供了一些运行时实用工具。如果需要依赖特定版本的这些功能，可以通过 <code>composer-runtime-api</code> 包实现。</p>
<h3 id="自动加载">自动加载</h3>
<p>最常用的运行时工具是自动加载器，在所有 Composer 版本中可用。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/vendor/autoload.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 现在可以使用项目的依赖了
</span></span></span><span class="line"><span class="cl"><span class="nv">$log</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Monolog\Logger</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="已安装版本信息">已安装版本信息</h3>
<p>Composer 2.0 引入了 <code>Composer\InstalledVersions</code> 类，提供了检查已安装包的静态方法。</p>
<h4 id="检查包是否存在">检查包是否存在</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">\Composer\InstalledVersions</span><span class="o">::</span><span class="na">isInstalled</span><span class="p">(</span><span class="s1">&#39;vendor/package&#39;</span><span class="p">);</span> <span class="c1">// 返回布尔值
</span></span></span><span class="line"><span class="cl"><span class="nx">\Composer\InstalledVersions</span><span class="o">::</span><span class="na">isInstalled</span><span class="p">(</span><span class="s1">&#39;psr/log-implementation&#39;</span><span class="p">);</span> <span class="c1">// 检查虚拟包
</span></span></span></code></pre></div><p>从 Composer 2.1 开始，还可以检查包是否通过 require-dev 安装：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">\Composer\InstalledVersions</span><span class="o">::</span><span class="na">isInstalled</span><span class="p">(</span><span class="s1">&#39;vendor/package&#39;</span><span class="p">,</span> <span class="k">false</span><span class="p">);</span> <span class="c1">// 仅检查 require 中的包
</span></span></span></code></pre></div><h4 id="检查包版本">检查包版本</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">Composer\Semver\VersionParser</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 检查包是否满足版本约束
</span></span></span><span class="line"><span class="cl"><span class="nx">\Composer\InstalledVersions</span><span class="o">::</span><span class="na">satisfies</span><span class="p">(</span><span class="k">new</span> <span class="nx">VersionParser</span><span class="p">,</span> <span class="s1">&#39;vendor/package&#39;</span><span class="p">,</span> <span class="s1">&#39;2.0.*&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 获取包的版本号
</span></span></span><span class="line"><span class="cl"><span class="nv">$version</span> <span class="o">=</span> <span class="nx">\Composer\InstalledVersions</span><span class="o">::</span><span class="na">getVersion</span><span class="p">(</span><span class="s1">&#39;vendor/package&#39;</span><span class="p">);</span> <span class="c1">// 标准化版本 (如 1.2.3.0)
</span></span></span><span class="line"><span class="cl"><span class="nv">$prettyVersion</span> <span class="o">=</span> <span class="nx">\Composer\InstalledVersions</span><span class="o">::</span><span class="na">getPrettyVersion</span><span class="p">(</span><span class="s1">&#39;vendor/package&#39;</span><span class="p">);</span> <span class="c1">// 原始版本 (如 v1.2.3)
</span></span></span></code></pre></div><h4 id="获取包的安装路径">获取包的安装路径</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 获取包安装的绝对路径
</span></span></span><span class="line"><span class="cl"><span class="nv">$path</span> <span class="o">=</span> <span class="nx">\Composer\InstalledVersions</span><span class="o">::</span><span class="na">getInstallPath</span><span class="p">(</span><span class="s1">&#39;vendor/package&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h4 id="查找指定类型的已安装包">查找指定类型的已安装包</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 获取所有类型为 &#39;foo-plugin&#39; 的已安装包
</span></span></span><span class="line"><span class="cl"><span class="nv">$packages</span> <span class="o">=</span> <span class="nx">\Composer\InstalledVersions</span><span class="o">::</span><span class="na">getInstalledPackagesByType</span><span class="p">(</span><span class="s1">&#39;foo-plugin&#39;</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="平台检查">平台检查</h3>
<p>Composer 2.0 引入了 <code>vendor/composer/platform_check.php</code> 文件，会在包含自动加载器时自动执行。它会验证当前 PHP 环境是否满足平台要求，如 PHP 版本和扩展。</p>
<p>默认情况下，只检查 PHP 版本（<code>php-only</code>）。可以通过 <code>platform-check</code> 配置选项修改行为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;config&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;platform-check&#34;</span><span class="p">:</span> <span class="kc">true</span>  <span class="c1">// 也检查 PHP 扩展
</span></span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>如果要禁用此安全检查：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;config&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;platform-check&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="二进制文件中的自动加载器路径">二进制文件中的自动加载器路径</h3>
<p>Composer 2.2 引入了全局变量 <code>$_composer_autoload_path</code>，它在运行 Composer 安装的二进制文件时设置。</p>
<h3 id="二进制文件中的-bin-dir-路径">二进制文件中的 bin-dir 路径</h3>
<p>Composer 2.2.2 引入了全局变量 <code>$_composer_bin_dir</code>，它在运行 Composer 安装的二进制文件时设置。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Composer vendor 提交至 Git</title>
      <link>https://zyf.im/2020/08/10/commit-composer-vendor-to-git/</link>
      <pubDate>Mon, 10 Aug 2020 14:24:47 +0000</pubDate>
      <guid>https://zyf.im/2020/08/10/commit-composer-vendor-to-git/</guid>
      <description>&lt;h2 id=&#34;应该将-vendor-提交到-git-吗&#34;&gt;应该将 vendor 提交到 Git 吗&lt;/h2&gt;
&lt;p&gt;一般建议是 &lt;strong&gt;不&lt;/strong&gt;。&lt;code&gt;vendor&lt;/code&gt; 目录应添加到 &lt;code&gt;.gitignore&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;最佳实践是让所有开发人员使用 Composer 来安装依赖项。类似地，构建服务器、CI、部署工具等都应该作为项目启动的一部分来运行 Composer。&lt;/p&gt;
&lt;p&gt;虽然在某些环境下这样做很诱人，但也会导致一些问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大型 VCS 存储库的大小和更新代码时的差异。&lt;/li&gt;
&lt;li&gt;在你自己的 VCS 复制你所有依赖的历史。&lt;/li&gt;
&lt;li&gt;将通过 git 安装的依赖项添加到 git repo 中将显示为 &lt;code&gt;submodules&lt;/code&gt;。这是有问题的，因为它们不是真正的 &lt;code&gt;submodules&lt;/code&gt;，您将会遇到问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你真的觉得你必须这样做，你有几个选择：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;限制自己安装带标记的版本（没有 dev 版本），这样就只能安装压缩版，并避免与 git &lt;code&gt;submodules&lt;/code&gt; 有关的问题。&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;--prefer-dist&lt;/code&gt; or set &lt;code&gt;preferred-install&lt;/code&gt; to &lt;code&gt;dist&lt;/code&gt; in your config.&lt;/li&gt;
&lt;li&gt;Remove the &lt;code&gt;.git&lt;/code&gt; directory of every dependency after the installation, then you can add them to your git repo. You can do that with &lt;code&gt;rm -rf vendor/\*\*/.git&lt;/code&gt; in ZSH or &lt;code&gt;find vendor/ -type d -name &amp;quot;.git&amp;quot; -exec rm -rf {} \;&lt;/code&gt; in Bash. 但这意味着您必须在运行 composer 更新之前从磁盘中删除这些依赖项。&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;.gitignore&lt;/code&gt; rule &lt;code&gt;/vendor/**/.git&lt;/code&gt; to ignore all the vendor &lt;code&gt;.git&lt;/code&gt; folders. 这种方法不需要在运行编写器更新之前从磁盘删除依赖项。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;我的做法&#34;&gt;我的做法&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;问题解决了，但是不确信做法是否正确。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="应该将-vendor-提交到-git-吗">应该将 vendor 提交到 Git 吗</h2>
<p>一般建议是 <strong>不</strong>。<code>vendor</code> 目录应添加到 <code>.gitignore</code>。</p>
<p>最佳实践是让所有开发人员使用 Composer 来安装依赖项。类似地，构建服务器、CI、部署工具等都应该作为项目启动的一部分来运行 Composer。</p>
<p>虽然在某些环境下这样做很诱人，但也会导致一些问题：</p>
<ul>
<li>大型 VCS 存储库的大小和更新代码时的差异。</li>
<li>在你自己的 VCS 复制你所有依赖的历史。</li>
<li>将通过 git 安装的依赖项添加到 git repo 中将显示为 <code>submodules</code>。这是有问题的，因为它们不是真正的 <code>submodules</code>，您将会遇到问题。</li>
</ul>
<p>如果你真的觉得你必须这样做，你有几个选择：</p>
<ul>
<li>限制自己安装带标记的版本（没有 dev 版本），这样就只能安装压缩版，并避免与 git <code>submodules</code> 有关的问题。</li>
<li>Use <code>--prefer-dist</code> or set <code>preferred-install</code> to <code>dist</code> in your config.</li>
<li>Remove the <code>.git</code> directory of every dependency after the installation, then you can add them to your git repo. You can do that with <code>rm -rf vendor/\*\*/.git</code> in ZSH or <code>find vendor/ -type d -name &quot;.git&quot; -exec rm -rf {} \;</code> in Bash. 但这意味着您必须在运行 composer 更新之前从磁盘中删除这些依赖项。</li>
<li>Add a <code>.gitignore</code> rule <code>/vendor/**/.git</code> to ignore all the vendor <code>.git</code> folders. 这种方法不需要在运行编写器更新之前从磁盘删除依赖项。</li>
</ul>
<h2 id="我的做法">我的做法</h2>
<blockquote>
<p>问题解决了，但是不确信做法是否正确。</p>
</blockquote>
<p>因为网络环境与部署的原因，在生产环境下是将 <code>vendor</code> 目录提交到 <code>git</code> 中的。使用过程中确实出现了，部分类库成为了 <code>submodules</code>，无法把真实的代码提交进 git。</p>
<p>可尝试执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rm rf --cache vendor
</span></span><span class="line"><span class="cl">git add .
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;add vendor&#34;</span>
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://getcomposer.org/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md">Should I commit the dependencies in my vendor directory? | getcomposer</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>配置 Laradock PhpStorm Xdubug</title>
      <link>https://zyf.im/2020/05/26/config-laradock-phpstorm-xdubug/</link>
      <pubDate>Tue, 26 May 2020 09:45:15 +0000</pubDate>
      <guid>https://zyf.im/2020/05/26/config-laradock-phpstorm-xdubug/</guid>
      <description>&lt;p&gt;最近在学习 Yii2 的源码，为了方便调试所以研究下 Laradock + PhpStorm + Xdubug 的配置。&lt;/p&gt;
&lt;h2 id=&#34;环境&#34;&gt;环境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Laradock v10.0&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;请保证 Laradock 是最新的版本，可以减少不必要的麻烦。也推荐使用我精简过的项目 &lt;a href=&#34;https://github.com/imzyf/my-dock&#34;&gt;imzyf/my-dock | github&lt;/a&gt;。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;配置-laradock&#34;&gt;配置 Laradock&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vim .env
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;WORKSPACE_INSTALL_XDEBUG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PHP_FPM_INSTALL_XDEBUG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重新编译 php-fpm 和 workspace 容器：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-compose build php-fpm workspace
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;配置-phpstorm&#34;&gt;配置 PhpStorm&lt;/h2&gt;
&lt;h3 id=&#34;配置-docker&#34;&gt;配置 Docker&lt;/h3&gt;
&lt;p&gt;Preferences &amp;gt; Build, Execution, Deploymnent &amp;gt; Docker&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;docker&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/82999144-302d2100-a03b-11ea-8a21-08bc67838fc2.png&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;配置-php&#34;&gt;配置 PHP&lt;/h3&gt;
&lt;p&gt;Preferences &amp;gt; Languages &amp;amp; Frameworks &amp;gt; PHP，PHP CLI Interpreter 点 &lt;code&gt;...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;php 1&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/82997395-e7746880-a038-11ea-98ca-d68052d5bd22.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;点击 +，选择 From Docker, Vagrant&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;php 2&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/82997724-55209480-a039-11ea-8235-4a0479aeb832.png&#34;&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>最近在学习 Yii2 的源码，为了方便调试所以研究下 Laradock + PhpStorm + Xdubug 的配置。</p>
<h2 id="环境">环境</h2>
<ul>
<li>macOS</li>
<li>Laradock v10.0</li>
</ul>
<p>请保证 Laradock 是最新的版本，可以减少不必要的麻烦。也推荐使用我精简过的项目 <a href="https://github.com/imzyf/my-dock">imzyf/my-dock | github</a>。</p>
<!-- more -->
<h2 id="配置-laradock">配置 Laradock</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim .env
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">WORKSPACE_INSTALL_XDEBUG</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">PHP_FPM_INSTALL_XDEBUG</span><span class="o">=</span><span class="nb">true</span>
</span></span></code></pre></div><p>重新编译 php-fpm 和 workspace 容器：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker-compose build php-fpm workspace
</span></span></code></pre></div><h2 id="配置-phpstorm">配置 PhpStorm</h2>
<h3 id="配置-docker">配置 Docker</h3>
<p>Preferences &gt; Build, Execution, Deploymnent &gt; Docker</p>
<p><img alt="docker" loading="lazy" src="https://user-images.githubusercontent.com/9289792/82999144-302d2100-a03b-11ea-8a21-08bc67838fc2.png"></p>
<h3 id="配置-php">配置 PHP</h3>
<p>Preferences &gt; Languages &amp; Frameworks &gt; PHP，PHP CLI Interpreter 点 <code>...</code></p>
<p><img alt="php 1" loading="lazy" src="https://user-images.githubusercontent.com/9289792/82997395-e7746880-a038-11ea-98ca-d68052d5bd22.png"></p>
<p>点击 +，选择 From Docker, Vagrant&hellip;</p>
<p><img alt="php 2" loading="lazy" src="https://user-images.githubusercontent.com/9289792/82997724-55209480-a039-11ea-8235-4a0479aeb832.png"></p>
<p>Debugger 可以显示出 Xdebug。</p>
<h3 id="配置-servers">配置 Servers</h3>
<p>Preferences &gt; Languages &amp; Frameworks &gt; PHP &gt; Servers</p>
<p><img alt="server" loading="lazy" src="https://user-images.githubusercontent.com/9289792/82998171-f7d91300-a039-11ea-89e7-50b79664b0f6.png"></p>
<p>注意：Name 必须填写 Laradock 中的 PHP_IDE_CONFIG 也就就是 <code>laradock</code>。</p>
<h3 id="配置-xdebug">配置 Xdebug</h3>
<p>Preferences &gt; Languages &amp; Frameworks &gt; PHP &gt; Debug。点击 <code>Validate</code>，填写。</p>
<p><img alt="Xdebug" loading="lazy" src="https://user-images.githubusercontent.com/9289792/82998451-530b0580-a03a-11ea-925a-1770df95eb66.png"></p>
<p>run &gt; Edit Configurations，添加 PHP Remote Debug。IDE key 为 <code>PHPSTORM</code>。</p>
<p><img alt="Xdebug2" loading="lazy" src="https://user-images.githubusercontent.com/9289792/82998663-95ccdd80-a03a-11ea-9dc4-5d00d012e7df.png"></p>
<h2 id="配置-chrome">配置 Chrome</h2>
<p>下载插件 <a href="https://chrome.google.com/webstore/detail/eadndfjplgieldjbigjakmdgkmoaaaoc">Xdebug helper</a>，右键图标 配置。</p>
<p><img alt="chrome" loading="lazy" src="https://user-images.githubusercontent.com/9289792/82998865-d3316b00-a03a-11ea-94cc-a6642fa0cdbf.png"></p>
<h2 id="enjoy">enjoy</h2>
<p><img alt="start" loading="lazy" src="https://user-images.githubusercontent.com/9289792/82999306-636fb000-a03b-11ea-9c0f-a059fbc47fd3.png"></p>
<p>开启 debug，然后访问页面。</p>
<p><img alt="fly" loading="lazy" src="https://user-images.githubusercontent.com/9289792/82999463-96b23f00-a03b-11ea-922a-4c91169628a9.png"></p>
<p>芜湖起飞。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://learnku.com/articles/24389">Laradock 使用 PhpStorm Debug 代码 | learnku</a></li>
<li><a href="https://medium.com/@chenpohsun_12588/set-debugger-using-xdebug-with-phpstorm-laradock-454e8c2ad0d9">Set Debugger Using Xdebug With PHPStorm &amp; Laradock | medium</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP float 精度</title>
      <link>https://zyf.im/2020/05/09/php-float-precision/</link>
      <pubDate>Sat, 09 May 2020 17:32:34 +0000</pubDate>
      <guid>https://zyf.im/2020/05/09/php-float-precision/</guid>
      <description>&lt;h2 id=&#34;实例-1&#34;&gt;实例 1&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1.1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gettype&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// string(6) &amp;#34;double&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// float(1.1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;实例-2&#34;&gt;实例 2&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;123456789.1100110011&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// float(123456789.11001)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%.11f&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// string(21) &amp;#34;123456789.11001099646&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;123456789.11001&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// float(123456789.11001)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%.11f&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// string(21) &amp;#34;123456789.11000999808&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;123456789.1100110011&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// float(123456789.11001)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// string(15) &amp;#34;123456789.11001&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// float(123456789.11001)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%.11f&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// string(21) &amp;#34;123456789.11000999808&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// bool(false) - 说明 $a 还是携带着 float 的精度
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// bool(true)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;实例-3&#34;&gt;实例 3&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// # 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;120085&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1200.85&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// bool(false)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// # 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;120085&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1200.85&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// bool(false)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// # 3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;120081&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1200.81&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// bool(true)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// # 4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;120085&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1200.85&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// float(1.4551915228367E-11)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;实例-4&#34;&gt;实例 4&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// bool(true)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// bool(false)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%.20f&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// string(22) &amp;#34;1.00000000000000000000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%.20f&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// string(22) &amp;#34;0.09999999999999997780&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// bool(true) 0.5 二进制 0.1，0.25 二进制 0.01
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;var_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.25&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// bool(true)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;实例-5&#34;&gt;实例 5&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;19.99&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 1999.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 1998 !!!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// &amp;#34;1999&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 1999
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;round&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 1999.0 !!!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;round&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 1999
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;分析&#34;&gt;分析&lt;/h2&gt;
&lt;p&gt;看文档：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="实例-1">实例 1</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$a</span> <span class="o">=</span> <span class="mf">1.1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">gettype</span><span class="p">(</span><span class="nv">$a</span><span class="p">));</span> <span class="c1">// string(6) &#34;double&#34;
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$a</span><span class="p">);</span> <span class="c1">// float(1.1)
</span></span></span></code></pre></div><h2 id="实例-2">实例 2</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$a</span> <span class="o">=</span> <span class="s2">&#34;123456789.1100110011&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$a</span> <span class="o">=</span> <span class="p">(</span><span class="nx">float</span><span class="p">)</span> <span class="nv">$a</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$a</span><span class="p">);</span> <span class="c1">// float(123456789.11001)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">sprintf</span><span class="p">(</span><span class="s1">&#39;%.11f&#39;</span><span class="p">,</span> <span class="nv">$a</span><span class="p">));</span> <span class="c1">// string(21) &#34;123456789.11001099646&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$b</span> <span class="o">=</span> <span class="mf">123456789.11001</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$b</span><span class="p">);</span> <span class="c1">// float(123456789.11001)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">sprintf</span><span class="p">(</span><span class="s1">&#39;%.11f&#39;</span><span class="p">,</span> <span class="nv">$b</span><span class="p">));</span> <span class="c1">// string(21) &#34;123456789.11000999808&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$c</span> <span class="o">=</span> <span class="s1">&#39;123456789.1100110011&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$c</span> <span class="o">=</span> <span class="p">(</span><span class="nx">float</span><span class="p">)</span> <span class="nv">$c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$c</span><span class="p">);</span> <span class="c1">// float(123456789.11001)
</span></span></span><span class="line"><span class="cl"><span class="nv">$c</span> <span class="o">=</span> <span class="p">(</span><span class="nx">string</span><span class="p">)</span> <span class="nv">$c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$c</span><span class="p">);</span> <span class="c1">// string(15) &#34;123456789.11001&#34;
</span></span></span><span class="line"><span class="cl"><span class="nv">$c</span> <span class="o">=</span> <span class="p">(</span><span class="nx">float</span><span class="p">)</span> <span class="nv">$c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$c</span><span class="p">);</span> <span class="c1">// float(123456789.11001)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">sprintf</span><span class="p">(</span><span class="s1">&#39;%.11f&#39;</span><span class="p">,</span> <span class="nv">$c</span><span class="p">));</span> <span class="c1">// string(21) &#34;123456789.11000999808&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$a</span> <span class="o">===</span> <span class="nv">$b</span><span class="p">);</span> <span class="c1">// bool(false) - 说明 $a 还是携带着 float 的精度
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$b</span> <span class="o">===</span> <span class="nv">$c</span><span class="p">);</span> <span class="c1">// bool(true)
</span></span></span></code></pre></div><h2 id="实例-3">实例 3</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// # 1
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">120085</span> <span class="o">===</span> <span class="mf">1200.85</span> <span class="o">*</span> <span class="mi">100</span><span class="p">);</span> <span class="c1">// bool(false)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// # 2
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">120085</span> <span class="o">==</span> <span class="mf">1200.85</span> <span class="o">*</span> <span class="mi">100</span><span class="p">);</span> <span class="c1">// bool(false)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// # 3
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">120081</span> <span class="o">==</span> <span class="mf">1200.81</span> <span class="o">*</span> <span class="mi">100</span><span class="p">);</span> <span class="c1">// bool(true)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// # 4
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">120085</span> <span class="o">-</span> <span class="mf">1200.85</span> <span class="o">*</span> <span class="mi">100</span><span class="p">);</span> <span class="c1">// float(1.4551915228367E-11)
</span></span></span></code></pre></div><h2 id="实例-4">实例 4</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$a</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$b</span> <span class="o">=</span> <span class="mf">0.9</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$c</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">((</span><span class="nv">$a</span> <span class="o">+</span> <span class="nv">$b</span><span class="p">)</span> <span class="o">==</span> <span class="nv">$c</span><span class="p">);</span> <span class="c1">// bool(true)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">((</span><span class="nv">$c</span> <span class="o">-</span> <span class="nv">$b</span><span class="p">)</span> <span class="o">==</span> <span class="nv">$a</span><span class="p">);</span> <span class="c1">// bool(false)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">sprintf</span><span class="p">(</span><span class="s1">&#39;%.20f&#39;</span><span class="p">,</span> <span class="nv">$a</span> <span class="o">+</span> <span class="nv">$b</span><span class="p">));</span> <span class="c1">// string(22) &#34;1.00000000000000000000&#34;
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">sprintf</span><span class="p">(</span><span class="s1">&#39;%.20f&#39;</span><span class="p">,</span> <span class="nv">$c</span> <span class="o">-</span> <span class="nv">$b</span><span class="p">));</span> <span class="c1">// string(22) &#34;0.09999999999999997780&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">((</span><span class="mf">0.5</span> <span class="o">-</span> <span class="mf">0.25</span><span class="p">)</span> <span class="o">===</span> <span class="mf">0.25</span><span class="p">);</span> <span class="c1">// bool(true) 0.5 二进制 0.1，0.25 二进制 0.01
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">((</span><span class="mf">0.25</span> <span class="o">+</span> <span class="mf">0.25</span><span class="p">)</span> <span class="o">===</span> <span class="mf">0.5</span><span class="p">);</span> <span class="c1">// bool(true)
</span></span></span></code></pre></div><h2 id="实例-5">实例 5</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$n</span> <span class="o">=</span> <span class="mf">19.99</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">dump</span><span class="p">(</span><span class="nv">$n</span> <span class="o">*</span> <span class="mi">100</span><span class="p">);</span> <span class="c1">// 1999.0
</span></span></span><span class="line"><span class="cl"><span class="nx">dump</span><span class="p">((</span><span class="nx">int</span><span class="p">)</span> <span class="p">(</span><span class="nv">$n</span> <span class="o">*</span> <span class="mi">100</span><span class="p">));</span> <span class="c1">// 1998 !!!
</span></span></span><span class="line"><span class="cl"><span class="nx">dump</span><span class="p">((</span><span class="nx">string</span><span class="p">)</span> <span class="p">(</span><span class="nv">$n</span> <span class="o">*</span> <span class="mi">100</span><span class="p">));</span> <span class="c1">// &#34;1999&#34;
</span></span></span><span class="line"><span class="cl"><span class="nx">dump</span><span class="p">((</span><span class="nx">int</span><span class="p">)</span> <span class="p">(</span><span class="nx">string</span><span class="p">)</span> <span class="p">(</span><span class="nv">$n</span> <span class="o">*</span> <span class="mi">100</span><span class="p">));</span> <span class="c1">// 1999
</span></span></span><span class="line"><span class="cl"><span class="nx">dump</span><span class="p">(</span><span class="nx">round</span><span class="p">(</span><span class="nv">$n</span> <span class="o">*</span> <span class="mi">100</span><span class="p">));</span> <span class="c1">// 1999.0 !!!
</span></span></span><span class="line"><span class="cl"><span class="nx">dump</span><span class="p">((</span><span class="nx">int</span><span class="p">)</span> <span class="nx">round</span><span class="p">(</span><span class="nv">$n</span> <span class="o">*</span> <span class="mi">100</span><span class="p">));</span> <span class="c1">// 1999
</span></span></span></code></pre></div><h2 id="分析">分析</h2>
<p>看文档：</p>
<ul>
<li><a href="https://www.php.net/manual/zh/function.gettype.php">gettype | php.net</a></li>
<li><a href="https://www.php.net/manual/zh/language.types.float.php">Float 浮点型 | php.net</a></li>
</ul>
<blockquote>
<p>浮点型（也叫浮点数 float，双精度数 double 或实数 real）
浮点数的字长和平台相关，尽管通常最大值是 1.8e308 并具有 14 位十进制数字的精度（64 位 IEEE 格式）。
所以永远不要相信浮点数结果精确到了最后一位，也永远不要比较两个浮点数是否相等。如果确实需要更高的精度，应该使用任意精度数学函数或者 gmp 函数。</p>
</blockquote>
<p>实例 1：说明在 PHP 中 <code>float</code> 与 <code>dobule</code> 是一回事。在 C 级别，所有内容都存储为 double。</p>
<p>实例 2、3：float 的比较结果是 <em>视情况而定</em>，<strong>永远不要相信浮点数结果精确到了最后一位</strong>。</p>
<p>实例 4：出现这个问题是因为浮点数计算涉及精度，当浮点数转为二进制时有可能会造成精度丢失。</p>
<ul>
<li>Arbitrary-precision arithmetic library for PHP <a href="https://github.com/brick/math">https://github.com/brick/math</a></li>
</ul>
<h2 id="浮点数转二进制方法">浮点数转二进制方法</h2>
<p>整数部分采用除以 2 取余方法，小数部分采用乘以 2 取整方法。</p>
<p>例如：把数字 8.5 转为二进制：</p>
<p>整数部分是 8：</p>
<ul>
<li>8/2=4 8%2=0</li>
<li>4/2=2 4%2=0</li>
<li>2/2=1 2%2=0</li>
<li>1 比 2 小，因此不需要计算下去，整数 8 的二进制为 1000</li>
</ul>
<p>小数部分是 0.5：</p>
<ul>
<li>0.5x2 = 1.0</li>
<li>因取整后小数部分为 0，因此不需要再计算下去，小数 0.5 的二进制为 0.1</li>
</ul>
<p><code>8.5</code> 的二进制为 <code>1000.1</code>。</p>
<p>计算数字 0.9 的二进制：</p>
<ul>
<li>0.9x2 = 1.8</li>
<li>0.8x2 = 1.6</li>
<li>0.6x2 = 1.2</li>
<li>0.2x2 = 0.4</li>
<li>0.4x2 = 0.8</li>
<li>0.8x2 = 1.6</li>
<li>&hellip; 之后不断循环下去，当截取精度为 N 时，N 后的数会被舍去，导致精度丢失。</li>
</ul>
<p>实例 4 中 <code>0.9</code> 在转为二进制时精度丢失，导致比较时出现错误。</p>
<blockquote>
<p>你看似有穷的小数，在计算机的二进制表示里却是无穷的。</p>
</blockquote>
<p>计算数字 0.25 的二进制：</p>
<ul>
<li>0.25x2 = 0.5</li>
<li>0.5x2 = 1.0</li>
</ul>
<p><code>0.25</code> 的二进制为 <code>0.01</code>。</p>
<h2 id="float-比较方法">float 比较方法</h2>
<h3 id="使用-round-方法处理后再比较">使用 round 方法处理后再比较</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mi">120085</span> <span class="o">==</span> <span class="nx">round</span><span class="p">(</span><span class="mf">1200.85</span> <span class="o">*</span> <span class="mi">100</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// bool(true)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mf">12008.5</span> <span class="o">===</span> <span class="nx">round</span><span class="p">(</span><span class="mf">1200.85</span> <span class="o">*</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// bool(true)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="mf">1200.85</span> <span class="o">===</span> <span class="nx">round</span><span class="p">(</span><span class="mf">1200.8499999</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// bool(true)
</span></span></span></code></pre></div><h3 id="使用高精度运算方法">使用高精度运算方法</h3>
<p>见文档 <a href="https://www.php.net/manual/zh/ref.bc.php">BC 数学 函数 | php.net</a>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">((</span><span class="mi">1</span> <span class="o">-</span> <span class="mf">0.9</span><span class="p">)</span> <span class="o">==</span> <span class="mf">0.1</span><span class="p">);</span> <span class="c1">// bool(false)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nx">bcsub</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mf">0.9</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span> <span class="o">==</span> <span class="mf">0.1</span><span class="p">);</span> <span class="c1">// bool(true)
</span></span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">((</span><span class="nx">float</span><span class="p">)</span> <span class="nx">bcsub</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mf">0.9</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span> <span class="o">===</span> <span class="mf">0.1</span><span class="p">);</span> <span class="c1">// bool(true)
</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://www.codecasts.com/blog/post/php-tricky-floats-comparison-with-int">PHP 浮点型与整型比较的小坑 | codecasts</a></li>
<li><a href="https://blog.csdn.net/fdipzone/article/details/48106065">php 浮点数比较方法 | csdn</a></li>
<li><a href="https://www.laruence.com/2013/03/26/2884.html">PHP 浮点数的一个常见问题的解答 | laruence</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>IDE Snippet</title>
      <link>https://zyf.im/2020/04/28/ide-snippet/</link>
      <pubDate>Tue, 28 Apr 2020 10:43:39 +0000</pubDate>
      <guid>https://zyf.im/2020/04/28/ide-snippet/</guid>
      <description>&lt;h2 id=&#34;phpstorm&#34;&gt;PhpStorm&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://zyf.im/2018/05/05/phpstorm-using-experience/&#34;&gt;PhpStorm 使用经验 | ZYF.IM&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;cursor&#34;&gt;Cursor&lt;/h2&gt;
&lt;h3 id=&#34;备份扩展&#34;&gt;备份扩展&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cursor --list-extensions &amp;gt; cursor_extensions.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;xargs -L &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; cursor --install-extension &amp;lt; cursor_extensions.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;vscode&#34;&gt;VSCode&lt;/h2&gt;
&lt;h3 id=&#34;配置文件&#34;&gt;配置文件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~/Library/Application\ Support/Cursor/User/settings.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/Library/Application\ Support/Cursor/User/keybindings.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;编辑-settingsjson&#34;&gt;编辑 settings.json&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;command + ,&lt;/code&gt; 右上角点击 &lt;code&gt;Open Settings(JSON)&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;terminal.integrated.fontSize&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;window.zoomLevel&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1.4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;editor.fontSize&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;editor.wordWrapColumn&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;300&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;editor.renderWhitespace&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;all&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;vim&#34;&gt;VIM&lt;/h2&gt;
&lt;h3 id=&#34;常用&#34;&gt;常用&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;全选（高亮显示）：按 esc 后，然后 &lt;code&gt;ggvG&lt;/code&gt; 或者 &lt;code&gt;ggVG&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;全部复制：按 esc 后，然后 &lt;code&gt;ggyG&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;全部删除：按 esc 后，然后 &lt;code&gt;dG&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;粘贴到终端-vim-缩进错乱&#34;&gt;粘贴到终端 vim 缩进错乱&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://stackoverflow.com/questions/2514445/turning-off-auto-indent-when-pasting-text-into-vim/38258720#38258720&#34;&gt;stackoverflow&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 vim 中粘贴前先输入：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;:set paste
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;原因：在终端的 vim 中没有相应的程序来处理这个从其他应用复制粘贴的过程，所以 vim 通过插入键盘输入的 buffer 来模拟这个粘贴的过程，这个时候 vim 会以为这是用户输入的。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="phpstorm">PhpStorm</h2>
<p><a href="/2018/05/05/phpstorm-using-experience/">PhpStorm 使用经验 | ZYF.IM</a></p>
<h2 id="cursor">Cursor</h2>
<h3 id="备份扩展">备份扩展</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cursor --list-extensions &gt; cursor_extensions.txt
</span></span><span class="line"><span class="cl">xargs -L <span class="m">1</span> cursor --install-extension &lt; cursor_extensions.txt
</span></span></code></pre></div><h2 id="vscode">VSCode</h2>
<h3 id="配置文件">配置文件</h3>
<ul>
<li><code>~/Library/Application\ Support/Cursor/User/settings.json</code></li>
<li><code>~/Library/Application\ Support/Cursor/User/keybindings.json</code></li>
</ul>
<h3 id="编辑-settingsjson">编辑 settings.json</h3>
<p><code>command + ,</code> 右上角点击 <code>Open Settings(JSON)</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;terminal.integrated.fontSize&#34;</span><span class="p">:</span> <span class="mi">14</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;window.zoomLevel&#34;</span><span class="p">:</span> <span class="mf">1.4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;editor.fontSize&#34;</span><span class="p">:</span> <span class="mi">14</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;editor.wordWrapColumn&#34;</span><span class="p">:</span> <span class="mi">300</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;editor.renderWhitespace&#34;</span><span class="p">:</span> <span class="s2">&#34;all&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="vim">VIM</h2>
<h3 id="常用">常用</h3>
<ul>
<li>全选（高亮显示）：按 esc 后，然后 <code>ggvG</code> 或者 <code>ggVG</code></li>
<li>全部复制：按 esc 后，然后 <code>ggyG</code></li>
<li>全部删除：按 esc 后，然后 <code>dG</code></li>
</ul>
<h3 id="粘贴到终端-vim-缩进错乱">粘贴到终端 vim 缩进错乱</h3>
<blockquote>
<p><a href="https://stackoverflow.com/questions/2514445/turning-off-auto-indent-when-pasting-text-into-vim/38258720#38258720">stackoverflow</a></p>
</blockquote>
<p>在 vim 中粘贴前先输入：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">:set paste
</span></span></code></pre></div><p>原因：在终端的 vim 中没有相应的程序来处理这个从其他应用复制粘贴的过程，所以 vim 通过插入键盘输入的 buffer 来模拟这个粘贴的过程，这个时候 vim 会以为这是用户输入的。</p>
<p>问题就是出在这：当上一行结束，光标进入下一行时 vim 会自动以上一行的的缩进为初始位置。这样就会破坏原始文件的缩进。</p>
<h3 id="基础命令">基础命令</h3>
<ul>
<li><code>gg</code> 是让光标移到首行，在 vim 才有效，vi 中无效</li>
<li><code>G</code> 光标移到最后一行</li>
<li><code>d</code> 删除选中内容</li>
<li><code>y</code> 复制选中内容到 0 号寄存器</li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP GD 入门使用</title>
      <link>https://zyf.im/2020/04/16/php-gd-getting-started/</link>
      <pubDate>Thu, 16 Apr 2020 17:24:37 +0000</pubDate>
      <guid>https://zyf.im/2020/04/16/php-gd-getting-started/</guid>
      <description>&lt;h2 id=&#34;gd-安装配置&#34;&gt;GD 安装、配置&lt;/h2&gt;
&lt;p&gt;考虑到功能需要使用字体库、图像格式 jpeg\png 所以先安装相关库。&lt;/p&gt;
&lt;h3 id=&#34;字体库-freetype-2&#34;&gt;字体库 FreeType 2&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.freetype.org/&#34;&gt;https://www.freetype.org/&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 在临时目录进行操作&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /tmp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# https://download.savannah.gnu.org/releases/freetype/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget http://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar zxvf freetype-2.10.1.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; freetype-2.10.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/usr/local/freetype &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;图像格式-jpeg&#34;&gt;图像格式 jpeg&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /tmp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# http://www.ijg.org/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget http://www.ijg.org/files/jpegsrc.v9d.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar zxvf jpegsrc.v9.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; jpeg-9/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/usr/local/jpeg &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;图像格式-png&#34;&gt;图像格式 png&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /tmp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# http://www.libpng.org/pub/png/libpng.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://download.sourceforge.net/libpng/libpng-1.6.37.tar.gz --no-check-certificate
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar zxvf libpng-1.6.37.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; libpng-1.6.37
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/usr/local/libpng &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;安装-gd&#34;&gt;安装 GD&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&#34;GD 安装、配置&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/80169757-e35bc200-8618-11ea-8851-a4bc7411f6f9.png&#34;&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="gd-安装配置">GD 安装、配置</h2>
<p>考虑到功能需要使用字体库、图像格式 jpeg\png 所以先安装相关库。</p>
<h3 id="字体库-freetype-2">字体库 FreeType 2</h3>
<p><a href="https://www.freetype.org/">https://www.freetype.org/</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 在临时目录进行操作</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /tmp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># https://download.savannah.gnu.org/releases/freetype/</span>
</span></span><span class="line"><span class="cl">wget http://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tar zxvf freetype-2.10.1.tar.gz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> freetype-2.10.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">./configure --prefix<span class="o">=</span>/usr/local/freetype <span class="o">&amp;&amp;</span> make <span class="o">&amp;&amp;</span> make install
</span></span></code></pre></div><h3 id="图像格式-jpeg">图像格式 jpeg</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /tmp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># http://www.ijg.org/</span>
</span></span><span class="line"><span class="cl">wget http://www.ijg.org/files/jpegsrc.v9d.tar.gz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tar zxvf jpegsrc.v9.tar.gz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> jpeg-9/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">./configure --prefix<span class="o">=</span>/usr/local/jpeg <span class="o">&amp;&amp;</span> make <span class="o">&amp;&amp;</span> make install
</span></span></code></pre></div><h3 id="图像格式-png">图像格式 png</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /tmp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># http://www.libpng.org/pub/png/libpng.html</span>
</span></span><span class="line"><span class="cl">wget https://download.sourceforge.net/libpng/libpng-1.6.37.tar.gz --no-check-certificate
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tar zxvf libpng-1.6.37.tar.gz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> libpng-1.6.37
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">./configure --prefix<span class="o">=</span>/usr/local/libpng <span class="o">&amp;&amp;</span> make <span class="o">&amp;&amp;</span> make install
</span></span></code></pre></div><h3 id="安装-gd">安装 GD</h3>
<p><img alt="GD 安装、配置" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80169757-e35bc200-8618-11ea-8851-a4bc7411f6f9.png"></p>
<p>背景：服务器 php 7.1 通过编译自行安装的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 到 php 源码 dir</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">{</span>php-source-dir<span class="o">}</span>/ext/gd/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 生成 configure 文件</span>
</span></span><span class="line"><span class="cl"><span class="o">{</span>php-dir<span class="o">}</span>/bin/phpize
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看可用参数</span>
</span></span><span class="line"><span class="cl">./configure --help
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置参数</span>
</span></span><span class="line"><span class="cl">./configure --with-php-config<span class="o">={</span>php-dir<span class="o">}</span>/bin/php-config --with-jpeg-dir<span class="o">=</span>/usr/local/jpeg --with-png-dir<span class="o">=</span>/usr/local/libpng --with-freetype-dir<span class="o">=</span>/usr/local/freetype --enable-gd-native-ttf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">make <span class="o">&amp;&amp;</span> make install
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">vim <span class="o">{</span>php-dir<span class="o">}</span>/lib/php.ini
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 最底下增加一行</span>
</span></span><span class="line"><span class="cl"><span class="nv">extension</span><span class="o">=</span>gd.so
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">service php-fpm reload
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查</span>
</span></span><span class="line"><span class="cl">php -r <span class="s2">&#34;var_dump(gd_info());&#34;</span>
</span></span></code></pre></div><p>编译前一定要记得 <code>make clean</code> 清除上次的编译内容，尤其是已经编译安装过的。</p>
<h2 id="实例">实例</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$pic</span> <span class="o">=</span> <span class="nx">imagecreate</span><span class="p">(</span><span class="nv">$maxWidth</span><span class="p">,</span> <span class="nv">$maxHeight</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">//定义颜色
</span></span></span><span class="line"><span class="cl"><span class="nv">$black</span> <span class="o">=</span> <span class="nx">imagecolorallocate</span><span class="p">(</span><span class="nv">$pic</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$white</span> <span class="o">=</span> <span class="nx">imagecolorallocate</span><span class="p">(</span><span class="nv">$pic</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 白底
</span></span></span><span class="line"><span class="cl"><span class="nx">imagefill</span><span class="p">(</span><span class="nv">$pic</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$white</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 打水印
</span></span></span><span class="line"><span class="cl"><span class="nx">imagettftext</span><span class="p">(</span><span class="nv">$pic</span><span class="p">,</span> <span class="nv">$fontSize</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="nv">$x</span><span class="p">,</span> <span class="nv">$y</span><span class="p">,</span> <span class="nv">$lightGrey</span><span class="p">,</span> <span class="nv">$fontFile</span><span class="p">,</span> <span class="nv">$mark</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">imagejpeg</span><span class="p">(</span><span class="nv">$pic</span><span class="p">,</span> <span class="nv">$resultPath</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">imagedestroy</span><span class="p">(</span><span class="nv">$pic</span><span class="p">);</span>
</span></span></code></pre></div><p>文字右对齐：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// https://php.golaravel.com/function.imagettfbbox.html
</span></span></span><span class="line"><span class="cl"><span class="nv">$bbox</span> <span class="o">=</span> <span class="nx">imagettfbbox</span><span class="p">(</span><span class="nv">$fontSize</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$fontFile</span><span class="p">,</span> <span class="nv">$text</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$offset</span> <span class="o">=</span> <span class="nv">$colWidth</span> <span class="o">-</span> <span class="nv">$bbox</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">-</span> <span class="mi">50</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">imagettftext</span><span class="p">(</span><span class="nv">$pic</span><span class="p">,</span> <span class="nv">$fontSize</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$x</span> <span class="o">+</span> <span class="nv">$offset</span><span class="p">,</span> <span class="nv">$y</span><span class="p">,</span> <span class="nv">$_color</span><span class="p">,</span> <span class="nv">$fontFile</span><span class="p">,</span> <span class="nv">$text</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="遇到的报错">遇到的报错</h2>
<h3 id="call-to-undefined-function-imagettftext">Call to undefined function imagettftext()</h3>
<p>出现此问题应该就是 <code>FreeType</code> 没有装好，可参考上面的步骤。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://php.golaravel.com/intro.image.html">php 中文手册 | golaravel</a></li>
<li><a href="https://stackoverflow.com/questions/7290958/php-fatal-error-call-to-undefined-function-imagettftext">PHP Fatal error: Call to undefined function imagettftext() | stackoverflow</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP Code Snippet</title>
      <link>https://zyf.im/2020/04/15/php-code-snippet/</link>
      <pubDate>Wed, 15 Apr 2020 18:03:44 +0000</pubDate>
      <guid>https://zyf.im/2020/04/15/php-code-snippet/</guid>
      <description>&lt;h2 id=&#34;php-sandbox&#34;&gt;PHP Sandbox&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://3v4l.org/&#34;&gt;Online PHP editor | 3v4l.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://onlinephp.io/&#34;&gt;PHP Sandbox | onlinephp.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;phpnet&#34;&gt;PHP.net&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.php.net/supported-versions.php&#34;&gt;Supported Versions | PHP.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.php.net/eol.php&#34;&gt;Unsupported Branches | PHP.net&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://php.watch/&#34;&gt;PHP News, Articles, Upcoming Changes, and more | PHP.Watch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/shivammathur/homebrew-php&#34;&gt;brew tap shivammathur/php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/shivammathur/homebrew-extensions&#34;&gt;brew tap shivammathur/extensions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;php-cli&#34;&gt;PHP CLI&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看 PHP 编译时的参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php -r &lt;span class=&#34;s2&#34;&gt;&amp;#34;phpinfo();&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep configure
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# in source code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --help
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --help &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep openssl
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看 .ini 配置文件路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php --ini
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php -r &lt;span class=&#34;s2&#34;&gt;&amp;#34;phpinfo();&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;Configuration File&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看 Modules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php -m
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示 PHP 信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php -i
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php -i &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep -E &lt;span class=&#34;s2&#34;&gt;&amp;#34;Loaded Configuration|Scan this dir&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php -r &lt;span class=&#34;s2&#34;&gt;&amp;#34;echo ini_get(&amp;#39;memory_limit&amp;#39;);&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php -r &lt;span class=&#34;s2&#34;&gt;&amp;#34;phpinfo();&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep memory
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示扩展配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php --ri gd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查扩展是否存在&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php --re decimal
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 交互式运行模式。具有函数、常量、类名、变量、静态方法调用和类常量的 `tab` 补全功能&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# http://php.net/manual/en/features.commandline.interactive.php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php -a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查 PHP-FPM 配置文件语法&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php-fpm -tt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# check openssl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;php -r &lt;span class=&#34;s2&#34;&gt;&amp;#34;print_r(openssl_get_cert_locations());&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;snippets&#34;&gt;Snippets&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 临时设置最大内存占用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;ini_set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;memory_limit&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;1024M&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 设置脚本最大执行时间为 0 永不过期
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;set_time_limit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;composer&#34;&gt;Composer&lt;/h2&gt;
&lt;h3 id=&#34;aliyun-repo&#34;&gt;aliyun repo&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://developer.aliyun.com/composer&#34;&gt;阿里云 Composer 全量镜像&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="php-sandbox">PHP Sandbox</h2>
<ul>
<li><a href="https://3v4l.org/">Online PHP editor | 3v4l.org</a></li>
<li><a href="https://onlinephp.io/">PHP Sandbox | onlinephp.io</a></li>
</ul>
<h2 id="phpnet">PHP.net</h2>
<ul>
<li><a href="https://www.php.net/supported-versions.php">Supported Versions | PHP.net</a></li>
<li><a href="https://www.php.net/eol.php">Unsupported Branches | PHP.net</a></li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="https://php.watch/">PHP News, Articles, Upcoming Changes, and more | PHP.Watch</a></li>
<li><a href="https://github.com/shivammathur/homebrew-php">brew tap shivammathur/php</a></li>
<li><a href="https://github.com/shivammathur/homebrew-extensions">brew tap shivammathur/extensions</a></li>
</ul>
<h2 id="php-cli">PHP CLI</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 查看 PHP 编译时的参数</span>
</span></span><span class="line"><span class="cl">php -r <span class="s2">&#34;phpinfo();&#34;</span> <span class="p">|</span> grep configure
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># in source code</span>
</span></span><span class="line"><span class="cl">./configure --help
</span></span><span class="line"><span class="cl">./configure --help <span class="p">|</span> grep openssl
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看 .ini 配置文件路径</span>
</span></span><span class="line"><span class="cl">php --ini
</span></span><span class="line"><span class="cl">php -r <span class="s2">&#34;phpinfo();&#34;</span> <span class="p">|</span> grep <span class="s2">&#34;Configuration File&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看 Modules</span>
</span></span><span class="line"><span class="cl">php -m
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示 PHP 信息</span>
</span></span><span class="line"><span class="cl">php -i
</span></span><span class="line"><span class="cl">php -i <span class="p">|</span> grep -E <span class="s2">&#34;Loaded Configuration|Scan this dir&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">php -r <span class="s2">&#34;echo ini_get(&#39;memory_limit&#39;);&#34;</span>
</span></span><span class="line"><span class="cl">php -r <span class="s2">&#34;phpinfo();&#34;</span> <span class="p">|</span> grep memory
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示扩展配置</span>
</span></span><span class="line"><span class="cl">php --ri gd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查扩展是否存在</span>
</span></span><span class="line"><span class="cl">php --re decimal
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 交互式运行模式。具有函数、常量、类名、变量、静态方法调用和类常量的 `tab` 补全功能</span>
</span></span><span class="line"><span class="cl"><span class="c1"># http://php.net/manual/en/features.commandline.interactive.php</span>
</span></span><span class="line"><span class="cl">php -a
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查 PHP-FPM 配置文件语法</span>
</span></span><span class="line"><span class="cl">php-fpm -tt
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># check openssl</span>
</span></span><span class="line"><span class="cl">php -r <span class="s2">&#34;print_r(openssl_get_cert_locations());&#34;</span>
</span></span></code></pre></div><h2 id="snippets">Snippets</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 临时设置最大内存占用
</span></span></span><span class="line"><span class="cl"><span class="nx">ini_set</span><span class="p">(</span><span class="s1">&#39;memory_limit&#39;</span><span class="p">,</span> <span class="s1">&#39;1024M&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 设置脚本最大执行时间为 0 永不过期
</span></span></span><span class="line"><span class="cl"><span class="nx">set_time_limit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="composer">Composer</h2>
<h3 id="aliyun-repo">aliyun repo</h3>
<p><a href="https://developer.aliyun.com/composer">阿里云 Composer 全量镜像</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">    <span class="s2">&#34;config&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;disable-tls&#34;</span>: true,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;gitlab-domains&#34;</span>: <span class="o">[]</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;optimize-autoloader&#34;</span>: true,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;preferred-install&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;*&#34;</span>: <span class="s2">&#34;dist&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;secure-http&#34;</span>: false,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;sort-packages&#34;</span>: true,
</span></span><span class="line"><span class="cl">    <span class="o">}</span>,
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;repositories&#34;</span>: <span class="o">[</span>
</span></span><span class="line"><span class="cl">        <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;type&#34;</span>: <span class="s2">&#34;cvs&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;url&#34;</span>: <span class="s2">&#34;...&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>,
</span></span><span class="line"><span class="cl">        <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;type&#34;</span>: <span class="s2">&#34;composer&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;url&#34;</span>: <span class="s2">&#34;https://mirrors.tencent.com/composer/&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>,
</span></span><span class="line"><span class="cl">        <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;type&#34;</span>: <span class="s2">&#34;composer&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;url&#34;</span>: <span class="s2">&#34;https://mirrors.aliyun.com/composer/&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>,
</span></span><span class="line"><span class="cl">        <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;type&#34;</span>: <span class="s2">&#34;composer&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;url&#34;</span>: <span class="s2">&#34;https://asset-packagist.org&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span></code></pre></div><h3 id="忽略-php-版本限制">忽略 php 版本限制</h3>
<p><strong>这个是错误做法，这样会造成库安装的版本错误。不应该使用。</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">composer require hellogerard/jobby --ignore-platform-reqs
</span></span></code></pre></div><p>推荐做法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">which composer
</span></span><span class="line"><span class="cl"><span class="c1"># /usr/local/bin/composer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># {正确的 PHP 版本}/bin/php /usr/local/bin/composer require hellogerard/jobby</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 如果内存不够，可以设置为不限制</span>
</span></span><span class="line"><span class="cl">/usr/local/opt/php@7.1/bin/php -d <span class="nv">memory_limit</span><span class="o">=</span>-1 /usr/local/bin/composer update -vvv
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 若项目之前已通过其他源安装，则需要更新 composer.lock 文件</span>
</span></span><span class="line"><span class="cl">composer update --lock
</span></span></code></pre></div><h2 id="phpstan">PHPStan</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="k">public</span> <span class="k">const</span> <span class="no">WHEELER</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="s1">&#39;car&#39;</span> <span class="o">=&gt;</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s1">&#39;bike&#39;</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd"> * @param key-of&lt;Foo::WHEELER&gt; $type
</span></span></span><span class="line"><span class="cl"><span class="sd"> * @param value-of&lt;Foo::WHEELER&gt; $wheels
</span></span></span><span class="line"><span class="cl"><span class="sd"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">repair</span><span class="p">(</span><span class="nx">string</span> <span class="nv">$type</span><span class="p">,</span> <span class="nx">int</span> <span class="nv">$wheels</span><span class="p">)</span><span class="o">:</span> <span class="nx">void</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// $type is &#39;bike&#39;|&#39;car&#39;
</span></span></span><span class="line"><span class="cl">    <span class="c1">// $wheels is 2|4
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">repair</span><span class="p">(</span><span class="s2">&#34;bus&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Parameter #1 $type of function repair expects &#39;bike&#39;|&#39;car&#39;, &#39;bus&#39; given.
</span></span></span></code></pre></div><h2 id="links">Links</h2>
<ul>
<li><a href="https://mwop.net/blog/2019-04-30-ondrej-multiversion-php.html">Managing Multiple PHP versions via the ondrej/php PPA</a></li>
<li><a href="https://man7.org/linux/man-pages/man1/update-alternatives.1.html">update-alternatives(1) — Linux manual page</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>【PRCC2019 全套入门教程】笔记</title>
      <link>https://zyf.im/2020/03/13/prcc2019-getting-started-notes/</link>
      <pubDate>Fri, 13 Mar 2020 22:25:25 +0000</pubDate>
      <guid>https://zyf.im/2020/03/13/prcc2019-getting-started-notes/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;u1s1 本教程质量一般，推荐看看 李兴兴 老师的教程。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;用视频记录生活越来越普及，年初旅行也拍了些素材，想着自己也当个 up 主。先来学学 PR。&lt;a href=&#34;https://www.bilibili.com/video/av37550078&#34;&gt;【PR】Premiere Pro CC 2019 全套入门教程 | bilibili&lt;/a&gt; 是我在 B 站看的第一套教学视频，坦白说内容不多，有不少重复的东西，但是这样适合完全零基础的同学。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;课代表来收我的课后作业：&lt;a href=&#34;https://www.bilibili.com/video/bv17E41157Pz&#34;&gt;【交作业】PR CC 2019 全套入门教程 课后作业 | 菲菲与帆&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;我的一条 VLOG：&lt;a href=&#34;https://www.bilibili.com/video/BV1K7411o73B&#34;&gt;【VLOG】#01 新年之旅 成都 第一日 | 菲菲与帆&lt;/a&gt; 欢迎来一键三连。&lt;/p&gt;
&lt;iframe src=&#34;//player.bilibili.com/player.html?aid=97184760&amp;bvid=BV1K7411o73B&amp;cid=166268624&amp;page=1&#34; scrolling=&#34;no&#34; border=&#34;0&#34; frameborder=&#34;no&#34; framespacing=&#34;0&#34; allowfullscreen=&#34;true&#34; width=&#34;480px&#34; height=&#34;270px&#34;&gt; &lt;/iframe&gt;
&lt;h2 id=&#34;01-基础流程&#34;&gt;01-基础流程&lt;/h2&gt;
&lt;p&gt;1、窗口乱了后：【窗口-工作区-编辑&amp;amp;重置为保存的布局】&lt;code&gt;alt + shift + 0&lt;/code&gt;
2、【文字工具】加字幕
3、修改字体：【窗口-基础图形】选择编辑
4、【剃刀】剪切多余素材
5、本素材图片来自 &lt;a href=&#34;http://unsample.net/&#34;&gt;http://unsample.net/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;02-转场效果&#34;&gt;02-转场效果&lt;/h2&gt;
&lt;p&gt;1、选择素材点击左键拖拽到【左下窗口】 右下的文件夹可以快速新建文件夹，方便素材分类
2、时序新建时，选择 HDV 720
3、&lt;code&gt;ctrl + d&lt;/code&gt; 添加转场效果
4、【左下窗口】（项目窗口）选择【效果】视频过渡，可以切换效果
5、点击时序上的过渡，【左上窗口】选择【效果控件】，可以编辑转场效果的持续时间、对齐等&lt;/p&gt;
&lt;h2 id=&#34;0304-视频转场特效音频特效&#34;&gt;03&amp;amp;04-视频转场特效&amp;amp;音频特效&lt;/h2&gt;
&lt;p&gt;1、【左上窗口】选择【效果控件】，可以编辑视频效果、过渡效果
2、视频效果，可以点击秒表的图标添加关键帧
3、新建时查看、修改【暂存盘】位置
4、【编辑-首选项-时间轴】可以修改：静止图片默认持续时间等，
已经导入的素材无效
5、【时序窗口】计时器可以点击编辑，输入 500 会跳到第 5 秒的位置
6、音频过渡，结尾声音淡出&lt;/p&gt;
&lt;h2 id=&#34;0506-视频剪辑音画对应&#34;&gt;05&amp;amp;06-视频剪辑&amp;amp;音画对应&lt;/h2&gt;
&lt;p&gt;1、【左上窗口】点击 &lt;code&gt;i&lt;/code&gt; 标记入点，点击 &lt;code&gt;o&lt;/code&gt; 标记出点
2、【左上窗口】窗口右下角的 + 是按钮编辑器
3、【左上窗口】标记完成后可以点击 插入 或者 覆盖
4、【节目窗口】点击 &lt;code&gt;m&lt;/code&gt; 可以标记
5、【节目窗口】窗口右下角的 +，添加转到下一个标记
6、转到下一个标记配合在时序上的标，踩点视频&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p>u1s1 本教程质量一般，推荐看看 李兴兴 老师的教程。</p>
</blockquote>
<p>用视频记录生活越来越普及，年初旅行也拍了些素材，想着自己也当个 up 主。先来学学 PR。<a href="https://www.bilibili.com/video/av37550078">【PR】Premiere Pro CC 2019 全套入门教程 | bilibili</a> 是我在 B 站看的第一套教学视频，坦白说内容不多，有不少重复的东西，但是这样适合完全零基础的同学。</p>
<!-- more -->
<p>课代表来收我的课后作业：<a href="https://www.bilibili.com/video/bv17E41157Pz">【交作业】PR CC 2019 全套入门教程 课后作业 | 菲菲与帆</a> 。</p>
<p>我的一条 VLOG：<a href="https://www.bilibili.com/video/BV1K7411o73B">【VLOG】#01 新年之旅 成都 第一日 | 菲菲与帆</a> 欢迎来一键三连。</p>
<iframe src="//player.bilibili.com/player.html?aid=97184760&bvid=BV1K7411o73B&cid=166268624&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" width="480px" height="270px"> </iframe>
<h2 id="01-基础流程">01-基础流程</h2>
<p>1、窗口乱了后：【窗口-工作区-编辑&amp;重置为保存的布局】<code>alt + shift + 0</code>
2、【文字工具】加字幕
3、修改字体：【窗口-基础图形】选择编辑
4、【剃刀】剪切多余素材
5、本素材图片来自 <a href="http://unsample.net/">http://unsample.net/</a></p>
<h2 id="02-转场效果">02-转场效果</h2>
<p>1、选择素材点击左键拖拽到【左下窗口】 右下的文件夹可以快速新建文件夹，方便素材分类
2、时序新建时，选择 HDV 720
3、<code>ctrl + d</code> 添加转场效果
4、【左下窗口】（项目窗口）选择【效果】视频过渡，可以切换效果
5、点击时序上的过渡，【左上窗口】选择【效果控件】，可以编辑转场效果的持续时间、对齐等</p>
<h2 id="0304-视频转场特效音频特效">03&amp;04-视频转场特效&amp;音频特效</h2>
<p>1、【左上窗口】选择【效果控件】，可以编辑视频效果、过渡效果
2、视频效果，可以点击秒表的图标添加关键帧
3、新建时查看、修改【暂存盘】位置
4、【编辑-首选项-时间轴】可以修改：静止图片默认持续时间等，
已经导入的素材无效
5、【时序窗口】计时器可以点击编辑，输入 500 会跳到第 5 秒的位置
6、音频过渡，结尾声音淡出</p>
<h2 id="0506-视频剪辑音画对应">05&amp;06-视频剪辑&amp;音画对应</h2>
<p>1、【左上窗口】点击 <code>i</code> 标记入点，点击 <code>o</code> 标记出点
2、【左上窗口】窗口右下角的 + 是按钮编辑器
3、【左上窗口】标记完成后可以点击 插入 或者 覆盖
4、【节目窗口】点击 <code>m</code> 可以标记
5、【节目窗口】窗口右下角的 +，添加转到下一个标记
6、转到下一个标记配合在时序上的标，踩点视频</p>
<h2 id="0708-简单动画素材嵌套">07&amp;08-简单动画&amp;素材嵌套</h2>
<p>1、【左上窗口】效果控件，可以修改素材位置、缩放
2、【左上窗口】效果控件，点击小秒表可以添加关键帧动画
3、导出素材前可以修改静止图片默认持续时间
4、创建多个时序，再创建一个 master 时序，作为主时序
5、时序拖入时序时，注意点击前面的 <code>V1</code> <code>A1</code>，否则可能只有视频或音频
6、【时序窗口】时序取消链接，可以删除音频轨
7、按 <code>alt</code> 键拖拽可以快速复制</p>
<h2 id="091011-自定义转场视频效果变形和扭曲">09&amp;10&amp;11-自定义转场&amp;视频效果&amp;变形和扭曲</h2>
<p>1、使用 PS 制作灰度图，新建 颜色模式 灰度，图片要和视频尺寸一致
2、在线图片编辑 <code>https://www.uupoop.com/</code>
3、图片使用 <code>tiff</code> 格式，原因不懂
4、视频过渡 擦除 渐变擦除，然后可以选择自己制作的图片
5、从【效果】可以选择后向上拖入【效果控件】
6、<code>fx</code> 可以开启或关闭效果，还有 清除、复制、粘贴
7、【项目窗口】新建 调整图层，可以给视频加一个整体的效果
8、要善于使用标记定位
9、视频效果：变形、扭曲</p>
<h2 id="12-模板的下载和使用">12-模板的下载和使用</h2>
<p>1、.mogrt 模板
2、导入方法 1：【图形】安装动态图形模板
3、导入方法 2：【窗口】基础图形，游览，右下角加号
4、导入方法 3：将模板文件复制到
Mac：<code>username/Library/Application Support/Adobe/Common/motion Graphics Template/</code>
Win：<code>root://Users/Username/AppData/Roaming/Adobe/Common/Motion Graphics Templates</code>
5、【基础图形】编辑，可以修改模板内容
6、按 <code>enter</code> 键，可以渲染视频</p>
<h2 id="13-标准模板的使用">13-标准模板的使用</h2>
<p>1、<code>.prproj</code> 模板
2、【视图】显示标尺、显示参考线，方便对齐</p>
<h2 id="1415-倒计数器及色彩调节传统与图形字幕">14&amp;15-倒计数器及色彩调节&amp;传统与图形字幕</h2>
<p>1、【项目窗口】新建 通用倒计时片头，就是到 2 就没了
2、【向前选择轨道工具】<code>ctrl + a</code>
3、视频效果 扭曲 球面化
4、视频效果 扭曲 偏移
5、视频效果 杂色与颗粒 杂色
6、【文件】新建 字幕 开放式字幕</p>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>回顾 2019</title>
      <link>https://zyf.im/2019/12/31/review-2019/</link>
      <pubDate>Tue, 31 Dec 2019 23:00:00 +0000</pubDate>
      <guid>https://zyf.im/2019/12/31/review-2019/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://y.qq.com/n/ryqq/songDetail/235474091&#34;&gt;新裤子 · 夏日终曲&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;2019 年的第一天从三亚开始，在 &lt;em&gt;第一市场海鲜市场&lt;/em&gt; 买了海味，然后直接到旁边的 &lt;em&gt;林姐香味海鲜&lt;/em&gt; 加工，迟到了超级好吃的香辣蟹。飞回北京就是滑雪团建，年后双双得到了最佳合作奖，感谢我们每周的火锅，让大家多了些感情。&lt;/p&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;
&lt;p&gt;年后买了烤箱烹饪技术再次升级，烤鸡翅、披萨、蛋挞甚至烤鱼，都已不在话下。受 shilei 推荐也喜欢玩德州扑克。看 B 站 UP 主自驾游，疯狂迷恋上了房车一段时间，还去看了一次房山的房车展（够远的），应该会结下不解之缘。&lt;/p&gt;
&lt;p&gt;3 月 29 日公司 7 周年，第一次庆祝公司周年，吃蛋糕前大家合了张影。这张照片我挺感动的，说不好具体的原因，可能是因为大家的笑容吧。清明小假在奥森玩了个露营，是真的有点冷。涛爷来北京面试，转头就成了科大博士，给劲儿。&lt;/p&gt;
&lt;p&gt;4 月中的一个双休日和爸妈南下，和比比定了亲 🎉🎉🎉。&lt;/p&gt;
&lt;p&gt;5 月乐队的夏天，是今年开过最好的综艺，刺猬、新裤子很棒。&lt;/p&gt;
&lt;p&gt;2019 年过去了，我很怀念它。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://y.qq.com/n/ryqq/songDetail/235474091">新裤子 · 夏日终曲</a></p>
<p>2019 年的第一天从三亚开始，在 <em>第一市场海鲜市场</em> 买了海味，然后直接到旁边的 <em>林姐香味海鲜</em> 加工，迟到了超级好吃的香辣蟹。飞回北京就是滑雪团建，年后双双得到了最佳合作奖，感谢我们每周的火锅，让大家多了些感情。</p>
<p>&ndash; EOF &ndash;</p>
<p>年后买了烤箱烹饪技术再次升级，烤鸡翅、披萨、蛋挞甚至烤鱼，都已不在话下。受 shilei 推荐也喜欢玩德州扑克。看 B 站 UP 主自驾游，疯狂迷恋上了房车一段时间，还去看了一次房山的房车展（够远的），应该会结下不解之缘。</p>
<p>3 月 29 日公司 7 周年，第一次庆祝公司周年，吃蛋糕前大家合了张影。这张照片我挺感动的，说不好具体的原因，可能是因为大家的笑容吧。清明小假在奥森玩了个露营，是真的有点冷。涛爷来北京面试，转头就成了科大博士，给劲儿。</p>
<p>4 月中的一个双休日和爸妈南下，和比比定了亲 🎉🎉🎉。</p>
<p>5 月乐队的夏天，是今年开过最好的综艺，刺猬、新裤子很棒。</p>
<p>2019 年过去了，我很怀念它。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Lonicera Framework</title>
      <link>https://zyf.im/2019/12/19/lonicera-framework/</link>
      <pubDate>Thu, 19 Dec 2019 16:46:06 +0000</pubDate>
      <guid>https://zyf.im/2019/12/19/lonicera-framework/</guid>
      <description>【PHP 核心技术与最佳实践】第二版 第 6 章 读书笔记</description>
      <content:encoded><![CDATA[<p>项目代码：<a href="https://github.com/imzyf/lonicera">imzyf/lonicera | GitHub</a></p>
<blockquote>
<p>【PHP 核心技术与最佳实践】第二版 第 6 章 读书笔记</p>
</blockquote>
<p>Lonicera Framework - Every French soldier carries a marshal’s baton in his knapsack.</p>
<h2 id="mvc">MVC</h2>
<p>MVC 模式的目的是实现一种动态的程序设计，使后续对程序的修改和扩展简化，并且使程序某一部分的重复利用成为可能。</p>
<h2 id="lonicera-01">Lonicera 0.1</h2>
<h3 id="bootstrap">bootstrap</h3>
<p>index.php 单一入口模式。</p>
<p>启动 PHP 内置 Web 服务器：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">php -S localhost:7070
</span></span></code></pre></div><h3 id="路由器层">路由器层</h3>
<p>更偏向于使用 PATH_INFO 方式来访问。</p>
<p>从传统 URL 参数模式的访问地址进行解析，提取里面的 group、controller、action、param 4 个参数，随后交给 bootstrap 进行 dispatch 处理。</p>
<h3 id="数据模型">数据模型</h3>
<p>用 PDO 来实现连接数据库。</p>
<ul>
<li>ORM Object Relational Mapping 对象与数据库的映射叫作对象关系映射</li>
<li>PO Persistent Object 把一个数据库中的表的一行记录对应的对象称为持久对象</li>
<li>BO Business Object 业务对象 把业务逻辑封装为一个对象</li>
<li>VO Value Object 值对象 界面显示的数据对象</li>
<li>DTO Data Transfer Object 用在热呵呵需要数据传输的地方</li>
<li>DAO Data Access Object 指代 Active Record 模式中的数据对象</li>
</ul>
<p>传统的 ORM 模式提倡数据对象和负责持久化的代码的分开，但是这并没有坚持数据操作的工作量。还有一种 ORM 模式叫作 Active Record。在 Active Record 中，模型层集成了 ORM 的功能，他们及代表实体，包含因为业务逻辑，又是数据对象，并负责把自己存储到数据库中。</p>
<p>Active Record 模式中的数据对象不再是 PO 对象，而是 DAO。</p>
<p>一系列的数据库操作组合起来，称之为 Service。Service 向下负责与数据库打交道，向上负责接收页面传递的参数以及数据的传输。理论上应该对 DAO 进项抽象到一个 Service 中。</p>
<h3 id="视图层">视图层</h3>
<p>PHP MVC 中的显示层开始朝着轻量化、API 化发展了。</p>
<p>增强一个类通常途径：</p>
<ol>
<li>使用 <code>__call</code> <code>__set</code> <code>__get</code> 等魔术方法</li>
<li>使用反射</li>
<li>使用 <code>trait</code></li>
<li>使用继承和组合</li>
</ol>
<h3 id="初步改进">初步改进</h3>
<p><code>spl_autoload_register</code> 统一加载文件。</p>
<h2 id="lonicera-02">Lonicera 0.2</h2>
<h3 id="引入异常机制">引入异常机制</h3>
<p>同时设置 <code>set_error_handler</code> 和 <code>set_exception_handler</code>。PHP 同时存在错误和异常两个互不包含的概念。</p>
<h3 id="拦截器与插件">拦截器与插件</h3>
<p><code>dispatch</code> 中处理。使用正则匹配、<code>call_user_func</code>。</p>
<h3 id="request-增强与安全防御">Request 增强与安全防御</h3>
<p>包装 <code>$_REQUEST</code>。</p>
<h2 id="lonicera-03">Lonicera 0.3</h2>
<h3 id="composer-类加载机制">Composer 类加载机制</h3>
<p><code>PSR-4</code>。</p>
<h3 id="model-增强">Model 增强</h3>
<p><code>illuminata/database</code>。</p>
<h3 id="控制反转与依赖注入">控制反转与依赖注入</h3>
<p><code>Inversion of Control</code> <code>IoC</code>，<code>Dependency Injection</code> <code>DI</code>。</p>
<ul>
<li>谁控制谁？IoC 容器控制了对象。</li>
<li>控制什么？主要控制了外部资源获取（不只是对象创建，还包括比如文件等）。</li>
</ul>
<p>传统应用程序是：由我们自己在对象中主动控制去直接获取依赖对象，而反转则是：由容器来帮忙创建及注入依赖对象。由于是容器帮我们查找及注入依赖对象，对象只是被动接受依赖对象，所以是反转。</p>
<ul>
<li>哪些方面反转了？依赖对象的获取被反转了。</li>
</ul>
<p>在我们使用 UserOrder 对象的时候，不再需要手动去创建 User 对象和 Order 对象了，而是直接问 IoC 容器去要 UserOrder 对象，IoC 容器会负责查找 UserOrder 的依赖并替我们创建 User 对象和 Order 对象，并管理它们。也就是说和我们打交道的是 IoC 容器。</p>
<p>依赖注入，是组件之间依赖关系由容器在运行期决定形象来说，即由容器动态地将某个依赖关系注入到组件之中。依赖注入的目的并非是为软件系统带来更多功能，而是为了提高组件重用的效率，并为系统搭建一个灵活、可扩展的平台。</p>
<p>通过依赖注入机制，我们只需要通过简单的配置，而无需任何代码就可指定目标需要的资源，完成自身的业务逻辑，而不需要关心具体的资源来自何处，由谁实现。</p>
<ul>
<li>谁依赖于谁？当然是应用程序依赖于 IoC 容器。</li>
<li>为什么需要依赖？应用程序需要 IoC 容器来提供对象需要的外部资源。</li>
<li>谁注入谁？IoC 容器注入应用程序某个对象，应用程序依赖的对象。</li>
<li>注入了什么？注入某个对象所需要的外部资源(包括对象、资源、常量数据)。</li>
</ul>
<p>IoC 和 DI 是两个相辅相成的概念，IoC 的实现是使用了 DI，而 DI 的目的是为了实现 IoC。实际上，它们是同一个概念的不同角度描述。</p>
<p>为什么要这么做，这么做有什么意义呢？依赖注入帮我们降低了创建对象的成本，使得对象之间松耦合。</p>
<p><code>composer</code> 通过 <code>composer.json</code> 来管理第三方依赖，从这个角度讲，<code>composer</code> 就是一个 IoC 容器。</p>
<p><code>PSR-11</code> <code>Psr\Contanier\ContainerInterface</code>。</p>
<h2 id="db-sql">DB SQL</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="k">NAMES</span><span class="w"> </span><span class="n">utf8mb4</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">FOREIGN_KEY_CHECKS</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- ----------------------------
</span></span></span><span class="line"><span class="cl"><span class="c1">-- Table structure for user
</span></span></span><span class="line"><span class="cl"><span class="c1">-- ----------------------------
</span></span></span><span class="line"><span class="cl"><span class="k">DROP</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="o">`</span><span class="k">user</span><span class="o">`</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="o">`</span><span class="k">user</span><span class="o">`</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="w"> </span><span class="nb">int</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="n">unsigned</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">`</span><span class="n">name</span><span class="o">`</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span><span class="w"> </span><span class="k">COLLATE</span><span class="w"> </span><span class="n">utf8mb4_unicode_ci</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="w"> </span><span class="k">COMMENT</span><span class="w"> </span><span class="s1">&#39;名字&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">`</span><span class="n">age</span><span class="o">`</span><span class="w"> </span><span class="nb">int</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="n">unsigned</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="s1">&#39;0&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="w"> </span><span class="p">(</span><span class="o">`</span><span class="n">id</span><span class="o">`</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w"> </span><span class="n">ENGINE</span><span class="o">=</span><span class="n">InnoDB</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="n">CHARSET</span><span class="o">=</span><span class="n">utf8mb4</span><span class="w"> </span><span class="k">COLLATE</span><span class="o">=</span><span class="n">utf8mb4_unicode_ci</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">FOREIGN_KEY_CHECKS</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Git 提交 message 规范</title>
      <link>https://zyf.im/2019/06/04/git-commit-message-style-guide/</link>
      <pubDate>Tue, 04 Jun 2019 14:07:46 +0000</pubDate>
      <guid>https://zyf.im/2019/06/04/git-commit-message-style-guide/</guid>
      <description>&lt;p&gt;commit message 应该清晰明了，说明本次提交的目的。基本公式：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;type&amp;gt;(&amp;lt;scope&amp;gt;): &amp;lt;subject&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- more --&gt;
&lt;p&gt;完整公式：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;type&amp;gt;(&amp;lt;scope&amp;gt;): &amp;lt;subject&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;BLANK LINE&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;BLANK LINE&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;footer&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;type&#34;&gt;type&lt;/h2&gt;
&lt;p&gt;用于说明 commit 的类别，只允许使用下面 7 个标识。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;feat：新功能（feature）&lt;/li&gt;
&lt;li&gt;fix：修补 bug&lt;/li&gt;
&lt;li&gt;docs：文档（documentation）&lt;/li&gt;
&lt;li&gt;style：格式（不影响代码运行的变动）&lt;/li&gt;
&lt;li&gt;refactor：重构（即不是新增功能，也不是修改 bug 的代码变动）&lt;/li&gt;
&lt;li&gt;test：增加测试&lt;/li&gt;
&lt;li&gt;chore：构建过程或辅助工具的变动&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;scope&#34;&gt;scope&lt;/h2&gt;
&lt;p&gt;用于说明 commit 影响的范围，比如数据层、控制层、视图层等等，视项目不同而不同。&lt;/p&gt;
&lt;h2 id=&#34;subject&#34;&gt;subject&lt;/h2&gt;
&lt;p&gt;是 commit 目的的简短描述，不超过 50 个字符。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;以动词开头，使用第一人称现在时，比如 change，而不是 changed 或 changes&lt;/li&gt;
&lt;li&gt;第一个字母小写&lt;/li&gt;
&lt;li&gt;结尾不加句号 &lt;code&gt;.&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;cli-工具&#34;&gt;cli 工具&lt;/h2&gt;
&lt;h3 id=&#34;commitizen-cli&#34;&gt;commitizen cli&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/commitizen/cz-cli&#34;&gt;commitizen/cz-cli&lt;/a&gt; 使用它提供的 &lt;code&gt;git cz&lt;/code&gt; 命令替代我们的 &lt;code&gt;git commit&lt;/code&gt; 命令，帮助我们生成符合规范的 commit message。&lt;/p&gt;
&lt;p&gt;除此之外，我们还需要为 &lt;code&gt;commitizen&lt;/code&gt; 指定一个 &lt;code&gt;Adapter&lt;/code&gt; 比如：&lt;a href=&#34;https://github.com/commitizen/cz-conventional-changelog&#34;&gt;commitizen/cz-conventional-changelog&lt;/a&gt;（一个符合 Angular 团队规范的 preset）使得 &lt;code&gt;commitizen&lt;/code&gt; 按照我们指定的规范帮助我们生成 commit message。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>commit message 应该清晰明了，说明本次提交的目的。基本公式：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">&lt;type&gt;(&lt;scope&gt;): &lt;subject&gt;
</span></span></code></pre></div><!-- more -->
<p>完整公式：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">&lt;type&gt;(&lt;scope&gt;): &lt;subject&gt;
</span></span><span class="line"><span class="cl">&lt;BLANK LINE&gt;
</span></span><span class="line"><span class="cl">&lt;body&gt;
</span></span><span class="line"><span class="cl">&lt;BLANK LINE&gt;
</span></span><span class="line"><span class="cl">&lt;footer&gt;
</span></span></code></pre></div><h2 id="type">type</h2>
<p>用于说明 commit 的类别，只允许使用下面 7 个标识。</p>
<ul>
<li>feat：新功能（feature）</li>
<li>fix：修补 bug</li>
<li>docs：文档（documentation）</li>
<li>style：格式（不影响代码运行的变动）</li>
<li>refactor：重构（即不是新增功能，也不是修改 bug 的代码变动）</li>
<li>test：增加测试</li>
<li>chore：构建过程或辅助工具的变动</li>
</ul>
<h2 id="scope">scope</h2>
<p>用于说明 commit 影响的范围，比如数据层、控制层、视图层等等，视项目不同而不同。</p>
<h2 id="subject">subject</h2>
<p>是 commit 目的的简短描述，不超过 50 个字符。</p>
<ol>
<li>以动词开头，使用第一人称现在时，比如 change，而不是 changed 或 changes</li>
<li>第一个字母小写</li>
<li>结尾不加句号 <code>.</code></li>
</ol>
<h2 id="cli-工具">cli 工具</h2>
<h3 id="commitizen-cli">commitizen cli</h3>
<p><a href="https://github.com/commitizen/cz-cli">commitizen/cz-cli</a> 使用它提供的 <code>git cz</code> 命令替代我们的 <code>git commit</code> 命令，帮助我们生成符合规范的 commit message。</p>
<p>除此之外，我们还需要为 <code>commitizen</code> 指定一个 <code>Adapter</code> 比如：<a href="https://github.com/commitizen/cz-conventional-changelog">commitizen/cz-conventional-changelog</a>（一个符合 Angular 团队规范的 preset）使得 <code>commitizen</code> 按照我们指定的规范帮助我们生成 commit message。</p>
<p>全局安装（首选）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install -g commitizen cz-conventional-changelog
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;{ &#34;path&#34;: &#34;cz-conventional-changelog&#34; }&#39;</span> &gt; ~/.czrc
</span></span></code></pre></div><p>项目级安装（首选）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install -D commitizen cz-conventional-changelog
</span></span></code></pre></div><p><code>package.json</code> 中配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="s2">&#34;script&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">   ...,
</span></span><span class="line"><span class="cl">   <span class="s2">&#34;commit&#34;</span>: <span class="s2">&#34;git-cz&#34;</span>,
</span></span><span class="line"><span class="cl"><span class="o">}</span>,
</span></span><span class="line"><span class="cl"><span class="s2">&#34;config&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">   <span class="s2">&#34;commitizen&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;path&#34;</span>: <span class="s2">&#34;node_modules/cz-conventional-changelog&#34;</span>
</span></span><span class="line"><span class="cl">   <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>如果全局安装过 <code>commitizen</code>，那么在对应的项目中执行 <code>git cz</code> or <code>npm run commit</code> 都可以。</p>
<h3 id="自定义规范">自定义规范</h3>
<p>也许 Angular 的那套规范我们不习惯，那么可以通过指定 Adapter <a href="https://github.com/leonardoanalista/cz-customizable">leonardoanalista/cz-customizable</a> 指定一套符合自己团队的规范。</p>
<h3 id="校验-message">校验 message</h3>
<p>可以通过 <a href="https://github.com/conventional-changelog/commitlint">conventional-changelog/commitlint</a> 校验 commit message。同时可以配合使用 <a href="https://github.com/typicode/husky">typicode/husky</a> 🐶 Git hooks made easy。</p>
<h3 id="生成-changelog">生成 CHANGELOG</h3>
<p><a href="https://github.com/conventional-changelog/standard-version">conventional-changelog/standard-version</a> 🏆 自动化版本和更新日志生成。</p>
<p>还看到一个有意思的库 <a href="https://github.com/semantic-release/semantic-release">📦🚀 semantic-release</a> 待研究。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://juejin.im/entry/5b429be75188251ac85830ff">你可能会忽略的 Git 提交规范 - juejin</a></li>
<li><a href="https://juejin.im/post/5afc5242f265da0b7f44bee4">优雅的提交你的 Git Commit Message - juejin</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html">Commit message 和 Change log 编写指南 - ruanyifeng</a></li>
<li><a href="https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit">AngularJS Git Commit Message Conventions - google docs</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>最左前缀原理与相关优化</title>
      <link>https://zyf.im/2019/05/25/the-left-prefix-index-rule/</link>
      <pubDate>Sat, 25 May 2019 11:05:46 +0000</pubDate>
      <guid>https://zyf.im/2019/05/25/the-left-prefix-index-rule/</guid>
      <description>&lt;p&gt;MySQL 中的索引可以以一定顺序引用多个列，这种索引叫做联合索引，一般的，一个联合索引是一个有序元组 &lt;code&gt;&amp;lt;a1, a2, …, an&amp;gt;&lt;/code&gt;，其中各个元素均为数据表的一列。另外，单列索引可以看成联合索引元素数为 1 的特例。&lt;/p&gt;
&lt;p&gt;我们在 &lt;a href=&#34;https://dev.mysql.com/doc/employee/en/employees-installation.html&#34;&gt;Employees Sample Database&lt;/a&gt; 中实验，MySQL 版本 5.7。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;以 employees.titles 为例，查看其索引：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SHOW&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;employees&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;titles&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;Table&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Non_unique&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Key_name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Seq_in_index&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;Column_name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;Collation&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;Cardinality&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Sub_part&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Packed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;Null&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Index_type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;Comment&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Index_comment&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;titles&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;emp_no&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;301292&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BTREE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;titles&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;442605&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BTREE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;titles&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_date&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;442605&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BTREE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;全列匹配&#34;&gt;全列匹配&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;EXPLAIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;employees&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;titles&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;emp_no&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;10009&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Senior Engineer&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_date&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;1995-02-18&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;select_type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;table&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;partitions&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;possible_keys&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;key_len&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ref&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;rows&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filtered&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Extra&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SIMPLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;titles&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;159&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;00&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当按照索引中所有列进行精确匹配（这里精确匹配指 &lt;code&gt;=&lt;/code&gt; 或 &lt;code&gt;IN&lt;/code&gt; 匹配）时，索引可以被用到。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>MySQL 中的索引可以以一定顺序引用多个列，这种索引叫做联合索引，一般的，一个联合索引是一个有序元组 <code>&lt;a1, a2, …, an&gt;</code>，其中各个元素均为数据表的一列。另外，单列索引可以看成联合索引元素数为 1 的特例。</p>
<p>我们在 <a href="https://dev.mysql.com/doc/employee/en/employees-installation.html">Employees Sample Database</a> 中实验，MySQL 版本 5.7。</p>
<!-- more -->
<p>以 employees.titles 为例，查看其索引：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="k">INDEX</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="k">Table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">Non_unique</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Key_name</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Seq_in_index</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Column_name</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Collation</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Cardinality</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Sub_part</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Packed</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Null</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Index_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Comment</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Index_comment</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w">          </span><span class="mi">0</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">  </span><span class="o">|</span><span class="w">            </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">emp_no</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">A</span><span class="w">         </span><span class="o">|</span><span class="w">      </span><span class="mi">301292</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">   </span><span class="o">|</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">BTREE</span><span class="w">      </span><span class="o">|</span><span class="w">         </span><span class="o">|</span><span class="w">               </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w">          </span><span class="mi">0</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">  </span><span class="o">|</span><span class="w">            </span><span class="mi">2</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">title</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">A</span><span class="w">         </span><span class="o">|</span><span class="w">      </span><span class="mi">442605</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">   </span><span class="o">|</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">BTREE</span><span class="w">      </span><span class="o">|</span><span class="w">         </span><span class="o">|</span><span class="w">               </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w">          </span><span class="mi">0</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">  </span><span class="o">|</span><span class="w">            </span><span class="mi">3</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">from_date</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">A</span><span class="w">         </span><span class="o">|</span><span class="w">      </span><span class="mi">442605</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">   </span><span class="o">|</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">BTREE</span><span class="w">      </span><span class="o">|</span><span class="w">         </span><span class="o">|</span><span class="w">               </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
</span></span></span></code></pre></div><h2 id="全列匹配">全列匹配</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;10009&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;Senior Engineer&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">from_date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;1995-02-18&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">               </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">const</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">159</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">const</span><span class="p">,</span><span class="n">const</span><span class="p">,</span><span class="n">const</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">100</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+
</span></span></span></code></pre></div><p>当按照索引中所有列进行精确匹配（这里精确匹配指 <code>=</code> 或 <code>IN</code> 匹配）时，索引可以被用到。</p>
<p>这里有一点需要注意，理论上索引对顺序是敏感的，但是由于 MySQL 的 <strong>查询优化器会自动调整 where 子句的条件顺序</strong> 以使用适合的索引，例如我们将 <code>where</code> 中的条件顺序颠倒：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">from_date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;1995-02-18&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="s1">&#39;10009&#39;</span><span class="w"> </span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;Senior Engineer&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">               </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">const</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">159</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">const</span><span class="p">,</span><span class="n">const</span><span class="p">,</span><span class="n">const</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">100</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+-------------------+------+----------+-------+
</span></span></span></code></pre></div><p>和上面是一样的。</p>
<h2 id="最左前缀匹配">最左前缀匹配</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;10009&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">4</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">const</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">3</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">100</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------+
</span></span></span></code></pre></div><p>当查询条件精确匹配索引的 <strong>左边连续一个或几个列</strong> 时，如 <code>&lt;emp_no&gt;</code> 或 <code>&lt;emp_no, title&gt;</code>，所以可以被用到，但是只能用到一部分，即条件所组成的最左前缀。</p>
<p>上面的查询从分析结果看用到了 <code>PRIMARY</code> 索引，但是 <code>key_len</code> 为 4，说明只用到了索引的第一列前缀。</p>
<h2 id="中间某个条件未提供">中间某个条件未提供</h2>
<p>查询条件用到了索引中列的精确匹配，但是中间某个条件未提供。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">emp_no</span><span class="o">=</span><span class="s1">&#39;10009&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">from_date</span><span class="o">=</span><span class="s1">&#39;1995-02-18&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">4</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">const</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">10</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
</span></span></span></code></pre></div><p>此时索引使用情况和情况二相同，因为 <code>title</code> 未提供，所以查询只用到了索引的第一列，而后面的 <code>from_date</code> 虽然也在索引中，但是由于 <code>title</code> 不存在而无法和左前缀连接，因此需要对结果进行扫描过滤 <code>from_date</code>（这里由于 <code>emp_no</code> 唯一，所以不存在扫描）。</p>
<p>如果想让 <code>from_date</code> 也使用索引而不是 <code>where</code> 过滤，可以增加一个 <em>辅助索引</em> <code>&lt;emp_no, from_date&gt;</code>，此时上面的查询会使用这个索引。</p>
<p>除此之外，还可以使用一种称之为 <strong>隔离列</strong> 的优化方法，将 <code>emp_no</code> 与 <code>from_date</code> 之间的 <em>坑</em> 填上。</p>
<p>首先我们看下 <code>title</code> 有几种不同的值：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">DISTINCT</span><span class="p">(</span><span class="n">title</span><span class="p">)</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>只有 7 种。在这种成为 <em>坑</em> 的列值比较少的情况下，可以考虑用 <code>IN</code> 来填补这个 <em>坑</em> 从而形成最左前缀：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w"> </span><span class="n">emp_no</span><span class="o">=</span><span class="s1">&#39;10009&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">AND</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;Senior Engineer&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;Staff&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;Engineer&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;Senior Staff&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;Assistant Engineer&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;Technique Leader&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;Manager&#39;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">AND</span><span class="w"> </span><span class="n">from_date</span><span class="o">=</span><span class="s1">&#39;1995-02-18&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">159</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">7</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">100</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span></code></pre></div><p>这次 <code>key_len</code> 为 <code>159</code>，说明索引被用全了，但是从 <code>type</code> 和 <code>rows</code> 看出 <code>IN</code> 实际上执行了一个 <code>range</code> 查询，这里检查了 7 个 key。看下两种查询的性能比较：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">profiling</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="p">...</span><span class="w">  </span><span class="c1">-- 1
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="p">...</span><span class="w"> </span><span class="c1">-- 2
</span></span></span><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">PROFILES</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Query_ID</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Duration</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">Query</span><span class="w">   </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----------+------------+---------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">        </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">0</span><span class="p">.</span><span class="mi">00083950</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">..</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">        </span><span class="mi">2</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">0</span><span class="p">.</span><span class="mi">00063700</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">..</span><span class="o">|</span><span class="w">
</span></span></span></code></pre></div><p>“填坑” 后性能提升了一点。如果经过 <code>emp_no</code> 筛选后余下很多数据，则后者性能优势会更加明显。当然，如果 <code>title</code> 的值很多，用填坑就不合适了，必须建立辅助索引。（笔者：多次测试后发现是有快有慢，可能是数据的原因，效果并不明显）</p>
<h2 id="查询条件没有指定索引第一列">查询条件没有指定索引第一列</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">from_date</span><span class="o">=</span><span class="s1">&#39;1995-02-18&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+------+---------+------+--------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+------+---------+------+--------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">ALL</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">          </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">    </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">442605</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">10</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+------+---------+------+--------+----------+-------------+
</span></span></span></code></pre></div><p>由于不是最左前缀，索引这样的查询显然用不到索引。</p>
<h2 id="匹配某列的前缀字符串">匹配某列的前缀字符串</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">emp_no</span><span class="o">=</span><span class="mi">10009</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="k">LIKE</span><span class="w"> </span><span class="s1">&#39;Senior%&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">156</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">100</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span></code></pre></div><p>此时可以用到索引，如果通配符 <code>%</code> 不出现在开头，则可以用到索引，但根据具体情况不同可能只会用其中一个前缀。</p>
<h2 id="范围查询">范围查询</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="s1">&#39;10010&#39;</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;Senior Engineer&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">4</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">14</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">10</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span></code></pre></div><p>范围列可以用到索引（必须是最左前缀），但是范围列后面的列无法用到索引。同时，索引最多用于一个范围列，因此如果查询条件中有两个范围列则无法全用到索引。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="s1">&#39;10010&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">AND</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;Senior Engineer&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">AND</span><span class="w"> </span><span class="n">from_date</span><span class="w"> </span><span class="k">BETWEEN</span><span class="w"> </span><span class="s1">&#39;1986-01-01&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="s1">&#39;1986-12-31&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">4</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">14</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="mi">1</span><span class="p">.</span><span class="mi">11</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span></code></pre></div><p>可以看到索引对第二个范围索引无能为力。这里特别要说明 MySQL 一个有意思的地方，那就是仅用 explain 可能无法区分 范围索引 和 多值匹配，因为在 <code>type</code> 中这两者都显示为 <code>range</code>。</p>
<p>同时，用了 <code>between</code> 并不意味着就是范围查询，例如下面的查询：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="k">BETWEEN</span><span class="w"> </span><span class="s1">&#39;10001&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="s1">&#39;10010&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">AND</span><span class="w"> </span><span class="n">title</span><span class="o">=</span><span class="s1">&#39;Senior Engineer&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">AND</span><span class="w"> </span><span class="n">from_date</span><span class="w"> </span><span class="k">BETWEEN</span><span class="w"> </span><span class="s1">&#39;1986-01-01&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="s1">&#39;1986-12-31&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">159</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">15</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="mi">1</span><span class="p">.</span><span class="mi">11</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span></code></pre></div><p>看起来是用了两个范围查询，但作用于 <code>emp_no</code> 上的 <code>BETWEEN</code> 实际上相当于 <code>IN</code>，也就是说 <code>emp_no</code> 实际是多值精确匹配。可以看到这个查询用到了索引全部三个列。因此在 MySQL 中要谨慎地区分多值匹配和范围匹配，否则会对 MySQL 的行为产生困惑。</p>
<p>还有个值得注意的事情：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">10000</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">10011</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">title</span><span class="o">=</span><span class="s1">&#39;Senior Engineer&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">from_date</span><span class="w"> </span><span class="k">BETWEEN</span><span class="w"> </span><span class="s1">&#39;1986-01-01&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="s1">&#39;1986-12-31&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">4</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">15</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="mi">1</span><span class="p">.</span><span class="mi">11</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">10001</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">10010</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">title</span><span class="o">=</span><span class="s1">&#39;Senior Engineer&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">from_date</span><span class="w"> </span><span class="k">BETWEEN</span><span class="w"> </span><span class="s1">&#39;1986-01-01&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="s1">&#39;1986-12-31&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">159</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">15</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="mi">1</span><span class="p">.</span><span class="mi">11</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
</span></span></span></code></pre></div><blockquote>
<p>疑问：<code>=</code> 影响 范围索引 还是 多值匹配？</p>
</blockquote>
<h2 id="查询条件中含有函数或表达式">查询条件中含有函数或表达式</h2>
<p>很不幸，如果查询条件中含有函数或表达式，则 MySQL 不会为这列使用索引（虽然某些在数学意义上可以使用）。例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">emp_no</span><span class="o">=</span><span class="s1">&#39;10009&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="k">left</span><span class="p">(</span><span class="n">title</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">)</span><span class="o">=</span><span class="s1">&#39;Senior&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">     </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">4</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="n">const</span><span class="w"> </span><span class="o">|</span><span class="w">    </span><span class="mi">3</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">100</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
</span></span></span></code></pre></div><p>虽然这个查询和情况五中功能相同，但是由于使用了函数 left，则无法为 title 列应用索引，而情况五中用 LIKE 则可以。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">EXPLAIN</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">employees</span><span class="p">.</span><span class="n">titles</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">emp_no</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">10000</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+------+---------+------+--------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">select_type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">table</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">partitions</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">possible_keys</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">key</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">key_len</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">ref</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">rows</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">filtered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Extra</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+------+---------+------+--------+----------+-------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">SIMPLE</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">titles</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">       </span><span class="o">|</span><span class="w"> </span><span class="k">ALL</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">          </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w">    </span><span class="o">|</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">442605</span><span class="w"> </span><span class="o">|</span><span class="w">   </span><span class="mi">100</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Using</span><span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------------+--------+------------+------+---------------+------+---------+------+--------+----------+-------------+
</span></span></span></code></pre></div><p>显然这个查询等价于查询 <code>emp_no</code> 为 <code>10001</code> 的函数，但是由于查询条件是一个表达式，MySQL 无法为其使用索引。看来 MySQL 还没有智能到自动优化常量表达式的程度，因此在写查询语句时尽量避免表达式出现在查询中，而是先手工私下代数运算，转换为无表达式的查询语句。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://blog.codinglabs.org/articles/theory-of-mysql-index.html">MySQL 索引背后的数据结构及算法原理</a></li>
<li><a href="https://dev.mysql.com/doc/refman/5.7/en/show-profile.html">13.7.5.30 SHOW PROFILE Syntax</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>归并排序</title>
      <link>https://zyf.im/2019/05/23/merge-sort/</link>
      <pubDate>Thu, 23 May 2019 14:44:40 +0000</pubDate>
      <guid>https://zyf.im/2019/05/23/merge-sort/</guid>
      <description>&lt;p&gt;归并排序（英语：Merge sort，或 mergesort），是创建在归并操作上的一种有效的排序算法，效率为 &lt;code&gt;O(nlogn)&lt;/code&gt;。1945 年由约翰·冯·诺伊曼首次提出。该算法是采用分治法（Divide and Conquer）的一个非常典型的应用，且各层分治递归可以同时进行。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;&lt;img alt=&#34;merge-sort&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/58546744-9751a300-8238-11e9-84d1-0d33d5eaacee.gif&#34;&gt;&lt;/p&gt;
&lt;p&gt;采用分治法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分割：递归地把当前序列平均分割成两半。&lt;/li&gt;
&lt;li&gt;集成：在保持元素顺序的同时将上一步得到的子序列集成到一起（归并）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;归并操作（merge），也叫归并算法，指的是将两个已经排序的序列合并成一个序列的操作。归并排序算法依赖归并操作。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mergeSort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$len&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$len&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 递归结束条件, 到达这步的时候, 数组就只剩下一个元素了, 也就是分离了数组
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$len&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$left&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_slice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$mid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 拆分数组0-mid这部分给左边left
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$right&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_slice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$mid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 拆分数组mid-末尾这部分给右边right
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$left&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mergeSort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$left&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 左边拆分完后开始递归合并往上走
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$right&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mergeSort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$right&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 右边拆分完毕开始递归往上走
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$left&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$right&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 合并两个数组,继续递归
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// merge函数将指定的两个有序数组(arrA, arr)合并并且排序
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arrA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arrB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$arrC&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arrA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arrB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 这里不断的判断哪个值小, 就将小的值给到arrC, 但是到最后肯定要剩下几个值,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 不是剩下arrA里面的就是剩下arrB里面的而且这几个有序的值, 肯定比arrC里面所有的值都大所以使用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$arrC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arrA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arrB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_shift&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arrA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_shift&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arrB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arrC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arrA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arrB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$startTime&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;microtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;shuffle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;before sort: &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;implode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$sortArr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mergeSort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;after sort: &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;implode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sortArr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;use time: &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;microtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$startTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;s&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;假设被排序的数列中有 N 个数。遍历一趟的时间复杂度是 &lt;code&gt;O(N)&lt;/code&gt;，需要遍历多少次呢？&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>归并排序（英语：Merge sort，或 mergesort），是创建在归并操作上的一种有效的排序算法，效率为 <code>O(nlogn)</code>。1945 年由约翰·冯·诺伊曼首次提出。该算法是采用分治法（Divide and Conquer）的一个非常典型的应用，且各层分治递归可以同时进行。</p>
<!-- more -->
<p><img alt="merge-sort" loading="lazy" src="https://user-images.githubusercontent.com/9289792/58546744-9751a300-8238-11e9-84d1-0d33d5eaacee.gif"></p>
<p>采用分治法：</p>
<ul>
<li>分割：递归地把当前序列平均分割成两半。</li>
<li>集成：在保持元素顺序的同时将上一步得到的子序列集成到一起（归并）。</li>
</ul>
<p>归并操作（merge），也叫归并算法，指的是将两个已经排序的序列合并成一个序列的操作。归并排序算法依赖归并操作。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">mergeSort</span><span class="p">(</span><span class="nv">$arr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$len</span> <span class="o">=</span> <span class="nx">count</span><span class="p">(</span><span class="nv">$arr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nv">$len</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$arr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="c1">// 递归结束条件, 到达这步的时候, 数组就只剩下一个元素了, 也就是分离了数组
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nv">$mid</span> <span class="o">=</span> <span class="nv">$len</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$left</span> <span class="o">=</span> <span class="nx">array_slice</span><span class="p">(</span><span class="nv">$arr</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$mid</span><span class="p">);</span> <span class="c1">// 拆分数组0-mid这部分给左边left
</span></span></span><span class="line"><span class="cl">    <span class="nv">$right</span> <span class="o">=</span> <span class="nx">array_slice</span><span class="p">(</span><span class="nv">$arr</span><span class="p">,</span> <span class="nv">$mid</span><span class="p">);</span> <span class="c1">// 拆分数组mid-末尾这部分给右边right
</span></span></span><span class="line"><span class="cl">    <span class="nv">$left</span> <span class="o">=</span> <span class="nx">mergeSort</span><span class="p">(</span><span class="nv">$left</span><span class="p">);</span> <span class="c1">// 左边拆分完后开始递归合并往上走
</span></span></span><span class="line"><span class="cl">    <span class="nv">$right</span> <span class="o">=</span> <span class="nx">mergeSort</span><span class="p">(</span><span class="nv">$right</span><span class="p">);</span> <span class="c1">// 右边拆分完毕开始递归往上走
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nv">$arr</span> <span class="o">=</span> <span class="nx">merge</span><span class="p">(</span><span class="nv">$left</span><span class="p">,</span> <span class="nv">$right</span><span class="p">);</span> <span class="c1">// 合并两个数组,继续递归
</span></span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$arr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// merge函数将指定的两个有序数组(arrA, arr)合并并且排序
</span></span></span><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">merge</span><span class="p">(</span><span class="nv">$arrA</span><span class="p">,</span> <span class="nv">$arrB</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$arrC</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="nx">count</span><span class="p">(</span><span class="nv">$arrA</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nx">count</span><span class="p">(</span><span class="nv">$arrB</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 这里不断的判断哪个值小, 就将小的值给到arrC, 但是到最后肯定要剩下几个值,
</span></span></span><span class="line"><span class="cl">        <span class="c1">// 不是剩下arrA里面的就是剩下arrB里面的而且这几个有序的值, 肯定比arrC里面所有的值都大所以使用
</span></span></span><span class="line"><span class="cl">        <span class="nv">$arrC</span><span class="p">[]</span> <span class="o">=</span> <span class="nv">$arrA</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;</span> <span class="nv">$arrB</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">?</span> <span class="nx">array_shift</span><span class="p">(</span><span class="nv">$arrA</span><span class="p">)</span> <span class="o">:</span> <span class="nx">array_shift</span><span class="p">(</span><span class="nv">$arrB</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">array_merge</span><span class="p">(</span><span class="nv">$arrC</span><span class="p">,</span> <span class="nv">$arrA</span><span class="p">,</span> <span class="nv">$arrB</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$startTime</span> <span class="o">=</span> <span class="nx">microtime</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$arr</span> <span class="o">=</span> <span class="nx">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">shuffle</span><span class="p">(</span><span class="nv">$arr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s1">&#39;before sort: &#39;</span><span class="p">,</span> <span class="nx">implode</span><span class="p">(</span><span class="s1">&#39;, &#39;</span><span class="p">,</span> <span class="nv">$arr</span><span class="p">),</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$sortArr</span> <span class="o">=</span> <span class="nx">mergeSort</span><span class="p">(</span><span class="nv">$arr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s1">&#39;after sort: &#39;</span><span class="p">,</span> <span class="nx">implode</span><span class="p">(</span><span class="s1">&#39;, &#39;</span><span class="p">,</span> <span class="nv">$sortArr</span><span class="p">),</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="s1">&#39;use time: &#39;</span><span class="p">,</span> <span class="nx">microtime</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="nv">$startTime</span><span class="p">,</span> <span class="s2">&#34;s</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span></code></pre></div><p>假设被排序的数列中有 N 个数。遍历一趟的时间复杂度是 <code>O(N)</code>，需要遍历多少次呢？</p>
<p>归并排序的形式就是一棵二叉树，它需要遍历的次数就是二叉树的深度，而根据 <strong>完全二叉树</strong> 的可以得出它的时间复杂度是 <code>O(N*lgN)</code>。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://shockerli.net/post/merge-sort-implement-by-php/">PHP 算法 —— 归并排序</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Laravel 中 composer 加载流程</title>
      <link>https://zyf.im/2019/04/28/composer-autoload-in-laravel/</link>
      <pubDate>Sun, 28 Apr 2019 19:21:12 +0000</pubDate>
      <guid>https://zyf.im/2019/04/28/composer-autoload-in-laravel/</guid>
      <description>&lt;h2 id=&#34;启动&#34;&gt;启动&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Laravel 5.8&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;文章以 Laravel 学习。入口文件 &lt;code&gt;public/index.php&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Register The Auto Loader
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;__DIR__&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/../vendor/autoload.php&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;autoload.php&lt;/code&gt; 不负责具体功能逻辑，只做了两件事：初始化自动加载类、注册自动加载类。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;autoload_real.php&lt;/code&gt; 中的类名为 &lt;code&gt;ComposerAutoloaderInit...&lt;/code&gt; 这可能是为防止与用户自定义类名跟这个类重复冲突，加上了哈希值。&lt;/p&gt;
&lt;p&gt;其实还有一个做法我们更加熟悉，是定义一个命名空间。这里为什么不定义一个命名空间呢？一种理解：命名空间一般都是为了复用，而这个类只需要运行一次即可，以后也不会用得到，用哈希值更加合适。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;autoload_realphp&#34;&gt;autoload_real.php&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;autoload.php&lt;/code&gt; 主要调用了 &lt;code&gt;getLoader()&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getLoader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 单例模式，自动加载类只能有一个 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 获得自动加载核心类对象 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;spl_autoload_register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;loadClassLoader&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;\Composer\Autoload\ClassLoader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;spl_autoload_unregister&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;loadClassLoader&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 初始化自动加载核心类对象 3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$useStaticLoader&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PHP_VERSION_ID&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50600&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;defined&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;HHVM_VERSION&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;function_exists&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;zend_loader_file_encoded&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;zend_loader_file_encoded&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$useStaticLoader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;require_once&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;__DIR__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/autoload_static.php&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;call_user_func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;\Composer\Autoload\ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getInitializer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$map&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;__DIR__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/autoload_namespaces.php&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$map&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$namespace&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$namespace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$map&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;__DIR__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/autoload_psr4.php&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$map&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$namespace&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setPsr4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$namespace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$classMap&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;__DIR__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/autoload_classmap.php&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$classMap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addClassMap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$classMap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 注册自动加载核心类对象 4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 自动加载全局函数 5
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$useStaticLoader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$includeFiles&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Composer\Autoload\ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$includeFiles&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;__DIR__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/autoload_files.php&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$includeFiles&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fileIdentifier&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;composerRequire76e88f0b305cd64c7c84b90b278c31db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fileIdentifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;单例模式-1&#34;&gt;单例模式 1&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;构造-classloader-核心类-2&#34;&gt;构造 ClassLoader 核心类 2&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;spl_autoload_register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;loadClassLoader&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$loader&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;\Composer\Autoload\ClassLoader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;spl_autoload_unregister&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;loadClassLoader&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;loadClassLoader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Composer\Autoload\ClassLoader&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;__DIR__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/ClassLoader.php&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;composer&lt;/code&gt; 先向 &lt;code&gt;PHP&lt;/code&gt; 自动加载机制注册了一个函数，这个函数 &lt;code&gt;require&lt;/code&gt; 了 &lt;code&gt;ClassLoader&lt;/code&gt; 文件。成功 &lt;code&gt;new&lt;/code&gt; 出该文件中核心类 &lt;code&gt;ClassLoader()&lt;/code&gt; 后，又销毁了该函数。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="启动">启动</h2>
<ul>
<li>Laravel 5.8</li>
</ul>
<p>文章以 Laravel 学习。入口文件 <code>public/index.php</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// Register The Auto Loader
</span></span></span><span class="line"><span class="cl"><span class="k">require</span> <span class="no">__DIR__</span><span class="o">.</span><span class="s1">&#39;/../vendor/autoload.php&#39;</span><span class="p">;</span>
</span></span></code></pre></div><p><code>autoload.php</code> 不负责具体功能逻辑，只做了两件事：初始化自动加载类、注册自动加载类。</p>
<p><code>autoload_real.php</code> 中的类名为 <code>ComposerAutoloaderInit...</code> 这可能是为防止与用户自定义类名跟这个类重复冲突，加上了哈希值。</p>
<p>其实还有一个做法我们更加熟悉，是定义一个命名空间。这里为什么不定义一个命名空间呢？一种理解：命名空间一般都是为了复用，而这个类只需要运行一次即可，以后也不会用得到，用哈希值更加合适。</p>
<!-- more -->
<h2 id="autoload_realphp">autoload_real.php</h2>
<p><code>autoload.php</code> 主要调用了 <code>getLoader()</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">getLoader</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 单例模式，自动加载类只能有一个 1
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="k">null</span> <span class="o">!==</span> <span class="nx">self</span><span class="o">::</span><span class="nv">$loader</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">self</span><span class="o">::</span><span class="nv">$loader</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 获得自动加载核心类对象 2
</span></span></span><span class="line"><span class="cl">    <span class="nx">spl_autoload_register</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&#39;</span><span class="p">,</span> <span class="s1">&#39;loadClassLoader&#39;</span><span class="p">),</span> <span class="k">true</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">self</span><span class="o">::</span><span class="nv">$loader</span> <span class="o">=</span> <span class="nv">$loader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">\Composer\Autoload\ClassLoader</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">spl_autoload_unregister</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&#39;</span><span class="p">,</span> <span class="s1">&#39;loadClassLoader&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化自动加载核心类对象 3
</span></span></span><span class="line"><span class="cl">    <span class="nv">$useStaticLoader</span> <span class="o">=</span> <span class="nx">PHP_VERSION_ID</span> <span class="o">&gt;=</span> <span class="mi">50600</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">defined</span><span class="p">(</span><span class="s1">&#39;HHVM_VERSION&#39;</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="o">!</span><span class="nx">function_exists</span><span class="p">(</span><span class="s1">&#39;zend_loader_file_encoded&#39;</span><span class="p">)</span> <span class="o">||</span> <span class="o">!</span><span class="nx">zend_loader_file_encoded</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nv">$useStaticLoader</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">require_once</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/autoload_static.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">call_user_func</span><span class="p">(</span><span class="nx">\Composer\Autoload\ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db</span><span class="o">::</span><span class="na">getInitializer</span><span class="p">(</span><span class="nv">$loader</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$map</span> <span class="o">=</span> <span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/autoload_namespaces.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">foreach</span> <span class="p">(</span><span class="nv">$map</span> <span class="k">as</span> <span class="nv">$namespace</span> <span class="o">=&gt;</span> <span class="nv">$path</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">set</span><span class="p">(</span><span class="nv">$namespace</span><span class="p">,</span> <span class="nv">$path</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nv">$map</span> <span class="o">=</span> <span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/autoload_psr4.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">foreach</span> <span class="p">(</span><span class="nv">$map</span> <span class="k">as</span> <span class="nv">$namespace</span> <span class="o">=&gt;</span> <span class="nv">$path</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">setPsr4</span><span class="p">(</span><span class="nv">$namespace</span><span class="p">,</span> <span class="nv">$path</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nv">$classMap</span> <span class="o">=</span> <span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/autoload_classmap.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nv">$classMap</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">addClassMap</span><span class="p">(</span><span class="nv">$classMap</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 注册自动加载核心类对象 4
</span></span></span><span class="line"><span class="cl">    <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">register</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 自动加载全局函数 5
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nv">$useStaticLoader</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$includeFiles</span> <span class="o">=</span> <span class="nx">Composer\Autoload\ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db</span><span class="o">::</span><span class="nv">$files</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$includeFiles</span> <span class="o">=</span> <span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/autoload_files.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="nv">$includeFiles</span> <span class="k">as</span> <span class="nv">$fileIdentifier</span> <span class="o">=&gt;</span> <span class="nv">$file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">composerRequire76e88f0b305cd64c7c84b90b278c31db</span><span class="p">(</span><span class="nv">$fileIdentifier</span><span class="p">,</span> <span class="nv">$file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$loader</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="单例模式-1">单例模式 1</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="k">null</span> <span class="o">!==</span> <span class="nx">self</span><span class="o">::</span><span class="nv">$loader</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">self</span><span class="o">::</span><span class="nv">$loader</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="构造-classloader-核心类-2">构造 ClassLoader 核心类 2</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">spl_autoload_register</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&#39;</span><span class="p">,</span> <span class="s1">&#39;loadClassLoader&#39;</span><span class="p">),</span> <span class="k">true</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">self</span><span class="o">::</span><span class="nv">$loader</span> <span class="o">=</span> <span class="nv">$loader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">\Composer\Autoload\ClassLoader</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">spl_autoload_unregister</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&#39;</span><span class="p">,</span> <span class="s1">&#39;loadClassLoader&#39;</span><span class="p">));</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">loadClassLoader</span><span class="p">(</span><span class="nv">$class</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="s1">&#39;Composer\Autoload\ClassLoader&#39;</span> <span class="o">===</span> <span class="nv">$class</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/ClassLoader.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>composer</code> 先向 <code>PHP</code> 自动加载机制注册了一个函数，这个函数 <code>require</code> 了 <code>ClassLoader</code> 文件。成功 <code>new</code> 出该文件中核心类 <code>ClassLoader()</code> 后，又销毁了该函数。</p>
<p>为什么不直接 <code>require</code>？原因是：怕有的用户也定义了个 <code>\Composer\Autoload\ClassLoader</code> 命名空间，导致自动加载错误文件。</p>
<p>那为什么不跟引导类一样用个哈希值呢？原因是：这个类是可以复用的，框架允许用户使用这个类。</p>
<h2 id="初始化核心类对象-3">初始化核心类对象 3</h2>
<p>对自动加载类的初始化，主要是给自动加载核心类初始化顶级命名空间映射。初始化的方法有两种：</p>
<ol>
<li>使用 <code>autoload_static</code> 进行静态初始化</li>
<li>调用核心类接口初始化</li>
</ol>
<h3 id="autoload_static-静态初始化">autoload_static 静态初始化</h3>
<p>静态初始化只支持 <code>PHP 5.6</code> 以上版本、不支持 <code>HHVM</code> 虚拟机、不存在 <code>Zend-encoded file</code>。</p>
<p><code>autoload_static.php</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// autoload_static.php @generated by Composer
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nx">Composer\Autoload</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// hash 防止冲突
</span></span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="nv">$files</span> <span class="o">=</span> <span class="k">array</span> <span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="nv">$prefixLengthsPsr4</span> <span class="o">=</span> <span class="k">array</span> <span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="nv">$prefixDirsPsr4</span> <span class="o">=</span> <span class="k">array</span> <span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="nv">$fallbackDirsPsr4</span> <span class="o">=</span> <span class="k">array</span> <span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="nv">$prefixesPsr0</span> <span class="o">=</span> <span class="k">array</span> <span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="nv">$classMap</span> <span class="o">=</span> <span class="k">array</span> <span class="k">array</span> <span class="p">(</span><span class="o">...</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">getInitializer</span><span class="p">(</span><span class="nx">ClassLoader</span> <span class="nv">$loader</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">\Closure</span><span class="o">::</span><span class="na">bind</span><span class="p">(</span><span class="k">function</span> <span class="p">()</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$loader</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">prefixLengthsPsr4</span> <span class="o">=</span> <span class="nx">ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db</span><span class="o">::</span><span class="nv">$prefixLengthsPsr4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">prefixDirsPsr4</span> <span class="o">=</span> <span class="nx">ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db</span><span class="o">::</span><span class="nv">$prefixDirsPsr4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">fallbackDirsPsr4</span> <span class="o">=</span> <span class="nx">ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db</span><span class="o">::</span><span class="nv">$fallbackDirsPsr4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">prefixesPsr0</span> <span class="o">=</span> <span class="nx">ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db</span><span class="o">::</span><span class="nv">$prefixesPsr0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">classMap</span> <span class="o">=</span> <span class="nx">ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db</span><span class="o">::</span><span class="nv">$classMap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="p">},</span> <span class="k">null</span><span class="p">,</span> <span class="nx">ClassLoader</span><span class="o">::</span><span class="na">class</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这个静态初始化类的核心就是 <code>getInitializer()</code> 函数，它将自己类中的顶级命名空间映射给了 ClassLoader 类。</p>
<p>值得注意的是这个函数返回的是一个匿名函数，为什么呢？原因就是 <code>ClassLoader</code> 中的 <code>prefixLengthsPsr4</code> 、<code>prefixDirsPsr4</code> 等等方法都是 <code>private</code> 的。普通的函数没办法给类的 <code>private</code> 成员变量赋值。利用匿名函数的绑定功能就可以将把匿名函数转为 <code>ClassLoader</code> 类的成员函数。</p>
<p>关于匿名函数的 <a href="http://www.cnblogs.com/yjf512/p/4421289.html">绑定功能</a>。</p>
<p>接下来就是 顶级命名空间 初始化的关键了。</p>
<h4 id="classmap">classMap</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">public</span> <span class="k">static</span> <span class="nv">$classMap</span> <span class="o">=</span> <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;App\\Api\\Middleware\\DeviceRecord&#39;</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/../..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/app/Api/Middleware/DeviceRecord.php&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;App\\Api\\Middleware\\HeaderCheck&#39;</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/../..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/app/Api/Middleware/HeaderCheck.php&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p>直接命名空间全名与目录的映射，没有顶级命名空间。简单粗暴，也导致这个数组相当的大。</p>
<h4 id="psr0-顶级命名空间映射">PSR0 顶级命名空间映射</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">public</span> <span class="k">static</span> <span class="nv">$prefixesPsr0</span> <span class="o">=</span> <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;P&#39;</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Prophecy\\&#39;</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="mi">0</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/phpspec/prophecy/src&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Parsedown&#39;</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="mi">0</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/erusev/parsedown&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><p>为了快速找到顶级命名空间，这里使用命名空间第一个字母作为前缀索引。这个映射的用法比较明显，假如我们有 <code>Parsedown/example</code> 这样的命名空间，首先通过首字母 <code>P</code>，找到：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="s1">&#39;P&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span> <span class="p">(</span><span class="o">...</span><span class="p">)</span>
</span></span></code></pre></div><p>这个数组，然后就会遍历这个数组来和 <code>Parsedown/example</code> 比较，发现第一个 <code>Prophecy</code> 不符合，第二个 <code>Parsedown</code> 符合，然后得到了映射目录（映射目录可能不止一个）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="mi">0</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/erusev/parsedown&#39;</span><span class="p">,</span>
</span></span></code></pre></div><p>接着遍历这个数组，尝试 <code>__DIR__ . '/..' . '/erusev/parsedown/Parsedown/example.php'</code> 是否存在，如果不存在接着遍历数组（这个例子数组只有一个元素），如果数组遍历完都没有，就会加载失败。</p>
<h4 id="psr4-标准顶级命名空间映射">PSR4 标准顶级命名空间映射</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">public</span> <span class="k">static</span> <span class="nv">$prefixLengthsPsr4</span> <span class="o">=</span> <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;p&#39;</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;phpDocumentor\\Reflection\\&#39;</span> <span class="o">=&gt;</span> <span class="mi">25</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Z&#39;</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Zend\\Diactoros\\&#39;</span> <span class="o">=&gt;</span> <span class="mi">15</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">public</span> <span class="k">static</span> <span class="nv">$prefixDirsPsr4</span> <span class="o">=</span> <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;phpDocumentor\\Reflection\\&#39;</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="mi">0</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/phpdocumentor/reflection-common/src&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="mi">1</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/phpdocumentor/reflection-docblock/src&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="mi">2</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/phpdocumentor/type-resolver/src&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Zend\\Diactoros\\&#39;</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="mi">0</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/zendframework/zend-diactoros/src&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><p><code>PSR4</code> 标准 <code>顶级命名空间</code> 映射用了两个数组，第一个和 <code>PSR0</code> 一样用命名空间第一个字母作为前缀索引，然后是 <code>顶级命名空间</code>，但是最终并不是文件路径，而是 <code>顶级命名空间</code> 的长度。为什么呢？因为 <code>PSR4</code> 的文件目录更加灵活，更加简洁。</p>
<p><code>PSR0</code> 中 <code>顶级命名空间</code> 目录 <strong>直接加</strong> 到命名空间前面就可以得到路径：</p>
<pre tabindex="0"><code>                                        ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
Parsedown/example =&gt; __DIR__ . &#39;/..&#39; . &#39;/erusev/parsedown/Parsedown/example.php
                                                         ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
</code></pre><p>而 <code>PSR4</code> 却是用顶级命名空间目录 <strong>替换</strong> 顶级命名空间，所以获得顶级命名空间的 <strong>长度</strong> 很重要：</p>
<pre tabindex="0"><code>                                        ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
Parsedown/example =&gt; __DIR__ . &#39;/..&#39; . &#39;/erusev/parsedown/example.php
                                                ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
</code></pre><p>举例：假如我们找 <code>Symfony\\Polyfill\\Mbstring\\example</code> 这个类，和 <code>PSR0</code> 一样通过前缀索引和字符串匹配我们得到了:</p>
<pre tabindex="0"><code>&#39;Symfony\\Polyfill\\Mbstring\\&#39; =&gt; 26,
</code></pre><p>这条记录，键是顶级命名空间，值是命名空间的长度。拿到顶级命名空间后去 <code>$prefixDirsPsr4</code> 获取它的映射目录数组（注意映射目录可能不止一条）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="s1">&#39;Symfony\\Polyfill\\Mbstring\\&#39;</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">array</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="mi">0</span> <span class="o">=&gt;</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/..&#39;</span> <span class="o">.</span> <span class="s1">&#39;/symfony/polyfill-mbstring&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">),</span>
</span></span></code></pre></div><p>将 <code>Symfony\\Polyfill\\Mbstring\\example</code> 前 26 个字母替换为 <code>__DIR__ . '/..' . '/symfony/polyfill-mbstring</code> 也就是：</p>
<pre tabindex="0"><code>__DIR__ . &#39;/..&#39; . &#39;/symfony/polyfill-mbstring/example.php
</code></pre><p>先验证磁盘上这个文件是否存在，如果不存在接着遍历。如果遍历后没有找到，则加载失败。</p>
<p>自动加载核心类 <code>ClassLoader</code> 的静态初始化完成！</p>
<blockquote>
<p>其实还有 <code>$fallbackDirsPsr4</code>，暂未研究</p>
</blockquote>
<h3 id="调用接口初始化">调用接口初始化</h3>
<p>如果 <code>PHP</code> 版本低于 <code>5.6</code> 或者使用 <code>HHVM</code> 虚拟机环境或者存在 <code>zend_loader_file_encoded</code>，那么就要使用核心类的接口进行初始化。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">PSR0 取出命名空间的第一个字母作为索引，一个索引对应多个顶级命名空间，一个顶级命名空间对应多个目录路径，具体形式可以查看上面的 autoload_static 的 $prefixesPsr0。
</span></span></span><span class="line"><span class="cl"><span class="cm">
</span></span></span><span class="line"><span class="cl"><span class="cm">如果没有顶级命名空间，就只存储一个路径名，以便在后面尝试加载。
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl"><span class="nv">$map</span> <span class="o">=</span> <span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/autoload_namespaces.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$map</span> <span class="k">as</span> <span class="nv">$namespace</span> <span class="o">=&gt;</span> <span class="nv">$path</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">set</span><span class="p">(</span><span class="nv">$namespace</span><span class="p">,</span> <span class="nv">$path</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">PSR4 如果没有顶级命名空间，就直接保存目录。
</span></span></span><span class="line"><span class="cl"><span class="cm">如果有命名空间的话，要保证顶级命名空间最后是 \，然后分别保存
</span></span></span><span class="line"><span class="cl"><span class="cm">( 前缀 -&gt; 顶级命名空间，顶级命名空间 -&gt; 顶级命名空间长度 )
</span></span></span><span class="line"><span class="cl"><span class="cm">( 顶级命名空间 -&gt; 目录 )
</span></span></span><span class="line"><span class="cl"><span class="cm">
</span></span></span><span class="line"><span class="cl"><span class="cm">这两个映射数组。具体形式可以查看上面我们讲的 autoload_static 的 prefixLengthsPsr4、$prefixDirsPsr4 。
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl"><span class="nv">$map</span> <span class="o">=</span> <span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/autoload_psr4.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$map</span> <span class="k">as</span> <span class="nv">$namespace</span> <span class="o">=&gt;</span> <span class="nv">$path</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">setPsr4</span><span class="p">(</span><span class="nv">$namespace</span><span class="p">,</span> <span class="nv">$path</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">整个命名空间与目录之间的映射
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl"><span class="nv">$classMap</span> <span class="o">=</span> <span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/autoload_classmap.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nv">$classMap</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$loader</span><span class="o">-&gt;</span><span class="na">addClassMap</span><span class="p">(</span><span class="nv">$classMap</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="注册核心类对象-4">注册核心类对象 4</h2>
<p>Composer 自动加载功能的启动与初始化，经过启动与初始化，自动加载核心类对象已经获得了顶级命名空间与相应目录的映射，换句话说，如果有命名空间 <code>App\Console\Kernel</code>，我们已经知道了 <code>App\</code> 对应的目录，接下来我们就要解决下面的就是 <code>\Console\Kernel</code> 这一段。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd"> * Registers this instance as an autoloader.
</span></span></span><span class="line"><span class="cl"><span class="sd"> *
</span></span></span><span class="line"><span class="cl"><span class="sd"> * @param bool $prepend Whether to prepend the autoloader or not
</span></span></span><span class="line"><span class="cl"><span class="sd">*/</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span> <span class="k">function</span> <span class="nf">register</span><span class="p">(</span><span class="nv">$prepend</span> <span class="o">=</span> <span class="k">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">spl_autoload_register</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="nv">$this</span><span class="p">,</span> <span class="s1">&#39;loadClass&#39;</span><span class="p">),</span> <span class="k">true</span><span class="p">,</span> <span class="nv">$prepend</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>一行代码实现自动加载。核心在 <code>ClassLoader</code> 的 <code>loadClass()</code> 函数上，这个函数负责按照 <code>PSR</code> 标准将顶层命名空间以下的内容转为对应的目录，也就是上面所说的将 <code>App\Console\Kernel</code> 中 <code>Console\Kernel</code> 这一段转为目录。</p>
<h2 id="自动加载全局函数-5">自动加载全局函数 5</h2>
<p><code>Composer</code> 不止可以自动加载命名空间，还可以加载全局函数。就是把全局函数写到特定的文件里面去，在程序运行前挨个 <code>require</code> 就行了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nv">$useStaticLoader</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 静态初始化
</span></span></span><span class="line"><span class="cl">    <span class="nv">$includeFiles</span> <span class="o">=</span> <span class="nx">Composer\Autoload\ComposerStaticInit76e88f0b305cd64c7c84b90b278c31db</span><span class="o">::</span><span class="nv">$files</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 普通初始化
</span></span></span><span class="line"><span class="cl">    <span class="nv">$includeFiles</span> <span class="o">=</span> <span class="k">require</span> <span class="no">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/autoload_files.php&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$includeFiles</span> <span class="k">as</span> <span class="nv">$fileIdentifier</span> <span class="o">=&gt;</span> <span class="nv">$file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">composerRequire76e88f0b305cd64c7c84b90b278c31db</span><span class="p">(</span><span class="nv">$fileIdentifier</span><span class="p">,</span> <span class="nv">$file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">composerRequire76e88f0b305cd64c7c84b90b278c31db</span><span class="p">(</span><span class="nv">$fileIdentifier</span><span class="p">,</span> <span class="nv">$file</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="k">empty</span><span class="p">(</span><span class="nv">$GLOBALS</span><span class="p">[</span><span class="s1">&#39;__composer_autoload_files&#39;</span><span class="p">][</span><span class="nv">$fileIdentifier</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">require</span> <span class="nv">$file</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nv">$GLOBALS</span><span class="p">[</span><span class="s1">&#39;__composer_autoload_files&#39;</span><span class="p">][</span><span class="nv">$fileIdentifier</span><span class="p">]</span> <span class="o">=</span> <span class="k">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="问题-1">问题 1</h3>
<p>为什么不直接 <code>require</code> <code>$includeFiles</code> 里面的每个文件名，而要用类外面的函数 <code>composerRequire...</code> ？</p>
<ul>
<li>避免和用户定义函数冲突</li>
<li>防止有人在全局函数所在的文件写 <code>$this</code> 或者 <code>self</code></li>
</ul>
<p>假如 <code>$includeFiles</code> 有个 <code>app/helper.php</code> 文件，这个 <code>helper.php</code> 文件的函数外有一行代码： <code>$this-&gt;foo()</code>，如果引导类在 <code>getLoader()</code> 函数直接 <code>require($file)</code>，那么引导类就会运行这句代码，调用自己的 <code>foo()</code> 函数，这显然是错的。</p>
<p>事实上 <code>helper.php</code> 就不应该出现 <code>$this</code> 或 <code>self</code> 这样的代码，这样写一般都是用户写错了的，一旦这样的事情发生：</p>
<ul>
<li>第一种情况：引导类恰好有 <code>foo()</code> 函数，那么就会莫名其妙执行了引导类的 <code>foo()</code>。</li>
<li>第二种情况：引导类没有 <code>foo()</code> 函数，但是却甩出来引导类没有 <code>foo()</code> 方法这样的错误提示，用户不知道自己哪里错了。把 <code>require</code> 语句放到 <strong>引导类的外面</strong>，遇到 <code>$this</code> 或者 <code>self</code> ，程序就会告诉用户根本没有类， <code>$this</code> 或 <code>self</code> 无效，错误信息更加明朗。</li>
</ul>
<h3 id="问题-2">问题 2</h3>
<p>为什么要用 <code>hash</code> 作为 <code>$fileIdentifier</code>？</p>
<p>这个变量是用来控制全局函数只被 <code>require</code> 一次的，那为什么不用 <code>require_once</code> 呢？事实上 <code>require_once</code> 比 <code>require</code> 效率低很多，使用全局变量 <code>$GLOBALS</code> 这样控制加载会更快。猜测另一个原因应该是 <code>require_once</code> 对相对路径的支持并不理想，所以 <code>composer</code> 尽量少用 <code>require_once</code>。</p>
<h2 id="运行">运行</h2>
<p><code>ClassLoader</code> 将 <code>loadClass()</code> 函数注册到 <code>PHP SPL</code> 中的 <code>spl_autoload_register()</code> 里面去。这样，每当 PHP 遇到一个不认识的命名空间的时候，PHP 会自动调用注册到 <code>spl_autoload_register()</code> 里面的函数堆栈，运行其中的每个函数，直到找到命名空间对应的文件。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd"> * Loads the given class or interface.
</span></span></span><span class="line"><span class="cl"><span class="sd"> *
</span></span></span><span class="line"><span class="cl"><span class="sd"> * @param  string    $class The name of the class
</span></span></span><span class="line"><span class="cl"><span class="sd">    * @return bool|null True if loaded, null otherwise
</span></span></span><span class="line"><span class="cl"><span class="sd">    */</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span> <span class="k">function</span> <span class="nf">loadClass</span><span class="p">(</span><span class="nv">$class</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nv">$file</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">findFile</span><span class="p">(</span><span class="nv">$class</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">includeFile</span><span class="p">(</span><span class="nv">$file</span><span class="p">);</span> <span class="c1">// include $file; Prevents access to $this/self from included files.
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd"> * Finds the path to the file where the class is defined.
</span></span></span><span class="line"><span class="cl"><span class="sd"> *
</span></span></span><span class="line"><span class="cl"><span class="sd"> * @param string $class The name of the class
</span></span></span><span class="line"><span class="cl"><span class="sd">    *
</span></span></span><span class="line"><span class="cl"><span class="sd">    * @return string|false The path if found, false otherwise
</span></span></span><span class="line"><span class="cl"><span class="sd">    */</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span> <span class="k">function</span> <span class="nf">findFile</span><span class="p">(</span><span class="nv">$class</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// class map lookup
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">isset</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">classMap</span><span class="p">[</span><span class="nv">$class</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">classMap</span><span class="p">[</span><span class="nv">$class</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// classMapAuthoritative 关闭搜索未在类映射中注册的类的 prefix and fallback directories。- 不清楚干啥的 暂没研究
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">classMapAuthoritative</span> <span class="o">||</span> <span class="nx">isset</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">missingClasses</span><span class="p">[</span><span class="nv">$class</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 如果启用扩展名，则使用 APCu 前缀来缓存已找到/未找到的类。 - 不清楚干啥的 暂没研究
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="k">null</span> <span class="o">!==</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">apcuPrefix</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$file</span> <span class="o">=</span> <span class="nx">apcu_fetch</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">apcuPrefix</span><span class="o">.</span><span class="nv">$class</span><span class="p">,</span> <span class="nv">$hit</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nv">$hit</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nv">$file</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nv">$file</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">findFileWithExtension</span><span class="p">(</span><span class="nv">$class</span><span class="p">,</span> <span class="s1">&#39;.php&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Search for Hack files if we are running on HHVM
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="k">false</span> <span class="o">===</span> <span class="nv">$file</span> <span class="o">&amp;&amp;</span> <span class="nx">defined</span><span class="p">(</span><span class="s1">&#39;HHVM_VERSION&#39;</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$file</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">findFileWithExtension</span><span class="p">(</span><span class="nv">$class</span><span class="p">,</span> <span class="s1">&#39;.hh&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="k">null</span> <span class="o">!==</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">apcuPrefix</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">apcu_add</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">apcuPrefix</span><span class="o">.</span><span class="nv">$class</span><span class="p">,</span> <span class="nv">$file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="k">false</span> <span class="o">===</span> <span class="nv">$file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Remember that this class does not exist.
</span></span></span><span class="line"><span class="cl">        <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">missingClasses</span><span class="p">[</span><span class="nv">$class</span><span class="p">]</span> <span class="o">=</span> <span class="k">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$file</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>loadClass()</code> 主要调用 <code>findFile()</code> 函数。<code>findFile()</code> 在解析命名空间的时候主要分为两部分：</p>
<ul>
<li><code>classMap</code> 直接看命名空间是否在映射数组</li>
<li><code>findFileWithExtension()</code> 包含了 <code>PSR0</code>、<code>PSR4</code></li>
</ul>
<p>如果我们在代码中写 <code>'phpDocumentor\Reflection\example</code>，PHP 会通过 SPL 调用 <code>loadClass</code> -&gt; <code>findFile</code> -&gt; <code>findFileWithExtension</code>。</p>
<ul>
<li>首先默认用 <code>.php</code> 后缀名调用 <code>findFileWithExtension</code> 函数里，利用 <code>PSR4</code> 标准尝试解析目录文件，如果文件不存在则继续用 <code>PSR0</code> 标准解析</li>
<li>如果解析出来的目录文件仍然不存在，但是环境是 <code>HHVM</code> 虚拟机，继续用后缀名 <code>.hh</code> 再次调用 <code>findFileWithExtension</code> 函数，如果不存在，说明此命名空间无法加载，放到 <code>classMap</code> 中设为 <code>false</code>，以便以后更快地加载</li>
</ul>
<h3 id="psr4">PSR4</h3>
<p>对于 <code>phpDocumentor\Reflection\example</code>，当尝试利用 <code>PSR4</code> 标准映射目录时，步骤如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// $class: phpDocumentor\Reflection\example
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PSR-4 lookup
</span></span></span><span class="line"><span class="cl"><span class="nv">$logicalPathPsr4</span> <span class="o">=</span> <span class="nx">strtr</span><span class="p">(</span><span class="nv">$class</span><span class="p">,</span> <span class="s1">&#39;\\&#39;</span><span class="p">,</span> <span class="nx">DIRECTORY_SEPARATOR</span><span class="p">)</span> <span class="o">.</span> <span class="nv">$ext</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// $logicalPathPsr4: phpDocumentor/Reflection/example.php(hh)`
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$first</span> <span class="o">=</span> <span class="nv">$class</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="c1">// $first: p
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">isset</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">prefixLengthsPsr4</span><span class="p">[</span><span class="nv">$first</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* &#39;p&#39; =&gt;
</span></span></span><span class="line"><span class="cl"><span class="cm">    array (
</span></span></span><span class="line"><span class="cl"><span class="cm">        &#39;phpDocumentor\\Reflection\\&#39; =&gt; 25,
</span></span></span><span class="line"><span class="cl"><span class="cm">    ),
</span></span></span><span class="line"><span class="cl"><span class="cm">    */</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$subPath</span> <span class="o">=</span> <span class="nv">$class</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// $subPath: phpDocumentor\Reflection\example
</span></span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="k">false</span> <span class="o">!==</span> <span class="nv">$lastPos</span> <span class="o">=</span> <span class="nx">strrpos</span><span class="p">(</span><span class="nv">$subPath</span><span class="p">,</span> <span class="s1">&#39;\\&#39;</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// $lastPos 13
</span></span></span><span class="line"><span class="cl">        <span class="nv">$subPath</span> <span class="o">=</span> <span class="nx">substr</span><span class="p">(</span><span class="nv">$subPath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$lastPos</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$search</span> <span class="o">=</span> <span class="nv">$subPath</span><span class="o">.</span><span class="s1">&#39;\\&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">isset</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">prefixDirsPsr4</span><span class="p">[</span><span class="nv">$search</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// search phpDocumentor\\Reflection\\
</span></span></span><span class="line"><span class="cl">            <span class="c1">// $lastPos 25
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="cm">/* &#39;phpDocumentor\\Reflection\\&#39; =&gt;
</span></span></span><span class="line"><span class="cl"><span class="cm">                array (
</span></span></span><span class="line"><span class="cl"><span class="cm">                    0 =&gt; __DIR__ . &#39;/..&#39; . &#39;/phpdocumentor/reflection-common/src&#39;,
</span></span></span><span class="line"><span class="cl"><span class="cm">                    1 =&gt; __DIR__ . &#39;/..&#39; . &#39;/phpdocumentor/reflection-docblock/src&#39;,
</span></span></span><span class="line"><span class="cl"><span class="cm">                    2 =&gt; __DIR__ . &#39;/..&#39; . &#39;/phpdocumentor/type-resolver/src&#39;,
</span></span></span><span class="line"><span class="cl"><span class="cm">                ),
</span></span></span><span class="line"><span class="cl"><span class="cm">            */</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$pathEnd</span> <span class="o">=</span> <span class="nx">DIRECTORY_SEPARATOR</span> <span class="o">.</span> <span class="nx">substr</span><span class="p">(</span><span class="nv">$logicalPathPsr4</span><span class="p">,</span> <span class="nv">$lastPos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// $pathEnd /example.php(hh)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">foreach</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">prefixDirsPsr4</span><span class="p">[</span><span class="nv">$search</span><span class="p">]</span> <span class="k">as</span> <span class="nv">$dir</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 遍历 3 个
</span></span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="nx">file_exists</span><span class="p">(</span><span class="nv">$file</span> <span class="o">=</span> <span class="nv">$dir</span> <span class="o">.</span> <span class="nv">$pathEnd</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// $file __DIR__ . &#39;/..&#39; . /phpdocumentor/type-resolver/src/example.php(hh)`
</span></span></span><span class="line"><span class="cl">                    <span class="k">return</span> <span class="nv">$file</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="psr0">PSR0</h3>
<p>如果 <code>PSR4</code> 标准加载失败，则要进行 <code>PSR0</code> 标准加载。对于 <code>phpDocumentor\Reflection\example</code>，当尝试利用 <code>PSR0</code> 标准映射目录时，步骤如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// $class: phpDocumentor\Reflection\example
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PSR-0 lookup
</span></span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="k">false</span> <span class="o">!==</span> <span class="nv">$pos</span> <span class="o">=</span> <span class="nx">strrpos</span><span class="p">(</span><span class="nv">$class</span><span class="p">,</span> <span class="s1">&#39;\\&#39;</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// namespaced class name
</span></span></span><span class="line"><span class="cl">    <span class="nv">$logicalPathPsr0</span> <span class="o">=</span> <span class="nx">substr</span><span class="p">(</span><span class="nv">$logicalPathPsr4</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="o">.</span> <span class="nx">strtr</span><span class="p">(</span><span class="nx">substr</span><span class="p">(</span><span class="nv">$logicalPathPsr4</span><span class="p">,</span> <span class="nv">$pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">),</span> <span class="s1">&#39;_&#39;</span><span class="p">,</span> <span class="nx">DIRECTORY_SEPARATOR</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// PEAR-like class name
</span></span></span><span class="line"><span class="cl">    <span class="nv">$logicalPathPsr0</span> <span class="o">=</span> <span class="nx">strtr</span><span class="p">(</span><span class="nv">$class</span><span class="p">,</span> <span class="s1">&#39;_&#39;</span><span class="p">,</span> <span class="nx">DIRECTORY_SEPARATOR</span><span class="p">)</span> <span class="o">.</span> <span class="nv">$ext</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// $logicalPathPsr0: phpDocumentor/Reflection/example.php(hh)`
</span></span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">isset</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">prefixesPsr0</span><span class="p">[</span><span class="nv">$first</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">foreach</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">prefixesPsr0</span><span class="p">[</span><span class="nv">$first</span><span class="p">]</span> <span class="k">as</span> <span class="nv">$prefix</span> <span class="o">=&gt;</span> <span class="nv">$dirs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* &#39;P&#39; =&gt;
</span></span></span><span class="line"><span class="cl"><span class="cm">        array (
</span></span></span><span class="line"><span class="cl"><span class="cm">            &#39;Prophecy\\&#39; =&gt;
</span></span></span><span class="line"><span class="cl"><span class="cm">            array (
</span></span></span><span class="line"><span class="cl"><span class="cm">                0 =&gt; __DIR__ . &#39;/..&#39; . &#39;/phpspec/prophecy/src&#39;,
</span></span></span><span class="line"><span class="cl"><span class="cm">            ),
</span></span></span><span class="line"><span class="cl"><span class="cm">            &#39;Parsedown&#39; =&gt;
</span></span></span><span class="line"><span class="cl"><span class="cm">            array (
</span></span></span><span class="line"><span class="cl"><span class="cm">                0 =&gt; __DIR__ . &#39;/..&#39; . &#39;/erusev/parsedown&#39;,
</span></span></span><span class="line"><span class="cl"><span class="cm">            ),
</span></span></span><span class="line"><span class="cl"><span class="cm">        ), */</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="mi">0</span> <span class="o">===</span> <span class="nx">strpos</span><span class="p">(</span><span class="nv">$class</span><span class="p">,</span> <span class="nv">$prefix</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">foreach</span> <span class="p">(</span><span class="nv">$dirs</span> <span class="k">as</span> <span class="nv">$dir</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="nx">file_exists</span><span class="p">(</span><span class="nv">$file</span> <span class="o">=</span> <span class="nv">$dir</span> <span class="o">.</span> <span class="nx">DIRECTORY_SEPARATOR</span> <span class="o">.</span> <span class="nv">$logicalPathPsr0</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// $file __DIR__ . &#39;/..&#39; . &#39;/phpspec/prophecy/src&#39; . phpDocumentor/Reflection/example.php(hh)
</span></span></span><span class="line"><span class="cl">                    <span class="k">return</span> <span class="nv">$file</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="qa">Q&amp;A</h2>
<p>个人一些疑问：</p>
<h3 id="防止用户自定义与-classloader-命名空间冲突">防止用户自定义与 ClassLoader 命名空间冲突</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">spl_autoload_register</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&#39;</span><span class="p">,</span> <span class="s1">&#39;loadClassLoader&#39;</span><span class="p">),</span> <span class="k">true</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">self</span><span class="o">::</span><span class="nv">$loader</span> <span class="o">=</span> <span class="nv">$loader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">\Composer\Autoload\ClassLoader</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">spl_autoload_unregister</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;ComposerAutoloaderInit76e88f0b305cd64c7c84b90b278c31db&#39;</span><span class="p">,</span> <span class="s1">&#39;loadClassLoader&#39;</span><span class="p">));</span>
</span></span></code></pre></div><p>为什么这样可以解决：与用户也定义了个 <code>\Composer\Autoload\ClassLoader</code> 命名空间，导致自动加载错误文件。</p>
<p>与第四个参数 <code>$prepend</code> <code>true</code> 有关吗？</p>
<h3 id="composer-staticloader-有什么优势">composer StaticLoader 有什么优势</h3>
<p><code>composer</code> 在加载类和加载全局方法时，都有两种方式。</p>
<pre tabindex="0"><code>$useStaticLoader = PHP_VERSION_ID &gt;= 50600 &amp;&amp; !defined(&#39;HHVM_VERSION&#39;) &amp;&amp; (!function_exists(&#39;zend_loader_file_encoded&#39;) || !zend_loader_file_encoded());
</code></pre><p>以 <code>$useStaticLoader</code> 的值进行选择，为什么一定分两种，静态方法是有什么优势吗？</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/LeoYang90/laravel-source-analysis/blob/master/PHP%20Composer%E2%80%94%E2%80%94%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md">PHP Composer - 初始化源码分析</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>寻找数组中轴索引</title>
      <link>https://zyf.im/2019/03/06/find-pivot-index/</link>
      <pubDate>Wed, 06 Mar 2019 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2019/03/06/find-pivot-index/</guid>
      <description>&lt;p&gt;将 pivot 索引定义为：左边的数字之和等于索引右边的数字之和。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nums&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Explanation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nums&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Explanation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;There&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;no&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;that&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;satisfies&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;the&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;conditions&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;the&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;problem&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;statement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- more --&gt;
&lt;p&gt;Note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The length of &lt;code&gt;nums&lt;/code&gt; will be in the range &lt;code&gt;[0, 10000]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Each element &lt;code&gt;nums[i]&lt;/code&gt; will be an integer in the range &lt;code&gt;[-1000, 1000]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;关键点&#34;&gt;关键点&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;动态规划&lt;/li&gt;
&lt;li&gt;数组的和 - 中轴数 = 中轴数左边数组的和 * 2&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;解答&#34;&gt;解答&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;findPivot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 数组和&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;reduce&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 左侧数组和&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;leftSum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;enumerated&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;leftSum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;leftSum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://my.oschina.net/liyurong/blog/1608204&#34;&gt;找到数组中左右两边的和相等的 pivot 的下标 Find Pivot Index&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>将 pivot 索引定义为：左边的数字之和等于索引右边的数字之和。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">Input</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="n">nums</span> <span class="p">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">Output</span><span class="p">:</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="n">Explanation</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="mi">1</span> <span class="o">+</span> <span class="mi">7</span> <span class="o">+</span> <span class="mi">3</span> <span class="p">=</span> <span class="mi">5</span> <span class="o">+</span> <span class="mi">6</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Input</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="n">nums</span> <span class="p">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">Output</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">Explanation</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="n">There</span> <span class="k">is</span> <span class="n">no</span> <span class="n">index</span> <span class="n">that</span> <span class="n">satisfies</span> <span class="n">the</span> <span class="n">conditions</span> <span class="k">in</span> <span class="n">the</span> <span class="n">problem</span> <span class="n">statement</span><span class="p">.</span>
</span></span></code></pre></div><!-- more -->
<p>Note:</p>
<ul>
<li>The length of <code>nums</code> will be in the range <code>[0, 10000]</code>.</li>
<li>Each element <code>nums[i]</code> will be an integer in the range <code>[-1000, 1000]</code>.</li>
</ul>
<h2 id="关键点">关键点</h2>
<ul>
<li>动态规划</li>
<li>数组的和 - 中轴数 = 中轴数左边数组的和 * 2</li>
</ul>
<h2 id="解答">解答</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">findPivot</span><span class="p">(</span><span class="kc">_</span> <span class="n">array</span><span class="p">:</span> <span class="p">[</span><span class="nb">Int</span><span class="p">])</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 数组和</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">sum</span> <span class="p">=</span> <span class="n">array</span><span class="p">.</span><span class="bp">reduce</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">+</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 左侧数组和</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">leftSum</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="k">in</span> <span class="n">array</span><span class="p">.</span><span class="n">enumerated</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">sum</span> <span class="o">-</span> <span class="n">value</span> <span class="p">==</span> <span class="n">leftSum</span> <span class="o">*</span> <span class="mi">2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">key</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">leftSum</span> <span class="o">+=</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">array</span> <span class="p">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">search</span><span class="p">(</span><span class="n">array</span><span class="p">)</span> <span class="c1">// 3</span>
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://my.oschina.net/liyurong/blog/1608204">找到数组中左右两边的和相等的 pivot 的下标 Find Pivot Index</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>m 进制转 n 进制</title>
      <link>https://zyf.im/2019/03/02/convert-m-number-to-n-number/</link>
      <pubDate>Sat, 02 Mar 2019 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2019/03/02/convert-m-number-to-n-number/</guid>
      <description>&lt;h2 id=&#34;思路&#34;&gt;思路&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;m 进制 -&amp;gt; 十进制 -&amp;gt; n 进制&lt;/li&gt;
&lt;li&gt;利用柯里化生成函数（炫技 🐶）&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;m-进制---十进制&#34;&gt;m 进制 -&amp;gt; 十进制&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// carry 范围值: 2-36
// origin 范围值: 0-9 [ascii 48-58], A-Z [65-90], a-z [97-122]
func carryToDecimalism(_ carry: Int) -&amp;gt; (_ origin: String) -&amp;gt; Int {
    return { origin in
        // 得到字符串对应的 ascii 码
        let asciis = origin.uppercased().unicodeScalars.map { Int($0.value) }
        // 累加每一位
        let result = asciis.reversed().enumerated().map { (index, ascii) -&amp;gt; Int in
            var standard: Int
            if 65 &amp;lt;= ascii &amp;amp;&amp;amp; ascii &amp;lt;= 90 {
                standard = ascii - 65 + 10
            } else {
                standard = ascii - 48
            }
            return standard * Int(pow(Double(carry), Double(index)))
        }.reduce(0, +)
        return result
    }
}

let 十六进制转十进制 = carryToDecimalism(16)
print(十六进制转十进制(&amp;#34;1a&amp;#34;)) // 26

let 二进制转十进制 = carryToDecimalism(2)
print(二进制转十进制(&amp;#34;110&amp;#34;)) // 6
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;十进制---n-进制&#34;&gt;十进制 -&amp;gt; n 进制&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func decimalismToCarry(_ carry: Int) -&amp;gt; (_ origin: Int) -&amp;gt; String {
    return { origin in
        var result = [Int]()
        var remain = origin
        while remain &amp;gt; 0 {
            result.append(remain % carry)
            remain /= carry
        }
        if carry &amp;lt;= 10 {
            return result.reversed().map(String.init).joined()
        } else {
            return result.reversed().map { i -&amp;gt; String in
                return i &amp;lt; 10 ? String(i) : String(UnicodeScalar(i + 55)!)
            }.joined()
        }
    }
}

let 十进制转二进制 = decimalismToCarry(2)
print(十进制转二进制(26)) // &amp;#34;11010&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://ascii.911cha.com/&#34;&gt;ASCII 码对照表&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="思路">思路</h2>
<ul>
<li>m 进制 -&gt; 十进制 -&gt; n 进制</li>
<li>利用柯里化生成函数（炫技 🐶）</li>
</ul>
<!-- more -->
<h2 id="m-进制---十进制">m 进制 -&gt; 十进制</h2>
<pre tabindex="0"><code>// carry 范围值: 2-36
// origin 范围值: 0-9 [ascii 48-58], A-Z [65-90], a-z [97-122]
func carryToDecimalism(_ carry: Int) -&gt; (_ origin: String) -&gt; Int {
    return { origin in
        // 得到字符串对应的 ascii 码
        let asciis = origin.uppercased().unicodeScalars.map { Int($0.value) }
        // 累加每一位
        let result = asciis.reversed().enumerated().map { (index, ascii) -&gt; Int in
            var standard: Int
            if 65 &lt;= ascii &amp;&amp; ascii &lt;= 90 {
                standard = ascii - 65 + 10
            } else {
                standard = ascii - 48
            }
            return standard * Int(pow(Double(carry), Double(index)))
        }.reduce(0, +)
        return result
    }
}

let 十六进制转十进制 = carryToDecimalism(16)
print(十六进制转十进制(&#34;1a&#34;)) // 26

let 二进制转十进制 = carryToDecimalism(2)
print(二进制转十进制(&#34;110&#34;)) // 6
</code></pre><h2 id="十进制---n-进制">十进制 -&gt; n 进制</h2>
<pre tabindex="0"><code>func decimalismToCarry(_ carry: Int) -&gt; (_ origin: Int) -&gt; String {
    return { origin in
        var result = [Int]()
        var remain = origin
        while remain &gt; 0 {
            result.append(remain % carry)
            remain /= carry
        }
        if carry &lt;= 10 {
            return result.reversed().map(String.init).joined()
        } else {
            return result.reversed().map { i -&gt; String in
                return i &lt; 10 ? String(i) : String(UnicodeScalar(i + 55)!)
            }.joined()
        }
    }
}

let 十进制转二进制 = decimalismToCarry(2)
print(十进制转二进制(26)) // &#34;11010&#34;
</code></pre><h2 id="references">References</h2>
<ul>
<li><a href="http://ascii.911cha.com/">ASCII 码对照表</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>超长阶乘的计算</title>
      <link>https://zyf.im/2019/03/01/extra-long-factorials/</link>
      <pubDate>Fri, 01 Mar 2019 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2019/03/01/extra-long-factorials/</guid>
      <description>&lt;p&gt;打印 &lt;code&gt;n!&lt;/code&gt; 的结果（1 &amp;lt;= n &amp;lt;= 100）。注意：当 &lt;code&gt;n &amp;gt; 20&lt;/code&gt; 时 64 位的 &lt;code&gt;Int&lt;/code&gt; 将无法直接存储结果。&lt;/p&gt;
&lt;h2 id=&#34;思路&#34;&gt;思路&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;将大数字用 &lt;strong&gt;数组&lt;/strong&gt; 形式表示。比如 987 使用 [9,8,7] 代替。&lt;/li&gt;
&lt;li&gt;每一位乘以 n，再进行进位操作，得到新数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;nums&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;tmpNums&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nums&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// [18, 16, 14]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 遍历 tmpNums 每一个数字，进行进制操作&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;bp&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmpNums&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joined&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 1974&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;解答项目&#34;&gt;解答项目&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extraLongFactorials&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;guard&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 结果数组&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 数组翻转 从低位开始每一位乘以本次的数字&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;tmpNums&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reversed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 进位数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;carryNum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 重置结果&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;tmpNums&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;forEach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 每一位加上上一位的进的数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;tmpNum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;carryNum&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 向下一位进制的数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;carryNum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tmpNum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 本位实际剩下的数 插入结果&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmpNum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 处理剩余进位数 进位数是可能大于 100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;carryNum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 逐渐插入进制&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;carryNum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;carryNum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 翻转回数组&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reversed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 连接字符串&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;bp&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joined&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.hackerrank.com/challenges/extra-long-factorials/problem&#34;&gt;Extra Long Factorials | HackerRank&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/43830151/swift-3-calculate-factorial-number-result-becomes-too-high&#34;&gt;Swift 3 calculate factorial number. Result becomes too high?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>打印 <code>n!</code> 的结果（1 &lt;= n &lt;= 100）。注意：当 <code>n &gt; 20</code> 时 64 位的 <code>Int</code> 将无法直接存储结果。</p>
<h2 id="思路">思路</h2>
<ul>
<li>将大数字用 <strong>数组</strong> 形式表示。比如 987 使用 [9,8,7] 代替。</li>
<li>每一位乘以 n，再进行进位操作，得到新数组。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">nums</span> <span class="p">=</span> <span class="p">[</span><span class="mi">9</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">7</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">tmpNums</span> <span class="p">=</span> <span class="n">nums</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">*</span> <span class="mi">2</span> <span class="p">}</span> <span class="c1">// [18, 16, 14]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 遍历 tmpNums 每一个数字，进行进制操作</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="mi">18</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">14</span><span class="p">]</span> <span class="p">-&gt;</span> <span class="p">[</span><span class="mi">18</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span> <span class="p">-&gt;</span> <span class="p">[</span><span class="mi">19</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span> <span class="p">-&gt;</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="bp">print</span><span class="p">(</span><span class="n">tmpNums</span><span class="p">.</span><span class="bp">map</span><span class="p">(</span><span class="nb">String</span><span class="p">.</span><span class="kd">init</span><span class="p">).</span><span class="n">joined</span><span class="p">())</span> <span class="c1">// 1974</span>
</span></span></code></pre></div><h2 id="解答项目">解答项目</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">extraLongFactorials</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">Int</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">guard</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 结果数组</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">result</span><span class="p">:</span> <span class="p">[</span><span class="nb">Int</span><span class="p">]</span> <span class="p">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">index</span> <span class="k">in</span> <span class="mf">1.</span><span class="p">..</span><span class="n">n</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 数组翻转 从低位开始每一位乘以本次的数字</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nv">tmpNums</span> <span class="p">=</span> <span class="n">result</span><span class="p">.</span><span class="n">reversed</span><span class="p">().</span><span class="bp">map</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">*</span> <span class="n">index</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 进位数</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nv">carryNum</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 重置结果</span>
</span></span><span class="line"><span class="cl">        <span class="n">result</span> <span class="p">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">        <span class="n">tmpNums</span><span class="p">.</span><span class="n">forEach</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 每一位加上上一位的进的数</span>
</span></span><span class="line"><span class="cl">            <span class="kd">let</span> <span class="nv">tmpNum</span> <span class="p">=</span> <span class="nv">$0</span> <span class="o">+</span> <span class="n">carryNum</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 向下一位进制的数</span>
</span></span><span class="line"><span class="cl">            <span class="n">carryNum</span> <span class="p">=</span> <span class="n">tmpNum</span> <span class="o">/</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 本位实际剩下的数 插入结果</span>
</span></span><span class="line"><span class="cl">            <span class="n">result</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">tmpNum</span> <span class="o">%</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 处理剩余进位数 进位数是可能大于 100</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="n">carryNum</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 逐渐插入进制</span>
</span></span><span class="line"><span class="cl">            <span class="n">result</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">carryNum</span> <span class="o">%</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">carryNum</span> <span class="o">/=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 翻转回数组</span>
</span></span><span class="line"><span class="cl">        <span class="n">result</span> <span class="p">=</span> <span class="n">result</span><span class="p">.</span><span class="n">reversed</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 连接字符串</span>
</span></span><span class="line"><span class="cl">    <span class="bp">print</span><span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="bp">map</span><span class="p">(</span><span class="nb">String</span><span class="p">.</span><span class="kd">init</span><span class="p">).</span><span class="n">joined</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://www.hackerrank.com/challenges/extra-long-factorials/problem">Extra Long Factorials | HackerRank</a></li>
<li><a href="https://stackoverflow.com/questions/43830151/swift-3-calculate-factorial-number-result-becomes-too-high">Swift 3 calculate factorial number. Result becomes too high?</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>【Swifter - Swift 开发者必备 Tips】笔记</title>
      <link>https://zyf.im/2019/02/15/swifter-tips-reading-notes/</link>
      <pubDate>Fri, 15 Feb 2019 17:00:00 +0000</pubDate>
      <guid>https://zyf.im/2019/02/15/swifter-tips-reading-notes/</guid>
      <description>&lt;p&gt;再读王巍的《Swifter - Swift 开发者必备 Tips》，看看有什么新收获。&lt;/p&gt;
&lt;h2 id=&#34;柯里化currying&#34;&gt;柯里化（Currying）&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96&#34;&gt;柯里化&lt;/a&gt;是把接受多个参数的函数变换成接受一个单一参数（最初函数的第一个参数）的函数，并且返回接受余下的参数而且返回结果的新函数的技术。这个词自己是第一次见到。&lt;/p&gt;
&lt;p&gt;自己的理解是：把接受多个参数的函数变换为先接受一个参数，然后返回一个函数，这个函数再接受其他参数。&lt;/p&gt;
&lt;p&gt;两个细节：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只有一个参数，并且这个参数是该函数的第一个参数。必须按照参数的定义顺序来调用柯里化函数。&lt;/li&gt;
&lt;li&gt;柯里化函数的函数体只会执行一次，只会在调用完最后一个参数的时候执行柯里化函数体。&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- more --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;/// 一个数加 x 的函数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;addTo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;adder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;adder&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// +2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;addTwo&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;addTo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;addTwo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// +10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;addTen&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;addTo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;addTen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;柯里化是一种量产相似方法的好办法，可以通过柯里化一个方法模板来避免写出很多重复代码，也方便了今后维护。&lt;/p&gt;
&lt;p&gt;书中还提到了一个封装 &lt;a href=&#34;https://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/&#34;&gt;Selector&lt;/a&gt; 的例子，但是没懂，欢迎指教。&lt;/p&gt;
&lt;p&gt;Reference:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.jianshu.com/p/5b27fec8c616&#34;&gt;Swift 函数柯里化介绍及使用场景&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;将-protocol-的方法声明为-mutating&#34;&gt;将 protocol 的方法声明为 mutating&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;protocol&lt;/code&gt; 不仅可以被 &lt;code&gt;class&lt;/code&gt; 类型实现，也适用于 &lt;code&gt;struct&lt;/code&gt; 和 &lt;code&gt;enum&lt;/code&gt;。因为这个原因，就要考虑定义的方法是否应该使用 &lt;code&gt;mutating&lt;/code&gt; 来修饰。在 &lt;code&gt;protocol&lt;/code&gt; 中使用 &lt;code&gt;mutating&lt;/code&gt; 修饰的方法，对于 &lt;code&gt;class&lt;/code&gt; 的实现是完全透明的。&lt;/p&gt;
&lt;h2 id=&#34;多元组tuple&#34;&gt;多元组（Tuple）&lt;/h2&gt;
&lt;p&gt;Python 中有见过类似用法。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;/// 交互数据&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;swapMe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;swapMe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// a: 20  b: 10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;/// 可读的返回值&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;rect&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CGRect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;height&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;remainder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;divided&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;atDistance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;minYEdge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// slice {x 0 y 0 w 100 h 20}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// remainder {x 0 y 20 w 100 h 80}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;autoclosure-和-&#34;&gt;&lt;code&gt;@autoclosure&lt;/code&gt; 和 &lt;code&gt;??&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;@autoclosure&lt;/code&gt; 做的事情就是把一句表达式自动的封装成一个闭包（closure）。这样有时候在语法上看起来就会非常漂亮。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>再读王巍的《Swifter - Swift 开发者必备 Tips》，看看有什么新收获。</p>
<h2 id="柯里化currying">柯里化（Currying）</h2>
<p><a href="https://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96">柯里化</a>是把接受多个参数的函数变换成接受一个单一参数（最初函数的第一个参数）的函数，并且返回接受余下的参数而且返回结果的新函数的技术。这个词自己是第一次见到。</p>
<p>自己的理解是：把接受多个参数的函数变换为先接受一个参数，然后返回一个函数，这个函数再接受其他参数。</p>
<p>两个细节：</p>
<ul>
<li>只有一个参数，并且这个参数是该函数的第一个参数。必须按照参数的定义顺序来调用柯里化函数。</li>
<li>柯里化函数的函数体只会执行一次，只会在调用完最后一个参数的时候执行柯里化函数体。</li>
</ul>
<!-- more -->
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">/// 一个数加 x 的函数</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">addTo</span><span class="p">(</span><span class="kc">_</span> <span class="n">adder</span><span class="p">:</span> <span class="nb">Int</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="p">(</span><span class="nb">Int</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span> <span class="n">adder</span> <span class="o">+</span> <span class="nv">$0</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// +2</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">addTwo</span> <span class="p">=</span> <span class="n">addTo</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">result</span> <span class="p">=</span> <span class="n">addTwo</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span> <span class="c1">// 8</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// +10</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">addTen</span> <span class="p">=</span> <span class="n">addTo</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">addTen</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span> <span class="c1">// 16</span>
</span></span></code></pre></div><p>柯里化是一种量产相似方法的好办法，可以通过柯里化一个方法模板来避免写出很多重复代码，也方便了今后维护。</p>
<p>书中还提到了一个封装 <a href="https://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/">Selector</a> 的例子，但是没懂，欢迎指教。</p>
<p>Reference:</p>
<ul>
<li><a href="https://www.jianshu.com/p/5b27fec8c616">Swift 函数柯里化介绍及使用场景</a></li>
</ul>
<h2 id="将-protocol-的方法声明为-mutating">将 protocol 的方法声明为 mutating</h2>
<p><code>protocol</code> 不仅可以被 <code>class</code> 类型实现，也适用于 <code>struct</code> 和 <code>enum</code>。因为这个原因，就要考虑定义的方法是否应该使用 <code>mutating</code> 来修饰。在 <code>protocol</code> 中使用 <code>mutating</code> 修饰的方法，对于 <code>class</code> 的实现是完全透明的。</p>
<h2 id="多元组tuple">多元组（Tuple）</h2>
<p>Python 中有见过类似用法。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">/// 交互数据</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">swapMe</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="kc">_</span> <span class="n">a</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">T</span><span class="p">,</span> <span class="kc">_</span> <span class="n">b</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="p">=</span> <span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">a</span> <span class="p">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">b</span> <span class="p">=</span> <span class="mi">20</span>
</span></span><span class="line"><span class="cl"><span class="n">swapMe</span><span class="p">(&amp;</span><span class="n">a</span><span class="p">,</span> <span class="p">&amp;</span><span class="n">b</span><span class="p">)</span> <span class="c1">// a: 20  b: 10</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">/// 可读的返回值</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">rect</span> <span class="p">=</span> <span class="n">CGRect</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="n">width</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span> <span class="n">height</span><span class="p">:</span> <span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="p">(</span><span class="n">slice</span><span class="p">,</span> <span class="n">remainder</span><span class="p">)</span> <span class="p">=</span> <span class="n">rect</span><span class="p">.</span><span class="n">divided</span><span class="p">(</span><span class="n">atDistance</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span> <span class="n">from</span><span class="p">:</span> <span class="p">.</span><span class="n">minYEdge</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// slice {x 0 y 0 w 100 h 20}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// remainder {x 0 y 20 w 100 h 80}</span>
</span></span></code></pre></div><h2 id="autoclosure-和-"><code>@autoclosure</code> 和 <code>??</code></h2>
<p><code>@autoclosure</code> 做的事情就是把一句表达式自动的封装成一个闭包（closure）。这样有时候在语法上看起来就会非常漂亮。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">logIfTrue</span><span class="p">(</span><span class="kc">_</span> <span class="n">predicate</span><span class="p">:</span> <span class="kr">@autoclosure</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nb">Bool</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">predicate</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="bp">print</span><span class="p">(</span><span class="s">&#34;True&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">logIfTrue</span><span class="p">(</span><span class="mi">2</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 而不是 logIfTrue { 2 &gt; 1 }</span>
</span></span></code></pre></div><p>Swift 把 <code>2 &gt; 1</code> 这个表达式自动转换为 <code>() -&gt; Bool</code>。</p>
<p>Swift 中 <code>??</code> 定义为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">??&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="kr">optional</span><span class="p">:</span> <span class="n">T</span><span class="p">?,</span> <span class="n">defaultValue</span><span class="p">:</span> <span class="kr">@autoclosure</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="n">T</span><span class="p">?)</span> <span class="p">-&gt;</span> <span class="n">T</span><span class="p">?</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">??&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="kr">optional</span><span class="p">:</span> <span class="n">T</span><span class="p">?,</span> <span class="n">defaultValue</span><span class="p">:</span> <span class="kr">@autoclosure</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="n">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="n">T</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">/// 猜测 ?? 的实现</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">??&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="kr">optional</span><span class="p">:</span> <span class="n">T</span><span class="p">?,</span> <span class="n">defaultValue</span><span class="p">:</span> <span class="kr">@autoclosure</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="n">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="n">T</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="kr">optional</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="p">.</span><span class="n">some</span><span class="p">(</span><span class="kd">let</span> <span class="nv">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">    <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">defaultValue</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>使用 <code>@autoclosure</code> 来修饰默认值看起来有些画蛇添足，但是如果默认值是通过一系列复杂计算得到话，在 optional 不为 <code>nil</code> 的情况下就会造成浪费。<code>@autoclosure</code> 将计算推迟到 <code>optional</code> 为 <code>nil</code>。</p>
<h2 id="escaping"><code>@escaping</code></h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">doWork</span><span class="p">(</span><span class="n">block</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nb">Void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">block</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">doWork</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="bp">print</span><span class="p">(</span><span class="s">&#34;work&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这里默认了一个隐藏假设：参数中 <code>block</code> 的内容会在 <code>doWork</code> 返回之前就完成了，也就是说，对于 <code>block</code> 的调用是同步行为。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">doWorkAsync</span><span class="p">(</span><span class="n">block</span><span class="p">:</span> <span class="p">@</span><span class="n">escaping</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DispatchQueue</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">block</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>@escaping</code> 表明这个闭包是会 <strong>逃逸</strong> 出该方法。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">S</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">foo</span> <span class="p">=</span> <span class="s">&#34;foo&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">func</span> <span class="nf">method1</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">doWork</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="bp">print</span><span class="p">(</span><span class="n">foo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">foo</span> <span class="p">=</span> <span class="s">&#34;method1&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">func</span> <span class="nf">method2</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">doWorkAsync</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="bp">print</span><span class="p">(</span><span class="kc">self</span><span class="p">.</span><span class="n">foo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">foo</span> <span class="p">=</span> <span class="s">&#34;method2&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">func</span> <span class="nf">method3</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">foo</span> <span class="p">=</span> <span class="s">&#34;method4&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">doWorkAsync</span> <span class="p">{</span> <span class="p">[</span><span class="kr">weak</span> <span class="kc">self</span><span class="p">]</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">            <span class="bp">print</span><span class="p">(</span><span class="kc">self</span><span class="p">?.</span><span class="n">foo</span> <span class="p">??</span> <span class="s">&#34;nil&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">foo</span> <span class="p">=</span> <span class="s">&#34;method3&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">S</span><span class="p">().</span><span class="n">method1</span><span class="p">()</span>  <span class="c1">// foo - 同步</span>
</span></span><span class="line"><span class="cl"><span class="n">S</span><span class="p">().</span><span class="n">method2</span><span class="p">()</span>  <span class="c1">// method2 - 由于强引用</span>
</span></span><span class="line"><span class="cl"><span class="n">S</span><span class="p">().</span><span class="n">method3</span><span class="p">()</span>  <span class="c1">// nil - 已经释放</span>
</span></span></code></pre></div><h2 id="optional-chaining">Optional Chaining</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">playClosure</span> <span class="p">=</span> <span class="p">{</span> <span class="p">(</span><span class="n">child</span><span class="p">:</span> <span class="n">Child</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Void</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="n">child</span><span class="p">.</span><span class="n">pet</span><span class="p">?.</span><span class="n">toy</span><span class="p">?.</span><span class="n">play</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这里 <code>Void</code> 是不合理的，因为真正的结果是一个 <code>Optional</code> 的结果，所以应该为 <code>Void?</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">playClosure</span> <span class="p">=</span> <span class="p">{</span> <span class="p">(</span><span class="n">child</span><span class="p">:</span> <span class="n">Child</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Void</span><span class="p">?</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="n">child</span><span class="p">.</span><span class="n">pet</span><span class="p">?.</span><span class="n">toy</span><span class="p">?.</span><span class="n">play</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 判断方法是否调用成功：</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="kd">let</span> <span class="nv">result</span> <span class="p">=</span> <span class="n">playClosure</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="bp">print</span><span class="p">(</span><span class="s">&#34;happy&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="bp">print</span><span class="p">(</span><span class="s">&#34;can&#39;t play&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>回顾 2018</title>
      <link>https://zyf.im/2018/12/31/review-2018/</link>
      <pubDate>Mon, 31 Dec 2018 21:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/12/31/review-2018/</guid>
      <description>&lt;iframe frameborder=&#34;no&#34; border=&#34;0&#34; marginwidth=&#34;0&#34; marginheight=&#34;0&#34; width=298 height=52 src=&#34;//music.163.com/outchain/player?type=2&amp;id=476618833&amp;auto=0&amp;height=32&#34;&gt;&lt;/iframe&gt;
&lt;p&gt;重新翻阅的自己工作邮件的发件箱，回顾一年工作。新年伊始自己还是在开发 P 项目的 iOS App，开始写 Q&amp;amp;A 功能。一些不算太难的 tableView 布局的需求，对我来说，都是头大的问题。&lt;/p&gt;
&lt;p&gt;这段时期招聘时的面试，竟成为我学习 App 开发的一扇小窗。&lt;/p&gt;
&lt;p&gt;年前收到了奖金还是挺开心的。leader 新年寄语：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;要发声，要当主力&lt;/li&gt;
&lt;li&gt;当有好的想法时，要学会说服别人&lt;/li&gt;
&lt;li&gt;要有耐心，Yifan 需要时间的沉淀&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;前两点意思差不多，这段时间思想上困扰我的是：自己对自己的定位是一个初级工程师，认为会的东西、经验不多，见识少，我尊重比我年长的工程师的想法与观点，也相信他们是经过长远思考过的。这个思维设定，我觉得没有什么问题。但是也许有人忽略了 &lt;strong&gt;责任&lt;/strong&gt;，对方案负责，对项目负责。开发方案一再重建性修改，接口结构没有规范。&lt;/p&gt;
&lt;p&gt;2017 的总结说胜利属于伏地魔，本想苟着发育，这时发现：苟是苟不住的，这个世界 &lt;strong&gt;需要英雄&lt;/strong&gt; carry。&lt;/p&gt;
&lt;p&gt;后来我感觉应该将公司看做一个舞台，舞台上有灯光、音效就要利用，展示自己、锻炼自己，即使是出糗，那就整理整理再来一次，who care? 成长是最重要的。&lt;/p&gt;
&lt;p&gt;Course 模块是前工程师用 Objective-C 写的，离职后一直没有再维护，过年期间自己重构了所有 Objective-C 的代码，项目完全转为了一个纯 Swift 项目。使用 Realm 作为数据本地化方案，选择的原因也很朴素，GitHub 哪个星多我就优先选用什么。&lt;/p&gt;
&lt;p&gt;后来参考 &lt;a href=&#34;https://github.com/6ag&#34;&gt;Jack Feng - 6ag&lt;/a&gt; 的几个 Swift 开源项目，新创建了 P 项目的工具 App，也对主项目结构做了重新的整理，在这里再次特别感谢。&lt;/p&gt;
&lt;p&gt;西安运营部的成立，加多了 C 项目的后台需求，难以都顾及项目两头。使用 laravel-admin 搭建新后台，也开始使用 Docker 部署项目，感觉从此离不开 Docker 了，像极了遇到 Git 时的感觉。&lt;/p&gt;
&lt;p&gt;C 项目主站的前端是在服务端渲染，非常传统的模式，多个工程师转手也是十分混乱。参考了 &lt;a href=&#34;https://baijunyao.com/&#34;&gt;白俊遥&lt;/a&gt; 工程的博客、laravel 项目，修改了项目结构，添加了 gulp 工具制定了工作流，虽然没有实现前后端的完全分离，但终究是向现代化前端走出了一步。&lt;/p&gt;
&lt;p&gt;转眼就到了年中调薪，公司不含糊，薪水涨到了我满意的值。这对我很关键，调整的不单单是我的薪水，也调整了我的心态。因为当时我认为没有强的工程师、甚至归我负责，却拿着比我还高的薪水。现在总算是有了一个平衡。&lt;/p&gt;
&lt;p&gt;不断向 C 项目投入更多的资源，项目 &lt;strong&gt;指标&lt;/strong&gt; 的要求越来越多。比如：优化了项目、优化了查询，到底优化了多少，怎么量化？这些在之前一直不被重视，改好了就都算叫优化了。项目中出现的问题错误，都要查找真实具体的原因，而不是说一个可能的什么原因，就当解答的了。我一开始也很难适应这些，但心里是认同的。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=298 height=52 src="//music.163.com/outchain/player?type=2&id=476618833&auto=0&height=32"></iframe>
<p>重新翻阅的自己工作邮件的发件箱，回顾一年工作。新年伊始自己还是在开发 P 项目的 iOS App，开始写 Q&amp;A 功能。一些不算太难的 tableView 布局的需求，对我来说，都是头大的问题。</p>
<p>这段时期招聘时的面试，竟成为我学习 App 开发的一扇小窗。</p>
<p>年前收到了奖金还是挺开心的。leader 新年寄语：</p>
<ul>
<li>要发声，要当主力</li>
<li>当有好的想法时，要学会说服别人</li>
<li>要有耐心，Yifan 需要时间的沉淀</li>
</ul>
<p>前两点意思差不多，这段时间思想上困扰我的是：自己对自己的定位是一个初级工程师，认为会的东西、经验不多，见识少，我尊重比我年长的工程师的想法与观点，也相信他们是经过长远思考过的。这个思维设定，我觉得没有什么问题。但是也许有人忽略了 <strong>责任</strong>，对方案负责，对项目负责。开发方案一再重建性修改，接口结构没有规范。</p>
<p>2017 的总结说胜利属于伏地魔，本想苟着发育，这时发现：苟是苟不住的，这个世界 <strong>需要英雄</strong> carry。</p>
<p>后来我感觉应该将公司看做一个舞台，舞台上有灯光、音效就要利用，展示自己、锻炼自己，即使是出糗，那就整理整理再来一次，who care? 成长是最重要的。</p>
<p>Course 模块是前工程师用 Objective-C 写的，离职后一直没有再维护，过年期间自己重构了所有 Objective-C 的代码，项目完全转为了一个纯 Swift 项目。使用 Realm 作为数据本地化方案，选择的原因也很朴素，GitHub 哪个星多我就优先选用什么。</p>
<p>后来参考 <a href="https://github.com/6ag">Jack Feng - 6ag</a> 的几个 Swift 开源项目，新创建了 P 项目的工具 App，也对主项目结构做了重新的整理，在这里再次特别感谢。</p>
<p>西安运营部的成立，加多了 C 项目的后台需求，难以都顾及项目两头。使用 laravel-admin 搭建新后台，也开始使用 Docker 部署项目，感觉从此离不开 Docker 了，像极了遇到 Git 时的感觉。</p>
<p>C 项目主站的前端是在服务端渲染，非常传统的模式，多个工程师转手也是十分混乱。参考了 <a href="https://baijunyao.com/">白俊遥</a> 工程的博客、laravel 项目，修改了项目结构，添加了 gulp 工具制定了工作流，虽然没有实现前后端的完全分离，但终究是向现代化前端走出了一步。</p>
<p>转眼就到了年中调薪，公司不含糊，薪水涨到了我满意的值。这对我很关键，调整的不单单是我的薪水，也调整了我的心态。因为当时我认为没有强的工程师、甚至归我负责，却拿着比我还高的薪水。现在总算是有了一个平衡。</p>
<p>不断向 C 项目投入更多的资源，项目 <strong>指标</strong> 的要求越来越多。比如：优化了项目、优化了查询，到底优化了多少，怎么量化？这些在之前一直不被重视，改好了就都算叫优化了。项目中出现的问题错误，都要查找真实具体的原因，而不是说一个可能的什么原因，就当解答的了。我一开始也很难适应这些，但心里是认同的。</p>
<p>C 项目数据源重新整理，终于摸到了一次算法的边。“你觉的算法没用，是因为你不会。” 数据标记、正确率、召回率、特征词提取、文本相似度、词条打分、背包算法…… 一段最让人回忆的开发经历，与业务开发完全不同的模式。</p>
<p>C 项目衍生出新项目 G，我一个人做，一次实践 Laravel 框架的机会。</p>
<p>再次和 Z 哥合作了一个 App 项目，私活做的真是的太累了，极不推荐。其实这段时期工作上并没有在做 iOS，因为这个项目没让自己 iOS 的技能凉下来，为后面回归 iOS 项目保持了状态。</p>
<p>参加了年度的 Swift 大会，在群里简单了很多大佬、看过博客的工程师，没想到他们都很乐意加微信。也发现他们相互都认识，这就是圈子吧。</p>
<p>2018 是世界杯年，我不懂球就是看个热闹，最开心的是公司有活动基金，我拉着不少新同事们吃喝了不少。冬季后发展成了和同事的每周的火锅，真挺快乐的。</p>
<p>买了心心念的 Switch 叫的闪送，上午下单一会后，就像收外卖一样收了机子。荒野之息是好玩，呃… 男主竟然叫林克不是塞尔达，你敢想？</p>
<p>因为 MSI 的胜利开始关注 RNG LPL。这年 LPL 后面的比赛我基本都看了，中午还会看 dys 的直播实况。洲际赛 RW 救世主真是精彩，“当没人相信你的时候，你自己相信了自己，并且赢得了胜利，这便是成为英雄的道路。” 登峰造極 2018 我们是冠军。</p>
<p>永利*暴雷崩盘，损失不少，派出所报案。家里，多年的期房要发钥匙了，感恩父母。一次团建向比比求婚成功。</p>
<p>这年最后一天是在三亚，吃到了棒棒的早晨，去了蜈支洲岛，感受了乘风破浪，遇到了一个有趣的东北司机，参加了酒店的晚会，看了喝酒赢到的电影。</p>
<p>2018 Happy Ending.</p>
<blockquote>
<p>做了一个梦 我们 iG 拿冠军了 啊哈哈哈…
我说是不是假的 确实是个梦吧，
但是就是当时虽然在做 但是特别开心。
所以我们尽量就是自己努力一下，
然后每个人发挥好一点的话，
我觉得真的有可能 也可以拿冠军的。</p>
</blockquote>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>二分查找 Binary Search</title>
      <link>https://zyf.im/2018/12/10/binary-search/</link>
      <pubDate>Mon, 10 Dec 2018 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/12/10/binary-search/</guid>
      <description>&lt;p&gt;快速从一个数组中查找一个元素。&lt;/p&gt;
&lt;h2 id=&#34;linear-search-线性查找&#34;&gt;Linear Search 线性查找&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;linearSearch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Equatable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;线性查找在最坏情况：遍历了整个数组，但没有找到合适的元素。平均要遍历一半的元素性能为 &lt;code&gt;O(n)&lt;/code&gt;，而二分查找的效率为 &lt;code&gt;O(log n)&lt;/code&gt;，也就是说一个有 &lt;code&gt;1,000,000&lt;/code&gt; 元素的数组只需要 &lt;code&gt;20&lt;/code&gt; 步就可以找到想要的元素 &lt;code&gt;log_2(1,000,000) = 19.9&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但是二分查找要求数组必须是排好序。&lt;/p&gt;
&lt;p&gt;二分查找步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将数组分为两半。&lt;/li&gt;
&lt;li&gt;判断想要找的元素是在左边数组还是右边，这也是数组需要排好顺序的原因。&lt;/li&gt;
&lt;li&gt;如果要的元素在左边，就将左边的数组分成更小的两部分，并判断要的元素在哪部分。&lt;/li&gt;
&lt;li&gt;重复步骤直到找到想要的元素。如果数组不能进一步查分，就说明要找的元素不在数组中。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;divide-and-conquer&lt;/p&gt;
&lt;h2 id=&#34;the-code&#34;&gt;The code&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func binarySearch&amp;lt;T: Comparable&amp;gt;(_ a: [T], key: T, range: Range&amp;lt;Int&amp;gt;) -&amp;gt; Int? {
    if range.lowerBound &amp;gt;= range.upperBound {
        // If we get here, then the search key is not present in the array.
        return nil

    } else {
        // Calculate where to split the array.
        let midIndex = range.lowerBound + (range.upperBound - range.lowerBound) / 2

        // Is the search key in the left half?
        if a[midIndex] &amp;gt; key {
            return binarySearch(a, key: key, range: range.lowerBound ..&amp;lt; midIndex)

        // Is the search key in the right half?
        // 这里 + 1 的原因是排除 midIndex 中间值
        } else if a[midIndex] &amp;lt; key {
            return binarySearch(a, key: key, range: midIndex + 1 ..&amp;lt; range.upperBound)

        // If we get here, then we&amp;#39;ve found the search key!
        } else {
            return midIndex
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// 19 numbers
let numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67]

// 0 ..&amp;lt; numbers.count 覆盖所有范围
binarySearch(numbers, key: 43, range: 0 ..&amp;lt; numbers.count)  // gives 13
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;二分查找是将数组分为两个，但是我们不需要正真的创建两个新数组。取而代之，我们使用 Swift &lt;code&gt;Range&lt;/code&gt; 对象跟踪这些拆分。左闭右开。upperBound 总是比最后一个元素的索引多一。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>快速从一个数组中查找一个元素。</p>
<h2 id="linear-search-线性查找">Linear Search 线性查找</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">linearSearch</span><span class="p">&lt;</span><span class="n">T</span><span class="p">:</span> <span class="nb">Equatable</span><span class="p">&gt;(</span><span class="kc">_</span> <span class="n">a</span><span class="p">:</span> <span class="p">[</span><span class="n">T</span><span class="p">],</span> <span class="kc">_</span> <span class="n">key</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Int</span><span class="p">?</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span> <span class="p">..</span><span class="o">&lt;</span> <span class="n">a</span><span class="p">.</span><span class="bp">count</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">==</span> <span class="n">key</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">i</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>线性查找在最坏情况：遍历了整个数组，但没有找到合适的元素。平均要遍历一半的元素性能为 <code>O(n)</code>，而二分查找的效率为 <code>O(log n)</code>，也就是说一个有 <code>1,000,000</code> 元素的数组只需要 <code>20</code> 步就可以找到想要的元素 <code>log_2(1,000,000) = 19.9</code>。</p>
<p>但是二分查找要求数组必须是排好序。</p>
<p>二分查找步骤：</p>
<ol>
<li>将数组分为两半。</li>
<li>判断想要找的元素是在左边数组还是右边，这也是数组需要排好顺序的原因。</li>
<li>如果要的元素在左边，就将左边的数组分成更小的两部分，并判断要的元素在哪部分。</li>
<li>重复步骤直到找到想要的元素。如果数组不能进一步查分，就说明要找的元素不在数组中。</li>
</ol>
<p>divide-and-conquer</p>
<h2 id="the-code">The code</h2>
<pre tabindex="0"><code>func binarySearch&lt;T: Comparable&gt;(_ a: [T], key: T, range: Range&lt;Int&gt;) -&gt; Int? {
    if range.lowerBound &gt;= range.upperBound {
        // If we get here, then the search key is not present in the array.
        return nil

    } else {
        // Calculate where to split the array.
        let midIndex = range.lowerBound + (range.upperBound - range.lowerBound) / 2

        // Is the search key in the left half?
        if a[midIndex] &gt; key {
            return binarySearch(a, key: key, range: range.lowerBound ..&lt; midIndex)

        // Is the search key in the right half?
        // 这里 + 1 的原因是排除 midIndex 中间值
        } else if a[midIndex] &lt; key {
            return binarySearch(a, key: key, range: midIndex + 1 ..&lt; range.upperBound)

        // If we get here, then we&#39;ve found the search key!
        } else {
            return midIndex
        }
    }
}
</code></pre><pre tabindex="0"><code>// 19 numbers
let numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67]

// 0 ..&lt; numbers.count 覆盖所有范围
binarySearch(numbers, key: 43, range: 0 ..&lt; numbers.count)  // gives 13
</code></pre><p>二分查找是将数组分为两个，但是我们不需要正真的创建两个新数组。取而代之，我们使用 Swift <code>Range</code> 对象跟踪这些拆分。左闭右开。upperBound 总是比最后一个元素的索引多一。</p>
<pre tabindex="0"><code>midIndex = (lowerBound + upperBound) / 2
</code></pre><p>如果这样写将存在一个 bug，就是当这两值非常大时，将存在一个越界的问题。</p>
<h2 id="iterative-vs-recursive-迭代-vs-递归">Iterative vs recursive 迭代 vs 递归</h2>
<p>二分查找本质是递归。</p>
<p>使用迭代的方式实现：</p>
<pre tabindex="0"><code>func binarySearch(_ a: [Int], key: Int) -&gt; Int? {
    var lowerBound = 0
    var upperBound = a.count
    while lowerBound &lt; upperBound {
        // 这行僵硬了 没有必要的
        var range = lowerBound ..&lt; upperBound
        let midIndex = range.lowerBound + (range.upperBound - range.lowerBound) / 2
        let midValue = a[midIndex]
        if key &lt; midValue {
            upperBound = midIndex
        } else if key &gt; midValue {
            lowerBound = midIndex + 1
        } else {
            return midIndex
        }
    }
    return nil
}
</code></pre><h2 id="the-end">The end</h2>
<p>查找前数组一定要先排序吗？这取决于排序花费的时间，有时候：数组排序加二分查找比线性搜索还要慢。二分查找的优势在于一次排序后多次查找。</p>
<p>文章代码：<a href="https://github.com/imzyf/data-structure-and-algorithm/tree/master/004-Binary%20Search">GitHub - imzyf/data-structure-and-algorithm/004-Binary Search/</a>。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/raywenderlich/swift-algorithm-club/tree/master/Binary%20Search">raywenderlich/swift-algorithm-club/Binary Search</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>插入排序 Insertion Sort</title>
      <link>https://zyf.im/2018/11/24/insertion-sort/</link>
      <pubDate>Sat, 24 Nov 2018 16:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/11/24/insertion-sort/</guid>
      <description>&lt;p&gt;将一个数组从高到低或者从低到高排序。&lt;/p&gt;
&lt;p&gt;插入排序算法的工作原理：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将若干数字放在一个数组里，数组是乱序的。&lt;/li&gt;
&lt;li&gt;从数组中挑选一个数字，它是哪个并不重要，但是为了方便我们挑选数组头部的这个。&lt;/li&gt;
&lt;li&gt;将这个数字插入到一个新的数组里。&lt;/li&gt;
&lt;li&gt;从乱序数组里挑选下一个数字也将它放到新数组里。这个数字要么在第一个数字前或者后，所以这个两个数字是被排序的。&lt;/li&gt;
&lt;li&gt;再次重从乱序数组里挑选下一个数字也将它放到新数组里，并将数字放在正确的位置。&lt;/li&gt;
&lt;li&gt;一直如此进行直到乱序数组中没有数字。这时也将等到一个排序好的新数组。&lt;/li&gt;
&lt;/ol&gt;
&lt;!-- more --&gt;
&lt;p&gt;自己的一个实现：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;let array = [2, 1, 3, 8, 3, 5, 4]

var newArray = [Int]()
for (k, v) in array.enumerated() {
    for (nK, nV) in newArray.enumerated() {
        // 本次的数 小于 存在的数的第一个(nv)
        if v &amp;lt; nV {
            newArray.insert(v, at: nK)
            break
        }
    }
    // 没有插入成功 放在末尾
    if newArray.count &amp;lt; k + 1 {
        newArray.append(v)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;in-place-sort&#34;&gt;In-place sort&lt;/h2&gt;
&lt;p&gt;上面的排序需要两个数组，一个原始的，一个排好顺序的。但是我们也可以 &lt;em&gt;就地排序&lt;/em&gt; 无需创建一个额外的数组。我们只需要跟踪记录原始数组中哪里部分排好顺序了，哪一部分还没有排序。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>将一个数组从高到低或者从低到高排序。</p>
<p>插入排序算法的工作原理：</p>
<ol>
<li>将若干数字放在一个数组里，数组是乱序的。</li>
<li>从数组中挑选一个数字，它是哪个并不重要，但是为了方便我们挑选数组头部的这个。</li>
<li>将这个数字插入到一个新的数组里。</li>
<li>从乱序数组里挑选下一个数字也将它放到新数组里。这个数字要么在第一个数字前或者后，所以这个两个数字是被排序的。</li>
<li>再次重从乱序数组里挑选下一个数字也将它放到新数组里，并将数字放在正确的位置。</li>
<li>一直如此进行直到乱序数组中没有数字。这时也将等到一个排序好的新数组。</li>
</ol>
<!-- more -->
<p>自己的一个实现：</p>
<pre tabindex="0"><code>let array = [2, 1, 3, 8, 3, 5, 4]

var newArray = [Int]()
for (k, v) in array.enumerated() {
    for (nK, nV) in newArray.enumerated() {
        // 本次的数 小于 存在的数的第一个(nv)
        if v &lt; nV {
            newArray.insert(v, at: nK)
            break
        }
    }
    // 没有插入成功 放在末尾
    if newArray.count &lt; k + 1 {
        newArray.append(v)
    }
}
</code></pre><h2 id="in-place-sort">In-place sort</h2>
<p>上面的排序需要两个数组，一个原始的，一个排好顺序的。但是我们也可以 <em>就地排序</em> 无需创建一个额外的数组。我们只需要跟踪记录原始数组中哪里部分排好顺序了，哪一部分还没有排序。</p>
<p>举例：<code>[ 8, 3, 5, 4, 6 ]</code> 使用 <code>|</code> 分割是否排好顺序的部分。</p>
<pre tabindex="0"><code>// 开始时 | 在最前
[| 8, 3, 5, 4, 6 ]

// 开始向左移动，左侧只有个 8 无论什么顺序都是正确的，右侧是未排序的部分
[ 8 | 3, 5, 4, 6 ]

// 依次进行 将未排序的头部元素放在已排部分的正确位置
[ 3, 8 | 5, 4, 6 ]
[ 3, 5, 8 | 4, 6 ]
[ 3, 4, 5, 8 | 6 ]
[ 3, 4, 5, 6, 8 |]
</code></pre><p>每次 <code>|</code> 移动，都对左侧进行排序，未排序的部分逐渐减少，排序部分增加。直到未排序部分为零。</p>
<h2 id="how-to-insert">How to insert</h2>
<p>将未排序的头部元素放在已排部分的正确位置，如何这到这点的？</p>
<pre tabindex="0"><code>// 从此状态开始。下个说的 4，我们需要将 4 插入到 [ 3, 5, 8 ] 这个已经排好的数组里
[ 3, 5, 8 | 4, 6 ]

// 移动 |，这时我们注意 8 这个元素
[ 3, 5, 8, 4 | 6 ]
        ^

// 8 大于 4，所有 8 应该在 4 的右边，8 与 4 进行位置调换
[ 3, 5, 4, 8 | 6 ]
        &lt;--&gt;
      swapped

// 将 4 与现在的前面的元素 5 进行比较，5 大于 4，所以 5 与 4 进行位置调换
[ 3, 4, 5, 8 | 6 ]
     &lt;--&gt;
    swapped

// 3 小于 4 这个数，所以我们完成了对 4 的排序，这时从头到 |，是排好顺序的
[ 3, 4, 5, 8 | 6 ]
</code></pre><p>这就是对插入排序算法的内循环的描述。</p>
<h2 id="the-code">The code</h2>
<pre tabindex="0"><code>func insertionSort(_ array: [Int]) -&gt; [Int] {
    // 1
    var a = array
    // 2
    for x in 1..&lt;a.count {
        var y = x
        // 3
        while y &gt; 0 &amp;&amp; a[y] &lt; a[y - 1] {
            a.swapAt(y - 1, y)
            y -= 1
        }
    }
    return a
}
</code></pre><ol>
<li>将 <code>array</code> 复制一个副本。因为我们无法直接修改参数中的 <code>array</code>，就想 Swift 自身的 <code>sort()</code>，<code>insertionSort()</code> 将返回一个排序顺序的副本数组。</li>
<li>两个循环在方法中。外层循环遍历轮到排序的元素，也就是从待排数组中挑选出头部的元素。<code>x</code> 索引为排好顺序的结尾索引同时也是待排数组的开头。记住，如何时间从开头到 <code>x</code> 永远都是排好顺序的，从 <code>x</code> 到最后的元素都是未排序的。</li>
<li>内层循环查询 x 索引位置的元素。这个元素可能小于之前排序顺序数组中的每一个元素。内层循环从后倒序遍历每一个已排序的元素，每次发现这个元素之前的元素比它大，则交互位置。当内层循环完成时，数组从开头到 x 将又是已排序的。</li>
</ol>
<p>tip：外层循环从索引 1 开始，而不是 0。将第一个元素从堆移动到排序部分实际上并没有改变任何东西，所以我们不妨跳过它。</p>
<h2 id="no-more-swaps">No more swaps</h2>
<p>上面的插入排序可以正常的工作了。我们还可以通过移除调用 swap() 让程序更快一些。</p>
<p>我们可以将所有需要换位置的元素向右移动一个位置，然后将新数字复制到正确的位置。</p>
<pre tabindex="0"><code>[ 3, 5, 8, 4 | 6 ]   remember 4
           *

[ 3, 5, 8, 8 | 6 ]   shift 8 to the right
        ---&gt;

[ 3, 5, 5, 8 | 6 ]   shift 5 to the right
     ---&gt;

[ 3, 4, 5, 8 | 6 ]   copy 4 into place
     *
</code></pre><pre tabindex="0"><code>func insertionSort(_ array: [Int]) -&gt; [Int] {
    var a = array
    for x in 1..&lt;a.count {
        var y = x
        let temp = a[y]
        // tip
        while y &gt; 0 &amp;&amp; temp &lt; a[y - 1] {
            // 1
            a[y] = a[y - 1]
            y -= 1
        }
        // 2
        a[y] = temp
    }
    return a
}
</code></pre><ol>
<li>原本需要换位置的元素右移一个位置。</li>
<li>当内层结束时，<code>y</code> 的索引位置就是新元素的排序后的位置，将元素放在此。</li>
</ol>
<p>tip：这里我自己写成了 <code>while y &gt; 0 &amp;&amp; a[y] &lt; a[y - 1]</code> 这是不对的，因为我要找的是 <strong>原本</strong> 的 <code>a[y]</code> 的位置，但是循环一次后 <code>a[y]</code> 将发生变化。</p>
<h2 id="making-it-generic">Making it generic</h2>
<pre tabindex="0"><code>func insertionSort&lt;T&gt;(_ array: [T], _ isOrderedBefore: (T, T) -&gt; Bool) -&gt; [T] {
    var a = array
    for x in 1..&lt;a.count {
        var y = x
        let temp = a[y]
        while y &gt; 0, isOrderedBefore(temp, a[y - 1])  {
            a[y] = a[y - 1]
            y -= 1
        }
        a[y] = temp
    }

    return a
}
</code></pre><p>通过闭包来执行大小比较。</p>
<pre tabindex="0"><code>let numbers = [ 10, -1, 3, 9, 2, 27, 8, 5, 1, 3, 0, 26 ]
insertionSort(numbers, &lt;)

let objects = [ obj1, obj2, obj3, ... ]
insertionSort(objects) { $0.priority &lt; $1.priority }
</code></pre><p>插入排序是一种稳定 <code>stable</code> 的排序。当排序后具有相同排序键的元素保持相同的相对顺序时，排序是稳定的。这对于诸如数字或字符串之类的简单值并不重要，但在排序更复杂的对象时这很重要。在上面的示例中，如果两个对象具有相同的优先级，则无论其他属性的值如何，这两个对象都不会被交换。</p>
<h2 id="performance">Performance</h2>
<p>最差的插入排序是 <code>O(n^2)</code> 因为俩个相近的循环嵌套。其他排序算法（如快速排序和合并排序）具有 <code>O(n log n)</code> 性能，在大输入时速度更快。</p>
<p>插入排序实际上对于排序小数组非常快。某些标准库具有排序功能，当分区大小为 <code>10</code> 或更小时，可以从快速排序切换到插入排序。</p>
<p>将 <code>insertSort()</code> 与 Swift 的内置 <code>sort()</code> 进行比较。在大约 <code>100</code> 元素左右的阵列上，速度差异很小。但是，随着输入变大，<code>O(n^2)</code> 快速开始执行比 <code>O(n log n)</code> 差很多，并且插入排序无法跟上。</p>
<p>文章代码：<a href="https://github.com/imzyf/data-structure-and-algorithm/tree/master/003-Insertion%20Sort">GitHub - imzyf/data-structure-and-algorithm/003-Insertion Sort/</a>。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/raywenderlich/swift-algorithm-club/tree/master/Insertion%20Sort">raywenderlich/swift-algorithm-club/Insertion Sort</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>队列 Queue Data Structure</title>
      <link>https://zyf.im/2018/11/22/queue-data-structure/</link>
      <pubDate>Thu, 22 Nov 2018 16:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/11/22/queue-data-structure/</guid>
      <description>&lt;p&gt;实现一个 &lt;code&gt;队列&lt;/code&gt;，包括 &lt;code&gt;enqueue&lt;/code&gt;、&lt;code&gt;dequeue&lt;/code&gt;、&lt;code&gt;peek&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;queue&#34;&gt;Queue&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;队列&lt;/code&gt; 核心也是 array，A queue gives you a FIFO or first-in, first-out order. 队列是：先进先出的。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Queue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;fileprivate&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- more --&gt;
&lt;h2 id=&#34;enqueue&#34;&gt;enqueue&lt;/h2&gt;
&lt;p&gt;进队，在数组尾部追加元素。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;mutating&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;enqueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;dequeue&#34;&gt;dequeue&lt;/h2&gt;
&lt;p&gt;出队，将首位的元素移除。因为首位元素移除后，其他元素依次向前移动，所以是 O(n)。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;isEmpty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Bool&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 使用数组自身的方法，而不是 array.count &amp;gt; 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;isEmpty&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;mutating&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dequeue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 使用定义的变量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;isEmpty&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;removeLast&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;peek&#34;&gt;peek&lt;/h2&gt;
&lt;p&gt;查看队首元素。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;/// peek() 改为更有语义话的只读变量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;front&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;first&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;优化出队&#34;&gt;优化出队&lt;/h2&gt;
&lt;p&gt;在出队后不移动元素而是移动 &lt;code&gt;起始索引&lt;/code&gt;，就像动的收银台而不是排队的人。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;/// 优化 队列 的出队&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;OptimizedQueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;/// 这里改为了可选型，为了可以清理无效的元素&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;fileprivate&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?]()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;/// 起始索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;fileprivate&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 减去 起始索引 前面的数量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;isEmpty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Bool&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 根据实际数量判断&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 保持不变&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;mutating&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;enqueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;mutating&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dequeue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;guard&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;element&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 置空当前位置元素&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 前移起始索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 空索引的占用比例&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;percentage&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Double&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Double&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 50 0.25 都是魔法数字，主要是为了控制数组修剪的频率，可以自行调整&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;percentage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.25&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 将起始空元素删除&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;removeFirst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 重置 起始索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;element&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;front&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;isEmpty&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 根据 起始索引进行 返回&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;文章代码：&lt;a href=&#34;https://github.com/imzyf/data-structure-and-algorithm/tree/master/002-Queue&#34;&gt;GitHub - imzyf/data-structure-and-algorithm/002-Queue/&lt;/a&gt;。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>实现一个 <code>队列</code>，包括 <code>enqueue</code>、<code>dequeue</code>、<code>peek</code>。</p>
<h2 id="queue">Queue</h2>
<p><code>队列</code> 核心也是 array，A queue gives you a FIFO or first-in, first-out order. 队列是：先进先出的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">struct</span> <span class="nc">Queue</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">fileprivate</span> <span class="kd">var</span> <span class="nv">array</span> <span class="p">=</span> <span class="p">[</span><span class="n">T</span><span class="p">]()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><!-- more -->
<h2 id="enqueue">enqueue</h2>
<p>进队，在数组尾部追加元素。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kr">mutating</span> <span class="kd">func</span> <span class="nf">enqueue</span><span class="p">(</span><span class="kc">_</span> <span class="n">element</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="dequeue">dequeue</h2>
<p>出队，将首位的元素移除。因为首位元素移除后，其他元素依次向前移动，所以是 O(n)。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">var</span> <span class="nv">isEmpty</span><span class="p">:</span> <span class="nb">Bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 使用数组自身的方法，而不是 array.count &gt; 0</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">array</span><span class="p">.</span><span class="bp">isEmpty</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kr">mutating</span> <span class="kd">func</span> <span class="nf">dequeue</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="n">T</span><span class="p">?</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 使用定义的变量</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="bp">isEmpty</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">array</span><span class="p">.</span><span class="bp">removeLast</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="peek">peek</h2>
<p>查看队首元素。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">/// peek() 改为更有语义话的只读变量</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">var</span> <span class="nv">front</span><span class="p">:</span> <span class="n">T</span><span class="p">?</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">array</span><span class="p">.</span><span class="bp">first</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="优化出队">优化出队</h2>
<p>在出队后不移动元素而是移动 <code>起始索引</code>，就像动的收银台而不是排队的人。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">/// 优化 队列 的出队</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">struct</span> <span class="nc">OptimizedQueue</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">/// 这里改为了可选型，为了可以清理无效的元素</span>
</span></span><span class="line"><span class="cl">    <span class="n">fileprivate</span> <span class="kd">var</span> <span class="nv">array</span> <span class="p">=</span> <span class="p">[</span><span class="n">T</span><span class="p">?]()</span>
</span></span><span class="line"><span class="cl">    <span class="c1">/// 起始索引</span>
</span></span><span class="line"><span class="cl">    <span class="n">fileprivate</span> <span class="kd">var</span> <span class="nv">head</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">var</span> <span class="nv">count</span><span class="p">:</span> <span class="nb">Int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 减去 起始索引 前面的数量</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">array</span><span class="p">.</span><span class="bp">count</span> <span class="o">-</span> <span class="n">head</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">var</span> <span class="nv">isEmpty</span><span class="p">:</span> <span class="nb">Bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 根据实际数量判断</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">count</span> <span class="p">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 保持不变</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kr">mutating</span> <span class="kd">func</span> <span class="nf">enqueue</span><span class="p">(</span><span class="kc">_</span> <span class="n">element</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">array</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kr">mutating</span> <span class="kd">func</span> <span class="nf">dequeue</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="n">T</span><span class="p">?</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">guard</span> <span class="n">head</span> <span class="o">&lt;</span> <span class="n">array</span><span class="p">.</span><span class="bp">count</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="kd">let</span> <span class="nv">element</span> <span class="p">=</span> <span class="n">array</span><span class="p">[</span><span class="n">head</span><span class="p">]</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 置空当前位置元素</span>
</span></span><span class="line"><span class="cl">        <span class="n">array</span><span class="p">[</span><span class="n">head</span><span class="p">]</span> <span class="p">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 前移起始索引</span>
</span></span><span class="line"><span class="cl">        <span class="n">head</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 空索引的占用比例</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nv">percentage</span> <span class="p">=</span> <span class="nb">Double</span><span class="p">(</span><span class="n">head</span><span class="p">)</span><span class="o">/</span><span class="nb">Double</span><span class="p">(</span><span class="n">array</span><span class="p">.</span><span class="bp">count</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 50 0.25 都是魔法数字，主要是为了控制数组修剪的频率，可以自行调整</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">array</span><span class="p">.</span><span class="bp">count</span> <span class="o">&gt;</span> <span class="mi">50</span> <span class="o">&amp;&amp;</span> <span class="n">percentage</span> <span class="o">&gt;</span> <span class="mf">0.25</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 将起始空元素删除</span>
</span></span><span class="line"><span class="cl">            <span class="n">array</span><span class="p">.</span><span class="n">removeFirst</span><span class="p">(</span><span class="n">head</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 重置 起始索引</span>
</span></span><span class="line"><span class="cl">            <span class="n">head</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">element</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">var</span> <span class="nv">front</span><span class="p">:</span> <span class="n">T</span><span class="p">?</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="bp">isEmpty</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 根据 起始索引进行 返回</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">array</span><span class="p">[</span><span class="n">head</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>文章代码：<a href="https://github.com/imzyf/data-structure-and-algorithm/tree/master/002-Queue">GitHub - imzyf/data-structure-and-algorithm/002-Queue/</a>。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/raywenderlich/swift-algorithm-club/tree/master/Queue">raywenderlich/swift-algorithm-club/Queue</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>栈 Stack Data Structure</title>
      <link>https://zyf.im/2018/11/22/stack-data-structure/</link>
      <pubDate>Thu, 22 Nov 2018 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/11/22/stack-data-structure/</guid>
      <description>&lt;p&gt;加入 &lt;a href=&#34;https://github.com/raywenderlich/swift-algorithm-club&#34;&gt;Swift Algorithm Club&lt;/a&gt; /&amp;lsquo;ælgə&amp;rsquo;rɪðəm/，回炉重新学习数据结构与算法。&lt;/p&gt;
&lt;p&gt;自己创建的项目：&lt;a href=&#34;https://github.com/imzyf/data-structure-and-algorithm&#34;&gt;GitHub - imzyf/data-structure-and-algorithm&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;实现一个 &lt;code&gt;栈&lt;/code&gt; /stæk/，包含 &lt;code&gt;push&lt;/code&gt; &lt;code&gt;peek&lt;/code&gt; &lt;code&gt;pop&lt;/code&gt; 与 &lt;code&gt;Generics&lt;/code&gt; 泛型。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;stack&#34;&gt;stack&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;栈&lt;/code&gt; 非常像一个数组，它包括少量的方法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;push 添加一个新元素到栈顶&lt;/li&gt;
&lt;li&gt;pop 从栈顶移除一个元素&lt;/li&gt;
&lt;li&gt;peek 查看栈顶的一个元素但是不 pop&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A stack gives you a LIFO or last-in first-out order. 栈是后进先出，队列是先进先出。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public struct Stack&amp;lt;Element&amp;gt; {
    fileprivate var array: [Element] = []
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;push&#34;&gt;push&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;push&lt;/code&gt; 是在数组的尾部添加元素是以 &lt;code&gt;O(1)&lt;/code&gt;，如果是在数组最前添加是 &lt;code&gt;O(n)&lt;/code&gt; 这是昂贵的。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public mutating func push(_ element: Element) {
  array.append(element)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;因为使用的 &lt;code&gt;struct&lt;/code&gt;，修改属性值的方法要加 &lt;code&gt;mutating&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;pop&#34;&gt;pop&lt;/h2&gt;
&lt;p&gt;想从一个空栈中弹出最后一个元素将返回 &lt;code&gt;nil&lt;/code&gt;。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>加入 <a href="https://github.com/raywenderlich/swift-algorithm-club">Swift Algorithm Club</a> /&lsquo;ælgə&rsquo;rɪðəm/，回炉重新学习数据结构与算法。</p>
<p>自己创建的项目：<a href="https://github.com/imzyf/data-structure-and-algorithm">GitHub - imzyf/data-structure-and-algorithm</a>。</p>
<p>实现一个 <code>栈</code> /stæk/，包含 <code>push</code> <code>peek</code> <code>pop</code> 与 <code>Generics</code> 泛型。</p>
<!-- more -->
<h2 id="stack">stack</h2>
<p><code>栈</code> 非常像一个数组，它包括少量的方法。</p>
<ul>
<li>push 添加一个新元素到栈顶</li>
<li>pop 从栈顶移除一个元素</li>
<li>peek 查看栈顶的一个元素但是不 pop</li>
</ul>
<p>A stack gives you a LIFO or last-in first-out order. 栈是后进先出，队列是先进先出。</p>
<pre tabindex="0"><code>public struct Stack&lt;Element&gt; {
    fileprivate var array: [Element] = []
}
</code></pre><h2 id="push">push</h2>
<p><code>push</code> 是在数组的尾部添加元素是以 <code>O(1)</code>，如果是在数组最前添加是 <code>O(n)</code> 这是昂贵的。</p>
<pre tabindex="0"><code>public mutating func push(_ element: Element) {
  array.append(element)
}
</code></pre><p>因为使用的 <code>struct</code>，修改属性值的方法要加 <code>mutating</code>。</p>
<h2 id="pop">pop</h2>
<p>想从一个空栈中弹出最后一个元素将返回 <code>nil</code>。</p>
<pre tabindex="0"><code>public mutating func pop(_ element: Element) {
    return array.popLast()
}
</code></pre><h2 id="peek">peek</h2>
<p>与 <code>pop</code> 有点像，但是并没有移除栈顶的元素。</p>
<pre tabindex="0"><code>/// peek 改为更加语义化的 top 只读变量
public var top: T? {
    return array.last
}
</code></pre><h2 id="other">other</h2>
<p>两个其他的常用属性，栈是否为空，栈中元素的个数。</p>
<pre tabindex="0"><code>public var isEmpty: Bool {
  return array.isEmpty
}

public var count: Int {
  return array.count
}
</code></pre><h2 id="references">References</h2>
<ul>
<li><a href="https://www.raywenderlich.com/800-swift-algorithm-club-swift-stack-data-structure">Swift Algorithm Club: Swift Stack Data Structure</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>在 MySQL 中选择合适的日期类型</title>
      <link>https://zyf.im/2018/05/25/select-the-appropriate-date-type-in-mysql/</link>
      <pubDate>Fri, 25 May 2018 16:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/05/25/select-the-appropriate-date-type-in-mysql/</guid>
      <description>&lt;p&gt;如何在 MySQL 中选择合适的日期类型困扰了很久，&lt;code&gt;varchar&lt;/code&gt;、&lt;code&gt;int&lt;/code&gt;、&lt;code&gt;timestamp&lt;/code&gt;、&lt;code&gt;datetime&lt;/code&gt; 都有尝试过，近来有所感悟，做此总结。&lt;/p&gt;
&lt;p&gt;注：此总结考虑了 PHP 和 Laravel 框架的特点。&lt;/p&gt;
&lt;h2 id=&#34;使用-varchar&#34;&gt;使用 varchar&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;varchar&lt;/code&gt; 存储日期时间的格式完全可以自己控制，&lt;code&gt;月/日/年&lt;/code&gt; 还是 &lt;code&gt;年-月-日&lt;/code&gt; 需求怎么说就怎么存，读取后展示是也不用在格式化。同时伏笔也就此埋下：日期时间格式没强制约束，总有一天字段里出现了与众不同的格式；要是日期时间会 &lt;em&gt;变化&lt;/em&gt; 或作为 &lt;em&gt;查询条件&lt;/em&gt; 或要进行 &lt;em&gt;排序&lt;/em&gt; 时就又是一坑，还是要格式化标准格式再处理。可以说 &lt;code&gt;varchar&lt;/code&gt; 应该是最差的选择了。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;使用-int-与-timestamp&#34;&gt;使用 int 与 timestamp&lt;/h2&gt;
&lt;p&gt;PHP &lt;code&gt;time()&lt;/code&gt; 可以直接获取当前时间戳秒数，数据库字段要也是 &lt;code&gt;int&lt;/code&gt; 一存就完事了，不会有格式问题，谁用什么样转什么样。但是在数据库工具中查看此字段时显示不够直观，范围时会不方便，这些在使用 &lt;code&gt;timestamp&lt;/code&gt; 是会得到解决。&lt;/p&gt;
&lt;p&gt;timestamp 是我一直迷惑的一个类型。我写了几个例子做测试：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将 Laravel 项目设置为 &lt;code&gt;CST&lt;/code&gt; 中国标准时间，MySQL 时区设置为 &lt;code&gt;UTC&lt;/code&gt;，使用 &lt;code&gt;now()&lt;/code&gt; 获取当前日期时间，比如：&lt;code&gt;2018-5-25 11:00:00&lt;/code&gt; 存入 &lt;code&gt;timestamp&lt;/code&gt; 类型的字段中，使用数据库工具查看字段结果为仍然为 &lt;code&gt;2018-5-25 11:00:00&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;继续上面的操作，项目中使用查询语句查询刚才的记录，结果显示为 &lt;code&gt;2018-5-25 11:00:00&lt;/code&gt;，将项目时区从 &lt;code&gt;CST&lt;/code&gt; 改为 &lt;code&gt;UTC&lt;/code&gt; 后再次查询的结果仍然为 &lt;code&gt;2018-5-25 11:00:00&lt;/code&gt; 没有变化。&lt;/li&gt;
&lt;li&gt;继续上面的操作，将数据库的时区改为 &lt;code&gt;+8:00&lt;/code&gt;，数据库工具、项目查询后的结果为 &lt;code&gt;2018-5-25 19:00:00&lt;/code&gt; 发生了变化，修改项目为 &lt;code&gt;CST&lt;/code&gt; 查询结果是 &lt;code&gt;2018-5-25 19:00:00&lt;/code&gt; 和刚才一样也变化了。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个测试说明了：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>如何在 MySQL 中选择合适的日期类型困扰了很久，<code>varchar</code>、<code>int</code>、<code>timestamp</code>、<code>datetime</code> 都有尝试过，近来有所感悟，做此总结。</p>
<p>注：此总结考虑了 PHP 和 Laravel 框架的特点。</p>
<h2 id="使用-varchar">使用 varchar</h2>
<p><code>varchar</code> 存储日期时间的格式完全可以自己控制，<code>月/日/年</code> 还是 <code>年-月-日</code> 需求怎么说就怎么存，读取后展示是也不用在格式化。同时伏笔也就此埋下：日期时间格式没强制约束，总有一天字段里出现了与众不同的格式；要是日期时间会 <em>变化</em> 或作为 <em>查询条件</em> 或要进行 <em>排序</em> 时就又是一坑，还是要格式化标准格式再处理。可以说 <code>varchar</code> 应该是最差的选择了。</p>
<!-- more -->
<h2 id="使用-int-与-timestamp">使用 int 与 timestamp</h2>
<p>PHP <code>time()</code> 可以直接获取当前时间戳秒数，数据库字段要也是 <code>int</code> 一存就完事了，不会有格式问题，谁用什么样转什么样。但是在数据库工具中查看此字段时显示不够直观，范围时会不方便，这些在使用 <code>timestamp</code> 是会得到解决。</p>
<p>timestamp 是我一直迷惑的一个类型。我写了几个例子做测试：</p>
<ol>
<li>将 Laravel 项目设置为 <code>CST</code> 中国标准时间，MySQL 时区设置为 <code>UTC</code>，使用 <code>now()</code> 获取当前日期时间，比如：<code>2018-5-25 11:00:00</code> 存入 <code>timestamp</code> 类型的字段中，使用数据库工具查看字段结果为仍然为 <code>2018-5-25 11:00:00</code>。</li>
<li>继续上面的操作，项目中使用查询语句查询刚才的记录，结果显示为 <code>2018-5-25 11:00:00</code>，将项目时区从 <code>CST</code> 改为 <code>UTC</code> 后再次查询的结果仍然为 <code>2018-5-25 11:00:00</code> 没有变化。</li>
<li>继续上面的操作，将数据库的时区改为 <code>+8:00</code>，数据库工具、项目查询后的结果为 <code>2018-5-25 19:00:00</code> 发生了变化，修改项目为 <code>CST</code> 查询结果是 <code>2018-5-25 19:00:00</code> 和刚才一样也变化了。</li>
</ol>
<p>这个测试说明了：</p>
<ul>
<li>项目的时区影响的是 PHP 的时区，影响的是 <code>now()</code> 产生的日期时间。</li>
<li>一个日期时间从项目存入数据库时，这个日期时间的时区是数据库设置的时区，和项目无关了。传入什么样子数据库存什么样子，更换了参照系，但是没有转换日期时间。</li>
<li>从数据库中读一个时间戳日期时间，这个是时间戳日期时间受到数据库的时区影响，和项目的时区无关。</li>
</ul>
<p>我现在认识的结论：</p>
<ul>
<li>因为数据库时区不可能轻易改变，所以依靠数据库转换时区不可能。</li>
<li>没看出来时间戳对于处理时区对 <code>datatime</code> 有什么过人之处，若有就是时间戳将时间标准设置在了 <code>UTC</code>，占 4 字节更小些。</li>
<li>数据库时区和项目时区不一致可能会坑，如果一直不一致同时各个项目也一直错下去，没不会有察觉。</li>
<li>对应国际化项目，是不是应该将数据库和项目还有服务器都设置为 <code>UTC</code>？谁使用谁根据用户的时区进行处理。</li>
</ul>
<h2 id="使用-timestamp-与-datetime">使用 timestamp 与 datetime</h2>
<p><code>datetime</code> 字段类型存储的时间不会随时间库时区发生变化，占 8 个字节，可存储 <code>1000-01-01 00:00:00</code> 到 <code>9999-12-31 23:59:59</code>。而时间戳只能存储 <code>1970</code> 年到 <code>2038</code> 年多，<code>2038</code> 还有 20 年怎么感觉马上就到了。</p>
<p><code>datetime</code> 个人推荐的存储时间的格式。</p>
<h2 id="时区处理常见问题">时区处理常见问题</h2>
<p>北京用户在北京时间 <code>2018-05-27 10:00:00</code> 发布一个文章。</p>
<p>如果在前端发布日期时间为 <code>2018-05-27 10:00:00</code>，在北京用户看来是没问题的，但是在美国的用户看来是奇怪的，因为美国没有到 <code>2018-05-27 10:00:00</code> 他们甚至还在 26 日。</p>
<p>这时 <code>1 min ago</code> 这种展示形式就解决了这个问题，<code>ago</code> 是一种绝对的方式，在一个月之后再显示完整的日期时间，此时世界各地肯定已经度过了这个要展示日期时间，无论在哪国用户显示 <code>2018-05-27 10:00:00</code> 都不会奇怪了。</p>
<p>同时也要注意，我们将各个时区都设置为 <code>UTC</code> 后，<code>ago</code> 的时间很好计算，但是在一个月之后日前时间完全显示，美国用户看北京用户的发布日期时间也应该是在中国看来的 <code>2018-05-27 10:00:00</code>，此发布日期不应该以美国用户为标准。这个问题的解决方法还在考虑，日期时间是存储多个字段来区分用于显示或比较，还是自行转换处理，需要实践测试。</p>
<h2 id="mysql-查看修改时区">MySQL 查看、修改时区</h2>
<h3 id="查看时区">查看时区</h3>
<pre tabindex="0"><code>&gt; select now();   # 或 select curtime();

&gt; show variables like &#34;%time_zone%&#34;; # SELECT @@global.time_zone, @@session.time_zone;
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | CST    |
| time_zone        | SYSTEM |
+------------------+--------+

# time_zone 说明 mysql 使用 system 的时区，system_time_zone 说明 system 使用 CST 时区
</code></pre><h3 id="修改时区">修改时区</h3>
<pre tabindex="0"><code>&gt; set global time_zone = &#39;+8:00&#39;;  # 修改 mysql 全局时区为北京时间，即我们所在的东8区
&gt; set time_zone = &#39;+8:00&#39;;  # 修改当前会话时区 SET time_zone = &#39;Asia/Shanghai&#39;
&gt; flush privileges;  # 立即生效
</code></pre><p>通过修改 <code>my.cnf</code> 配置文件来修改时区</p>
<pre tabindex="0"><code># vim /etc/my.cnf
# 在[mysqld]区域中加上
default-time-zone=&#39;+8:00&#39;

# /etc/init.d/mysqld restart  # 重启 mysql 使新时区生效
</code></pre><h2 id="最后的总结">最后的总结</h2>
<p>全球化的项目应当考虑：</p>
<ul>
<li>项目、数据库、服务器都建议设置为 <code>UTC</code></li>
<li>要考虑时分秒的字段用 <code>datetime</code> 类型，不用考虑时分秒的字段用 <code>date</code></li>
<li><code>created_at</code> 和 <code>updated_at</code> 字段，考虑 <code>Laravel</code> 框架的特点使用 <code>timestamp</code></li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="http://coolnull.com/4091.html">mysql 修改时区的几种方法</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>【Modern PHP】笔记</title>
      <link>https://zyf.im/2018/05/08/modern-php-reading-notes/</link>
      <pubDate>Tue, 08 May 2018 17:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/05/08/modern-php-reading-notes/</guid>
      <description>&lt;p&gt;又回到 PHP Web 开发，使用 Laravel 框架，重读《Modern PHP》。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PHP 正在重生。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;特性&#34;&gt;特性&lt;/h2&gt;
&lt;h3 id=&#34;命名空间&#34;&gt;命名空间&lt;/h3&gt;
&lt;p&gt;声明命名空间：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;namespace&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Oreilly\ModernPHP&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;导入和别名：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Symfony\Component\HttpFoundation\Response&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Oops&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;400&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$r&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;PHP 5.6 开始可以导入函数和常量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;Namespace&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;\functionName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;constant&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;Namespace&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;\CONST_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;functionName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CONST_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;使用接口&#34;&gt;使用接口&lt;/h3&gt;
&lt;p&gt;接口是两个 PHP 对象之间的契约，其目的不是让一个对象依赖另一个对象的身份，而是依赖另一个对象的能力。&lt;/p&gt;
&lt;p&gt;使用接口编写更加灵活，能委托别人实现细节。&lt;/p&gt;
&lt;h3 id=&#34;性状-trait&#34;&gt;性状 trait&lt;/h3&gt;
&lt;p&gt;性状是类的部分实现，可以混入一个或者多个现有的 PHP 类中。性状有两个作用：表明类可以做什么（像是接口）；提供模块化实现（像是类）。&lt;/p&gt;
&lt;p&gt;如果想让两个无关的 PHP 类具有类似的行为，应该怎么呢？性状就是为了解决这种问题而诞生的。性状能把模块化的实现方式注入多个无关的类中。而且性状还能促进代码的重用。&lt;/p&gt;
&lt;p&gt;这与创建一个接口，两个无关的类实现这个接口的优势在于：不用写相同的实现代码，符合 DRY 原则。&lt;/p&gt;
&lt;p&gt;PHP 解释器在编译时会把性状复制粘贴到类的定义体中，但是不会处理这个操作引入的不兼容问题。如果性状假定类中有特定的属性和方法（在性状中没有定义），要确保相应的类中有对应的属性和方法。&lt;/p&gt;
&lt;h3 id=&#34;生成器&#34;&gt;生成器&lt;/h3&gt;
&lt;p&gt;Generator 是 PHP 5.5.0 引入的功能。生成器是简单的迭代器，仅此而已。&lt;/p&gt;
&lt;p&gt;PHP 生成器不要求类实现 Iterator 接口，从而减轻了类的负担。生成器会根据需求计算并产生要迭代的值。这对应该的性能有重大影响。假如标准的 PHP 迭代器经常在内存中执行迭代操作，这要预先计算出数据集，性能低；此时我们可以使用生成器，即时计算并产出后续值，不占用宝贵的内存资源。&lt;/p&gt;
&lt;p&gt;PHP 生成器不能满足所有迭代操作的需求，因为如果不查询，生成器永远不知道下一个要迭代的值是什么，在生成器中无法后退和快进。生成器还是一次性，无法多次迭代同一个生成器。不过，如果需要，可以重建或克隆生成器。&lt;/p&gt;
&lt;p&gt;PHP 生成器是 PHP 函数，只不过要在函数中一次或者多次使用 yield 关键字。生成器从不返回值，值产出值。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;myGenerator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;value1&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;value2&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;value3&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;myGenerator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$yieldedValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$yieldedValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PHP_EOL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;value1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;value2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;value3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用生成器处理 CSV：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>又回到 PHP Web 开发，使用 Laravel 框架，重读《Modern PHP》。</p>
<blockquote>
<p>PHP 正在重生。</p>
</blockquote>
<h2 id="特性">特性</h2>
<h3 id="命名空间">命名空间</h3>
<p>声明命名空间：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nx">Oreilly\ModernPHP</span><span class="p">;</span>
</span></span></code></pre></div><p>导入和别名：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">Symfony\Component\HttpFoundation\Response</span> <span class="k">as</span> <span class="nx">Res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$r</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Res</span><span class="p">(</span><span class="s1">&#39;Oops&#39;</span><span class="p">,</span> <span class="mi">400</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nv">$r</span><span class="o">-&gt;</span><span class="na">send</span><span class="p">();</span>
</span></span></code></pre></div><p>PHP 5.6 开始可以导入函数和常量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">func</span> <span class="k">Namespace</span><span class="nx">\functionName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">constant</span> <span class="k">Namespace</span><span class="nx">\CONST_NAME</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">functionName</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">CONST_NAME</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="使用接口">使用接口</h3>
<p>接口是两个 PHP 对象之间的契约，其目的不是让一个对象依赖另一个对象的身份，而是依赖另一个对象的能力。</p>
<p>使用接口编写更加灵活，能委托别人实现细节。</p>
<h3 id="性状-trait">性状 trait</h3>
<p>性状是类的部分实现，可以混入一个或者多个现有的 PHP 类中。性状有两个作用：表明类可以做什么（像是接口）；提供模块化实现（像是类）。</p>
<p>如果想让两个无关的 PHP 类具有类似的行为，应该怎么呢？性状就是为了解决这种问题而诞生的。性状能把模块化的实现方式注入多个无关的类中。而且性状还能促进代码的重用。</p>
<p>这与创建一个接口，两个无关的类实现这个接口的优势在于：不用写相同的实现代码，符合 DRY 原则。</p>
<p>PHP 解释器在编译时会把性状复制粘贴到类的定义体中，但是不会处理这个操作引入的不兼容问题。如果性状假定类中有特定的属性和方法（在性状中没有定义），要确保相应的类中有对应的属性和方法。</p>
<h3 id="生成器">生成器</h3>
<p>Generator 是 PHP 5.5.0 引入的功能。生成器是简单的迭代器，仅此而已。</p>
<p>PHP 生成器不要求类实现 Iterator 接口，从而减轻了类的负担。生成器会根据需求计算并产生要迭代的值。这对应该的性能有重大影响。假如标准的 PHP 迭代器经常在内存中执行迭代操作，这要预先计算出数据集，性能低；此时我们可以使用生成器，即时计算并产出后续值，不占用宝贵的内存资源。</p>
<p>PHP 生成器不能满足所有迭代操作的需求，因为如果不查询，生成器永远不知道下一个要迭代的值是什么，在生成器中无法后退和快进。生成器还是一次性，无法多次迭代同一个生成器。不过，如果需要，可以重建或克隆生成器。</p>
<p>PHP 生成器是 PHP 函数，只不过要在函数中一次或者多次使用 yield 关键字。生成器从不返回值，值产出值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">myGenerator</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">yield</span> <span class="s1">&#39;value1&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">yield</span> <span class="s1">&#39;value2&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">yield</span> <span class="s1">&#39;value3&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nx">myGenerator</span><span class="p">()</span>  <span class="k">as</span> <span class="nv">$yieldedValue</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="k">echo</span> <span class="nv">$yieldedValue</span><span class="p">,</span> <span class="nx">PHP_EOL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">value1</span>
</span></span><span class="line"><span class="cl"><span class="nx">value2</span>
</span></span><span class="line"><span class="cl"><span class="nx">value3</span>
</span></span></code></pre></div><p>使用生成器处理 CSV：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">getRows</span><span class="p">(</span><span class="nv">$file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$handle</span> <span class="o">=</span> <span class="nx">fopen</span><span class="p">(</span><span class="nv">$file</span><span class="p">,</span> <span class="s1">&#39;rb&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nv">$handle</span> <span class="o">===</span> <span class="k">false</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">throw</span> <span class="k">new</span> <span class="nx">Exception</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="nx">feof</span><span class="p">(</span><span class="nv">$handle</span><span class="p">)</span> <span class="o">===</span> <span class="k">false</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">yield</span> <span class="nx">fgetcsv</span><span class="p">(</span><span class="nv">$handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fclose</span><span class="p">(</span><span class="nv">$handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nx">getRows</span><span class="p">(</span><span class="s1">&#39;data.csv&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="nv">$row</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="nx">print_r</span><span class="p">(</span><span class="nv">$row</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="闭包">闭包</h3>
<p>理论上讲，闭包和匿名函数是不同的概念。不过，PHP 将其视作相同的概念。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$closure</span> <span class="o">=</span> <span class="k">function</span> <span class="p">(</span><span class="nv">$name</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">sprintf</span><span class="p">(</span><span class="s1">&#39;Hello %s&#39;</span><span class="p">,</span> <span class="nv">$name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$closure</span><span class="p">(</span><span class="s2">&#34;Josh&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>我们之所以能调用 $closure 变量，是因为这个变量的值是一个闭包，而且闭包对象实现了 <code>__invoke()</code> 魔术方法。只要变量名后有()，PHP 就会查找并调用 <code>__invoke()</code> 方法。</p>
<p>PHP 闭包常被当做函数和方法的回调使用。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$numbersPlusOne</span> <span class="o">=</span> <span class="nx">array_map</span><span class="p">(</span><span class="k">function</span> <span class="p">(</span><span class="nv">$number</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$number</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">print_r</span><span class="p">(</span><span class="nv">$numbersPlusOne</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// [2,3,4]
</span></span></span></code></pre></div><p>在有闭包之前，只能单独创建具名函数，然后使用名称引用那个函数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$numbersPlusOne</span> <span class="o">=</span> <span class="nx">array_map</span><span class="p">(</span><span class="s1">&#39;incrementNumber&#39;</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 如果只需要使用一次回调，没必要单独定义。把闭包当成回调使用，写出的代码更整洁、更清晰。
</span></span></span></code></pre></div><p>使用 use 关键字附加闭包状态：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">enclosePerson</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">function</span> <span class="p">(</span><span class="nv">$doCommand</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$name</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">sprintf</span><span class="p">(</span><span class="s1">&#39;%s, %s&#39;</span><span class="p">,</span> <span class="nv">$name</span><span class="p">,</span> <span class="nv">$doCommand</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 把字符串 Clay 封装到闭包里
</span></span></span><span class="line"><span class="cl"><span class="nv">$clay</span> <span class="o">=</span> <span class="nx">enclosePerson</span><span class="p">(</span><span class="s1">&#39;Clay&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 传入参数，调用闭包
</span></span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nv">$clay</span><span class="p">(</span><span class="s1">&#39;get me sweet tea!&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;Clay, get me sweet tea!&#34;
</span></span></span></code></pre></div><p>具名函数 enclosePerson() 有个名为 $name 的参数，这个函数返回一个闭包对象，而且这个闭包封装了 $name 参数。即便返回的闭包对象跳出了 enclosePerson() 函数的作用域，它也会记住 $name 参数的值，因为 $name 变量仍在闭包中。</p>
<p>PHP 闭包是对象。闭包对象的默认状态没什么用，不过有一个 <code>__invoke()</code> 魔术方法和 <code>bindTo()</code> 方法。</p>
<h3 id="zend-opcache">Zend OPcache</h3>
<p>字节码缓存能存储预先编译好的 PHP 字节码。这意味着，请求 PHP 脚本时，PHP 解释器不用每次都读取、解析和编译 PHP 代码。</p>
<h3 id="内置的-http-服务器">内置的 HTTP 服务器</h3>
<p>启动这个服务器：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">php</span> <span class="o">-</span><span class="nx">S</span> <span class="nx">localhost</span><span class="o">:</span><span class="mi">4000</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 让 PHP Web 服务器监听所有接口
</span></span></span><span class="line"><span class="cl"><span class="nx">php</span> <span class="o">-</span><span class="nx">S</span> <span class="mf">0.0</span><span class="o">.</span><span class="mf">0.0</span><span class="o">:</span><span class="mi">4000</span>
</span></span></code></pre></div><h2 id="标准">标准</h2>
<h3 id="psr-是什么">PSR 是什么</h3>
<p>PHP Standards Recommendation.</p>
<ul>
<li>PSR-1 基本的代码风格</li>
<li>PSR-2 严格的代码风格</li>
<li>PSR-3 日志记录器接口</li>
<li>PSR-4 自动加载</li>
</ul>
<h2 id="组件">组件</h2>
<h3 id="查找组件">查找组件</h3>
<ul>
<li><a href="https://github.com/ziadoz/awesome-php#text-editors-and-ides">Awesome PHP</a></li>
<li><a href="https://packagist.org">Packagist</a></li>
</ul>
<h2 id="良好实践">良好实践</h2>
<h3 id="流">流</h3>
<p>流式数据的种类各异，每种类型需要独特的协议，以便读写数据。称这些协议为流封装协议。</p>
<ol>
<li>开始通信</li>
<li>读取数据</li>
<li>写入数据</li>
<li>结束通信</li>
</ol>
<p>指定协议和目标的方法是使用流标识符：</p>
<p><code>&lt;scheme&gt;://&lt;target&gt;</code></p>
<p>使用 HTTP 流封装协议创建了一个与 Flickr API 通信的 PHP 流：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl"><span class="nv">$json</span> <span class="o">=</span> <span class="nx">file_get_contents</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;http://api.flickr.com/services/feeds/photos_public.gne?format=json&#39;</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><p>不要误以为这是普通的网页 URL，file_get_contents() 函数的字符串参数其实是一个流标识符。http 协议会让 PHP 使用 HTTP 流封装协议。在这个参数中，http 之后是流的目标。很多 PHP 开发者不知道普通的 URL 其实是 PHP 流封装协议标识的伪装。</p>
<p>我们使用 file_get_contents() fopen() fwrite() 和 fclose() 函数读写文件系统。因为 PHP 默认使用的流封装协议是 file://，使用我们很少认为这些函数使用的是 PHP 流。</p>
<p>隐式使用 file:// 流封装协议：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$handle</span> <span class="o">=</span> <span class="nx">fopen</span><span class="p">(</span><span class="s1">&#39;/etc/hosts&#39;</span><span class="p">,</span> <span class="s1">&#39;rb&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">(</span><span class="nx">feof</span><span class="p">(</span><span class="nv">$handle</span><span class="p">)</span> <span class="o">!==</span> <span class="nx">ture</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">echo</span> <span class="nx">fgets</span><span class="p">(</span><span class="nv">$handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">fclose</span><span class="p">(</span><span class="nv">$handle</span><span class="p">);</span>
</span></span></code></pre></div><p>显示使用 file:// 流封装协议：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="nv">$handle</span> <span class="o">=</span> <span class="nx">fopen</span><span class="p">(</span><span class="s1">&#39;file://etc/hosts&#39;</span><span class="p">,</span> <span class="s1">&#39;rb&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span></code></pre></div><p>我们通常会省略 file:// 封装协议，这是 PHP 使用的默认值。</p>
<p>编写命令行脚本的 PHP 开发者会感激 php:// 流封装协议。这个流封装协议的作用是与 PHP 脚本的标准输入、标准输出和标准错误文件描描述符通信。</p>
<p>php://stdin 只读 PHP 流，其中的数据来自标准输入。例如，接收命令行传入脚本的信息。</p>
<p>php://stdout 把数据写入当前的缓冲区。这个流只能写，无法读或寻址。</p>
<p>php://memory 从系统内存中读取数据，或者把数据写入系统内存。缺点是，可用内存是有限的。使用 php://temp 流更安全。</p>
<p>php://temp 和 php://memory 类似，不过没有可以内存时，PHP 会把数据写入临时文件。</p>
<h3 id="错误和异常">错误和异常</h3>
<p>提到了 <a href="https://github.com/Seldaek/monolog">Monolog</a> 记录日志。</p>
<h2 id="调优">调优</h2>
<h3 id="内存">内存</h3>
<p><em>一共能分配给 PHP 多少内存？</em></p>
<p>Linode 2GB 的 sever 留 512MB 给 PHP。</p>
<p><em>单个 PHP 进程平均消耗多少内存？</em></p>
<p>使用 top 命令查看。一般 PHP 进程消耗 5 ~ 20MB 内存。</p>
<p><em>能负担的起多少个 PHP-FPM 进程？</em></p>
<p>假设 PHP 分配了 512MB 内存，每个 PHP 平均消耗 15MB 内存，从而确定能负担的起 34 个进程。</p>
<p>压力测试工具：</p>
<ul>
<li><a href="https://httpd.apache.org/docs/2.4/programs/ab.html">ab - Apache HTTP server benchmarking tool</a></li>
<li><a href="https://www.joedog.org/siege-home/">Siege</a></li>
</ul>
<h3 id="zend-opcache-1">Zend OPcache</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">opcache</span><span class="o">.</span><span class="nx">memory_consumption</span> <span class="o">=</span> <span class="mi">64</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 为操作码缓存分配的内存量（单位 MB）。
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">opcache</span><span class="o">.</span><span class="nx">interned_strings_buffer</span> <span class="o">=</span> <span class="mi">16</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 用来存储驻留字符串的内存量（单位 MB，默认 4MB）。
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">opcache</span><span class="o">.</span><span class="nx">max_accelerated_file</span> <span class="o">=</span> <span class="mi">4000</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 操作码缓存中最多能存储多少个 PHP 脚本。这个值一定比 PHP 应用中的文件数量大。
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">opcache</span><span class="o">.</span><span class="nx">validate_timestamps</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 为 1 时，一段时间后 PHP 会检查 PHP 脚本的内容是否变化。检查的时间间隔由 revalidate_freq 指定。
</span></span></span><span class="line"><span class="cl"><span class="c1"># 开发环境设为 1，在生成环境中为 0。
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">opcache</span><span class="o">.</span><span class="nx">revalidate_freq</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 设置多久检查一次 PHP 脚本的内容是否有变化。
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">opcache</span><span class="o">.</span><span class="nx">fast_shutdown</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 这么设置能让操作码使用更快的停机步骤，把对象析构和内存释放交给 Zend Engine 的内存管理器完成。
</span></span></span></code></pre></div><h3 id="文件上传">文件上传</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">file_uploads</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="nx">upload_max_filesize</span> <span class="o">=</span> <span class="mi">10</span><span class="nx">M</span>
</span></span><span class="line"><span class="cl"><span class="nx">max_file_uploads</span> <span class="o">=</span> <span class="mi">3</span>
</span></span></code></pre></div><p>如果需要上传非常大的文件，还要调整 nginx 虚拟主机配置中的 client_max_body_size 设置。</p>
<h3 id="会话处理">会话处理</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">session</span><span class="o">.</span><span class="nx">save_handler</span> <span class="o">=</span> <span class="s1">&#39;memcached&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nx">session</span><span class="o">.</span><span class="nx">save_path</span> <span class="o">=</span> <span class="s1">&#39;127.0.0.2:11211&#39;</span>
</span></span></code></pre></div><h3 id="缓冲输出">缓冲输出</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">output_buffering</span> <span class="o">=</span> <span class="mi">4096</span>
</span></span><span class="line"><span class="cl"><span class="nx">implicit_flush</span> <span class="o">=</span> <span class="k">false</span>
</span></span></code></pre></div><p>确保使用的值是 4（32 位系统）或者 8（64 位系统）的倍数。</p>
<h3 id="真实路径缓存">真实路径缓存</h3>
<p>realpath cache，PHP 会缓存应用使用的文件路径，这样每次包含或者导入文件时就无需不断搜索包含路径了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nx">realpath_cache_size</span> <span class="o">=</span> <span class="mi">64</span><span class="nx">k</span>
</span></span></code></pre></div><h2 id="部署">部署</h2>
<p>提到了 <a href="http://capistranorb.com/">Capistrano</a> 待研究。</p>
<h2 id="测试">测试</h2>
<ul>
<li>PHPUnit</li>
<li>Xdebug</li>
<li>使用 Travis CI 持续测试</li>
</ul>
<h2 id="分析">分析</h2>
<ul>
<li>XHProf 较新的 PHP 应用分析器</li>
<li>XHGUI</li>
<li>New Relic</li>
<li>Blackfire</li>
</ul>
<h2 id="hhvm-和-hack">HHVM 和 Hack</h2>
<p>Hip-Hop Virtual Machine.</p>
<p>Hack 是一门建立在 PHP 之上的编程语音，引入了静态类型，新的数据结构和额外的接口，同时还能向后兼容现有的动态类型 PHP 代码。</p>
<p>动态类型和静态类型，二者之间的区别在于何时检查 PHP 类型。动态类型在运行时检查类型，而静态类型在编译时检查类型。</p>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>PhpStorm 使用经验</title>
      <link>https://zyf.im/2018/05/05/phpstorm-using-experience/</link>
      <pubDate>Sat, 05 May 2018 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/05/05/phpstorm-using-experience/</guid>
      <description>&lt;h2 id=&#34;getting-started&#34;&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.jetbrains.com/phpstorm/getting-started/episode-1/&#34;&gt;Two shortcuts to get started&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Shift+Shift(⇧+⇧)&lt;/code&gt; helps you find anything within your project.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Alt+Enter(Option+Enter)&lt;/code&gt; provides instant access to contextual actions and quick fixes relevant to the selected code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Getting started with PHP in PhpStorm&lt;/p&gt;
&lt;p&gt;还有个 &lt;code&gt;One Dark theme&lt;/code&gt; 但是 &lt;code&gt;Material Theme UI&lt;/code&gt; 已经包含这个主题。&lt;/p&gt;
&lt;p&gt;配置：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Preferences &amp;gt; Appearance &amp;amp; Behavior &amp;gt; Appearance&lt;/code&gt; 下，右侧配置：&lt;code&gt;Theme: Darcula&lt;/code&gt;，勾选 &lt;code&gt;User custom font: .AppleSystemUIFont&lt;/code&gt; &lt;code&gt;Size: 18&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Preferences &amp;gt; Editor &amp;gt; Font&lt;/code&gt; 下，右侧配置：&lt;code&gt;Font: Menlo&lt;/code&gt; &lt;code&gt;Size: 18&lt;/code&gt; &lt;code&gt;Line spacing: 1.2&lt;/code&gt;。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="getting-started">Getting Started</h2>
<p><a href="https://www.jetbrains.com/phpstorm/getting-started/episode-1/">Two shortcuts to get started</a></p>
<ul>
<li><code>Shift+Shift(⇧+⇧)</code> helps you find anything within your project.</li>
<li><code>Alt+Enter(Option+Enter)</code> provides instant access to contextual actions and quick fixes relevant to the selected code.</li>
</ul>
<p>Getting started with PHP in PhpStorm</p>
<p>还有个 <code>One Dark theme</code> 但是 <code>Material Theme UI</code> 已经包含这个主题。</p>
<p>配置：</p>
<p><code>Preferences &gt; Appearance &amp; Behavior &gt; Appearance</code> 下，右侧配置：<code>Theme: Darcula</code>，勾选 <code>User custom font: .AppleSystemUIFont</code> <code>Size: 18</code>。</p>
<p><code>Preferences &gt; Editor &gt; Font</code> 下，右侧配置：<code>Font: Menlo</code> <code>Size: 18</code> <code>Line spacing: 1.2</code>。</p>
<p><code>Preferences &gt; Editor &gt; Color Scheme</code> 下，右侧配置：<code>Seheme: Atom One Dark(Material)</code> 点击右侧的三个点 <code>Duplicate</code> 复制一份。</p>
<p><code>Preferences &gt; Editor &gt; Color Scheme &gt; General</code> 下，右侧配置：</p>
<ul>
<li>Editor &gt; Gutter background，右侧 <code>Background: 292929</code>（设置行号背景色）</li>
<li>Editor &gt; Vertical Scrollar &gt; Thumb 与 Thumb while scrolling，右侧 <code>Background: 305599C0</code>（设置垂直滑块色）</li>
<li>Editor &gt; Text &gt; Default text，右侧 <code>Background: 292929</code>（设置面板背景色）</li>
</ul>
<h2 id="快捷键">快捷键</h2>
<ul>
<li><code>command + ,</code> 打开设置</li>
<li><code>command + shift + t</code> 在代码和对应单元测试间用快速切换</li>
<li><code>command + e</code> 查看最近编辑文件列表</li>
<li><code>shift + shift</code> 搜索文件/类/操作/设置</li>
<li><code>command + option + t</code> 快速包裹 if/foreach/while 等控制结构
<ul>
<li><code>command + option + +/-</code> 快速展开/折叠</li>
</ul>
</li>
<li><code>option + F12</code> 呼出内置终端</li>
<li><code>command + shift + a</code> &ldquo;Quick Lists&rdquo; 创建高频操作集合</li>
<li>Refactor
<ul>
<li><code>control + t</code> 重构代码优化</li>
<li><code>command + n</code> 代码生成</li>
<li><code>option + return</code> 语法修复、代码优化等建议</li>
<li><code>option + 鼠标选择</code> 同时修改多处相同内容</li>
</ul>
</li>
<li>Git
<ul>
<li><code>command + k</code> Commit Changes</li>
<li><code>command + shift + k</code> Push Changes</li>
<li><code>control + v</code> 查看版本操作菜单</li>
</ul>
</li>
<li>Debug
<ul>
<li><code>command + F8</code>   # 添加断点</li>
<li><code>control + d</code>    # 启动调试</li>
<li><code>F8</code>             # Step Over</li>
<li><code>F7</code>             # Step Into</li>
<li><code>shift + F8</code>     # Step Out</li>
</ul>
</li>
</ul>
<h2 id="技巧">技巧</h2>
<h3 id="围绕选择输入">围绕选择输入</h3>
<p>设置选择了一个词后，再按单引号或双引号，将选中的单词用引号括起来。</p>
<p><code>Preferences</code> 中搜索 <code>Surround Selection on typing quote or brace</code> 将其勾选（<code>Editor &gt; General &gt; Smart Keys</code> 下）。</p>
<h3 id="关闭文档提示">关闭文档提示</h3>
<p>鼠标放在方法上会出现文档提示，想关闭。</p>
<p><code>Preferences</code> 中搜索 <code>Show quick documentation on mouse move</code> 取消勾选（<code>Editor &gt; Code Editing</code> 下）。</p>
<h3 id="使用-php-cs-fixer">使用 PHP-CS-Fixer</h3>
<blockquote>
<p><a href="https://github.com/FriendsOfPHP/PHP-CS-Fixer">The PHP Coding Standards Fixer</a> (PHP CS Fixer) tool fixes your code to follow standards.</p>
</blockquote>
<p>工作环境：MacBook。</p>
<p>打开 PhpStorm <code>Preferences &gt; Tools &gt; External Tools</code> 添加：</p>
<p><img alt="180416-use-php-cs-fixer-in-phpstorm-001" loading="lazy" src="https://user-images.githubusercontent.com/9289792/88664953-715fb100-d110-11ea-970e-9dcb72945ebb.png"></p>
<ul>
<li>Program: <code>/usr/local/bin/php-cs-fixer</code></li>
<li>Arguments: <code>--verbose fix &quot;$FileDir$/$FileName$&quot; --dry-run --rules=@PSR1,@PSR2,@Symfony</code>（Note that previous verions of PHP-CS-Fixer used &ndash;levels instead of &ndash;rules. 未找到）</li>
<li>Working directory: <code>$ProjectFileDir$</code></li>
<li>我取消勾选了 <code>Open console for tool output</code>，可以不输出日志信息</li>
</ul>
<p>为了方便使用，保存文件时就可以格式化，设置快捷键 <code>Preferences &gt; Keymap &gt; Macros</code>：</p>
<p><img alt="180416-use-php-cs-fixer-in-phpstorm-002" loading="lazy" src="https://user-images.githubusercontent.com/9289792/88665170-c996b300-d110-11ea-8acf-62dad3694f2d.png"></p>
<p>设置 php-cs-fix 单独的快捷键 <code>Preferences &gt; Keymap &gt; External Tools</code>：</p>
<p><img alt="180416-use-php-cs-fixer-in-phpstorm-003" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80202900-e5de0c00-8658-11ea-826f-b4d058fa2209.png"></p>
<h3 id="关闭不常用的插件">关闭不常用的插件</h3>
<p><code>Preferences &gt; Plugins &gt; Installed</code> 向下滚动，<code>Bundled</code> 中有不少预装但不常用的可以禁掉。</p>
<h2 id="遇到过的一些问题">遇到过的一些问题</h2>
<h3 id="文件类型错误">文件类型错误</h3>
<p>一个文件被新建后，明明扩展名没有错，但是却没有语法高亮，删除文件后也不解决问题。</p>
<p>解决办法：<code>Editor &gt; File Types</code> 找 <code>Text</code> 将里面涉及的文件删除掉。</p>
<ul>
<li><a href="https://segmentfault.com/q/1010000004495692">phpstorm 文件类型错误</a></li>
</ul>
<h3 id="undefined-function-xxx">Undefined function XXX</h3>
<p>出现 PHP 的原生方法未定义的警告。</p>
<p>解决方法：<code>File &gt; Invalidate Caches / Restart</code></p>
<h3 id="typo-in-word-xxx">Typo: In word XXX</h3>
<p>提示单词拼写错误，但是其中没有问题，比如全拼的名字。</p>
<p>解决方法：option + enter -&gt; Save to dictionary</p>
<ul>
<li><a href="https://www.jetbrains.com/help/phpstorm/spellchecking.html">Spellchecking | jetbrains</a></li>
</ul>
<h3 id="warning-multiple-definitions-exists-for-class">warning: Multiple definitions exists for class</h3>
<p>I resolved this by going to <code>Preferences-&gt; Languages &amp; Frameworks-&gt; PHP</code>; and then under <code>Include path</code>, remove the conflicting path. (In my case a package reference in the vendor directory to a package I was developing inside my Laravel project)</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://laravel-china.org/articles/4172/create-beautiful-phpstorm-interface">打造漂亮的 PhpStorm 界面</a></li>
<li><a href="http://www.pilishen.com/posts/phpstorm-tips-and-tricks">大牛们的 PHPstorm 使用技巧和建议</a></li>
<li><a href="https://gist.github.com/nienkedekker/3ddb9ece42233698c0e3f3e42cf1ff34">Use PHP-CS-Fixer in PHPStorm</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>使用 Certbot 获取 Let’s Encrypt 颁发的 TLS 证书</title>
      <link>https://zyf.im/2018/04/26/lets-encrypt-wildcard-certificates/</link>
      <pubDate>Thu, 26 Apr 2018 16:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/04/26/lets-encrypt-wildcard-certificates/</guid>
      <description>&lt;h2 id=&#34;certbot-和-lets-encrypt-的关系&#34;&gt;Certbot 和 Let’s Encrypt 的关系&lt;/h2&gt;
&lt;h3 id=&#34;lets-encrypt&#34;&gt;Let&amp;rsquo;s Encrypt&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一个免费、自动化、开放的公共证书颁发机构（CA）。&lt;/li&gt;
&lt;li&gt;通过 ACME（Automatic Certificate Management Environment）协议向域名所有者颁发 DV（Domain Validation）TLS/SSL 证书。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;certbot&#34;&gt;Certbot&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;由 Electronic Frontier Foundation (EFF) 维护的开源 ACME 客户端。&lt;/li&gt;
&lt;li&gt;主要目标是简化与 Let&amp;rsquo;s Encrypt 之间的交互：
&lt;ul&gt;
&lt;li&gt;自动化域名验证（HTTP-01、DNS-01、TLS-ALPN-01 等）&lt;/li&gt;
&lt;li&gt;安装并续期证书&lt;/li&gt;
&lt;li&gt;更新 Web 服务器配置（Apache、Nginx、Lighttpd 等）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Certbot 也能与任何兼容 ACME 的 CA 通信，不限于 Let&amp;rsquo;s Encrypt。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;典型工作流程&#34;&gt;典型工作流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;解析参数并检测服务器类型。&lt;/li&gt;
&lt;li&gt;选择并执行挑战（例如在 /.well-known/acme-challenge/ 下写入 token）。&lt;/li&gt;
&lt;li&gt;Let’s Encrypt 回访验证域名归属。&lt;/li&gt;
&lt;li&gt;验证通过后签发证书；Certbot 下载并安装到本地。&lt;/li&gt;
&lt;li&gt;创建定时任务 certbot renew 自动续期。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;开始实验&#34;&gt;开始实验&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;实验环境：Amazon Linux 2023 (AL2023)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;知识补充&#34;&gt;知识补充&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dnf（Dandified YUM）&lt;/code&gt; 是 RPM-系 Linux 发行版的下一代包管理器。它在功能上取代了传统的 &lt;code&gt;yum&lt;/code&gt;，两者命令参数几乎保持兼容。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RPM&lt;/code&gt; 一开始叫 Red Hat Package Manager，后来改名为递归含义的 RPM Package Manager。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RPM-系发行版&lt;/code&gt; 是把 RPM 作为原生软件包格式与核心包管理工具链的那一族 Linux 发行版。常听到的 Fedora / RHEL / CentOS / AlmaLinux / Rocky Linux / openSUSE / SUSE Linux Enterprise / Amazon Linux 2023 等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dnf&lt;/code&gt; 不是对 &lt;code&gt;rpm&lt;/code&gt; CLI 的简单封装，而是调用 &lt;code&gt;librpm&lt;/code&gt; 完成最终操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;包管理器&lt;/th&gt;
          &lt;th&gt;代表发行版&lt;/th&gt;
          &lt;th&gt;归属&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;dnf / yum&lt;/td&gt;
          &lt;td&gt;Fedora, RHEL, CentOS&lt;/td&gt;
          &lt;td&gt;RPM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;zypper&lt;/td&gt;
          &lt;td&gt;openSUSE, SLE&lt;/td&gt;
          &lt;td&gt;RPM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;apt / apt-get&lt;/td&gt;
          &lt;td&gt;Debian, Ubuntu&lt;/td&gt;
          &lt;td&gt;DEB&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;pacman&lt;/td&gt;
          &lt;td&gt;Arch Linux&lt;/td&gt;
          &lt;td&gt;tar.xz&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;apk&lt;/td&gt;
          &lt;td&gt;Alpine&lt;/td&gt;
          &lt;td&gt;.apk&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;snap&lt;/code&gt; 是一种由 Canonical（Ubuntu 的开发公司）推出的跨发行版应用打包和分发格式，也指围绕它的一整套生态系统。
&lt;ul&gt;
&lt;li&gt;跨发行版。同一个 snap 包可以在几十种发行版上直接安装运行，不依赖各自的 RPM/Deb 系统仓库。&lt;/li&gt;
&lt;li&gt;自包含（bundled）依赖。snap 包内部包含所有依赖，无需外部安装。&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://snapcraft.io/&#34;&gt;https://snapcraft.io/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;launch-an-aws-ec2-instance&#34;&gt;Launch an AWS EC2 instance&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Instance type: t2.nano&lt;/li&gt;
&lt;li&gt;Username: ec2-user&lt;/li&gt;
&lt;li&gt;Security Groups: Allow HTTP and HTTPS&lt;/li&gt;
&lt;li&gt;Public IPv4: 35.86.90.4&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;配置-nginx&#34;&gt;配置 nginx&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo dnf update -y
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo dnf install -y nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nginx -v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# nginx version: nginx/1.28.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl start nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl status nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Started nginx.service - The nginx HTTP and reverse proxy server.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;直接通过 &lt;a href=&#34;http://35.86.90.4&#34;&gt;http://35.86.90.4&lt;/a&gt;，会看到默认的 nginx 欢迎页面。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="certbot-和-lets-encrypt-的关系">Certbot 和 Let’s Encrypt 的关系</h2>
<h3 id="lets-encrypt">Let&rsquo;s Encrypt</h3>
<ul>
<li>一个免费、自动化、开放的公共证书颁发机构（CA）。</li>
<li>通过 ACME（Automatic Certificate Management Environment）协议向域名所有者颁发 DV（Domain Validation）TLS/SSL 证书。</li>
</ul>
<h3 id="certbot">Certbot</h3>
<ul>
<li>由 Electronic Frontier Foundation (EFF) 维护的开源 ACME 客户端。</li>
<li>主要目标是简化与 Let&rsquo;s Encrypt 之间的交互：
<ul>
<li>自动化域名验证（HTTP-01、DNS-01、TLS-ALPN-01 等）</li>
<li>安装并续期证书</li>
<li>更新 Web 服务器配置（Apache、Nginx、Lighttpd 等）</li>
</ul>
</li>
<li>Certbot 也能与任何兼容 ACME 的 CA 通信，不限于 Let&rsquo;s Encrypt。</li>
</ul>
<h3 id="典型工作流程">典型工作流程</h3>
<ol>
<li>解析参数并检测服务器类型。</li>
<li>选择并执行挑战（例如在 /.well-known/acme-challenge/ 下写入 token）。</li>
<li>Let’s Encrypt 回访验证域名归属。</li>
<li>验证通过后签发证书；Certbot 下载并安装到本地。</li>
<li>创建定时任务 certbot renew 自动续期。</li>
</ol>
<h2 id="开始实验">开始实验</h2>
<blockquote>
<p>实验环境：Amazon Linux 2023 (AL2023)</p>
</blockquote>
<h3 id="知识补充">知识补充</h3>
<ul>
<li><code>dnf（Dandified YUM）</code> 是 RPM-系 Linux 发行版的下一代包管理器。它在功能上取代了传统的 <code>yum</code>，两者命令参数几乎保持兼容。</li>
<li><code>RPM</code> 一开始叫 Red Hat Package Manager，后来改名为递归含义的 RPM Package Manager。</li>
<li><code>RPM-系发行版</code> 是把 RPM 作为原生软件包格式与核心包管理工具链的那一族 Linux 发行版。常听到的 Fedora / RHEL / CentOS / AlmaLinux / Rocky Linux / openSUSE / SUSE Linux Enterprise / Amazon Linux 2023 等。</li>
<li><code>dnf</code> 不是对 <code>rpm</code> CLI 的简单封装，而是调用 <code>librpm</code> 完成最终操作。</li>
</ul>
<table>
  <thead>
      <tr>
          <th>包管理器</th>
          <th>代表发行版</th>
          <th>归属</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>dnf / yum</td>
          <td>Fedora, RHEL, CentOS</td>
          <td>RPM</td>
      </tr>
      <tr>
          <td>zypper</td>
          <td>openSUSE, SLE</td>
          <td>RPM</td>
      </tr>
      <tr>
          <td>apt / apt-get</td>
          <td>Debian, Ubuntu</td>
          <td>DEB</td>
      </tr>
      <tr>
          <td>pacman</td>
          <td>Arch Linux</td>
          <td>tar.xz</td>
      </tr>
      <tr>
          <td>apk</td>
          <td>Alpine</td>
          <td>.apk</td>
      </tr>
  </tbody>
</table>
<ul>
<li><code>snap</code> 是一种由 Canonical（Ubuntu 的开发公司）推出的跨发行版应用打包和分发格式，也指围绕它的一整套生态系统。
<ul>
<li>跨发行版。同一个 snap 包可以在几十种发行版上直接安装运行，不依赖各自的 RPM/Deb 系统仓库。</li>
<li>自包含（bundled）依赖。snap 包内部包含所有依赖，无需外部安装。</li>
<li><a href="https://snapcraft.io/">https://snapcraft.io/</a></li>
</ul>
</li>
</ul>
<h3 id="launch-an-aws-ec2-instance">Launch an AWS EC2 instance</h3>
<ul>
<li>Instance type: t2.nano</li>
<li>Username: ec2-user</li>
<li>Security Groups: Allow HTTP and HTTPS</li>
<li>Public IPv4: 35.86.90.4</li>
</ul>
<h3 id="配置-nginx">配置 nginx</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo dnf update -y
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo dnf install -y nginx
</span></span><span class="line"><span class="cl">nginx -v
</span></span><span class="line"><span class="cl"><span class="c1"># nginx version: nginx/1.28.0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo systemctl start nginx
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> nginx
</span></span><span class="line"><span class="cl">systemctl status nginx
</span></span><span class="line"><span class="cl"><span class="c1"># Started nginx.service - The nginx HTTP and reverse proxy server.</span>
</span></span></code></pre></div><p>直接通过 <a href="http://35.86.90.4">http://35.86.90.4</a>，会看到默认的 nginx 欢迎页面。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vi /etc/nginx/conf.d/default.conf
</span></span></code></pre></div><pre tabindex="0"><code class="language-conf" data-lang="conf">#
server {
    listen       80;
    listen       [::]:80;
    server_name  tmp.yifans.net;
    root         /usr/share/nginx/html;

    error_page 404 /404.html;
    location = /404.html {
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }
}
</code></pre><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nginx -t
</span></span><span class="line"><span class="cl">sudo systemctl reload nginx
</span></span></code></pre></div><p>将自己的域名（<code>tmp.yifans.net</code>）解析到 <code>35.86.90.4</code>，然后访问 <a href="http://tmp.yifans.net">http://tmp.yifans.net</a> 同样看到 nginx 欢迎页面，游览器显示 Not Secure。</p>
<h3 id="配置-certbot">配置 certbot</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 暂无官方支持 AL2023</span>
</span></span><span class="line"><span class="cl">sudo wget -O /etc/yum.repos.d/snapd.repo <span class="se">\
</span></span></span><span class="line"><span class="cl">    https://bboozzoo.github.io/snapd-amazon-linux/al2023/snapd.repo
</span></span><span class="line"><span class="cl">sudo dnf install -y snapd
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> --now snapd.socket
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo snap install --classic certbot
</span></span><span class="line"><span class="cl">sudo ln -s /snap/bin/certbot /usr/bin/certbot
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">certbot --version
</span></span><span class="line"><span class="cl"><span class="c1"># certbot 4.1.1</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 前置 nginx 配置中要有 server_name tmp.yifans.net;</span>
</span></span><span class="line"><span class="cl">sudo certbot --nginx -d tmp.yifans.net
</span></span><span class="line"><span class="cl"><span class="c1"># 全自动完成</span>
</span></span></code></pre></div><p>访问 <a href="http://tmp.yifans.net">http://tmp.yifans.net</a> 会自动跳转 https。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 看看被 certbot 自动修改的 nginx 配置</span>
</span></span><span class="line"><span class="cl">cat /etc/nginx/conf.d/default.conf
</span></span></code></pre></div><pre tabindex="0"><code class="language-conf" data-lang="conf">server {
    server_name  tmp.yifans.net;
    root         /usr/share/nginx/html;

    error_page 404 /404.html;
    location = /404.html {
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/tmp.yifans.net/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/tmp.yifans.net/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = tmp.yifans.net) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen       80;
    listen       [::]:80;
    server_name  tmp.yifans.net;
    return 404; # managed by Certbot
}
</code></pre><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 看看 certbot 预置的参数</span>
</span></span><span class="line"><span class="cl"><span class="c1"># include /etc/letsencrypt/options-ssl-nginx.conf;</span>
</span></span><span class="line"><span class="cl">cat /etc/letsencrypt/options-ssl-nginx.conf
</span></span></code></pre></div><pre tabindex="0"><code class="language-conf" data-lang="conf"># This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file. Contents are based on https://ssl-config.mozilla.org

ssl_session_cache shared:le_nginx_SSL:10m;
# 在内存里创建名为 le_nginx_SSL 的共享会话缓存区域，大小 10 MiB。
# 能加速后续同客户端的 TLS 握手（避免完整密钥交换）。
# 10 MiB 足够保存约 40 000 个会话条目。

ssl_session_timeout 1440m;
# 单位是分钟（1440 m = 24 h）。
# 表示缓存的会话条目过期时间；设为一天可在常见浏览器会话周期内复用。

ssl_session_tickets off;
# 关闭 TLS Session Tickets。
# 原因：早期实现无法便捷轮换 ticket 加密密钥，导致前向安全受损。

ssl_protocols TLSv1.2 TLSv1.3;
# 明确只允许 1.2 / 1.3。
# 关闭已不安全的 SSLv3、TLS 1.0、TLS 1.1。

ssl_prefer_server_ciphers off;
# 让客户端来决定从交集里选哪一组加密套件（服务器不强制优先级）。
# 在只开放现代安全套件的前提下，这样做兼容性好且简化维护。
# 如果你想严格控制，可改为 on 并自行调整 ssl_ciphers 顺序。

ssl_ciphers &#34;ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384&#34;;
# 仅保留 GCM / ChaCha20-Poly1305 这一代 AEAD 套件；弃用 CBC、RC4、3DES 等。
# ECDHE-ECDSA（椭圆曲线 DH，证书是 ECDSA）
# ECDHE-RSA（证书是 RSA）
# DHE-RSA（传统 DH，给极旧设备兜底）
</code></pre><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">`ssl_dhparam` 告诉 Nginx 在使用 DHE（Diffie-Hellman Ephemeral）握手方式时，不用默认的 1024 位弱参数，而改用一份 自定义、更安全的 DH 参数文件。
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">DHE 是 TLS/SSL 协议中的一种「密钥交换算法」。它的核心作用只有两点：
</span></span><span class="line"><span class="cl">  - 让客户端与服务器在 公网信道 上安全地“协商出”一个对称加密用的随机密钥。
</span></span><span class="line"><span class="cl">  - 提供 前向安全（Forward Secrecy）：哪怕以后服务器的长期私钥被泄露，过去抓到的加密数据也无法解密。
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 看看证书</span>
</span></span><span class="line"><span class="cl">sudo openssl x509 -in /etc/letsencrypt/live/tmp.yifans.net/fullchain.pem -noout -text <span class="p">|</span> grep yifans.net
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        Subject: <span class="nv">CN</span><span class="o">=</span>tmp.yifans.net
</span></span><span class="line"><span class="cl">                DNS:tmp.yifans.net
</span></span></code></pre></div><h3 id="只获取证书">只获取证书</h3>
<p>首先需要将 <code>tmp-certonly.yifans.net</code> 解析到 <code>35.86.90.4</code>，为了自动完成 acme challenge。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vi /etc/nginx/conf.d/certonly.conf
</span></span></code></pre></div><pre tabindex="0"><code class="language-conf" data-lang="conf">server {
    listen       80;
    listen       [::]:80;
    server_name  tmp-certonly.yifans.net;
    root         /usr/share/nginx/html;

    error_page 404 /404.html;
    location = /404.html {
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }
}
</code></pre><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nginx -t
</span></span><span class="line"><span class="cl">sudo systemctl reload nginx
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo certbot certonly --nginx
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">We recommend selecting either all domains, or all domains in a VirtualHost/server block.
</span></span><span class="line"><span class="cl">- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
</span></span><span class="line"><span class="cl">1: tmp.yifans.net
</span></span><span class="line"><span class="cl">2: tmp-certonly.yifans.net
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 选择 2</span>
</span></span><span class="line"><span class="cl">Successfully received certificate.
</span></span><span class="line"><span class="cl">Certificate is saved at: /etc/letsencrypt/live/tmp-certonly.yifans.net/fullchain.pem
</span></span><span class="line"><span class="cl">Key is saved at:         /etc/letsencrypt/live/tmp-certonly.yifans.net/privkey.pem
</span></span><span class="line"><span class="cl">This certificate expires on 2025-10-01.
</span></span><span class="line"><span class="cl"><span class="c1"># /etc/nginx/conf.d/certonly.conf 没有被自动修改，需要手动添加证书</span>
</span></span></code></pre></div><h3 id="wildcard-certificate">Wildcard Certificate</h3>
<ul>
<li>需要 DNS Credentials。</li>
<li><code>*.example.com</code> 不覆盖 <code>example.com</code>, <code>hello.goodbye.example.com</code>。</li>
<li><code>*.goodbye.example.com</code> 可覆盖 <code>hello.goodbye.example.com</code>。</li>
<li>不可以申请 <code>*.*.example.com</code> 只能一个 <code>*</code> asterisk。</li>
</ul>
<p>可以通过 DNS Plugins 插件申请 Wildcard Certificate，但是我觉得 manual 更简单。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo certbot certonly --manual -d <span class="s1">&#39;*.tmp.yifans.net&#39;</span> --preferred-challenges dns
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Please deploy a DNS TXT record under the name:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">_acme-challenge.tmp.yifans.net.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">with the following value:
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 访问 https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.tmp.yifans.net. 预测试配置结果。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 如果看到对应的值，按 Enter 继续。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Successfully received certificate.
</span></span><span class="line"><span class="cl">Certificate is saved at: /etc/letsencrypt/live/tmp.yifans.net-0001/fullchain.pem
</span></span><span class="line"><span class="cl">Key is saved at:         /etc/letsencrypt/live/tmp.yifans.net-0001/privkey.pem
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 看看证书</span>
</span></span><span class="line"><span class="cl">sudo openssl x509 -in /etc/letsencrypt/live/tmp.yifans.net-0001/fullchain.pem -noout -text <span class="p">|</span> grep yifans.net
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        Subject: <span class="nv">CN</span><span class="o">=</span>*.tmp.yifans.net
</span></span><span class="line"><span class="cl">                DNS:*.tmp.yifans.net
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vi /etc/nginx/conf.d/wildcard.conf
</span></span></code></pre></div><pre tabindex="0"><code class="language-conf" data-lang="conf">server {
    server_name  *.tmp.yifans.net;
    root         /usr/share/nginx/html;

    error_page 404 /404.html;
    location = /404.html {
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/tmp.yifans.net-0001/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/tmp.yifans.net-0001/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

server {
    listen 80;
    server_name *.tmp.yifans.net;
    return 301 https://$host$request_uri;
}
</code></pre><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nginx -t
</span></span><span class="line"><span class="cl">sudo systemctl reload nginx
</span></span></code></pre></div><p>将 <code>1.tmp.yifans.net</code> 解析到 <code>35.86.90.4</code>，然后访问 <a href="https://1.tmp.yifans.net">https://1.tmp.yifans.net</a>，在 Chrome 的证书信息里可以看到 <code>Common Name (CN) *.tmp.yifans.net</code>。</p>
<h3 id="证书续期">证书续期</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo certbot renew --dry-run
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://certbot.eff.org/instructions?ws=nginx&amp;os=snap">Nginx on Linux (snap) | Certbot</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Swift 初始化</title>
      <link>https://zyf.im/2018/03/31/swift-init/</link>
      <pubDate>Sat, 31 Mar 2018 17:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/03/31/swift-init/</guid>
      <description>&lt;p&gt;因为自己是直接从 Swift 进入的 iOS 开发，Swift 与 Objective-C 初始化的对比就不多提了。感觉上 Swift 初始化的方式像 Java，自己也只是这样套着 Java 去理解，但也发现了不相同的地方。&lt;/p&gt;
&lt;h2 id=&#34;初始化顺序&#34;&gt;初始化顺序&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Blog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NSObject&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;param&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里有一条错误 &lt;code&gt;error: property &#39;self.param&#39; not initialized at implicitly generated super.init call&lt;/code&gt;，说明 &lt;code&gt;param&lt;/code&gt; 参数没有在隐式生成 &lt;code&gt;super.init&lt;/code&gt; 调用之前完成初始化。&lt;/p&gt;
&lt;p&gt;Swift 中并不是不调用 &lt;code&gt;super.init&lt;/code&gt;，而是为了方便开发者，由编译器完成了这一步。但要求在调用 &lt;code&gt;super.init&lt;/code&gt; 之前完成成员变量的初始化。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Blog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NSObject&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;param&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;param&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;swift init&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对于需要修改父类中成员变量值的情况，我们需要在调用 &lt;code&gt;super.init&lt;/code&gt; 之后再进行修改：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Cat&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;cat&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Tiger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Cat&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;power&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;power&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kc&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;tiger&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Swift 中类的初始化顺序：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>因为自己是直接从 Swift 进入的 iOS 开发，Swift 与 Objective-C 初始化的对比就不多提了。感觉上 Swift 初始化的方式像 Java，自己也只是这样套着 Java 去理解，但也发现了不相同的地方。</p>
<h2 id="初始化顺序">初始化顺序</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">Blog</span><span class="p">:</span> <span class="n">NSObject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">param</span><span class="p">:</span> <span class="nb">String</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这里有一条错误 <code>error: property 'self.param' not initialized at implicitly generated super.init call</code>，说明 <code>param</code> 参数没有在隐式生成 <code>super.init</code> 调用之前完成初始化。</p>
<p>Swift 中并不是不调用 <code>super.init</code>，而是为了方便开发者，由编译器完成了这一步。但要求在调用 <code>super.init</code> 之前完成成员变量的初始化。</p>
<!-- more -->
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">Blog</span><span class="p">:</span> <span class="n">NSObject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">param</span><span class="p">:</span> <span class="nb">String</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">param</span> <span class="p">=</span> <span class="s">&#34;swift init&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>对于需要修改父类中成员变量值的情况，我们需要在调用 <code>super.init</code> 之后再进行修改：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">Cat</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">name</span><span class="p">:</span> <span class="nb">String</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span> <span class="p">=</span> <span class="s">&#34;cat&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">Tiger</span><span class="p">:</span> <span class="n">Cat</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">power</span><span class="p">:</span> <span class="nb">Int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">power</span> <span class="p">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">name</span> <span class="p">=</span> <span class="s">&#34;tiger&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Swift 中类的初始化顺序：</p>
<ol>
<li>初始化自己的成员变量（必须）</li>
<li>调用父类初始化方法（如无需第三步，则这一步可省略）</li>
<li>修改父类成员变量（可选）</li>
</ol>
<p>补充说明：</p>
<ul>
<li><code>let</code> 声明的常量可以在初始化方法中进行赋值。Swift 中的 init 方法只会被调用一次，这与 Objective-C 不同</li>
<li>即使成员变量是可选类型，如 <code>let power: Int?</code> 仍然需要进行初始化；<code>var power: Int?</code> 则可以不用</li>
</ul>
<h2 id="关键字">关键字</h2>
<p>例子 1：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">CustomView</span><span class="p">:</span> <span class="n">UIView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">param</span><span class="p">:</span> <span class="nb">Int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// error 1</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">param</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">()</span> <span class="c1">// error 2</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="c1">// error 3</span>
</span></span></code></pre></div><p>将父类从 <code>NSObject</code> 修改为 <code>UIView</code>，竟然收到 3 条错误：</p>
<ol>
<li>initializer does not override a <strong>designated</strong> initializer from its superclass</li>
<li>must call a designated initializer of the superclass &lsquo;UIView&rsquo;</li>
<li><strong>&lsquo;required&rsquo;</strong> initializer &lsquo;init(coder:)&rsquo; must be provided by subclass of &lsquo;UIView&rsquo;</li>
</ol>
<p>例子 2：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">CustomView</span><span class="p">:</span> <span class="n">UIView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">convenience</span> <span class="kd">init</span><span class="p">(</span><span class="n">param</span><span class="p">:</span> <span class="nb">Int</span><span class="p">,</span> <span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span> <span class="c1">// error</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">required</span> <span class="kd">init</span><span class="p">?(</span><span class="n">coder</span> <span class="n">aDecoder</span><span class="p">:</span> <span class="n">NSCoder</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;init(coder:) has not been implemented&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>error：</p>
<ul>
<li><strong>convenience</strong> initializer for &lsquo;CustomView&rsquo; must delegate (with &lsquo;self.init&rsquo;) rather than chaining to a superclass</li>
</ul>
<p>上面两个例子出现了 3 个关键字：<code>designated</code>、<code>convenience</code> 和 <code>required</code>。</p>
<h3 id="designated">designated</h3>
<p>Swift 定义了两种类初始化器类型，用来保证所有成员属性能够获得一个初始化值。即 designated initializers [i&rsquo;niʃəlaizə] 和 convenience initializers。</p>
<blockquote>
<p>Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.</p>
</blockquote>
<ol>
<li>primary initializers：designated initializers 是一个类的主初始化器，理论上来说是一个类初始化的必经之路。（不同的初始化路径可能调用不同的 designated initializers）</li>
<li>fully initializes all properties：必须在 designated initializers 中完成所有成员属性的初始化</li>
<li>calls an appropriate superclass initializer：需要调用合适的父类初始化器完成初始化，不能随意调用</li>
</ol>
<p><em>在 Swift 中 designated initializers 的写法和一般的初始化方法无异</em>。在 Sample 1 中，我们试图去 override init，可以理解为我们就是在 override 一个 designated initializers，然后收到了错误 Initializer does not override a designated initializer from its superclass，可见我们并没有找到合适的 designated initializers。进入父类 UIView，可以看到下面两个初始化方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">init</span><span class="p">?(</span><span class="n">coder</span> <span class="n">aDecoder</span><span class="p">:</span> <span class="n">NSCoder</span><span class="p">)</span>
</span></span></code></pre></div><p>原来，这两个才是父类的 designated initializers，那我们改改试试：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">CustomView</span><span class="p">:</span> <span class="n">UIView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">param</span><span class="p">:</span> <span class="nb">Int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// error 1 fixed</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">param</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span> <span class="c1">// error 2 fixed</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="c1">// error 3</span>
</span></span></code></pre></div><p>error 1 fixed 由此可见：我们去 override 一个不是 designated initializers 的初始化器时，是不满足定义中所说的 primary initializers，这就可能导致这个初始化器不被执行，成员变量没有初始化，这样创建的“半成品”实例可能存在一些不安全的情况。</p>
<p>第二条 fully initializes all properties，这点我们并没有犯错，因为我们已经初始化了 CustomView 类中引入的 param 变量。</p>
<p>第三条 calls an appropriate superclass initializer 很明显就对应了 error 2，我们 override init(frame:)，那我们就必须调用对应的父类初始化方法。</p>
<p>error 3 提示我们 init(coder:) 是一个 &lsquo;required&rsquo; initializer，子类必须提供</p>
<h3 id="required">required</h3>
<blockquote>
<p>Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer.</p>
</blockquote>
<p>通过添加 required 关键字强制子类对某个初始化方法进行重写。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">CustomView</span><span class="p">:</span> <span class="n">UIView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">param</span><span class="p">:</span> <span class="nb">Int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// error 1 fixed</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">param</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span> <span class="c1">// error 2 fixed</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">required</span> <span class="kd">init</span><span class="p">?(</span><span class="n">coder</span> <span class="n">aDecoder</span><span class="p">:</span> <span class="n">NSCoder</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;init(coder:) has not been implemented&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="c1">// error 3 fixed</span>
</span></span></code></pre></div><p>error 3 fixed。插入的这个方法很奇怪，方法体里直接写 <code>fatalError(&quot;init(coder:) has not been implemented&quot;)</code>，那岂不是走到这里就 crash 了？</p>
<p>designated initializers 是一个类的主初始化器，理论上来说是一个类初始化的必经之路（不同的初始化路径可能调用不同的 designated initializers）。其实，这个 <code>init(coder:)</code> 与 <code>init(frame: frame)</code> 就是不同的初始化路径，当我们使用 xib 方式初始化一个 view 时，就会走到 <code>init(coder:)</code>。此时，如果我们没有真正实现这个方法，就会出现 fatal crash。</p>
<p>完整初始方案：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">CustomView</span><span class="p">:</span> <span class="n">UIView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">param</span><span class="p">:</span> <span class="nb">Int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">param</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">required</span> <span class="kd">init</span><span class="p">?(</span><span class="n">coder</span> <span class="n">aDecoder</span><span class="p">:</span> <span class="n">NSCoder</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">param</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">coder</span><span class="p">:</span> <span class="n">aDecoder</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="convenience">convenience</h3>
<blockquote>
<p>Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.</p>
</blockquote>
<p>convenience initializers 是对类初始化方法的补充，用于为类提供一些快捷的初始化方法。可以不创建这类方法，但如果创建了，就需要遵循原则：call a designated initializer from the same class，也就是说要调用该类自己的 <code>designated initializer</code>。那么我们应该 <code>override init(frame:)</code>，然后修改为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">CustomView</span><span class="p">:</span> <span class="n">UIView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">convenience</span> <span class="kd">init</span><span class="p">(</span><span class="n">param</span><span class="p">:</span> <span class="nb">Int</span><span class="p">,</span> <span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span> <span class="c1">// error fixed</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">required</span> <span class="kd">init</span><span class="p">?(</span><span class="n">coder</span> <span class="n">aDecoder</span><span class="p">:</span> <span class="n">NSCoder</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;init(coder:) has not been implemented&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>然后对成员变量 <code>param</code> 赋值：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">CustomView</span><span class="p">:</span> <span class="n">UIView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">param</span><span class="p">:</span> <span class="nb">Int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">convenience</span> <span class="kd">init</span><span class="p">(</span><span class="n">param</span><span class="p">:</span> <span class="nb">Int</span><span class="p">,</span> <span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">param</span> <span class="p">=</span> <span class="n">param</span> <span class="c1">// error</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span> <span class="c1">// error</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">required</span> <span class="kd">init</span><span class="p">?(</span><span class="n">coder</span> <span class="n">aDecoder</span><span class="p">:</span> <span class="n">NSCoder</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="bp">fatalError</span><span class="p">(</span><span class="s">&#34;init(coder:) has not been implemented&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>出现两个错误：</p>
<ul>
<li>Use of &lsquo;self&rsquo; in property access &lsquo;param&rsquo; before self.init initializes self</li>
<li>Property &lsquo;self.param&rsquo; not initialized at super.init call</li>
</ul>
<p>第二个错误我们清楚，是需要在调用 super.init 之前初始化本类成员属性。</p>
<p>第一个错误其实是 Swift 编译器提供的安全检查，文档原文如下：</p>
<blockquote>
<p>A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.</p>
</blockquote>
<p>原来 Swift 防止 convenience initializers 中赋值之后又被该类自己的 designated initializer 覆盖而做了检查。因此，正确的方式应该是调用该类的其他初始化方法之后再修改属性值，最终修改如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">CustomView</span><span class="p">:</span> <span class="n">UIView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">param</span><span class="p">:</span> <span class="nb">Int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">convenience</span> <span class="kd">init</span><span class="p">(</span><span class="n">param</span><span class="p">:</span> <span class="nb">Int</span><span class="p">,</span> <span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">param</span> <span class="p">=</span> <span class="n">param</span> <span class="c1">// error fixed</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">CGRect</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">param</span> <span class="p">=</span> <span class="mi">0</span> <span class="c1">// error fixed</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">required</span> <span class="kd">init</span><span class="p">?(</span><span class="n">coder</span> <span class="n">aDecoder</span><span class="p">:</span> <span class="n">NSCoder</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">param</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">coder</span><span class="p">:</span> <span class="n">aDecoder</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="小结">小结</h3>
<ol>
<li>子类中初始化方法必须覆盖全部初始化路径，以保证对象完全初始化</li>
<li>子类中 <code>designated initializer</code> 必须调用父类中对应的 <code>designated initializer</code>，以保证父类也能完成初始化</li>
<li>子类中如果重写父类中 <code>convenience initializer</code> 所需要的全部 <code>init</code> 方法，就可以在子类中使用父类的 <code>convenience initializer</code> 了</li>
<li>子类如果没有定义任何 <code>designated initializer</code>，则默认继承所有父类的 <code>designated initializer</code> 及 <code>convenience initializer</code></li>
<li>子类中必须实现的 <code>designated initializer</code>，可以通过添加 <code>required</code> 关键字强制子类重写其实现，以保证依赖该方法的 <code>convenience initializer</code> 始终可以使用</li>
<li><code>convenience initializer</code> 必须调用自身类中的其他初始化方法，并在最终必须调用一个 <code>designated initializer</code></li>
<li>在构造器完成初始化之前，不能调用任何实例方法或读取任何实例属性的值，<code>self</code> 本身也不能被引用</li>
</ol>
<h2 id="可失败初始化器">可失败初始化器</h2>
<p>可失败初始化器 <code>Failable Initializers</code>，即可能返回 <code>nil</code> 的初始化方法。</p>
<blockquote>
<p>A failable initializer creates an <em>optional value</em> of the type it initializes. You write <em>return nil within a failable initializer</em> to indicate a point at which initialization failure can be triggered.</p>
</blockquote>
<p>就是将初始化返回值变成 optional value，并在不满足初始化条件的地方 return nil。通过调用处判断是否有值即可知道是否初始化成功。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">Product</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">name</span><span class="p">:</span> <span class="nb">String</span>
</span></span><span class="line"><span class="cl">    <span class="kd">init</span><span class="p">?(</span><span class="n">name</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">name</span><span class="p">.</span><span class="bp">isEmpty</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">name</span> <span class="p">=</span> <span class="n">name</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">CartItem</span><span class="p">:</span> <span class="n">Product</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">quantity</span><span class="p">:</span> <span class="nb">Int</span>
</span></span><span class="line"><span class="cl">    <span class="kd">init</span><span class="p">?(</span><span class="n">name</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="n">quantity</span><span class="p">:</span> <span class="nb">Int</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">quantity</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="kc">self</span><span class="p">.</span><span class="n">quantity</span> <span class="p">=</span> <span class="n">quantity</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>CartItem 类的初始化方法先对传入参数 <code>quantity</code> 的值进行判断，小于 <code>1</code> 则为无效参数，然后 <code>return nil</code>（初始化失败），大于或等于 <code>1</code> 则继续调用父类 <code>Product</code> 的初始化方法，再次判断传入参数 <code>name</code>，为空则 <code>return nil</code>（初始化失败），否则继续初始化。</p>
<p>总的来说，可失败初始化器的设定，是在保证安全性的基础上提供了逻辑上更清晰的初始化方式。<code>Failable Initializers</code> 所有的结果都将是 <code>T?</code> 类型，通过 <code>Optional Binding</code> 方式，我们就能知道初始化是否成功，并安全地使用它们了。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://huizhao.win/2016/11/13/swift-init/">从 Swift 初始化说起</a></li>
<li><a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html">The Swift Programming Language (Swift 4.1) Initialization</a></li>
<li><a href="https://draveness.me/swift-zhong-init-de-shi-yong">Swift 类构造器的使用</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>【Git 权威指南】读书笔记 - 协同模型</title>
      <link>https://zyf.im/2018/01/19/got-git-reading-notes-model/</link>
      <pubDate>Fri, 19 Jan 2018 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/01/19/got-git-reading-notes-model/</guid>
      <description>&lt;p&gt;主要内容：【Git 协同模型】&lt;/p&gt;
&lt;h2 id=&#34;经典-git-协同模型&#34;&gt;经典 Git 协同模型&lt;/h2&gt;
&lt;h3 id=&#34;集中式协同模型&#34;&gt;集中式协同模型&lt;/h3&gt;
&lt;p&gt;可以像集中式版本控制系统那样使用 Git，在一个大家都可以访问到的服务器上架设 Git 服务器，每个人从该服务器克隆代码，本地提交推送到服务器上。&lt;/p&gt;
&lt;h2 id=&#34;金字塔式协同模型&#34;&gt;金字塔式协同模型&lt;/h2&gt;
&lt;p&gt;虽然理论上每个开发者的版本库都是平等的，但是会有一个公认的权威的版本库，这个版本库由一个或者多个核心开发者负责维护（具有推送的权限）。&lt;/p&gt;
&lt;p&gt;开源社区逐渐发展出金字塔模型，而这也是必然之选。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;topgit-协同模型&#34;&gt;Topgit 协同模型&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;笔者注：Topgit 是否已经过时？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;卖主分支 Vendor Branch&lt;/code&gt; 是在版本库中专门创建一个和上游同步的分支，一旦有上游代码发布就捡入到卖主分支中。&lt;/p&gt;
&lt;h2 id=&#34;子模组协同模型&#34;&gt;子模组协同模型&lt;/h2&gt;
&lt;h3 id=&#34;创建子模组&#34;&gt;创建子模组&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git submodule add /path/to/repos/libA.git lib/lib_a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;.gitmodules&lt;/code&gt; 的内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat .gitmodules
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;submodule &lt;span class=&#34;s2&#34;&gt;&amp;#34;lib/lib_a&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; lib/lib_a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; /path/to/repos/libA.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;克隆带子模组的版本库&#34;&gt;克隆带子模组的版本库&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone /path/to/repos/super.git /path/to/my/workspace/super-clone
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;子模组的版本库并不会默认克隆，如果需要克隆出子模组型式引用的外部库，需要执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git submodule init
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git submodule update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;在子模组中修改和子模组的更新&#34;&gt;在子模组中修改和子模组的更新&lt;/h3&gt;
&lt;p&gt;修改更新的方式和普通仓库一样。如果修改了子模块，要先推送子模块的修改，再推送主仓库，以防止其他人克隆 super 版本库、更新模组时因为找不到该子模组版本库相应的提交而导致出错。&lt;/p&gt;
&lt;p&gt;查看子模组状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git submodule status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;子树合并&#34;&gt;子树合并&lt;/h2&gt;
&lt;h3 id=&#34;引入外部版本库&#34;&gt;引入外部版本库&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 注册外部版本库&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git remote add util /path/to/repos/util.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git fetch util
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看所有分支&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git branch -a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 从 util/master 远程分支创建一个本地分支 util-branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git checkout -b util-branch util/master
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;子目录方式合并外部版本库&#34;&gt;子目录方式合并外部版本库&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 在主分支，将分支 util-branch 读取到当前分支的一个子目录下&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git read-tree --prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;lib util-branch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将 lib 目录下的文件更新出来&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git checkout -- lib
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在还不能忙着提交，因为如果现在进行提交就体现不出来两个分支的合并关系。需要使用 Git 底层的命令进行数据提交。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>主要内容：【Git 协同模型】</p>
<h2 id="经典-git-协同模型">经典 Git 协同模型</h2>
<h3 id="集中式协同模型">集中式协同模型</h3>
<p>可以像集中式版本控制系统那样使用 Git，在一个大家都可以访问到的服务器上架设 Git 服务器，每个人从该服务器克隆代码，本地提交推送到服务器上。</p>
<h2 id="金字塔式协同模型">金字塔式协同模型</h2>
<p>虽然理论上每个开发者的版本库都是平等的，但是会有一个公认的权威的版本库，这个版本库由一个或者多个核心开发者负责维护（具有推送的权限）。</p>
<p>开源社区逐渐发展出金字塔模型，而这也是必然之选。</p>
<!-- more -->
<h2 id="topgit-协同模型">Topgit 协同模型</h2>
<blockquote>
<p>笔者注：Topgit 是否已经过时？</p>
</blockquote>
<p><code>卖主分支 Vendor Branch</code> 是在版本库中专门创建一个和上游同步的分支，一旦有上游代码发布就捡入到卖主分支中。</p>
<h2 id="子模组协同模型">子模组协同模型</h2>
<h3 id="创建子模组">创建子模组</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git submodule add /path/to/repos/libA.git lib/lib_a
</span></span></code></pre></div><p><code>.gitmodules</code> 的内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat .gitmodules
</span></span><span class="line"><span class="cl"><span class="o">[</span>submodule <span class="s2">&#34;lib/lib_a&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">        <span class="nv">path</span> <span class="o">=</span> lib/lib_a
</span></span><span class="line"><span class="cl">        <span class="nv">url</span> <span class="o">=</span> /path/to/repos/libA.git
</span></span></code></pre></div><h3 id="克隆带子模组的版本库">克隆带子模组的版本库</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone /path/to/repos/super.git /path/to/my/workspace/super-clone
</span></span></code></pre></div><p>子模组的版本库并不会默认克隆，如果需要克隆出子模组型式引用的外部库，需要执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git submodule init
</span></span><span class="line"><span class="cl">git submodule update
</span></span></code></pre></div><h3 id="在子模组中修改和子模组的更新">在子模组中修改和子模组的更新</h3>
<p>修改更新的方式和普通仓库一样。如果修改了子模块，要先推送子模块的修改，再推送主仓库，以防止其他人克隆 super 版本库、更新模组时因为找不到该子模组版本库相应的提交而导致出错。</p>
<p>查看子模组状态：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git submodule status
</span></span></code></pre></div><h2 id="子树合并">子树合并</h2>
<h3 id="引入外部版本库">引入外部版本库</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 注册外部版本库</span>
</span></span><span class="line"><span class="cl">git remote add util /path/to/repos/util.git
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git fetch util
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看所有分支</span>
</span></span><span class="line"><span class="cl">git branch -a
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 从 util/master 远程分支创建一个本地分支 util-branch</span>
</span></span><span class="line"><span class="cl">git checkout -b util-branch util/master
</span></span></code></pre></div><h3 id="子目录方式合并外部版本库">子目录方式合并外部版本库</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 在主分支，将分支 util-branch 读取到当前分支的一个子目录下</span>
</span></span><span class="line"><span class="cl">git read-tree --prefix<span class="o">=</span>lib util-branch
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 将 lib 目录下的文件更新出来</span>
</span></span><span class="line"><span class="cl">git checkout -- lib
</span></span></code></pre></div><p>现在还不能忙着提交，因为如果现在进行提交就体现不出来两个分支的合并关系。需要使用 Git 底层的命令进行数据提交。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 将暂存区的目录树保存下来</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git write-tree
</span></span></code></pre></div><p>要手工创建一个合并提交，即新的提交要有两个父提交。这两个父提交分别是 master 分支和 util-branch 分支的最新提交。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;subtree merge&#34;</span> <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  git commit-tree 2153518409d218609af40babededec6e8ef51616 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -p 911b1af2e0c95a2fc1306b8dea707064d5386c2e <span class="se">\
</span></span></span><span class="line"><span class="cl">  -p 12408a149bfa78a4c2d4011f884aa2adb04f0934
</span></span><span class="line"><span class="cl">62ae6cc3f9280418bdb0fcf6c1e678905b1fe690
</span></span></code></pre></div><p>需要把当前的 master 分支重置到此提交 ID：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset 62ae6cc3f9280418bdb0fcf6c1e678905b1fe690
</span></span></code></pre></div><p>操作繁琐，可使用下面 <code>subtree</code> 命令进行代替。</p>
<h3 id="利用子树合并跟踪上游改动">利用子树合并跟踪上游改动</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout util-branch
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git pull
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git checkout master
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git merge -Xsubtree<span class="o">=</span>lib util-branch
</span></span></code></pre></div><h2 id="git-subtree">Git Subtree</h2>
<h3 id="管理子项目">管理子项目</h3>
<p>假设 <code>P1 项目</code>、<code>P2 项目</code> 共用 <code>S 项目</code></p>
<ul>
<li>关联 S 项目</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git subtree add --prefix<span class="o">=</span>&lt;S项目的相对路径&gt; &lt;S项目git地址&gt; &lt;分支&gt; --squash
</span></span></code></pre></div><p><code>--squash</code> 意思是把 <code>subtree</code> 的改动合并成一次 <code>commit</code>，这样就不用拉取子项目完整的历史记录。<code>--prefix</code> 之后的 <code>=</code> 等号也可以用空格。</p>
<ul>
<li>更新代码</li>
</ul>
<p>P1、P2 项目里各种提交 commit，其中有些 commit 会涉及到 <code>S 目录</code> 的更改，但是没关系。</p>
<ul>
<li>提交更改到子项目</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git subtree push --prefix<span class="o">=</span>&lt;S项目的相对路径&gt; &lt;S项目git地址&gt; &lt;分支&gt;
</span></span></code></pre></div><p>Git 会遍历 <code>步骤 2</code> 中所有的 <code>commit</code>，从中找出针对 <code>S 目录</code> 的更改，然后把这些更改记录提交到 <code>S 项目</code> 的 Git 服务器上，并保留 <code>步骤 2</code> 中的相关 <code>S 的提交</code> 记录到 <code>S仓库</code> 中。</p>
<ul>
<li>更新子目录</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git subtree pull --prefix<span class="o">=</span>&lt;S项目的相对路径&gt; &lt;S 项目 git 地址&gt; &lt;分支&gt; --squash
</span></span></code></pre></div><h3 id="拆分已有项目">拆分已有项目</h3>
<p>需要从现有项目中抽取公共模块单独进行 Git 管理，假设已有 <code>项目 P</code> 抽取 <code>项目 S</code>。</p>
<ul>
<li>提交日志分离</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git subtree split -P &lt;S项目的相对路径&gt; -b &lt;临时branch&gt;
</span></span></code></pre></div><p>Git 会遍历所有的 commit，分离出与 S 项目的相对路径相关的 commit，并存入临时 branch 中。</p>
<ul>
<li>创建子 repo</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir &lt;S项目新路径&gt;
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> S项目新路径
</span></span><span class="line"><span class="cl">git init
</span></span><span class="line"><span class="cl">git pull &lt;P项目的路径&gt; &lt;临时branch&gt;
</span></span><span class="line"><span class="cl">git remote add origin &lt;S项目的git仓库&gt;
</span></span><span class="line"><span class="cl">git push origin -u master
</span></span></code></pre></div><ul>
<li>清理数据</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> P项目的路径
</span></span><span class="line"><span class="cl">git rm -rf &lt;S项目的相对路径&gt;
</span></span><span class="line"><span class="cl">git commit -m <span class="s1">&#39;移除相应模块&#39;</span> <span class="c1"># 提交删除申请</span>
</span></span><span class="line"><span class="cl">git branch -D &lt;临时branch&gt; <span class="c1"># 删除临时分支</span>
</span></span></code></pre></div><ul>
<li>添加 subtree</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git subtree add --prefix<span class="o">=</span>&lt;S项目的相对路径&gt; &lt;S项目git地址&gt; &lt;分支&gt; --squash
</span></span><span class="line"><span class="cl">git push origin master
</span></span></code></pre></div><p>执行完第 2 步时，对应的目录已经剥离出来形成独立的项目了。第 3，4 步主要是把当前项目的对应的文件给删除，重新在 P 项目建立 Subtree。</p>
<p><em>tip:</em>
推送到 GitHub Page：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git subtree push --prefix<span class="o">=</span>dist origin gh-pages
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="http://www.worldhello.net/gotgit/04-git-model/">4. 协同模型 — GotGit</a></li>
<li><a href="https://www.jianshu.com/p/3096069e9b72">Git Subtree 的使用</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>【Git 权威指南】读书笔记 - 和声</title>
      <link>https://zyf.im/2018/01/17/got-git-reading-notes-harmony/</link>
      <pubDate>Wed, 17 Jan 2018 19:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/01/17/got-git-reading-notes-harmony/</guid>
      <description>&lt;p&gt;主要内容：【Git 协议与工作协同】、【冲突解决】、【Git 里程碑】、【Git 分支】、【远程版本库】、【补丁文件交互】&lt;/p&gt;
&lt;h2 id=&#34;git-协议与工作协同&#34;&gt;Git 协议与工作协同&lt;/h2&gt;
&lt;h3 id=&#34;git-支持的协议&#34;&gt;Git 支持的协议&lt;/h3&gt;
&lt;p&gt;SSH、GIT、HTTP、HTTPS、FTP、FTPS、RSYNC 及前面已经看到的本地协议。&lt;/p&gt;
&lt;p&gt;SSH 协议：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ssh://[user@]example.com[:port]/path/to/repo.git/

[user@]example.com:path/to/repo.git/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;GIT 协议，最常用的只读协议：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git://example.com[:port]/path/to/repo.git/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;HTTP[S] 协议：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;http[s]://example.com[:port]/path/to/repo.git/
&lt;/code&gt;&lt;/pre&gt;&lt;!-- more --&gt;
&lt;h3 id=&#34;强制非快进式推送&#34;&gt;强制非快进式推送&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git push -f
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;强制推送，会强制刷新服务器中的版本。&lt;/p&gt;
&lt;h3 id=&#34;禁止非快进式推送&#34;&gt;禁止非快进式推送&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git --git-dir=/path/to/repos/shared.git config receive.denyNonFastForwards true
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;冲突解决&#34;&gt;冲突解决&lt;/h2&gt;
&lt;h3 id=&#34;拉回操作中的合并&#34;&gt;拉回操作中的合并&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git pull = git fetch + git merge
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;合并策略&#34;&gt;合并策略&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://git-scm.com/docs/git-merge#_merge_strategies&#34;&gt;Merge Strategis&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Git 合并操作支持很多合并策略，默认会选择最适合的合并策略。例如，和一个分支进行合并时会选择 recursive 合并策略，当和两个或两个以上的其他分支进行合并时采用 octopus 合并策略。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git merge [-s &amp;lt;strategy&amp;gt;] [-X &amp;lt;strategy-option&amp;gt;] [&amp;lt;commit&amp;gt;...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This option forces conflicting hunks to be auto-resolved cleanly by favoring our version.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git merge -s recursive -X ours [&amp;lt;commit&amp;gt;...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Merge branch obsolete into the current branch, using ours merge strategy:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>主要内容：【Git 协议与工作协同】、【冲突解决】、【Git 里程碑】、【Git 分支】、【远程版本库】、【补丁文件交互】</p>
<h2 id="git-协议与工作协同">Git 协议与工作协同</h2>
<h3 id="git-支持的协议">Git 支持的协议</h3>
<p>SSH、GIT、HTTP、HTTPS、FTP、FTPS、RSYNC 及前面已经看到的本地协议。</p>
<p>SSH 协议：</p>
<pre tabindex="0"><code>ssh://[user@]example.com[:port]/path/to/repo.git/

[user@]example.com:path/to/repo.git/
</code></pre><p>GIT 协议，最常用的只读协议：</p>
<pre tabindex="0"><code>git://example.com[:port]/path/to/repo.git/
</code></pre><p>HTTP[S] 协议：</p>
<pre tabindex="0"><code>http[s]://example.com[:port]/path/to/repo.git/
</code></pre><!-- more -->
<h3 id="强制非快进式推送">强制非快进式推送</h3>
<pre tabindex="0"><code>git push -f
</code></pre><p>强制推送，会强制刷新服务器中的版本。</p>
<h3 id="禁止非快进式推送">禁止非快进式推送</h3>
<pre tabindex="0"><code>git --git-dir=/path/to/repos/shared.git config receive.denyNonFastForwards true
</code></pre><h2 id="冲突解决">冲突解决</h2>
<h3 id="拉回操作中的合并">拉回操作中的合并</h3>
<pre tabindex="0"><code>git pull = git fetch + git merge
</code></pre><h3 id="合并策略">合并策略</h3>
<blockquote>
<p><a href="https://git-scm.com/docs/git-merge#_merge_strategies">Merge Strategis</a></p>
</blockquote>
<p>Git 合并操作支持很多合并策略，默认会选择最适合的合并策略。例如，和一个分支进行合并时会选择 recursive 合并策略，当和两个或两个以上的其他分支进行合并时采用 octopus 合并策略。</p>
<pre tabindex="0"><code>git merge [-s &lt;strategy&gt;] [-X &lt;strategy-option&gt;] [&lt;commit&gt;...]
</code></pre><p>This option forces conflicting hunks to be auto-resolved cleanly by favoring our version.</p>
<pre tabindex="0"><code>git merge -s recursive -X ours [&lt;commit&gt;...]
</code></pre><p>Merge branch obsolete into the current branch, using ours merge strategy:</p>
<pre tabindex="0"><code>git merge -s ours obsolete
</code></pre><h2 id="git-里程碑">Git 里程碑</h2>
<h3 id="显示里程碑">显示里程碑</h3>
<pre tabindex="0"><code>git tag
</code></pre><p>显示说明。<code>-n&lt;num&gt;</code> 显示最多 <code>&lt;num&gt;</code> 行里程碑的说明：</p>
<pre tabindex="0"><code>git tag -n1
</code></pre><p>使用统配：</p>
<pre tabindex="0"><code>git tag -l v1.*
</code></pre><p>查看提交对应的里程碑及其他引用：</p>
<pre tabindex="0"><code>git log --oneline --decorate
</code></pre><h3 id="创建里程碑">创建里程碑</h3>
<p>当创建了里程碑 mytag 后，会在版本库的 .git/refs/tags 目录下创建一个新文件。实际上指向的是一个提交：</p>
<pre tabindex="0"><code>git tag mytag

git cat-file -t mytag
commit
</code></pre><p>带说明的 tag 指向的不再是一个提交，而是一个 tag 对象：</p>
<pre tabindex="0"><code>git tag -m &#34;My first annotated tag.&#34; mytag2

git cat-file -t mytag2
tag
</code></pre><p>为里程碑对象添加 GnuPG 签名：</p>
<pre tabindex="0"><code>git tag -s -m &#34;My first GPG-signed tag.&#34; mytag3
</code></pre><h3 id="删除里程碑">删除里程碑</h3>
<pre tabindex="0"><code>git tag -d mytag
Deleted tag &#39;mytag&#39; (was 60a2f4f)
</code></pre><h3 id="共享里程碑">共享里程碑</h3>
<p>创建的里程碑，默认只在本地版本库中可见，不会因为对分支执行推送而将里程碑也推送到远程版本库。</p>
<p>将 mytag 里程碑共享到上游版本库：</p>
<pre tabindex="0"><code>git push origin mytag
</code></pre><p>所有里程碑全部推送到远程版本库：</p>
<pre tabindex="0"><code>git push origin refs/tags/*
</code></pre><ul>
<li>里程碑共享，必须显式的推送。</li>
<li>执行获取或拉回操作，自动从远程版本库获取新里程碑，并在本地版本库中创建。</li>
<li>如果本地已有同名的里程碑，默认不会从上游同步里程碑，即使两者里程碑的指向是不同的。</li>
</ul>
<h3 id="删除远程版本库的里程碑">删除远程版本库的里程碑</h3>
<pre tabindex="0"><code>git push origin :mytag2
</code></pre><h3 id="里程碑命名规范">里程碑命名规范</h3>
<p><a href="https://semver.org/lang/zh-CN/">语义化版本 2.0.0</a></p>
<p>版本格式：主版本号.次版本号.修订号，版本号递增规则如下：</p>
<ul>
<li>主版本号：当你做了不兼容的 API 修改，</li>
<li>次版本号：当你做了向下兼容的功能性新增，</li>
<li>修订号：当你做了向下兼容的问题修正。</li>
</ul>
<p>先行版本号及版本编译信息可以加到 <code>主版本号.次版本号.修订号</code> 的后面，作为延伸。</p>
<h2 id="git-分支">Git 分支</h2>
<p>分支是代码管理的利器。如果没有有效的分支管理，代码管理就适应不了复杂的开发过程和项目的需要。</p>
<h3 id="分支命令概述">分支命令概述</h3>
<pre tabindex="0"><code># 显示
1. git branch

# 创建
2. git branch &lt;branchname&gt;
3. git branch &lt;branchname&gt; &lt;start-point&gt;

# 删除，-d 在删除分支 &lt;branchname&gt; 时会检查所要删除的分支是否已经合并到其他分支中，否则拒绝删除。
4. git branch -d &lt;branchname&gt;
5. git branch -D &lt;branchname&gt;

# 重命名，-m 如果版本库中已经存在名为 &lt;newbranch&gt; 的分支，拒绝执行重命名，而 7 会强制执行。
6. git branch -m &lt;oldbranch&gt; &lt;newbranch&gt;
7. git branch -M &lt;oldbranch&gt; &lt;newbranch&gt;
</code></pre><p>创建并切换到新分支：</p>
<pre tabindex="0"><code>git checkout -b &lt;new_branch&gt; [&lt;start_point&gt;]
</code></pre><h3 id="分支变基">分支变基</h3>
<ul>
<li>master</li>
<li>dev（开发完成，领先 master）</li>
</ul>
<pre tabindex="0"><code># 先切换到 dev
git checkout dev

# 变基操作
git rebase master

# 遇到冲突，解决冲突

# 添加到暂存区
git add -u

# 继续变基
git rebase --continue

# 直接将 dev 推送到远程 master
git push origin dev:master

# 切换到 master
git checkout master

# 拉取最新代码
git pull

# 删除 dev 分支
git branch -d dev
</code></pre><h2 id="远程版本库">远程版本库</h2>
<h3 id="远程分支">远程分支</h3>
<p>查看远程分支：</p>
<pre tabindex="0"><code>git branch -r
</code></pre><p>在向远程版本库执行获取操作时，不是把远程版本库的分支原封不动地复制到本地版本库的分支中，而是复制到另外的命名空间。</p>
<p>远程分支不是真正意义上的分支，是类似于里程碑一样的引用。如果针对远程分支执行检出命令，会看到大段的错误警告。</p>
<p>远程分支类似于里程碑，如果检出就会使得头指针 HEAD 处于分离头指针状态。实际上除了以 refs/heads 为前缀的引用之外，如果检出任何其他引用，都将使工作区处于分离头指针状态。如果对远程分支进行修改就需要创建新的本地分支。</p>
<h3 id="分支追踪">分支追踪</h3>
<p>为了能够在远程分支 <code>origin/hello-1.x</code> 上进行工作，需要基于该远程分支创建本地分支：</p>
<pre tabindex="0"><code>git checkout hello-1.x
</code></pre><p>从远程分支创建本地分支，自动建立了分支间的跟踪，而从一个本地分支创建另外一个本地分支则没有。</p>
<p>从 <code>hello-1.x</code> 分支中创建新的本地分支 <code>hello-jx</code>，并与远程建立联系。</p>
<pre tabindex="0"><code>git checkout -b hello-jx hello-1.x
</code></pre><pre tabindex="0"><code>cat .git/config
</code></pre><h3 id="远程版本库-1">远程版本库</h3>
<pre tabindex="0"><code># 设置
git remote add new-remote file:///path/to/repos/hello-user1.git

# 修改 url
git remote set-url new-remote file:///path/to/repos/hello-user2.git

# 单独修改 push 地址
git remote set-url --push new-remote /path/to/repos/hello-user2.git

# 修改 版本库名称
git remote rename new-remote user2

# 当注册了多个远程版本库并希望获取所有远程版本库的更新
git remote update

# 如果某个远程版本库不想在执行 git remote update 时获得更新
git config remote.user2.skipDefaultUpdate true

# 删除
git remote rm
</code></pre><h3 id="push-和-pull-操作与远程版本库">PUSH 和 PULL 操作与远程版本库</h3>
<p>在执行 <code>git pull</code> 操作的时候可以通过参数 <code>--rebase</code> 设置使用变基而非合并操作，将本地分支的改动变基到跟踪分支上。为了避免因为忘记使用 <code>--rebase</code> 参数导致分支的合并，可进行设置：</p>
<pre tabindex="0"><code>git config branch.&lt;branchname&gt;.rebase true
</code></pre><p>这样在遇到冲突（本地和远程分支出现偏离）的情况下，会采用变基操作，而不是默认的合并操作。</p>
<p>如果为本地版本库设置参数 <code>branch.autosetuprebase</code> ，值为 <code>true</code>，则在基于远程分支建立本地追踪分支时，会自动配置 <code>branch.&lt;branchname&gt;.rebase</code> 参数。</p>
<h3 id="分支和里程碑的安全性">分支和里程碑的安全性</h3>
<ul>
<li>
<p>用 <code>reflog</code> 记录对分支的操作历史。默认创建的带工作区的版本库都会包含 <code>core.logallrefupdates</code> 为 <code>true</code> 的配置，这样在版本库中建立的每个分支都会创建对应的 <code>reflog</code>。但是创建的裸版本库默认不包含这个设置，也就不会为每个分支设置 <code>reflog</code>。可能因为分支误操作导致数据丢失，可以考虑为裸版本库添加此配置。</p>
</li>
<li>
<p>关闭非快进式提交。配置 <code>receive.denyNonFastForwards</code> 设置为 <code>true</code>，则禁止一切非快进式推送。更好的方法是通过架设基于 SSH 协议的 Git 服务器，配置强制提交的用户权限。</p>
</li>
<li>
<p>关闭分支删除功能。配置 <code>receive.denyDeletes</code> 设置为 <code>true</code>，则禁止删除分支。更好的方法是：配置分支删除的用户权限。</p>
</li>
</ul>
<h2 id="补丁文件交互">补丁文件交互</h2>
<p>将最近三个提交转换为补丁文件：</p>
<pre tabindex="0"><code>git format-patch -s HEAD~3..HEAD
</code></pre><p><code>-s</code> 会在导出的补丁文件中添加当前用户的签名。</p>
<p>应用补丁：</p>
<pre tabindex="0"><code>git am user1-mail-archive
</code></pre><p><code>git apply</code> 可以应用一般格式的补丁文件，但是不能执行提交，也不能保持补丁中的作者信息。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://www.worldhello.net/gotgit/03-git-harmony">3. Git 和声 — GotGit</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>iOS 开发技巧与工具推荐</title>
      <link>https://zyf.im/2018/01/12/ios-dev-tips-and-tools/</link>
      <pubDate>Fri, 12 Jan 2018 13:00:00 +0000</pubDate>
      <guid>https://zyf.im/2018/01/12/ios-dev-tips-and-tools/</guid>
      <description>&lt;h2 id=&#34;状态栏&#34;&gt;状态栏&lt;/h2&gt;
&lt;h3 id=&#34;help&#34;&gt;Help&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Search&lt;/code&gt; 框可以方便地检索相关设置项。&lt;/p&gt;
&lt;img src=&#34;https://user-images.githubusercontent.com/9289792/80204076-c811a680-865a-11ea-8c0b-5e3b70ae22bd.jpg&#34; alt=&#34;xcode-unknown-tips-Search&#34; width=&#34;960px&#34; /&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;navigator&#34;&gt;Navigator&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;⌘ 1&lt;/code&gt; .. &lt;code&gt;⌘ 7&lt;/code&gt; 可以切换窗口。&lt;/p&gt;
&lt;h3 id=&#34;show-the-symbol-navigator&#34;&gt;Show the Symbol navigator&lt;/h3&gt;
&lt;p&gt;在类不多时，可以方便查看所有类的结构；类多时可使用下方的 filter 功能过滤。&lt;/p&gt;
&lt;img src=&#34;https://user-images.githubusercontent.com/9289792/80204084-ca740080-865a-11ea-8fb0-e102827a3123.jpg&#34; alt=&#34;xcode-unknown-tips-Show the Symbol navigator&#34; width=&#34;260px&#34; /&gt;
&lt;h3 id=&#34;show-the-breakpoint-navigator&#34;&gt;Show the Breakpoint navigator&lt;/h3&gt;
&lt;p&gt;点击右下角 &lt;code&gt;+&lt;/code&gt; 选择 &lt;code&gt;Exception Breakpoint..&lt;/code&gt;，可以添加更明确的报错断点，支持选择 Objective-C 或 Swift 语言。&lt;/p&gt;
&lt;img src=&#34;https://user-images.githubusercontent.com/9289792/80204086-cb0c9700-865a-11ea-93b5-7fc0edd67daf.jpg&#34; alt=&#34;xcode-unknown-tips-Show the Breakpoint navigator&#34; width=&#34;260px&#34; /&gt;
&lt;h2 id=&#34;utilities&#34;&gt;Utilities&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;⌘ ⌥ 1&lt;/code&gt; .. &lt;code&gt;⌘ ⌥ 7&lt;/code&gt; 可以切换窗口。&lt;/p&gt;
&lt;h3 id=&#34;show-the-code-snippet-library&#34;&gt;Show the Code Snippet Library&lt;/h3&gt;
&lt;p&gt;代码片段模板，也可以将自己写好的代码片段拖入。&lt;/p&gt;
&lt;img src=&#34;https://user-images.githubusercontent.com/9289792/80204090-cba52d80-865a-11ea-9d1f-d6f772fc7e1c.jpg&#34; alt=&#34;xcode-unknown-tips-Show the Code Snippet Labrary&#34; width=&#34;260px&#34; /&gt;
&lt;h3 id=&#34;show-the-media-library&#34;&gt;Show the Media Library&lt;/h3&gt;
&lt;p&gt;可以直接拖拽图片到 xib 中，自动创建 UIImageView。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="状态栏">状态栏</h2>
<h3 id="help">Help</h3>
<p><code>Search</code> 框可以方便地检索相关设置项。</p>
<img src="https://user-images.githubusercontent.com/9289792/80204076-c811a680-865a-11ea-8c0b-5e3b70ae22bd.jpg" alt="xcode-unknown-tips-Search" width="960px" />
<!-- more -->
<h2 id="navigator">Navigator</h2>
<p><code>⌘ 1</code> .. <code>⌘ 7</code> 可以切换窗口。</p>
<h3 id="show-the-symbol-navigator">Show the Symbol navigator</h3>
<p>在类不多时，可以方便查看所有类的结构；类多时可使用下方的 filter 功能过滤。</p>
<img src="https://user-images.githubusercontent.com/9289792/80204084-ca740080-865a-11ea-8fb0-e102827a3123.jpg" alt="xcode-unknown-tips-Show the Symbol navigator" width="260px" />
<h3 id="show-the-breakpoint-navigator">Show the Breakpoint navigator</h3>
<p>点击右下角 <code>+</code> 选择 <code>Exception Breakpoint..</code>，可以添加更明确的报错断点，支持选择 Objective-C 或 Swift 语言。</p>
<img src="https://user-images.githubusercontent.com/9289792/80204086-cb0c9700-865a-11ea-93b5-7fc0edd67daf.jpg" alt="xcode-unknown-tips-Show the Breakpoint navigator" width="260px" />
<h2 id="utilities">Utilities</h2>
<p><code>⌘ ⌥ 1</code> .. <code>⌘ ⌥ 7</code> 可以切换窗口。</p>
<h3 id="show-the-code-snippet-library">Show the Code Snippet Library</h3>
<p>代码片段模板，也可以将自己写好的代码片段拖入。</p>
<img src="https://user-images.githubusercontent.com/9289792/80204090-cba52d80-865a-11ea-9d1f-d6f772fc7e1c.jpg" alt="xcode-unknown-tips-Show the Code Snippet Labrary" width="260px" />
<h3 id="show-the-media-library">Show the Media Library</h3>
<p>可以直接拖拽图片到 xib 中，自动创建 UIImageView。</p>
<h2 id="xib">xib</h2>
<h3 id="快捷键">快捷键</h3>
<p>按住 <code>option</code> 拖拽可以快速复制组件。</p>
<h2 id="快捷键-1">快捷键</h2>
<h3 id="option-">option ⌥</h3>
<ul>
<li>按住 <code>⌥</code> 并点击代码或方法时，可查看行内文档帮助。</li>
<li>按住 <code>⌥</code> 点击文件可以在辅助编辑窗模式打开文件（多栏显示，适用于 xib 与代码绑定）。<em>推荐</em></li>
<li>按住 <code>⌥ ⇧</code> 点击文件可以选择文件打开的窗口位置。</li>
</ul>
<h3 id="显示和隐藏栏">显示和隐藏栏</h3>
<ul>
<li><code>⌘ ⇧ y</code> 显示/隐藏调试区域。</li>
<li><code>⌘ ⌥ ⏎</code> 显示辅助编辑栏，Open the assistant editor</li>
<li><code>⌘ ⏎</code> 隐藏辅助编辑器栏。</li>
</ul>
<h3 id="组合键">组合键</h3>
<blockquote>
<p>^ 为 control，⌥ 为 option</p>
</blockquote>
<ul>
<li><code>⌘ ⌥ {</code> <code>⌘ ⌥ }</code> 整行上下移动代码。</li>
<li>将光标放在方法名上任一位置，点开 <code>Edit</code>，按住 <code>⌃ ⇧</code> 将看到 <code>Copy Qualified Symbol Name</code>，按住 <code>⌃ ⇧ ⌥</code> 将看到 <code>Copy Symbol Name</code>。例如 <code>[UIColor colorWithRed:255/255.0f green:127/255.0f blue:80/255.0f alpha:1]</code> 将被拷贝为 <code>+[UIColor colorWithRed:green:blue:alpha:]</code>。</li>
<li>自动缩进代码：<code>^ i</code>，或全选 <code>⌘ a</code> 后 <code>^ i</code>。</li>
<li><code>⌘ ⇧ o</code> Open Quickly 快速查找文件。</li>
</ul>
<h2 id="uikit-技巧">UIKit 技巧</h2>
<h3 id="uitableviewcell-自适应-uitextview-高度">UITableViewCell 自适应 UITextView 高度</h3>
<p>使用 Auto Layout 实现：</p>
<ol>
<li>给 textView 上下左右建立相对于 cell 的约束</li>
<li>取消 textView 的 <code>Scrolling Enabled</code></li>
<li>设置 <code>tableView.estimatedRowHeight = 70</code></li>
<li>设置 <code>textView.delegate = self</code></li>
</ol>
<p>关键代码（避免 <code>reloadData</code> 导致键盘隐藏和跳动）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">textViewDidChange</span><span class="p">(</span><span class="kc">_</span> <span class="n">textView</span><span class="p">:</span> <span class="n">UITextView</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">currentOffset</span> <span class="p">=</span> <span class="n">tableView</span><span class="p">.</span><span class="n">contentOffset</span>
</span></span><span class="line"><span class="cl">    <span class="n">UIView</span><span class="p">.</span><span class="n">setAnimationsEnabled</span><span class="p">(</span><span class="kc">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">tableView</span><span class="p">.</span><span class="n">beginUpdates</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">tableView</span><span class="p">.</span><span class="n">endUpdates</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">UIView</span><span class="p">.</span><span class="n">setAnimationsEnabled</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">tableView</span><span class="p">.</span><span class="n">setContentOffset</span><span class="p">(</span><span class="n">currentOffset</span><span class="p">,</span> <span class="n">animated</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="command-line">Command Line</h2>
<ul>
<li><code>security find-identity -v</code> 显示已安装的有效证书身份。</li>
</ul>
<h2 id="常见问题">常见问题</h2>
<h3 id="too-many-symbol-files">Too many symbol files</h3>
<p>上传 App Store 后收到此警告，通常是因为三方库生成了冗余的 32 位 symbols 文件。</p>
<p>解决方法：在 <code>Podfile</code> 中添加：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">post_install</span> <span class="k">do</span> <span class="o">|</span><span class="n">installer</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">installer</span><span class="o">.</span><span class="n">pods_project</span><span class="o">.</span><span class="n">targets</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">target</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">target</span><span class="o">.</span><span class="n">build_configurations</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">config</span><span class="o">.</span><span class="n">build_settings</span><span class="o">[</span><span class="s1">&#39;ARCHS&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;arm64&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>检查 <code>info.plist</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;key&gt;</span>UIRequiredDeviceCapabilities<span class="nt">&lt;/key&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;array&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;string&gt;</span>arm64<span class="nt">&lt;/string&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/array&gt;</span>
</span></span></code></pre></div><p>在 Build Settings 中搜索 <code>valid architecture</code>，填写 <code>arm64</code>。</p>
<h2 id="库推荐">库推荐</h2>
<h3 id="rswift---强类型引用资源">R.swift - 强类型引用资源</h3>
<p><a href="https://github.com/mac-cain13/R.swift">R.swift</a> 获取强类型、自动编译的图片、字体、segues 等资源，避免字符串引用导致的运行时错误。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">// 传统方式（字符串，无编译检查）</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">icon</span> <span class="p">=</span> <span class="n">UIImage</span><span class="p">(</span><span class="n">named</span><span class="p">:</span> <span class="s">&#34;settings-icon&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">font</span> <span class="p">=</span> <span class="n">UIFont</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="s">&#34;San Francisco&#34;</span><span class="p">,</span> <span class="n">size</span><span class="p">:</span> <span class="mi">42</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// R.swift（强类型，编译时检查）</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">icon</span> <span class="p">=</span> <span class="n">R</span><span class="p">.</span><span class="n">image</span><span class="p">.</span><span class="n">settingsIcon</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">font</span> <span class="p">=</span> <span class="n">R</span><span class="p">.</span><span class="n">font</span><span class="p">.</span><span class="n">sanFrancisco</span><span class="p">(</span><span class="n">size</span><span class="p">:</span> <span class="mi">42</span><span class="p">)</span>
</span></span></code></pre></div><p>配置要点：</p>
<ul>
<li>添加 <code>R.generated.swift</code> 时不要勾选 <code>Copy items if needed</code></li>
<li>在 <code>.gitignore</code> 添加 <code>*.generated.swift</code></li>
<li>新增资源后需 <code>⌘ + B</code> 编译才可使用</li>
</ul>
<h3 id="promisekit---异步编程">PromiseKit - 异步编程</h3>
<p><a href="https://github.com/mxcl/PromiseKit">PromiseKit</a> 用于解决回调地狱问题：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">// Promise 链式调用</span>
</span></span><span class="line"><span class="cl"><span class="n">firstly</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">login</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}.</span><span class="n">then</span> <span class="p">{</span> <span class="n">creds</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="n">fetch</span><span class="p">(</span><span class="n">avatar</span><span class="p">:</span> <span class="n">creds</span><span class="p">.</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}.</span><span class="n">done</span> <span class="p">{</span> <span class="n">image</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="kc">self</span><span class="p">.</span><span class="n">imageView</span> <span class="p">=</span> <span class="n">image</span>
</span></span><span class="line"><span class="cl"><span class="p">}.</span><span class="k">catch</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 错误处理</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 并行操作</span>
</span></span><span class="line"><span class="cl"><span class="n">when</span><span class="p">(</span><span class="n">fulfilled</span><span class="p">:</span> <span class="n">operation1</span><span class="p">(),</span> <span class="n">operation2</span><span class="p">())</span>
</span></span></code></pre></div><p>核心概念：</p>
<ul>
<li><code>then</code> - 链式调用，返回新 Promise</li>
<li><code>done</code> - 链尾成功处理</li>
<li><code>catch</code> - 统一错误处理</li>
<li><code>when</code> - 并行等待多个 Promise</li>
<li><code>ensure</code> - 无论成功失败都执行</li>
</ul>
<h3 id="fastlane---自动化构建">fastlane - 自动化构建</h3>
<p><a href="https://fastlane.tools/">fastlane</a> 是 iOS/Android 自动化构建工具集：</p>
<ul>
<li><code>produce</code> - 创建 App ID</li>
<li><code>cert</code> / <code>sigh</code> - 管理证书和描述文件</li>
<li><code>gym</code> - 构建打包</li>
<li><code>deliver</code> - 上传 App Store</li>
<li><code>snapshot</code> - 自动截图</li>
<li><code>match</code> - 团队证书同步</li>
</ul>
<blockquote>
<p>建议直接参考 <a href="https://docs.fastlane.tools/">fastlane 官方文档</a>，并结合 Xcode Cloud 或 GitHub Actions 实现 CI/CD。</p>
</blockquote>
<h2 id="references">References</h2>
<ul>
<li><a href="https://juejin.im/post/5a7198ac51882573505189c8">[译]每个 iOS 开发者都该知道的 17 个 Xcode 小技巧</a></li>
<li><a href="https://github.com/mac-cain13/R.swift">R.swift - GitHub</a></li>
<li><a href="https://github.com/mxcl/PromiseKit/blob/master/Documentation/GettingStarted.md">PromiseKit - Getting Started</a></li>
<li><a href="https://docs.fastlane.tools/">fastlane 官方文档</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>回顾 2017</title>
      <link>https://zyf.im/2017/12/31/review-2017/</link>
      <pubDate>Sun, 31 Dec 2017 22:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/12/31/review-2017/</guid>
      <description>&lt;p&gt;2017 关键词：Birds、离职、狼人杀、白洋淀、iOS、自如。&lt;/p&gt;
&lt;iframe frameborder=&#34;no&#34; border=&#34;0&#34; marginwidth=&#34;0&#34; marginheight=&#34;0&#34; width=298 height=52 src=&#34;//music.163.com/outchain/player?type=2&amp;id=423314746&amp;auto=0&amp;height=32&#34;&gt;&lt;/iframe&gt;
&lt;h2 id=&#34;birds&#34;&gt;Birds&lt;/h2&gt;
&lt;p&gt;Birds 是自己编写时间最长的一个 Web 项目。项目接手、改版、重构、迭代、盈利，上半年每天工作都是面对它。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Birds 良好的表现与需求互相推动，这点很重要，假如有需求而项目没有成长，或项目成长没了需求，对工程来说都只能是纸上谈兵、无事可做。项目中的规范的重要性，无论是代码还是数据库，只要项目不是夭折终将显现，对于规范性我绝不会开倒车，做任何妥协，种种经历只说明：这里欠的东西总会还。如何应对需求的千变万化，小结几点：模块化、组件化思想，功能多可配置，功能可拔插，功能方法粒度要小；不要对程序肆意进行打补丁式的修改，减少对流程入侵；当某一处的逻辑增加时要及时重新修改、定义流程；对需求目的的正确理解更是能高效开发。&lt;/p&gt;
&lt;p&gt;对前端重新认识的一年。现在的前端和大学时刚刚接触时，已经大不一样了。前端能做的事情越来越多，很多业务逻辑都在前端处理，服务端只需要提供接口，分工合作更容易。浅尝 React Vue Webpack 后，也许前端才是自己的归宿。&lt;/p&gt;
&lt;h2 id=&#34;离职&#34;&gt;离职&lt;/h2&gt;
&lt;p&gt;离职的不是我。老人走，新人来，新人走。离职一词在我身边跟了一年都没有停下来。同事离职的原因很多，离开北京、公司发展、自我发展&amp;hellip; 马云说离职的原因就两个：1. 钱没给够。2. 受了委屈。我起初是认同这一说法的，但是后来觉得 “人” 或者说 “人的思想” 或者说 “人性” 在其中影响作用非常的大。相同的事情不同的角度看，结果相去甚远，如果基本的理念不同，那事情基本是无法调和的。&lt;/p&gt;
&lt;p&gt;对做的事情的认同感，对自我的定位与认识，对现实情况的冷静思考，这几点需要反复咀嚼。&lt;/p&gt;
&lt;p&gt;分享一篇文章：&lt;a href=&#34;http://stormzhang.com/2017/06/26/leave-if-no-experts/&#34;&gt;公司没大牛带，需要离职么？&lt;/a&gt;。对于刚入门的我们，这是一个很有代表性的问题。太多时候我们期待别人，忘了自己。公司有大牛，能跟着学是幸运，没有是常态。先接受了这个设定，会更容易找提高自己的方法。有没有大牛并不重要，我们是为了那个更好的自己。&lt;/p&gt;
&lt;h2 id=&#34;狼人杀&#34;&gt;狼人杀&lt;/h2&gt;
&lt;p&gt;2017 火的游戏很多：狼人杀、守望先锋、王者荣耀、吃鸡。狼人杀这一杀，好像杀到了毕业前和同学舍友在一起臭 high 的日子。可说骚话、互相 diss、互相吹捧，真真假假其乐无穷。最赤鸡的是鱼炸出了个女盆友，各路同学亲上加亲，无法克说。&lt;/p&gt;
&lt;h2 id=&#34;白洋淀&#34;&gt;白洋淀&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;我想 你说
你不要在孤单 让我做你的伴&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;白洋淀去过四次，那里的温泉过去两次，年中 Team Building 再喜加一，而这一次让这里有了美丽故事。喷泉、沙发，出现了对的人。&lt;/p&gt;
&lt;h2 id=&#34;ios&#34;&gt;iOS&lt;/h2&gt;
&lt;p&gt;React Native 的一波尝试后，走上了移动开发的道。Swift 让上 iOS 车的门槛降低不少，Swift API 趋向稳定，可以说这是最好的时候。GitHub 应该是再也放不下了，外文的书籍、文档确实靠谱啊，推荐一个教程网站 &lt;a href=&#34;https://www.raywenderlich.com/category/ios&#34;&gt;iOS Tutorials - Ray Wenderlich&lt;/a&gt; 真的零基础入门。&lt;/p&gt;
&lt;p&gt;我相信：一个人语言的界限，就是他世界的边界。&lt;/p&gt;
&lt;h2 id=&#34;自如&#34;&gt;自如&lt;/h2&gt;
&lt;p&gt;一场大火让无数人无家可归，年末换房也受牵连。被自如圈粉，其他中介的房子真的太 low 了。自如订房子居然也要抢，一波三折、失而复得。在没抢到房子时，真的想走，想离开这里了，很凄凉。最终是在公司对面住下了，过上了在家吃中饭晚饭的日子。&lt;/p&gt;
&lt;h2 id=&#34;2018-happy-new-year&#34;&gt;2018 Happy New Year&lt;/h2&gt;
&lt;p&gt;2017 自己是幸运的，2018 猥琐发育，不要浪，胜利属于伏地魔。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>2017 关键词：Birds、离职、狼人杀、白洋淀、iOS、自如。</p>
<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=298 height=52 src="//music.163.com/outchain/player?type=2&id=423314746&auto=0&height=32"></iframe>
<h2 id="birds">Birds</h2>
<p>Birds 是自己编写时间最长的一个 Web 项目。项目接手、改版、重构、迭代、盈利，上半年每天工作都是面对它。</p>
<!-- more -->
<p>Birds 良好的表现与需求互相推动，这点很重要，假如有需求而项目没有成长，或项目成长没了需求，对工程来说都只能是纸上谈兵、无事可做。项目中的规范的重要性，无论是代码还是数据库，只要项目不是夭折终将显现，对于规范性我绝不会开倒车，做任何妥协，种种经历只说明：这里欠的东西总会还。如何应对需求的千变万化，小结几点：模块化、组件化思想，功能多可配置，功能可拔插，功能方法粒度要小；不要对程序肆意进行打补丁式的修改，减少对流程入侵；当某一处的逻辑增加时要及时重新修改、定义流程；对需求目的的正确理解更是能高效开发。</p>
<p>对前端重新认识的一年。现在的前端和大学时刚刚接触时，已经大不一样了。前端能做的事情越来越多，很多业务逻辑都在前端处理，服务端只需要提供接口，分工合作更容易。浅尝 React Vue Webpack 后，也许前端才是自己的归宿。</p>
<h2 id="离职">离职</h2>
<p>离职的不是我。老人走，新人来，新人走。离职一词在我身边跟了一年都没有停下来。同事离职的原因很多，离开北京、公司发展、自我发展&hellip; 马云说离职的原因就两个：1. 钱没给够。2. 受了委屈。我起初是认同这一说法的，但是后来觉得 “人” 或者说 “人的思想” 或者说 “人性” 在其中影响作用非常的大。相同的事情不同的角度看，结果相去甚远，如果基本的理念不同，那事情基本是无法调和的。</p>
<p>对做的事情的认同感，对自我的定位与认识，对现实情况的冷静思考，这几点需要反复咀嚼。</p>
<p>分享一篇文章：<a href="http://stormzhang.com/2017/06/26/leave-if-no-experts/">公司没大牛带，需要离职么？</a>。对于刚入门的我们，这是一个很有代表性的问题。太多时候我们期待别人，忘了自己。公司有大牛，能跟着学是幸运，没有是常态。先接受了这个设定，会更容易找提高自己的方法。有没有大牛并不重要，我们是为了那个更好的自己。</p>
<h2 id="狼人杀">狼人杀</h2>
<p>2017 火的游戏很多：狼人杀、守望先锋、王者荣耀、吃鸡。狼人杀这一杀，好像杀到了毕业前和同学舍友在一起臭 high 的日子。可说骚话、互相 diss、互相吹捧，真真假假其乐无穷。最赤鸡的是鱼炸出了个女盆友，各路同学亲上加亲，无法克说。</p>
<h2 id="白洋淀">白洋淀</h2>
<blockquote>
<p>我想 你说
你不要在孤单 让我做你的伴</p>
</blockquote>
<p>白洋淀去过四次，那里的温泉过去两次，年中 Team Building 再喜加一，而这一次让这里有了美丽故事。喷泉、沙发，出现了对的人。</p>
<h2 id="ios">iOS</h2>
<p>React Native 的一波尝试后，走上了移动开发的道。Swift 让上 iOS 车的门槛降低不少，Swift API 趋向稳定，可以说这是最好的时候。GitHub 应该是再也放不下了，外文的书籍、文档确实靠谱啊，推荐一个教程网站 <a href="https://www.raywenderlich.com/category/ios">iOS Tutorials - Ray Wenderlich</a> 真的零基础入门。</p>
<p>我相信：一个人语言的界限，就是他世界的边界。</p>
<h2 id="自如">自如</h2>
<p>一场大火让无数人无家可归，年末换房也受牵连。被自如圈粉，其他中介的房子真的太 low 了。自如订房子居然也要抢，一波三折、失而复得。在没抢到房子时，真的想走，想离开这里了，很凄凉。最终是在公司对面住下了，过上了在家吃中饭晚饭的日子。</p>
<h2 id="2018-happy-new-year">2018 Happy New Year</h2>
<p>2017 自己是幸运的，2018 猥琐发育，不要浪，胜利属于伏地魔。</p>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>【Git 权威指南】读书笔记 - 独奏 - Part 4</title>
      <link>https://zyf.im/2017/12/25/got-git-reading-notes-solo-part4/</link>
      <pubDate>Mon, 25 Dec 2017 16:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/12/25/got-git-reading-notes-solo-part4/</guid>
      <description>&lt;p&gt;主要内容：【历史穿梭】、【改变历史】、【Git 克隆】&lt;/p&gt;
&lt;h2 id=&#34;历史穿梭&#34;&gt;历史穿梭&lt;/h2&gt;
&lt;p&gt;查看条件个数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git rev-list HEAD | wc -l
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;版本表示法git-rev-parse&#34;&gt;版本表示法：git rev-parse&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git rev-parse&lt;/code&gt; pick out and massage parameters for other git commands.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--git-dir&lt;/code&gt; 可以显示 Git 版本库的位置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--show-cdup&lt;/code&gt; 当前工作区目录的深度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--parseopt&lt;/code&gt; 可以用于被 Git 无关应用用于解析命令行参数&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# 显示分支，tag
git rev-parse --symbolic --branches

git rev-parse --symbolic --branches

# 显示定义的所有引用
git rev-parse --symbolic --glob=refs/*
&lt;/code&gt;&lt;/pre&gt;&lt;!-- more --&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# 显示多个表达式的 SHA1 哈希值：

git rev-parse  master  refs/heads/master
6652a0dce6a5067732c00ef0a220810a7230655e
6652a0dce6a5067732c00ef0a220810a7230655e

^后面的数字代表该提交的第几个父提交，~&amp;lt;n&amp;gt;就相当于连续&amp;lt;n&amp;gt;个符号^

git rev-parse  A~3  A^^^
e80aa7481beda65ae00e35afc4bc4b171f9b0ebf
e80aa7481beda65ae00e35afc4bc4b171f9b0ebf

# 暂存区里的文件和HEAD中的文件相同

git rev-parse  :gitg.png  HEAD:gitg.png
fc58966ccc1e5af24c2c9746196550241bc01c50
fc58966ccc1e5af24c2c9746196550241bc01c50

# 在提交日志中查找字串的方式显示提交
git rev-parse :/&amp;#34;Commit A&amp;#34;
81993234fc12a325d303eccea20f6fd629412712
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;版本范围表示法git-rev-list&#34;&gt;版本范围表示法：git rev-list&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git rev-list&lt;/code&gt; 可以帮助研究 Git 的各种版本范围语法。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>主要内容：【历史穿梭】、【改变历史】、【Git 克隆】</p>
<h2 id="历史穿梭">历史穿梭</h2>
<p>查看条件个数：</p>
<pre tabindex="0"><code>git rev-list HEAD | wc -l
</code></pre><h3 id="版本表示法git-rev-parse">版本表示法：git rev-parse</h3>
<p><code>git rev-parse</code> pick out and massage parameters for other git commands.</p>
<ul>
<li><code>--git-dir</code> 可以显示 Git 版本库的位置</li>
<li><code>--show-cdup</code> 当前工作区目录的深度</li>
<li><code>--parseopt</code> 可以用于被 Git 无关应用用于解析命令行参数</li>
</ul>
<pre tabindex="0"><code># 显示分支，tag
git rev-parse --symbolic --branches

git rev-parse --symbolic --branches

# 显示定义的所有引用
git rev-parse --symbolic --glob=refs/*
</code></pre><!-- more -->
<pre tabindex="0"><code># 显示多个表达式的 SHA1 哈希值：

git rev-parse  master  refs/heads/master
6652a0dce6a5067732c00ef0a220810a7230655e
6652a0dce6a5067732c00ef0a220810a7230655e

^后面的数字代表该提交的第几个父提交，~&lt;n&gt;就相当于连续&lt;n&gt;个符号^

git rev-parse  A~3  A^^^
e80aa7481beda65ae00e35afc4bc4b171f9b0ebf
e80aa7481beda65ae00e35afc4bc4b171f9b0ebf

# 暂存区里的文件和HEAD中的文件相同

git rev-parse  :gitg.png  HEAD:gitg.png
fc58966ccc1e5af24c2c9746196550241bc01c50
fc58966ccc1e5af24c2c9746196550241bc01c50

# 在提交日志中查找字串的方式显示提交
git rev-parse :/&#34;Commit A&#34;
81993234fc12a325d303eccea20f6fd629412712
</code></pre><h3 id="版本范围表示法git-rev-list">版本范围表示法：git rev-list</h3>
<p><code>git rev-list</code> 可以帮助研究 Git 的各种版本范围语法。</p>
<pre tabindex="0"><code># 从开始到 tag：A 的所有历史提交
git rev-list --oneline  A

# 每个 tag 历史提交的并集
git rev-list --oneline  D  F

# ^ 排除这个版本及其历史版本
git rev-list --oneline  ^G D

# ^G D 等价于 G..D
git rev-list --oneline  G..D

# 含 ^ 的参数顺序不重要，..
# ^B C 相当于 B..C
# C ^B 相当于 B..C
# C..B 相当于 ^C B
</code></pre><h3 id="浏览日志git">浏览日志：git</h3>
<p><code>--graph</code> 参数调用 <code>git log</code> 可以显示字符界面的提交关系图。</p>
<pre tabindex="0"><code>git config --global alias.glog &#34;log --graph&#34;
</code></pre><ul>
<li><code>--oneline</code> 单行显示</li>
<li><code>-&lt;n&gt;</code> 显示最近的 <n> 条日志</li>
<li><code>-p</code> 显示变动</li>
<li><code>--stat</code> 显示变动摘要</li>
</ul>
<p>查看、分析某一个提交：</p>
<pre tabindex="0"><code>git show D --stat
tag D
...

git cat-file -p D^0
</code></pre><h3 id="差异比较git-diff">差异比较：git diff</h3>
<ul>
<li>比较里程碑 B 和里程碑 A，用命令：git diff B A</li>
<li>比较工作区和里程碑 A，用命令：git diff A</li>
<li>比较暂存区和里程碑 A，用命令：git diff -cached A</li>
<li>比较工作区和暂存区，用命令：git diff</li>
<li>比较暂存区和 HEAD，用命令：git diff -cached</li>
<li>比较工作区和 HEAD，用命令：git diff HEAD</li>
</ul>
<p>显示不同版本下的文件差异：</p>
<pre tabindex="0"><code>git diff &lt;commit1&gt; &lt;commit2&gt; -- &lt;paths&gt;
</code></pre><p>非 Git 目录/文件的差异比较，可在版本库之外使用：</p>
<pre tabindex="0"><code>git diff &lt;path1&gt; &lt;path2&gt;
</code></pre><ul>
<li><code>--word-diff</code> 差异逐 <em>词</em> 比较，而非缺省的逐 <em>行</em> 比较</li>
</ul>
<h3 id="文件追溯git-blame">文件追溯：git blame</h3>
<p>逐行显示文件，在每一行的行首显示此行最早是在什么版本引入的，由谁引入。</p>
<p>只想查看某几行，使用 -L n,m 参数：</p>
<pre tabindex="0"><code>git blame -L 6,+5 README
</code></pre><h3 id="二分查找git-bisect">二分查找：git bisect</h3>
<p>定位问题代码。</p>
<h2 id="改变历史">改变历史</h2>
<p>作为分布式版本控制系统，一旦版本库被多人共享，改变历史就可能是无法完成的任务。</p>
<h3 id="悔棋">悔棋</h3>
<p><code>git commit –amend</code> 单步悔棋，修补式提交。</p>
<p>检出文件到前一版：</p>
<pre tabindex="0"><code>git checkout HEAD^ -- src/hello.h
</code></pre><h3 id="多步悔棋">多步悔棋</h3>
<pre tabindex="0"><code>git reset --soft HEAD^^
</code></pre><h3 id="回到未来">回到未来</h3>
<p>拣选指令 <code>git cherry-pick</code> 从众多的提交中挑选出一个提交应用在当前的工作分支中。</p>
<p>该命令需要提供一个提交 ID 作为参数，操作过程相当于将该提交导出为补丁文件，然后在当前 HEAD 上重放形成无论内容还是提交说明都一致的提交。</p>
<p>操作例子：<code>A B C D E F</code> 6 次提交。</p>
<p>例子 1.1：出掉 <code>D</code>：</p>
<pre tabindex="0"><code># 将 HEAD 指针切换到 C
git checkout C

# 拣选将 E 提交在当前 HEAD 上
git cherry-pick E

# 拣选操作将 F 提交在当前 HEAD 上
git cherry-pick master

# 将 master 分支指向新的提交 ID（f677821）上
git checkout master
git reset --hard HEAD@{1}
</code></pre><p>例子 1.2：D 融入 C：</p>
<pre tabindex="0"><code>git checkout D

# 将 C 和 D 融合
git reset --soft HEAD^^

# 提交说明重用C提交的提交说明
git commit -C C

git cherry-pick E
git cherry-pick F
</code></pre><p>git rebase 对提交执行变基操作，即可以实现将指定范围的提交“嫁接”到另外一个提交之上。</p>
<pre tabindex="0"><code>git rebase --onto &lt;newbase&gt; &lt;since&gt; &lt;till&gt;
</code></pre><p>变基操作的过程：</p>
<ol>
<li>首先执行 git checkout <till>，如果 till 不是一个分支，则变基操作是在 detached HEAD 分离头指针 状态的，当变基结束后，对 master 分支执行重置以实现把变基结果记录在分支中。</li>
<li>将<since>..<till>所标识的提交范围写到一个临时文件中。（<since>..<till>是指包括<till>的所有历史提交排除<since>以及<since>的历史提交后形成的版本范围）</li>
<li>当前分支强制重置（git reset &ndash;hard）到<newbase>。</li>
<li>从保存在临时文件中的提交列表中，一个一个将提交按照顺序重新提交到重置之后的分支上。</li>
<li>如果遇到提交已经在分支中包含，跳过该提交。</li>
<li>如果在提交过程遇到冲突，变基过程暂停。用户解决冲突后，执行 git rebase –continue 继续变基操作。或者执行 git rebase –skip 跳过此提交。或者执行 git rebase –abort 就此终止变基操作切换到变基前的分支上。</li>
</ol>
<p>例子 2.1：出掉 <code>D</code>：</p>
<pre tabindex="0"><code>git rebase --onto C D F
# or
git rebase --onto C E^ F
</code></pre><p>例子 2.2：D 融入 C：</p>
<pre tabindex="0"><code>git checkout D

# C和D融合
git reset --soft HEAD^^

git add .

# 复用 C 的提交信息
git commit -C C

# 记住这个提交 ID，可以用 tag 的方法
git tag newbase

git rebase --onto newbase E^ master
</code></pre><p><code>-i</code> 交互式变基方法。</p>
<p>例子 3.1：出掉 <code>D</code>：</p>
<pre tabindex="0"><code>git rebase -i D^

# d, drop = remove commit
# 提交 D 标示修改为 d
</code></pre><p>例子 3.2：D 融入 C：</p>
<pre tabindex="0"><code>git rebase -i C^

# 提交 D 标示修改为 s
</code></pre><h3 id="丢弃历史">丢弃历史</h3>
<p><strong>重点内容第一次 Get</strong></p>
<p>历史有的时候会成为负担。只保留最近的 100 次提交，抛弃之前的历史提交。那么应该如何操作呢？</p>
<p>例：清除 tag A 之前的提交历史：</p>
<pre tabindex="0"><code># 查看里程碑A指向的目录树
git cat-file -p A^{tree}

# 使用git commit-tree命令直接从该目录树创建提交
echo &#34;Commit from tree of tag A.&#34; | git commit-tree A^{tree}
8f7f94ba6a9d94ecc1c223aa4b311670599e1f86

# 命令git commit-tree的输出是一个提交的SHA1哈希值。
# 会发现这个提交没有历史提交，可以称之为孤儿提交。
git log 8f7f94ba6a9d94ecc1c223aa4b311670599e1f86

# 将master分支从里程碑到最新的提交全部迁移到刚刚生成的孤儿提交上。
git rebase --onto 8f7f94ba6a9d94ecc1c223aa4b311670599e1f86 A master
</code></pre><h3 id="反转提交">反转提交</h3>
<pre tabindex="0"><code>git revert HEAD
</code></pre><h2 id="git-克隆">Git 克隆</h2>
<h3 id="鸡蛋不装在一个篮子里">鸡蛋不装在一个篮子里</h3>
<pre tabindex="0"><code>1. git clone &lt;repository&gt; &lt;directory&gt;
2. git clone --bare   &lt;repository&gt; &lt;directory.git&gt;
3. git clone --mirror &lt;repository&gt; &lt;directory.git&gt;
</code></pre><p>一般约定俗成裸版本库的目录名以 <code>.git</code> 为后缀。</p>
<p>用法 3 区别于用法 2 之处在于用法 3 克隆出来的裸版本对上游版本库进行了注册，这样可以在裸版本库中使用 git fetch 命令和上游版本库进行持续同步。</p>
<h3 id="克隆生成裸版本库">克隆生成裸版本库</h3>
<pre tabindex="0"><code>git clone --bare /path/to/my/workspace/demo /path/to/repos/demo.git
</code></pre><p>demo.git 目录就是版本库目录，不含工作区。</p>
<pre tabindex="0"><code>git --git-dir=/path/to/repos/demo.git config core.bare
true

# 向其 push
git push /path/to/repos/demo.git
</code></pre><h3 id="创建生成裸版本库">创建生成裸版本库</h3>
<pre tabindex="0"><code>git init --bare /path/to/repos/demo-init.git
</code></pre><h2 id="references">References</h2>
<ul>
<li><a href="http://www.worldhello.net/gotgit/02-git-solo/index.html">2. Git 独奏 — GotGit</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>【Git 权威指南】读书笔记 - 独奏 - Part 3</title>
      <link>https://zyf.im/2017/08/03/got-git-reading-notes-solo-part3/</link>
      <pubDate>Thu, 03 Aug 2017 11:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/08/03/got-git-reading-notes-solo-part3/</guid>
      <description>&lt;p&gt;主要内容：【Git 基本操作】&lt;/p&gt;
&lt;h2 id=&#34;git-基本操作&#34;&gt;Git 基本操作&lt;/h2&gt;
&lt;h3 id=&#34;先来合个影&#34;&gt;先来合个影&lt;/h3&gt;
&lt;p&gt;在 Git 里，“留影”用的命令叫做 &lt;code&gt;tag&lt;/code&gt;，更加专业的术语叫做“里程碑”（打 tag，或打标签）。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git tag -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;Say bye-bye to all previous practice.&amp;#34;&lt;/span&gt; old_practice
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;里程碑无非也是一个引用，通过记录提交 ID（或者创建 Tag 对象）来为当前版本库状态进行“留影”。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git rev-parse refs/tags/old_practice
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;41bd4e2cce0f8baa9bb4cdda62927b408c846cd6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;git describe&lt;/code&gt; 显示当前版本库的最新提交的版本号。格式：&lt;code&gt;最近的 tag - 距离此 tag 的个数 - 该提交的 SHA1&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git describe
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;old_practice
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# .. commit something&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git describe
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;old_practice-2-g8861c65
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- more --&gt;
&lt;h3 id=&#34;删除文件&#34;&gt;删除文件&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rm *.txt&lt;/code&gt; 针对的是 &lt;code&gt;工作区&lt;/code&gt;，对 &lt;code&gt;暂存区&lt;/code&gt; 和 &lt;code&gt;版本库&lt;/code&gt; 没有任何影响。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git rm detached-commit.txt hack-1.txt new-commit.txt welcome.txt&lt;/code&gt; 删除动作加入了暂存区，commit 后在版本库罪行提交中删除了，在历史提交中尚在。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>主要内容：【Git 基本操作】</p>
<h2 id="git-基本操作">Git 基本操作</h2>
<h3 id="先来合个影">先来合个影</h3>
<p>在 Git 里，“留影”用的命令叫做 <code>tag</code>，更加专业的术语叫做“里程碑”（打 tag，或打标签）。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git tag -m <span class="s2">&#34;Say bye-bye to all previous practice.&#34;</span> old_practice
</span></span></code></pre></div><p>里程碑无非也是一个引用，通过记录提交 ID（或者创建 Tag 对象）来为当前版本库状态进行“留影”。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rev-parse refs/tags/old_practice
</span></span><span class="line"><span class="cl">41bd4e2cce0f8baa9bb4cdda62927b408c846cd6
</span></span></code></pre></div><p><code>git describe</code> 显示当前版本库的最新提交的版本号。格式：<code>最近的 tag - 距离此 tag 的个数 - 该提交的 SHA1</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git describe
</span></span><span class="line"><span class="cl">old_practice
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># .. commit something</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git describe
</span></span><span class="line"><span class="cl">old_practice-2-g8861c65
</span></span></code></pre></div><!-- more -->
<h3 id="删除文件">删除文件</h3>
<p><code>rm *.txt</code> 针对的是 <code>工作区</code>，对 <code>暂存区</code> 和 <code>版本库</code> 没有任何影响。</p>
<p><code>git rm detached-commit.txt hack-1.txt new-commit.txt welcome.txt</code> 删除动作加入了暂存区，commit 后在版本库罪行提交中删除了，在历史提交中尚在。</p>
<p>可以通过下面命令查看历史版本的文件列表：</p>
<pre tabindex="0"><code>git ls-files --with-tree=HEAD^
detached-commit.txt
new-commit.txt
welcome.txt
</code></pre><p>查看在历史版本中尚在的删除文件的内容：</p>
<pre tabindex="0"><code>git cat-file -p HEAD^:welcome.txt
Hello.
Nice to meet you.
</code></pre><p><code>git add -u</code> 命令会对工作区中 <em>所有改动</em> 以及 <em>删除文件</em> 添加到暂存区。</p>
<h3 id="恢复删除的文件">恢复删除的文件</h3>
<pre tabindex="0"><code>git cat-file -p HEAD~1:welcome.txt &gt; welcome.txt
</code></pre><p>然后加入暂存区，提交到版本库：</p>
<pre tabindex="0"><code>git add -A
git commit -m &#34;restore file: welcome.txt&#34;
</code></pre><p>这是通过再次添加的方式恢复被删除的文件。</p>
<p><code>git add -A</code> 命令会对工作区中 <em>所有改动</em> 以及 <em>新增文件</em> 添加到暂存区。</p>
<h3 id="移动文件">移动文件</h3>
<pre tabindex="0"><code>git mv welcome.txt README
</code></pre><h3 id="交互界面">交互界面</h3>
<pre tabindex="0"><code>git add -i
</code></pre><h3 id="文件忽略">文件忽略</h3>
<pre tabindex="0"><code>cat &gt; .gitignore &lt;&lt; EOF
&gt; hello
&gt; *.o
&gt; *.h
&gt; EOF
</code></pre><p>查看被忽略的文件：</p>
<pre tabindex="0"><code>git status --ignored -s
</code></pre><p>将忽略的文件强制添加到仓库中：</p>
<pre tabindex="0"><code>git add -f hello.h
git commit -m &#34;add hello.h&#34;
[master 48456ab] add hello.h
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 src/hello.h
</code></pre><p><em>忽略只对未跟踪文件有效，对于已加入版本库的文件无效。</em> 将 <code>hello.h</code> 添加到版本库后，对 <code>hello.h</code> 的修改都会立刻被跟踪到。<code>.gitignore</code> 只是对未入库的文件起作用。</p>
<p><strong>本地独享式忽略文件</strong></p>
<p>.gitignore 设置的文件忽略是共享式的，是因为.gitignore 被添加到版本库后成为了版本库的一部分，文件忽略在他人的工作区中同样生效。</p>
<p>享式忽略有两种方式：</p>
<ul>
<li>在版本库.git 目录下的一个文件.git/info/exclude 来设置文件忽略。</li>
<li>通过 Git 的配置变量 core.excludesfile 指定的一个忽略文件，其设置的忽略对所有文件均有效。</li>
</ul>
<pre tabindex="0"><code>git config --global core.excludesfile /home/jiangxin/_gitignore
git config core.excludesfile
/home/jiangxin/_gitignore

cat /home/jiangxin/_gitignore
*~        # vim 临时文件
*.pyc     # python 的编译文件
.*.mmx    # 不是正则表达式哦，因为 FreeMind-MMX 的辅助文件以点开头
</code></pre><p><strong>Git 忽略语法</strong></p>
<ul>
<li><code>#</code> 表示开始的行被忽略。</li>
<li>通配符。* 任意多字符，? 一个字符，[abc] 可选字符范围。</li>
<li><code>最后面是一个 /</code> 表示忽略的是整个目录，同名文件不忽略，否则同名的文件和目录都忽略。</li>
<li><code>最前面 !</code> 表示不忽略。</li>
</ul>
<pre tabindex="0"><code># 这是注释行 —— 被忽略
*.a       # 忽略所有以 .a 为扩展名的文件。
!lib.a    # 但是 lib.a 文件或者目录不要忽略，即使前面设置了对 *.a 的忽略。
/TODO     # 只忽略根目录下的 TODO 文件，子目录的 TODO 文件不忽略。
build/    # 忽略所有 build/ 目录下的文件。
doc/*.txt # 忽略文件如 doc/notes.txt，但是文件如 doc/server/arch.txt 不被忽略。
</code></pre><h3 id="文件归档">文件归档</h3>
<p>如果想压缩工作区文件归档，可能一不小心会把版本库（.git 目录）包含其中，甚至将工作区中的忽略文件、临时文件也包含其中。所以 Git 提供了一个归档命令 <code>git archive</code>。</p>
<pre tabindex="0"><code># 基于最新提交建立归档文件latest.zip
git archive -o latest.zip HEAD

# 只将目录 src 和 doc 建立到归档 partial.tar 中
git archive -o partial.tar  HEAD src doc

# 基于里程碑v1.0建立归档，并且为归档中文件添加目录前缀1.0
git archive --format=tar --prefix=1.0/ v1.0 | gzip &gt; foo-1.0.tar.gz
</code></pre><h2 id="references">References</h2>
<ul>
<li><a href="http://www.worldhello.net/gotgit/02-git-solo/index.html">2. Git 独奏 — GotGit</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>【Git 权威指南】读书笔记 - 独奏 - Part 2</title>
      <link>https://zyf.im/2017/07/20/got-git-reading-notes-solo-part2/</link>
      <pubDate>Thu, 20 Jul 2017 11:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/07/20/got-git-reading-notes-solo-part2/</guid>
      <description>&lt;p&gt;主要内容：【Git 重置】、【Git 检出】、【恢复进度】&lt;/p&gt;
&lt;h2 id=&#34;git-重置&#34;&gt;Git 重置&lt;/h2&gt;
&lt;h3 id=&#34;分支游标-master-的探秘&#34;&gt;分支游标 master 的探秘&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git log --graph --oneline
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* e695606 which version checked in?
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* a0c641e who does commit?
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* 9e8a761 initialized.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;引用 &lt;code&gt;refs/heads/master&lt;/code&gt; 就好像是一个游标，在有新的提交发生的时候指向了新的提交。&lt;/p&gt;
&lt;p&gt;Git 提供了 &lt;code&gt;git reset&lt;/code&gt; 命令，可以将“游标”指向任意一个存在的提交 ID。注意下面的命令中使用了 &lt;code&gt;--hard&lt;/code&gt; 参数，会破坏工作区未提交的改动，慎用。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git reset --hard HEAD^
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;HEAD is now at e695606 which version checked in?
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- more --&gt;
&lt;h3 id=&#34;用-reflog-挽救错误的重置&#34;&gt;用 reflog 挽救错误的重置&lt;/h3&gt;
&lt;p&gt;通过 &lt;code&gt;.git/logs&lt;/code&gt; 目录下日志文件记录了分支的变更。默认非裸版本库（带有工作区）都提供分支日志功能，这是因为带有工作区的版本库都有如下设置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config core.logallrefupdates
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看一下 &lt;code&gt;master&lt;/code&gt; 分支的日志文件 &lt;code&gt;.git/logs/refs/heads/master&lt;/code&gt; 中的内容。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tail -5 .git/logs/refs/heads/master
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Git 提供了一个 &lt;code&gt;git reflog&lt;/code&gt; 命令，对这个文件进行操作。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>主要内容：【Git 重置】、【Git 检出】、【恢复进度】</p>
<h2 id="git-重置">Git 重置</h2>
<h3 id="分支游标-master-的探秘">分支游标 master 的探秘</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log --graph --oneline
</span></span><span class="line"><span class="cl">* e695606 which version checked in?
</span></span><span class="line"><span class="cl">* a0c641e who does commit?
</span></span><span class="line"><span class="cl">* 9e8a761 initialized.
</span></span></code></pre></div><p>引用 <code>refs/heads/master</code> 就好像是一个游标，在有新的提交发生的时候指向了新的提交。</p>
<p>Git 提供了 <code>git reset</code> 命令，可以将“游标”指向任意一个存在的提交 ID。注意下面的命令中使用了 <code>--hard</code> 参数，会破坏工作区未提交的改动，慎用。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset --hard HEAD^
</span></span><span class="line"><span class="cl">HEAD is now at e695606 which version checked in?
</span></span></code></pre></div><!-- more -->
<h3 id="用-reflog-挽救错误的重置">用 reflog 挽救错误的重置</h3>
<p>通过 <code>.git/logs</code> 目录下日志文件记录了分支的变更。默认非裸版本库（带有工作区）都提供分支日志功能，这是因为带有工作区的版本库都有如下设置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config core.logallrefupdates
</span></span><span class="line"><span class="cl"><span class="nb">true</span>
</span></span></code></pre></div><p>查看一下 <code>master</code> 分支的日志文件 <code>.git/logs/refs/heads/master</code> 中的内容。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">tail -5 .git/logs/refs/heads/master
</span></span></code></pre></div><p>Git 提供了一个 <code>git reflog</code> 命令，对这个文件进行操作。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reflog show master <span class="p">|</span> head -5
</span></span><span class="line"><span class="cl">9e8a761 master@<span class="o">{</span>0<span class="o">}</span>: 9e8a761: updating HEAD
</span></span><span class="line"><span class="cl">e695606 master@<span class="o">{</span>1<span class="o">}</span>: HEAD^: updating HEAD
</span></span><span class="line"><span class="cl">4902dc3 master@<span class="o">{</span>2<span class="o">}</span>: commit: does master follow this new commit?
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>重置 master 为两次改变之前的值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset --hard master@<span class="o">{</span>2<span class="o">}</span>
</span></span></code></pre></div><h3 id="深入了解-git-reset-命令">深入了解 git reset 命令</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset <span class="o">[</span>-q<span class="o">]</span> <span class="o">[</span>&lt;commit&gt;<span class="o">]</span> <span class="o">[</span>--<span class="o">]</span> &lt;paths&gt;...
</span></span><span class="line"><span class="cl">git reset <span class="o">[</span>--soft <span class="p">|</span> --mixed <span class="p">|</span> --hard <span class="p">|</span> --merge <span class="p">|</span> --keep<span class="o">]</span> <span class="o">[</span>-q<span class="o">]</span> <span class="o">[</span>&lt;commit&gt;<span class="o">]</span>
</span></span></code></pre></div><p>为了避免路径和引用（或者提交 ID）同名而冲突，可以在 <code>&lt;paths&gt;</code> 前用两个连续的短线（减号）作为分隔。</p>
<p><img alt="20-got-git-reading-notes-solo-git-reset" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80202770-b5966d80-8658-11ea-92e4-c348b8f12313.png"></p>
<ul>
<li><code>--hard</code> 会执行上图中的 1、2、3 全部的三个动作。</li>
</ul>
<ol>
<li>替换引用的指向。引用指向新的提交 ID。</li>
<li>替换暂存区。替换后，暂存区的内容和引用指向的目录树一致。</li>
<li>替换工作区。替换后，工作区的内容变得和暂存区一致，也和 HEAD 所指向的目录树内容相同。</li>
</ol>
<ul>
<li><code>--soft</code> 会执行上图中的操作 1。</li>
<li><code>--mixed</code>（缺省）会执行上图中的操作 1 和操作 2。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset
</span></span><span class="line"><span class="cl">git reset HEAD
</span></span><span class="line"><span class="cl"><span class="c1"># 仅用 HEAD 指向的目录树重置暂存区，工作区不会受到影响，相当于将之前用 git add 命令更新到暂存区的内容撤出暂存区。引用也未改变，因为引用重置到 HEAD 相当于没有重置。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git reset -- filename
</span></span><span class="line"><span class="cl">git reset HEAD filename
</span></span><span class="line"><span class="cl"><span class="c1"># 仅将文件 filename 撤出暂存区，暂存区中其他文件不改变。相当于对命令 git add filename 的反向操作。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git reset --soft HEAD^
</span></span><span class="line"><span class="cl"><span class="c1"># 工作区和暂存区不改变，但是引用向前回退一次。当对最新提交的提交说明或者提交的更改不满意时，撤销最新的提交以便重新提交。git commit 的反向操作。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 在之前曾经介绍过一个修补提交命令 git commit --amend，用于对最新的提交进行重新提交以修补错误的提交说明或者错误的提交文件。修补提交命令实际上相当于执行了下面两条命令。（注：文件 .git/COMMIT_EDITMSG 保存了上次的提交日志）</span>
</span></span><span class="line"><span class="cl">git reset --soft HEAD^
</span></span><span class="line"><span class="cl">git commit -e -F .git/COMMIT_EDITMSG
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git reset HEAD^
</span></span><span class="line"><span class="cl"><span class="c1"># 工作区不改变，但是暂存区会回退到上一次提交之前，引用也会回退一次。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git reset --hard HEAD^
</span></span><span class="line"><span class="cl"><span class="c1"># 彻底撤销最近的提交。引用回退到前一次，而且工作区和暂存区都会回退到上一次提交的状态。自上一次以来的提交全部丢失。</span>
</span></span></code></pre></div><h2 id="git-检出">Git 检出</h2>
<p>重置命令 <code>git reset</code> 的一个用途就是修改引用（如 master）的游标。如果 HEAD 要改变该如何改变呢？检出命令 <code>git checkout</code> 该命令的实质就是修改 HEAD 本身的指向，该命令不会影响分支“游标”（如 master）。</p>
<h3 id="head-的重置即检出">HEAD 的重置即检出</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git co HEAD^
</span></span><span class="line"><span class="cl">Note: checking out <span class="s1">&#39;HEAD^&#39;</span>.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">You are in <span class="s1">&#39;detached HEAD&#39;</span> state. You can look around, make experimental
</span></span><span class="line"><span class="cl">changes and commit them, and you can discard any commits you make in this
</span></span><span class="line"><span class="cl">state without impacting any branches by performing another checkout.
</span></span><span class="line"><span class="cl"><span class="c1"># 您现在处于 &#39;分离头指针&#39; 状态。您可以检查、测试和提交，而不影响任何分支。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 通过执行另外的一个 checkout 检出指令会丢弃在此状态下的修改和提交。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">If you want to create a new branch to retain commits you create, you may
</span></span><span class="line"><span class="cl"><span class="k">do</span> so <span class="o">(</span>now or later<span class="o">)</span> by using -b with the checkout <span class="nb">command</span> again. Example:
</span></span><span class="line"><span class="cl"><span class="c1"># 如果想保留在此状态下的修改和提交，使用 -b 参数调用 checkout 检出指令以</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 创建新的跟踪分支。如：</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git checkout -b &lt;new-branch-name&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">HEAD is now at 3175afd...
</span></span></code></pre></div><p>什么叫做 <code>detached HEAD</code> “分离头指针”状态？查看一下此时 HEAD 的内容就明白了。</p>
<pre tabindex="0"><code>cat .git/HEAD
3175afde9450a1dc40b09d05a012b45e967cb80f
</code></pre><p>原来“分离头指针”状态指的就是 HEAD 头指针指向了一个具体的提交 ID，而不是一个引用（分支）。注意上面的 <code>reflog</code> 是 <code>HEAD</code> 头指针的变迁记录，而非 <code>master</code> 分支。</p>
<p>查看一下 HEAD 和 master 对应的提交 ID，会发现现在它们指向的不一样。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rev-parse HEAD master
</span></span><span class="line"><span class="cl">3175afde9450a1dc40b09d05a012b45e967cb80f
</span></span><span class="line"><span class="cl">bd08cb462d38b54b930cf1934b0c33f2e4592390
</span></span></code></pre></div><p>在“分离头指针”模式仍然可以进行提交：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git status
</span></span><span class="line"><span class="cl">HEAD detached at 3175afd
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>但是在 checkout 到其他分支时，刚才的提交会丢失，但是这个提交仍然在版本库中，由于这个提交没有被任何分支跟踪到，因此并不能保证这个提交会永久存在。</p>
<p>实际上当 reflog 中含有该提交的日志过期后，这个提交随时都会从版本库中彻底清除。</p>
<h3 id="挽救分离头指针">挽救分离头指针</h3>
<p>在“分离头指针”模式下进行的测试提交除了使用提交 ID <code>acc2f69</code> 访问之外，不能通过 master 分支或其他引用访问到。使用合并操作 <code>git merge</code> 将提交 acc2f69 合并到 master 分支中来。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git merge acc2f69
</span></span><span class="line"><span class="cl">Merge made by recursive.
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><h2 id="恢复进度">恢复进度</h2>
<h3 id="继续暂存区未完成的实践">继续暂存区未完成的实践</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 保存当前工作进度</span>
</span></span><span class="line"><span class="cl">git stash
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看保存的进度用命令</span>
</span></span><span class="line"><span class="cl">git stash list
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 最近保存的进度进行恢复</span>
</span></span><span class="line"><span class="cl">git stash pop
</span></span><span class="line"><span class="cl">git stash pop <span class="o">[</span>--index<span class="o">]</span> <span class="o">[</span>&lt;stash&gt;<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># --index 除了恢复工作区的文件外，还尝试恢复暂存区</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 从该 &lt;stash&gt; 中恢复</span>
</span></span></code></pre></div><pre tabindex="0"><code>git stash [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
                [-u|--include-untracked] [-a|--all] [&lt;message&gt;]]
</code></pre><ul>
<li><code>--patch</code> 会显示工作区和 HEAD 的差异，通过对差异文件的编辑决定在进度中最终要保存的工作区的内容，通过编辑差异文件可以在进度中排除无关内容。</li>
<li>使用 <code>-k</code> 或者 <code>--keep-index</code> 参数，在保存进度后不会将暂存区重置。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 不删除恢复的进度之外，其余和 git stash pop 命令一样</span>
</span></span><span class="line"><span class="cl">git stash apply <span class="o">[</span>--index<span class="o">]</span> <span class="o">[</span>&lt;stash&gt;<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 删除一个存储的进度</span>
</span></span><span class="line"><span class="cl">git stash drop <span class="o">[</span>&lt;stash&gt;<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 删除所有存储的进度</span>
</span></span><span class="line"><span class="cl">git stash clear
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 基于进度创建分支</span>
</span></span><span class="line"><span class="cl">git stash branch &lt;branchname&gt; &lt;stash&gt;
</span></span></code></pre></div><h3 id="探秘-git-stash">探秘 git stash</h3>
<p>在执行 <code>git stash</code> 命令时，Git 实际调用了一个脚本文件实现相关的功能。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git --exec-path
</span></span><span class="line"><span class="cl">/usr/lib/git-core
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">file /usr/lib/git-core/git-stash
</span></span><span class="line"><span class="cl">/usr/lib/git-core/git-stash: POSIX shell script text executable
</span></span></code></pre></div><p>本地没有被版本控制系统跟踪的文件并不能保存进度。因此本地新文件需要执行添加 <code>add</code> 再执行 git stash 命令。</p>
<p>在用 <code>git stash</code> 命令保存进度时，提供说明更容易找到对应的进度文件。</p>
<p>每个进度的标识都是 <code>stash@{&lt;n&gt;}</code> 格式，像极了前面介绍的 <code>reflog</code> 的格式。<code>git stash</code> 的就是用到了前面介绍的引用和引用变更日志 <code>reflog</code> 来实现的。</p>
<p>用 git stash 保存进度，实际上会将进度保存在引用 refs/stash 所指向的提交中。多次的进度保存，实际上相当于引用 refs/stash 一次又一次的变化，而 refs/stash 引用的变化由 reflog（即.git/logs/refs/stash）所记录下来。</p>
<h3 id="如何在引用-refsstash-中同时保存暂存区的进度和工作区中的进度">如何在引用 refs/stash 中同时保存暂存区的进度和工作区中的进度</h3>
<pre tabindex="0"><code>git log --graph --pretty=raw  refs/stash -2
*   commit e5c0cdc2dedc3e50e6b72a683d928e19a1d9de48
|\  tree 780c22449b7ff67e2820e09a6332c360ddc80578
| | parent 2b31c199d5b81099d2ecd91619027ab63e8974ef
| | parent c5edbdcc90addb06577ff60f644acd1542369194
| | author Jiang Xin &lt;jiangxin@ossxp.com&gt; 1291623066 +0800
| | committer Jiang Xin &lt;jiangxin@ossxp.com&gt; 1291623066 +0800
| |
| |     WIP on master: 2b31c19 Merge commit &#39;acc2f69&#39;
| |
| * commit c5edbdcc90addb06577ff60f644acd1542369194
|/  tree 780c22449b7ff67e2820e09a6332c360ddc80578
|   parent 2b31c199d5b81099d2ecd91619027ab63e8974ef
|   author Jiang Xin &lt;jiangxin@ossxp.com&gt; 1291623066 +0800
|   committer Jiang Xin &lt;jiangxin@ossxp.com&gt; 1291623066 +0800
|
|       index on master: 2b31c19 Merge commit &#39;acc2f69&#39;
</code></pre><p>最新的提交说明中有 <code>WIP</code>（Work In Progess）字样，说明代表了工作区进度。而最新提交的第二个父提交（上图中显示为第二个提交）有 index on master 字样，说明这个提交代表着暂存区的进度。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://www.worldhello.net/gotgit/02-git-solo/index.html">2. Git 独奏 — GotGit</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>【Git 权威指南】读书笔记 - 独奏 - Part 1</title>
      <link>https://zyf.im/2017/07/19/got-git-reading-notes-solo-part1/</link>
      <pubDate>Wed, 19 Jul 2017 16:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/07/19/got-git-reading-notes-solo-part1/</guid>
      <description>&lt;p&gt;主要内容：【Git 初始化】、【Git 暂存区】、【Git 对象】&lt;/p&gt;
&lt;h2 id=&#34;git-初始化&#34;&gt;Git 初始化&lt;/h2&gt;
&lt;p&gt;设置一下 Git 的环境变量，这个设置是一次性的工作。即这些设置会在全局文件（用户主目录下的 &lt;code&gt;~/.gitconfig&lt;/code&gt;）或系统文件（&lt;code&gt;/etc/gitconfig&lt;/code&gt;）中做永久的记录。&lt;/p&gt;
&lt;p&gt;配置的用户名和邮件地址将在版本库提交时作为提交者的用户名和邮件地址。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global user.name &lt;span class=&#34;s2&#34;&gt;&amp;#34;Jiang Xin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global user.email jiangxin@ossxp.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;设置一些-git-别名以便可以使用更为简洁的子命令&#34;&gt;设置一些 Git 别名，以便可以使用更为简洁的子命令&lt;/h3&gt;
&lt;p&gt;只在本用户的全局配置中添加 Git 命令别名：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global alias.br branch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global alias.ci &lt;span class=&#34;s2&#34;&gt;&amp;#34;commit -s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global alias.co checkout
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global alias.st &lt;span class=&#34;s2&#34;&gt;&amp;#34;-p status&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- more --&gt;
&lt;h3 id=&#34;版本库的初始化&#34;&gt;版本库的初始化&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir demo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; demo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git init
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;初始化空的 Git 版本库于 &lt;code&gt;/path/to/my/workspace/demo/.git/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;git init 命令的后面直接输入目录名称&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /path/to/my/workspace
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git init demo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ls -aF
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./  ../  .git/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个隐藏的 &lt;code&gt;.git&lt;/code&gt; 目录就是 Git 版本库（又叫仓库，repository）。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>主要内容：【Git 初始化】、【Git 暂存区】、【Git 对象】</p>
<h2 id="git-初始化">Git 初始化</h2>
<p>设置一下 Git 的环境变量，这个设置是一次性的工作。即这些设置会在全局文件（用户主目录下的 <code>~/.gitconfig</code>）或系统文件（<code>/etc/gitconfig</code>）中做永久的记录。</p>
<p>配置的用户名和邮件地址将在版本库提交时作为提交者的用户名和邮件地址。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global user.name <span class="s2">&#34;Jiang Xin&#34;</span>
</span></span><span class="line"><span class="cl">git config --global user.email jiangxin@ossxp.com
</span></span></code></pre></div><h3 id="设置一些-git-别名以便可以使用更为简洁的子命令">设置一些 Git 别名，以便可以使用更为简洁的子命令</h3>
<p>只在本用户的全局配置中添加 Git 命令别名：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global alias.br branch
</span></span><span class="line"><span class="cl">git config --global alias.ci <span class="s2">&#34;commit -s&#34;</span>
</span></span><span class="line"><span class="cl">git config --global alias.co checkout
</span></span><span class="line"><span class="cl">git config --global alias.st <span class="s2">&#34;-p status&#34;</span>
</span></span></code></pre></div><!-- more -->
<h3 id="版本库的初始化">版本库的初始化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir demo
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> demo
</span></span><span class="line"><span class="cl">git init
</span></span></code></pre></div><p>初始化空的 Git 版本库于 <code>/path/to/my/workspace/demo/.git/</code></p>
<p>git init 命令的后面直接输入目录名称</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /path/to/my/workspace
</span></span><span class="line"><span class="cl">git init demo
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ls -aF
</span></span><span class="line"><span class="cl">./  ../  .git/
</span></span></code></pre></div><p>这个隐藏的 <code>.git</code> 目录就是 Git 版本库（又叫仓库，repository）。</p>
<p><code>.git</code> 版本库目录所在的目录，即 <code>/path/to/my/workspace/demo</code> 目录称为 <strong>工作区</strong>。</p>
<p>将新建立的文件添加到版本库</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add welcome.txt
</span></span></code></pre></div><p>再执行一次提交操作，使用 <code>-m</code> 参数直接给出了提交说明。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git ci -m <span class="s2">&#34;initialized&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>master <span class="o">(</span>root-commit<span class="o">)</span> 7f0b2be<span class="o">]</span> init
</span></span><span class="line"><span class="cl"> <span class="m">1</span> file changed, <span class="m">0</span> insertions<span class="o">(</span>+<span class="o">)</span>, <span class="m">0</span> deletions<span class="o">(</span>-<span class="o">)</span>
</span></span><span class="line"><span class="cl"> create mode <span class="m">100644</span> welcome.txt
</span></span></code></pre></div><p><code>git ci</code> 是上面配置的别名，我个人觉得 <code>-s</code> 这个参数比较冗余。</p>
<h3 id="思考为什么工作区下有一个-git-目录">思考：为什么工作区下有一个 <code>.git</code> 目录？</h3>
<p>Git 的这种设计，将版本库放在工作区根目录下，所有的版本控制操作（除了和其他远程版本库之间的互操作）都在本地即可完成，不像 Subversion 只有寥寥无几的几个命令才能脱离网络执行。而且 Git 也没有 CVS 和 Subversion 的安全泄漏问题（只要保护好 .git 目录），也没有 Subversion 在本地文件搜索时出现搜索结果混乱的问题，甚至 Git 还提供了一条 <code>git grep</code> 命令来更好地搜索工作区的文件内容。</p>
<pre tabindex="0"><code>git grep &#34;工作区文件内容搜索&#34;
</code></pre><h3 id="当工作区中包含了子目录在子目录中执行-git-命令时如何定位版本库呢">当工作区中包含了子目录，在子目录中执行 Git 命令时，如何定位版本库呢？</h3>
<p>当在 Git 工作区目录下执行操作的时候，会对目录依次向上递归查找 <code>.git</code> 目录，找到的 <code>.git</code> 目录就是工作区对应的版本库，<code>.git</code> 所在的目录就是工作区的根目录，文件 <code>.git/index</code> 记录了工作区文件的状态（实际上是 <strong>暂存区</strong> 的状态）。</p>
<p>如果跟踪一下执行 <code>git status</code> 命令时的磁盘访问，会看到沿目录依次向上递归的过程。</p>
<pre tabindex="0"><code>strace -e &#39;trace=file&#39; git status
</code></pre><h3 id="那么有什么办法知道-git-版本库的位置以及工作区的根目录在哪里呢">那么有什么办法知道 Git 版本库的位置，以及工作区的根目录在哪里呢？</h3>
<p>显示版本库 <code>.git</code> 目录所在的位置。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rev-parse --git-dir
</span></span><span class="line"><span class="cl">/path/to/my/workspace/demo/.git
</span></span></code></pre></div><p>显示工作区根目录。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rev-parse --show-toplevel
</span></span><span class="line"><span class="cl">/path/to/my/workspace/demo
</span></span></code></pre></div><h3 id="把版本库-git-目录放在工作区是不是太不安全了">把版本库 <code>.git</code> 目录放在工作区，是不是太不安全了？</h3>
<p>Git 克隆可以降低因为版本库和工作区混杂在一起导致的版本库被破坏的风险。在本机另外的磁盘/目录中建立 Git 克隆，并在工作区有改动提交时，手动或自动地执行向克隆版本库的推送 <code>git push</code> 操作。如果使用网络协议，还可以实现在其他机器上建立克隆，这样就更安全了（双机备份）。</p>
<h3 id="思考git-config-命令参数的区别">思考：<code>git config</code> 命令参数的区别？</h3>
<p>将打开 /path/to/my/workspace/demo/.git/config 文件进行编辑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /path/to/my/workspace/demo/
</span></span><span class="line"><span class="cl">git config -e
</span></span></code></pre></div><p>将打开 /home/jiangxin/.gitconfig（用户主目录下的 .gitconfig 文件）全局配置文件进行编辑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config -e --global
</span></span></code></pre></div><p>将打开 /etc/gitconfig 系统级配置文件进行编辑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config -e --system
</span></span></code></pre></div><p>Git 的三个配置文件分别是 <code>版本库级别的配置文件</code>、<code>全局配置文件</code>（用户主目录下）和 <code>系统级配置文件</code>（/etc 目录下）。</p>
<p>其中 <code>版本库级别配置文件</code> 的优先级最高，<code>全局配置文件</code> 其次，<code>系统级配置文件</code> 优先级最低。</p>
<p>Git 配置文件采用的是 INI 文件格式。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">cat /path/to/my/workspace/demo/.git/config</span>
</span></span><span class="line"><span class="cl"><span class="k">[core]</span>
</span></span><span class="line"><span class="cl">        <span class="na">repositoryformatversion</span> <span class="o">=</span> <span class="s">0
</span></span></span><span class="line"><span class="cl"><span class="s">        filemode = true
</span></span></span><span class="line"><span class="cl"><span class="s">        bare = false
</span></span></span><span class="line"><span class="cl"><span class="s">        logallrefupdates = true</span>
</span></span></code></pre></div><p>例如读取 <code>[core]</code> 小节的 <code>bare</code> 的属性值</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config &lt;section&gt;.&lt;key&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git config core.bare
</span></span><span class="line"><span class="cl"><span class="nb">false</span>
</span></span></code></pre></div><p>更改或设置 INI 文件中某个属性</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config &lt;section&gt;.&lt;key&gt; &lt;value&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git config a.b something
</span></span><span class="line"><span class="cl">git config x.y.z others
</span></span></code></pre></div><p>打开 <code>.git/config</code> 文件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>a<span class="o">]</span>
</span></span><span class="line"><span class="cl">        <span class="nv">b</span> <span class="o">=</span> something
</span></span><span class="line"><span class="cl"><span class="o">[</span>x <span class="s2">&#34;y&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">        <span class="nv">z</span> <span class="o">=</span> others
</span></span></code></pre></div><p>可以用 <code>git config</code> 命令操作任何其他的 INI 文件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">GIT_CONFIG</span><span class="o">=</span>test.ini git config a.b.c.d <span class="s2">&#34;hello, world&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">GIT_CONFIG</span><span class="o">=</span>test.ini git config a.b.c.d
</span></span><span class="line"><span class="cl">hello, world
</span></span></code></pre></div><h3 id="思考是谁完成的提交">思考：是谁完成的提交？</h3>
<p>当最新的提交删除了 <code>user.name</code> 和 <code>user.email</code>，提交时 Git 对提交者的用户名和邮件地址做了大胆的猜测，这个猜测可能是错的。</p>
<p>重新设置 <code>user.name</code> 和 <code>user.email</code>，然后执行下面的命令，重新修改最新的提交，改正作者和提交者的错误信息。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit --amend --allow-empty --reset-author
</span></span></code></pre></div><ul>
<li>参数 <code>--amend</code> 是对刚刚的提交进行修补，这样就可以改正前面错误的提交（用户信息错误），而不会产生另外的新提交。</li>
<li>参数 <code>--allow-empty</code> 是因为要进行修补的提交实际上是一个空白提交，Git 默认不允许空白提交。</li>
<li>参数 <code>--reset-author</code> 的含义是将 Author（提交者）的 ID 重置，否则只会影响最新的 Commit（提交者）的 ID。这条命令也会重置 AuthorDate 信息。</li>
</ul>
<h3 id="思考随意设置提交者姓名是否太不安全">思考：随意设置提交者姓名，是否太不安全？</h3>
<p>Git 可以随意设置提交的用户名和邮件地址信息，这是分布式版本控制系统的特性使然，每个人都是自己版本库的主人，很难也没有必要进行身份认证从而使用经过认证的用户名作为提交的用户名。</p>
<p>但是可以使用 GitLab 等服务管理权限。</p>
<h3 id="思考命令别名是干什么的">思考：命令别名是干什么的？</h3>
<p>命令别名可以帮助用户解决从其他版本控制系统迁移到 Git 后的使用习惯问题。</p>
<h3 id="备份本章的工作成果">备份本章的工作成果</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /path/to/my/workspace
</span></span><span class="line"><span class="cl">git clone demo demo-step-1
</span></span><span class="line"><span class="cl">Cloning into demo-step-1...
</span></span><span class="line"><span class="cl"><span class="k">done</span>.
</span></span></code></pre></div><h2 id="git-暂存区">Git 暂存区</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log --stat
</span></span></code></pre></div><p>可以用 <code>git log</code> 查看提交日志，附加的 <code>--stat</code> 参数看到每次提交的文件变更统计。</p>
<h3 id="修改不能直接提交">修改不能直接提交？</h3>
<p>现在就将修改的文件“添加”到提交暂存区：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add welcome.txt
</span></span></code></pre></div><p>这时如果和 HEAD（当前版本库的头指针）或者 master 分支（当前工作分支）进行比较，会发现有差异。这个差异才是正常的，因为尚未真正提交么。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git diff HEAD
</span></span></code></pre></div><p>用简洁方式显示状态</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git status -s
</span></span><span class="line"><span class="cl">M  welcome.txt
</span></span></code></pre></div><p>通过参数 <code>--cached</code> 或者 <code>--staged</code> 参数调用 git diff 命令，看到的是提交暂存区 <code>stage</code> 和版本库中文件的差异。不然看到的是工作区的变动。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git diff --cached
</span></span></code></pre></div><p>现在执行 git commit 命令进行提交。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit -m <span class="s2">&#34;which version checked in?&#34;</span>
</span></span></code></pre></div><p>如何证明提交成功了呢？通过查看提交日志，看到了新的提交。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log --pretty<span class="o">=</span>oneline
</span></span></code></pre></div><h3 id="理解-git-暂存区-stage">理解 Git 暂存区 stage</h3>
<p>当执行 <code>git status</code> 命令（或者 <code>git diff</code> 命令）扫描工作区改动的时候，先依据 <code>.git/index</code> 文件中记录的（工作区跟踪文件的）时间戳、长度等信息判断工作区文件是否改变。</p>
<p>文件 <code>.git/index</code> 实际上就是一个包含文件索引的目录树，像是一个虚拟的工作区。在这个虚拟工作区的目录树中，记录了文件名、文件的状态信息（时间戳、文件长度等）。文件的内容并不存储其中，而是保存在 Git 对象库 <code>.git/objects</code> 目录中，文件索引建立了文件和对象库中对象实体之间的对应。</p>
<p><img alt="got-git-reading-notes-solo-git-stage" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80202431-33a64480-8658-11ea-8771-07b2bf81f657.png"></p>
<ul>
<li>图中可以看出此时 HEAD 实际是指向 master 分支的一个“游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。</li>
<li>图中的 objects 标识的区域为 Git 的对象库，实际位于 <code>.git/objects</code> 目录下，会在后面的章节重点介绍。</li>
<li>当执行 <code>git reset HEAD</code> 命令时，暂存区的目录树会被重写，被 master 分支指向的目录树所替换，但是工作区不受影响。</li>
<li>当执行 <code>git rm --cached &lt;file&gt;</code> 命令时，会直接从暂存区删除文件，工作区则不做出改变。</li>
<li>当执行 <code>git checkout .</code> 或者 <code>git checkout -- &lt;file&gt;</code> 命令时，会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险，会清除工作区中未添加到暂存区的改动。</li>
<li>当执行 <code>git checkout HEAD .</code> 或者 <code>git checkout HEAD &lt;file&gt;</code> 命令时，会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的，因为不但会清除工作区中未提交的改动，也会清除暂存区中未提交的改动。</li>
</ul>
<h3 id="git-diff-魔法">Git diff 魔法</h3>
<p>有什么办法能够像查看工作区一样的，直观的查看暂存区以及 HEAD 当中的目录树么？</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git ls-tree -l HEAD
</span></span><span class="line"><span class="cl"><span class="m">100644</span> blob fd3c069c1de4f4bc9b15940f490aeb48852f3c42      <span class="m">25</span>    welcome.txt
</span></span></code></pre></div><p>要显示暂存区的目录树，可以使用 <code>git ls-files</code> 命令。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git ls-files -s
</span></span><span class="line"><span class="cl"><span class="m">100644</span> 18832d35117ef2f013c4009f5b2128dfaeff354f <span class="m">0</span>       a/b/c/hello.txt
</span></span></code></pre></div><p><img alt="got-git-reading-notes-solo-git-diff" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80202327-06599680-8658-11ea-8329-93b424bc3726.png"></p>
<h3 id="不要使用-git-commit--a">不要使用 git commit -a</h3>
<p>提交命令 <code>git commit</code> 可以带上 <code>-a</code> 参数，对本地所有变更的文件执行提交操作，包括本地修改的文件，删除的文件，但不包括未被版本库跟踪的文件。</p>
<p>这个“偷懒”的提交命令，就会丢掉 Git 暂存区带给用户最大的好处：对提交内容进行控制的能力。</p>
<h2 id="git-对象">Git 对象</h2>
<p>什么是 <code>HEAD</code>？什么是 <code>master</code>？为什么它们二者可以相互替换使用？为什么 Git 中的很多对象像提交、树、文件内容等都用 40 位的 <code>SHA1</code> 哈希值来表示？</p>
<h3 id="git-对象库探秘">Git 对象库探秘</h3>
<p>40 位十六进制数字组成的 <code>SHA1</code> 哈希值</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log -1 --pretty<span class="o">=</span>raw
</span></span><span class="line"><span class="cl"><span class="c1"># 这是本次提交的唯一标识。</span>
</span></span><span class="line"><span class="cl">commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86
</span></span><span class="line"><span class="cl"><span class="c1"># 这是本次提交所对应的目录树。</span>
</span></span><span class="line"><span class="cl">tree f58da9a820e3fd9d84ab2ca2f1b467ac265038f9
</span></span><span class="line"><span class="cl"><span class="c1"># 这是本地提交的父提交（上一次提交）。</span>
</span></span><span class="line"><span class="cl">parent a0c641e92b10d8bcca1ed1bf84ca80340fdefee6
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    which version checked in?
</span></span></code></pre></div><p>研究 Git 对象 ID 的命令是 <code>git cat-file</code>，用下面的命令可以查看一下这三个 ID 的类型。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git cat-file -t e695606
</span></span><span class="line"><span class="cl">commit
</span></span><span class="line"><span class="cl">git cat-file -t f58d
</span></span><span class="line"><span class="cl">tree
</span></span><span class="line"><span class="cl">git cat-file -t fd3c06
</span></span><span class="line"><span class="cl">blob
</span></span></code></pre></div><!-- more -->
<p>再用 <code>git cat-file</code> 命令查看一下这几个对象的内容。对于 <code>blob</code> 对象，这个对象保存着文件 welcome.txt 的内容。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git cat-file -p fd3c06
</span></span><span class="line"><span class="cl">Hello.
</span></span><span class="line"><span class="cl">Nice to meet you.
</span></span></code></pre></div><p>这个写对象都存在 Git 库中的 <code>objects</code> 目录下，ID 的前两位作为目录名，后 38 位作为文件名。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">for</span> id in e695606 f58da9a a0c641e fd3c069<span class="p">;</span> <span class="k">do</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  ls .git/objects/<span class="si">${</span><span class="nv">id</span><span class="p">:</span><span class="nv">0</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span>/<span class="si">${</span><span class="nv">id</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span>*<span class="p">;</span> <span class="k">done</span>
</span></span><span class="line"><span class="cl">.git/objects/e6/95606fc5e31b2ff9038a48a3d363f4c21a3d86
</span></span><span class="line"><span class="cl">.git/objects/f5/8da9a820e3fd9d84ab2ca2f1b467ac265038f9
</span></span><span class="line"><span class="cl">.git/objects/a0/c641e92b10d8bcca1ed1bf84ca80340fdefee6
</span></span><span class="line"><span class="cl">.git/objects/fd/3c069c1de4f4bc9b15940f490aeb48852f3c42
</span></span></code></pre></div><p><img alt="got-git-reading-notes-solo-git-objects" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80202330-08235a00-8658-11ea-9040-b280b00b7e43.png"></p>
<h3 id="head-和-master-的奥秘">HEAD 和 master 的奥秘</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log -1 HEAD
</span></span><span class="line"><span class="cl">git log -1 master
</span></span><span class="line"><span class="cl">git log -1 refs/heads/master
</span></span></code></pre></div><p>在当前版本库中，<code>HEAD</code>、<code>master</code> 和 <code>refs/heads/master</code> 具有相同的指向。现在到版本库 <code>.git</code> 中一探它们的究竟：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">find .git -name HEAD -o -name master
</span></span><span class="line"><span class="cl">.git/HEAD
</span></span><span class="line"><span class="cl">.git/logs/HEAD
</span></span><span class="line"><span class="cl">.git/logs/refs/heads/master
</span></span><span class="line"><span class="cl">.git/refs/heads/master
</span></span></code></pre></div><p>显示一下 <code>.git/HEAD</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat .git/HEAD
</span></span><span class="line"><span class="cl">ref: refs/heads/master
</span></span></code></pre></div><p>指向一个引用：<code>refs/heads/master</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat .git/refs/heads/master
</span></span><span class="line"><span class="cl">e695606fc5e31b2ff9038a48a3d363f4c21a3d86
</span></span></code></pre></div><p>显示该提交的内容</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git cat-file -p e695606fc5e31b2ff9038a48a3d363f4c21a3d86
</span></span><span class="line"><span class="cl">tree f58da9a820e3fd9d84ab2ca2f1b467ac265038f9
</span></span><span class="line"><span class="cl">parent a0c641e92b10d8bcca1ed1bf84ca80340fdefee6
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">which version checked in?
</span></span></code></pre></div><p>原来分支 <code>master</code> 指向的是一个提交 ID（最新提交）。</p>
<p>这样的分支实现是多么的巧妙啊：既然可以从任何提交开始建立一条历史跟踪链，那么用一个文件指向这个链条的最新提交，那么这个文件就可以用于追踪整个提交历史了。</p>
<p>这个文件就是 <code>.git/refs/heads/master</code> 文件。</p>
<p><img alt="got-git-reading-notes-solo-git-repos-detail" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80202610-78ca7680-8658-11ea-9b85-651555b8f005.png"></p>
<p>目录 <code>.git/refs</code> 是保存引用的命名空间，其中 <code>.git/refs/heads</code> 目录下的引用又称为分支。对于分支既可以使用正规的长格式的表示法，如 <code>refs/heads/master</code>，也可以去掉前面的两级目录用 <code>master</code> 来表示。Git 有一个底层命令 <code>git rev-parse</code> 可以用于显示引用对应的提交 ID。</p>
<h3 id="问题sha1-哈希值到底是什么如何生成的">问题：SHA1 哈希值到底是什么，如何生成的？</h3>
<p>哈希(hash)是一种数据摘要算法（或称散列算法），是信息安全领域当中重要的理论基石。该算法将任意长度的输入经过散列运算转换为固定长度的输出。固定长度的输出可以称为对应的输入的数字摘要或哈希值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> -n Git <span class="p">|</span>sha1sum
</span></span><span class="line"><span class="cl">5819778898df55e3a762f0c5728b457970d72cae  -
</span></span></code></pre></div><p>提交的 SHA1 哈希值生成方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git cat-file commit HEAD <span class="p">|</span> wc -c
</span></span><span class="line"><span class="cl"><span class="m">234</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 在提交信息的前面加上内容 `commit 234&lt;null&gt;`（`&lt;null&gt;`为空字符），然后执行 SHA1 哈希算法。</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span> <span class="nb">printf</span> <span class="s2">&#34;commit 234\000&#34;</span><span class="p">;</span> git cat-file commit HEAD <span class="o">)</span> <span class="p">|</span> sha1sum
</span></span><span class="line"><span class="cl">e695606fc5e31b2ff9038a48a3d363f4c21a3d86  -
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 上面命令得到的哈希值和用 `git rev-parse` 看到的是一样的。</span>
</span></span><span class="line"><span class="cl">git rev-parse HEAD
</span></span><span class="line"><span class="cl">e695606fc5e31b2ff9038a48a3d363f4c21a3d86
</span></span></code></pre></div><p>文件内容的 SHA1 哈希值生成方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 文件总共包含 25 字节的内容。</span>
</span></span><span class="line"><span class="cl">git cat-file blob HEAD:welcome.txt <span class="p">|</span> wc -c
</span></span><span class="line"><span class="cl"><span class="m">25</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 在文件内容的前面加上blob 25&lt;null&gt;的内容，然后执行SHA1哈希算法。</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span> <span class="nb">printf</span> <span class="s2">&#34;blob 25\000&#34;</span><span class="p">;</span> git cat-file blob HEAD:welcome.txt <span class="o">)</span> <span class="p">|</span> sha1sum
</span></span><span class="line"><span class="cl">fd3c069c1de4f4bc9b15940f490aeb48852f3c42  -
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 上面命令得到的哈希值和用git rev-parse看到的是一样的。</span>
</span></span><span class="line"><span class="cl">git rev-parse HEAD:welcome.txt
</span></span><span class="line"><span class="cl">fd3c069c1de4f4bc9b15940f490aeb48852f3c42
</span></span></code></pre></div><p>树的 SHA1 哈希值的形成方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># HEAD对应的树的内容共包含39个字节。</span>
</span></span><span class="line"><span class="cl">git cat-file tree HEAD^<span class="o">{</span>tree<span class="o">}</span> <span class="p">|</span> wc -c
</span></span><span class="line"><span class="cl"><span class="m">39</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 在树的内容的前面加上tree 39&lt;null&gt;的内容，然后执行SHA1哈希算法。</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span> <span class="nb">printf</span> <span class="s2">&#34;tree 39\000&#34;</span><span class="p">;</span> git cat-file tree HEAD^<span class="o">{</span>tree<span class="o">}</span> <span class="o">)</span> <span class="p">|</span> sha1sum
</span></span><span class="line"><span class="cl">f58da9a820e3fd9d84ab2ca2f1b467ac265038f9  -
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 上面命令得到的哈希值和用git rev-parse看到的是一样的。</span>
</span></span><span class="line"><span class="cl">git rev-parse HEAD^<span class="o">{</span>tree<span class="o">}</span>
</span></span><span class="line"><span class="cl">f58da9a820e3fd9d84ab2ca2f1b467ac265038f9
</span></span></code></pre></div><h3 id="问题为什么不用顺序的数字来表示提交">问题：为什么不用顺序的数字来表示提交？</h3>
<p>集中式版本控制系统因为只有一个集中式的版本库，可以很容易的实现依次递增的全局唯一的提交号。Git 作为分布式版本控制系统，开发可以是非线性的。这就要求提交的编号不能仅仅是本地局部有效，而是要“全球唯一”。</p>
<p>采用部分的 SHA1 哈希值。不必写全 40 位的哈希值，只采用开头的部分，不和现有其他的冲突即可。</p>
<p>使用 <code>master</code> 代表分支 <code>master</code> 中最新的提交，使用全称 <code>refs/heads/master</code> 亦可。</p>
<p>使用 <code>HEAD</code> 代表版本库中最近的一次提交。</p>
<p>符号 <code>^</code> 可以用于指代父提交。例如：</p>
<ul>
<li><code>HEAD^</code> 代表版本库中上一次提交，即最近一次提交的父提交。</li>
<li><code>HEAD^^</code> 则代表 <code>HEAD^</code> 的父提交。</li>
</ul>
<p>对于一个提交有多个父提交，可以在符号 <code>^</code> 后面用数字表示是第几个父提交。例如：</p>
<ul>
<li><code>a573106^2</code> 含义是提交 <code>a573106</code> 的多个父提交中的第二个父提交。</li>
<li><code>HEAD^1</code> 相当于 <code>HEAD^</code> 含义是 HEAD 多个父提交中的第一个。</li>
<li><code>HEAD^^2</code> 含义是 <code>HEAD^</code>（HEAD 父提交）的多个父提交中的第二个。</li>
</ul>
<p>符号 <code>~&lt;n&gt;</code> 也可以用于指代祖先提交。效果等同：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">a573106~5
</span></span><span class="line"><span class="cl">a573106^^^^^
</span></span></code></pre></div><p>提交所对应的树对象：<code>a573106^{tree}</code></p>
<p>某一此提交对应的文件对象：<code>a573106:path/to/file</code></p>
<p>暂存区中的文件对象：<code>:path/to/file</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rev-parse HEAD
</span></span><span class="line"><span class="cl">git cat-file -p e695
</span></span><span class="line"><span class="cl">git cat-file -p e695^
</span></span><span class="line"><span class="cl">git rev-parse e695^<span class="o">{</span>tree<span class="o">}</span>
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="http://www.worldhello.net/gotgit/02-git-solo/index.html">2. Git 独奏 — GotGit</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>【Git 权威指南】读书笔记 - 初识 Git</title>
      <link>https://zyf.im/2017/07/12/got-git-reading-notes-meet-git/</link>
      <pubDate>Wed, 12 Jul 2017 15:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/07/12/got-git-reading-notes-meet-git/</guid>
      <description>&lt;p&gt;Git 是一款分布式版本控制系统，有别于 CVS 和 SVN 等集中式版本控制系统，Git 可以让研发团队更加高效地协同工作、提高生产率。使用 Git，开发人员的工作不会因为频繁地遭遇提交冲突而中断，管理人员也无须为数据的备份而担心。经过 Linux 这样庞大的项目的考验之后，Git 被证明可以胜任任何规模的团队，即便这个团队的成员分布于世界各地。&lt;/p&gt;
&lt;p&gt;Git 是开源社区奉献给每一个人的宝贝，用好它可以实现个人的知识积累、保护好自己的数据，而且还能与他人分享自己的成果。&lt;/p&gt;
&lt;h2 id=&#34;版本控制的前世和今生&#34;&gt;版本控制的前世和今生&lt;/h2&gt;
&lt;p&gt;即便是在 CVS 出现之前的“史前时代”，也已经有了非常好用的源码比较和打补丁的工具：&lt;code&gt;diff&lt;/code&gt; 和 &lt;code&gt;patch&lt;/code&gt;，他们今天生命力依然顽强。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;对这 &lt;code&gt;hello&lt;/code&gt; &lt;code&gt;world&lt;/code&gt; 两个文件执行 diff 命令，查看两个文件的差异。如下所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;diff -u hello world &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; less -N
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面执行 &lt;code&gt;diff&lt;/code&gt; 命令的 &lt;code&gt;-u&lt;/code&gt; 参数很重要，使得差异输出中带有上下文。管道后面带有 &lt;code&gt;-N&lt;/code&gt; 参数的 &lt;code&gt;less&lt;/code&gt; 命令（按字母 &lt;code&gt;q&lt;/code&gt; 退出）会在输出的每一行前面添加行号，便于对输出结果进行说明。&lt;/p&gt;
&lt;p&gt;命令 &lt;code&gt;patch&lt;/code&gt; 相当于 &lt;code&gt;diff&lt;/code&gt; 的反向操作&lt;/p&gt;
&lt;p&gt;分布式版本控制系统最大的反传统之处在于，可以不需要集中式的版本库，每个人都工作在通过克隆操作建立的本地版本库中，也就是说每个人都拥有一个完整的版本库。分布式版本控制系统的几乎所有操作包括查看提交日志、提交、创建里程碑和分支、合并分支、回退等都直接在本地完成而不需要网络连接。每个人都是本地版本库的主人，不再有谁能提交谁不能提交的限制，加之多样的协同工作模型（版本库间推送、拉回，及补丁文件传送等）让开源项目的参与度有爆发式增长。&lt;/p&gt;
&lt;h2 id=&#34;爱上-git-的理由&#34;&gt;爱上 Git 的理由&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;每日的工作备份。鸡蛋不全放在一个篮子里。&lt;/li&gt;
&lt;li&gt;异地协同工作。通过一个远程版本库，同步数据。&lt;/li&gt;
&lt;li&gt;现场版本控制。在部署的现场，进行源代码的修改，能够将修改结果甚至修改过程一并带走，并能够将修改结果合并至项目对应的代码库中。&lt;/li&gt;
&lt;li&gt;避免引入辅助目录。只在工作区的顶级目录下创建名为 &lt;code&gt;.git&lt;/code&gt; 的目录（版本库目录），如果认为唯一的一个 &lt;code&gt;.git&lt;/code&gt; 目录也过于碍眼，可以将其放到工作区之外的任意目录。一旦这么做了，你在执行 Git 命令时，要通过命令行 &lt;code&gt;--git-dir&lt;/code&gt; 或环境变量 &lt;code&gt;GIT_DIR&lt;/code&gt; 为工作区指定版本库目录，甚至还要指定工作区目录。&lt;/li&gt;
&lt;li&gt;重写提交说明。这个命令如果不带 &lt;code&gt;-m&lt;/code&gt; 参数，会进入提交说明编辑界面。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit --amend
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;想吃后悔药。假如提交的数据中不小心包含了一个不应该检入的虚拟机文件——大约有 1 个 GB。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git rm --cached winxp.img
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit --amend
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;更好用的提交列表。正确的版本控制系统的使用方法是：一次提交只干一件事。而不要在下班时才想起来要提交，那样的话版本控制系统就被降格为文件备份系统了。&lt;/li&gt;
&lt;li&gt;更好的差异比较。&lt;code&gt;git diff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;工作进度保存。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git stash
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git checkout &amp;lt;new_branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# do something&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git checkout &amp;lt;orignal_branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git stash pop
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;代理 SVN 提交实现移动式办公。&lt;/li&gt;
&lt;li&gt;无处不在的分页器。&lt;code&gt;-p&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;快。Git 作为分布式版本控制系统几乎所有的操作都在本地进行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;安装-git&#34;&gt;安装 Git&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo aptitude install git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo aptitude install git-doc git-svn git-email gitk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;软件包 git-svn、git-email、gitk 本来也是 Git 软件包的一部分，但是因为有着不一样的软件包依赖（如更多 perl 模组，tk 等），所以单独作为软件包发布。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Git 是一款分布式版本控制系统，有别于 CVS 和 SVN 等集中式版本控制系统，Git 可以让研发团队更加高效地协同工作、提高生产率。使用 Git，开发人员的工作不会因为频繁地遭遇提交冲突而中断，管理人员也无须为数据的备份而担心。经过 Linux 这样庞大的项目的考验之后，Git 被证明可以胜任任何规模的团队，即便这个团队的成员分布于世界各地。</p>
<p>Git 是开源社区奉献给每一个人的宝贝，用好它可以实现个人的知识积累、保护好自己的数据，而且还能与他人分享自己的成果。</p>
<h2 id="版本控制的前世和今生">版本控制的前世和今生</h2>
<p>即便是在 CVS 出现之前的“史前时代”，也已经有了非常好用的源码比较和打补丁的工具：<code>diff</code> 和 <code>patch</code>，他们今天生命力依然顽强。</p>
<!-- more -->
<p>对这 <code>hello</code> <code>world</code> 两个文件执行 diff 命令，查看两个文件的差异。如下所示：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">diff -u hello world <span class="p">|</span> less -N
</span></span></code></pre></div><p>上面执行 <code>diff</code> 命令的 <code>-u</code> 参数很重要，使得差异输出中带有上下文。管道后面带有 <code>-N</code> 参数的 <code>less</code> 命令（按字母 <code>q</code> 退出）会在输出的每一行前面添加行号，便于对输出结果进行说明。</p>
<p>命令 <code>patch</code> 相当于 <code>diff</code> 的反向操作</p>
<p>分布式版本控制系统最大的反传统之处在于，可以不需要集中式的版本库，每个人都工作在通过克隆操作建立的本地版本库中，也就是说每个人都拥有一个完整的版本库。分布式版本控制系统的几乎所有操作包括查看提交日志、提交、创建里程碑和分支、合并分支、回退等都直接在本地完成而不需要网络连接。每个人都是本地版本库的主人，不再有谁能提交谁不能提交的限制，加之多样的协同工作模型（版本库间推送、拉回，及补丁文件传送等）让开源项目的参与度有爆发式增长。</p>
<h2 id="爱上-git-的理由">爱上 Git 的理由</h2>
<ul>
<li>每日的工作备份。鸡蛋不全放在一个篮子里。</li>
<li>异地协同工作。通过一个远程版本库，同步数据。</li>
<li>现场版本控制。在部署的现场，进行源代码的修改，能够将修改结果甚至修改过程一并带走，并能够将修改结果合并至项目对应的代码库中。</li>
<li>避免引入辅助目录。只在工作区的顶级目录下创建名为 <code>.git</code> 的目录（版本库目录），如果认为唯一的一个 <code>.git</code> 目录也过于碍眼，可以将其放到工作区之外的任意目录。一旦这么做了，你在执行 Git 命令时，要通过命令行 <code>--git-dir</code> 或环境变量 <code>GIT_DIR</code> 为工作区指定版本库目录，甚至还要指定工作区目录。</li>
<li>重写提交说明。这个命令如果不带 <code>-m</code> 参数，会进入提交说明编辑界面。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit --amend
</span></span></code></pre></div><ul>
<li>想吃后悔药。假如提交的数据中不小心包含了一个不应该检入的虚拟机文件——大约有 1 个 GB。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rm --cached winxp.img
</span></span><span class="line"><span class="cl">git commit --amend
</span></span></code></pre></div><ul>
<li>更好用的提交列表。正确的版本控制系统的使用方法是：一次提交只干一件事。而不要在下班时才想起来要提交，那样的话版本控制系统就被降格为文件备份系统了。</li>
<li>更好的差异比较。<code>git diff</code></li>
<li>工作进度保存。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git stash
</span></span><span class="line"><span class="cl">git checkout &lt;new_branch&gt;
</span></span><span class="line"><span class="cl"><span class="c1"># do something</span>
</span></span><span class="line"><span class="cl">git checkout &lt;orignal_branch&gt;
</span></span><span class="line"><span class="cl">git stash pop
</span></span></code></pre></div><ul>
<li>代理 SVN 提交实现移动式办公。</li>
<li>无处不在的分页器。<code>-p</code></li>
<li>快。Git 作为分布式版本控制系统几乎所有的操作都在本地进行。</li>
</ul>
<h2 id="安装-git">安装 Git</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo aptitude install git
</span></span><span class="line"><span class="cl">sudo aptitude install git-doc git-svn git-email gitk
</span></span></code></pre></div><p>软件包 git-svn、git-email、gitk 本来也是 Git 软件包的一部分，但是因为有着不一样的软件包依赖（如更多 perl 模组，tk 等），所以单独作为软件包发布。</p>
<p>软件包 git-doc 则包含了 Git 的 HTML 格式文档，可以选择安装。如果安装了 Git 的 HTML 格式的文档，则可以通过执行 <code>git help -w &lt;sub-command&gt;</code> 命令，自动用 Web 浏览器打开相关子命令 <code>&lt;sub-command&gt;</code> 的 HTML 帮助。</p>
<h2 id="中文支持">中文支持</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global core.quotepath <span class="nb">false</span>
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="http://www.worldhello.net/gotgit/01-meet-git/index.html">1. 初识 Git — GotGit</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>NGINX 启用 HTTP/2</title>
      <link>https://zyf.im/2017/06/06/nginx-enable-http2/</link>
      <pubDate>Tue, 06 Jun 2017 21:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/06/06/nginx-enable-http2/</guid>
      <description>&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;HTTP/2（RFC 7540，2015 年发布）是自 1999 年 HTTP/1.1 以来的第一次重大升级，目标是在 不改变应用语义 的前提下，改善延迟、并发和网络效率。浏览器端基本只在 TLS 上启用（h2），明文版本 h2c 仅用于内部服务间通信。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;h2：HTTP/2 over TLS（最常见，浏览器只支持这一种）&lt;/li&gt;
&lt;li&gt;h2c：HTTP/2 over TCP（无加密）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;设计背景&#34;&gt;设计背景&lt;/h3&gt;
&lt;p&gt;移动端和富媒体时代，请求量激增，HTTP/1.1 的队头阻塞、多个 TCP 连接并行带来的 慢启动/拥塞竞争 成为瓶颈。&lt;/p&gt;
&lt;p&gt;谷歌的 SPDY 原型验证了“单连接、多路复用、压缩头部”等思路；IETF 在 SPDY 3.1 基础上标准化为 HTTP/2。&lt;/p&gt;
&lt;h3 id=&#34;核心特性&#34;&gt;核心特性&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;特性&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;二进制分帧 (Binary Framing)&lt;/td&gt;
          &lt;td&gt;把 HTTP 报文拆成小型 Frame；机器易解析、可并行&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;多路复用 (Multiplexing)&lt;/td&gt;
          &lt;td&gt;多个请求/响应 共享一个 TCP 连接，互不阻塞&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;流与优先级 (Stream &amp;amp; Priority)&lt;/td&gt;
          &lt;td&gt;每个请求是双向 Stream，可指定权重与依赖&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;头部压缩 (HPACK)&lt;/td&gt;
          &lt;td&gt;静态 Huffman + 动态表，大幅减少重复的 Header 字节&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;服务器推送 (Server Push)&lt;/td&gt;
          &lt;td&gt;服务器可在请求外预送资源 (已被多数浏览器弱化/关闭)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;流量控制&lt;/td&gt;
          &lt;td&gt;端到端窗口，避免单个大文件占满带宽&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;开始实验&#34;&gt;开始实验&lt;/h2&gt;
&lt;p&gt;启用 HTTP/2 需要使用 HTTPS，请先参考 &lt;a href=&#34;https://zyf.im/2025/06/26/use-https-in-local-environment/&#34;&gt;在本地环境使用 HTTPS ｜ ZYF.IM&lt;/a&gt;。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="简介">简介</h2>
<p>HTTP/2（RFC 7540，2015 年发布）是自 1999 年 HTTP/1.1 以来的第一次重大升级，目标是在 不改变应用语义 的前提下，改善延迟、并发和网络效率。浏览器端基本只在 TLS 上启用（h2），明文版本 h2c 仅用于内部服务间通信。</p>
<ul>
<li>h2：HTTP/2 over TLS（最常见，浏览器只支持这一种）</li>
<li>h2c：HTTP/2 over TCP（无加密）</li>
</ul>
<h3 id="设计背景">设计背景</h3>
<p>移动端和富媒体时代，请求量激增，HTTP/1.1 的队头阻塞、多个 TCP 连接并行带来的 慢启动/拥塞竞争 成为瓶颈。</p>
<p>谷歌的 SPDY 原型验证了“单连接、多路复用、压缩头部”等思路；IETF 在 SPDY 3.1 基础上标准化为 HTTP/2。</p>
<h3 id="核心特性">核心特性</h3>
<table>
  <thead>
      <tr>
          <th>特性</th>
          <th>作用</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>二进制分帧 (Binary Framing)</td>
          <td>把 HTTP 报文拆成小型 Frame；机器易解析、可并行</td>
      </tr>
      <tr>
          <td>多路复用 (Multiplexing)</td>
          <td>多个请求/响应 共享一个 TCP 连接，互不阻塞</td>
      </tr>
      <tr>
          <td>流与优先级 (Stream &amp; Priority)</td>
          <td>每个请求是双向 Stream，可指定权重与依赖</td>
      </tr>
      <tr>
          <td>头部压缩 (HPACK)</td>
          <td>静态 Huffman + 动态表，大幅减少重复的 Header 字节</td>
      </tr>
      <tr>
          <td>服务器推送 (Server Push)</td>
          <td>服务器可在请求外预送资源 (已被多数浏览器弱化/关闭)</td>
      </tr>
      <tr>
          <td>流量控制</td>
          <td>端到端窗口，避免单个大文件占满带宽</td>
      </tr>
  </tbody>
</table>
<h2 id="开始实验">开始实验</h2>
<p>启用 HTTP/2 需要使用 HTTPS，请先参考 <a href="/2025/06/26/use-https-in-local-environment/">在本地环境使用 HTTPS ｜ ZYF.IM</a>。</p>
<p>修改 NGINX default.conf 配置文件，启用 HTTP/2：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># vi default.conf</span>
</span></span><span class="line"><span class="cl">server <span class="o">{</span>
</span></span><span class="line"><span class="cl">    listen <span class="m">443</span> ssl<span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 启用 HTTP/2</span>
</span></span><span class="line"><span class="cl">    http2 on<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    server_name localhost<span class="p">;</span>
</span></span><span class="line"><span class="cl">    ssl_certificate /etc/ssl/certs/localhost.pem<span class="p">;</span>
</span></span><span class="line"><span class="cl">    ssl_certificate_key /etc/ssl/certs/localhost-key.pem<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    ssl_protocols TLSv1.2 TLSv1.3<span class="p">;</span>
</span></span><span class="line"><span class="cl">    ssl_ciphers HIGH:!aNULL:!MD5<span class="p">;</span>
</span></span><span class="line"><span class="cl">    ssl_prefer_server_ciphers on<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    location / <span class="o">{</span>
</span></span><span class="line"><span class="cl">        root /usr/share/nginx/html<span class="p">;</span>
</span></span><span class="line"><span class="cl">        index  index.html index.htm<span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker container restart nginx-ssl
</span></span></code></pre></div><h2 id="测试">测试</h2>
<h3 id="游览器">游览器</h3>
<p>游览器访问 <code>https://localhost:8443/</code>，在 Chrome 开发者工具 Network 面板勾选 <code>Protocol</code>，可看到 <code>h2</code> 字样。</p>
<p>或者使用 Chrome 插件 <a href="https://chromewebstore.google.com/detail/http-indicator/hgcomhbcacfkpffiphlmnlhpppcjgmbl?hl=en-US">HTTP Indicator</a>：</p>
<ul>
<li>HTTP/1.1 显示为灰色。</li>
<li>HTTP/2 显示为蓝色。</li>
<li>HTTP/3 显示为绿色。</li>
</ul>
<h3 id="curl">curl</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># -I (--head) 只返回响应头</span>
</span></span><span class="line"><span class="cl">curl -I https://localhost:8443/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">HTTP/2 <span class="m">200</span>
</span></span><span class="line"><span class="cl">server: nginx/1.29.0
</span></span><span class="line"><span class="cl">date: Fri, <span class="m">27</span> Jun <span class="m">2025</span> 01:56:55 GMT
</span></span><span class="line"><span class="cl">content-type: text/html
</span></span><span class="line"><span class="cl">content-length: <span class="m">615</span>
</span></span><span class="line"><span class="cl">last-modified: Tue, <span class="m">24</span> Jun <span class="m">2025</span> 17:57:38 GMT
</span></span><span class="line"><span class="cl">etag: <span class="s2">&#34;685ae712-267&#34;</span>
</span></span><span class="line"><span class="cl">accept-ranges: bytes
</span></span></code></pre></div><h2 id="http3">HTTP/3</h2>
<p>请移步 <a href="/2025/06/27/nginx-enable-http3/">NGINX 启用 HTTP/3 ｜ ZYF.IM</a>。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://nginx.org/en/docs/http/ngx_http_v2_module.html">NGINX HTTP/2 模块</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>【摔跤吧，爸爸】随笔</title>
      <link>https://zyf.im/2017/05/14/i-am-proud-of-you/</link>
      <pubDate>Sun, 14 May 2017 13:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/05/14/i-am-proud-of-you/</guid>
      <description>&lt;p&gt;周末看了《摔跤吧，爸爸》，也是第一次独自电影院看电影，试写一篇影评纪念下。&lt;/p&gt;
&lt;p&gt;注意：&lt;strong&gt;严重剧透预警。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;主角爸爸是印度全国摔跤冠军，一心想着为国家赢取一枚金牌。可自己没能实现梦想，把梦想转移给了自己还未出世的孩子。可事与愿违，想要男孩的主角爸爸的前三个孩子都是女孩，第四个还是女孩。主角爸爸近乎要放弃为国争取金牌的梦想时，却意外看的了大女儿、二女儿身上的摔跤天赋。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;主角妈妈在得知主角爸爸准备将女儿们训练为拳击手时，道出了 “你不能将你的梦想施加在女儿们身上”，主角爸爸沉思片刻 “给我一年时间，其间你不要插手，如果没成功，我将有永远放弃我的梦想”。在主角爸爸说出这句话之前，我是反对父母将自己的梦想让孩子去实现的，也更没有去争取过孩子们的想法，现实中这样做的父母我是常为 Loser. 但是主角爸爸的回答让我看到的不是一个：被自己个人梦想冲昏头，用孩子的全部去成就自己的父亲。主角爸爸是讲道理的，一年后如果失败也会甘愿放弃。坚持与固执的区别也许就在此。&lt;/p&gt;
&lt;p&gt;主角爸爸变身为女儿们的魔鬼摔跤教练，每天除了上学就是训练，跑步时衣服不合适换男生衣服，找侄子当陪练，没有力量加餐牛奶、鸡肉，长头发难清理剪成了寸头。受苦中的女儿们找妈妈求情，主角妈妈遵守承诺，不干预主角爸爸的训练。这里真的要给主角妈妈点赞，后面女儿们的成功，女儿们、主角爸爸的努力在明面放着，而主角妈妈的守诺不干预，对主角爸爸的信赖，是同样的伟大。不然那柔弱的耳边风不知乱了多少坚定的意志。&lt;/p&gt;
&lt;p&gt;训练太辛苦的女儿们采用消极怠工对抗主角爸爸。破坏闹钟、破坏场地、假摔示弱。而旷工一天参加好友的婚礼现场被主角爸爸收拾。在与好友诉苦时，才发现了：对比其他女孩一望到底的家庭妇女人生，主角爸爸的狠心是那样让人羡慕。&lt;/p&gt;
&lt;p&gt;观念改变的女儿们开始主动训练。大女儿首次参赛就初露锋芒，差一点将男孩对手打败。逐渐成长的大女儿愈战愈勇的直到赢得全国冠军。全国冠军都会进入国家体育学校学习，大女儿也要离开主角爸爸接受新教练的训练，备战世界大赛。从小镇走出来的大女儿，没了主角爸爸的严格管束，开始着迷于这外面多彩的世界。吃油炸食品、留起头发、逛街、电影院。假期回家的大女儿用自己新学的技巧击败了主角爸爸，更是产生自我膨胀，对主角爸爸传统技术技巧的不屑与不信任。&lt;/p&gt;
&lt;p&gt;国际大赛上大女儿频频失利，无能教练将此归为命运。与此同时坚信主角爸爸训练方式的二儿女也获得全国冠军，进入国家队。在二儿女和主角妈妈的劝解下，一通电话化解了大女儿和主角爸爸的隔阂。主角爸爸亲自来到体育学校，为大女儿备战下次的国际大赛。&lt;/p&gt;
&lt;p&gt;主角爸爸在仔细研究大女儿对手后，制定了针对性的战术帮助大女儿杀入决赛。无能教练为了不让主角爸爸抢了自己的功劳，在决赛开始前将主角爸爸骗入小黑屋，无法让主角爸爸指导大女儿比赛。决赛第二局大女儿没能把握赛点，让对手追平。主角爸爸继续被困，只能默默祈祷。决赛最后一局，大女儿大比分落后，最后 10 秒大女儿脑海闪现着主角爸爸的叮咛，一击 5 分绝杀翻盘，赢得世界冠军。冠军的国歌声奏起，小黑屋中的主角爸爸也被路人解救，冲入赛场的主角爸爸与大女儿四目相对，此时这已经不是大女儿或主角爸爸哪一个人的冠军，而是所有正在与命运抗战者的胜利。&lt;/p&gt;
&lt;p&gt;主角爸爸：“你是我的骄傲”&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;电影的专业手法和技术自己不懂，但是自己有个感觉：影片后期有大量的摔跤比赛，时不时有种看奥运赛的感觉，但是自己并没有感到乏味。电影故事情节很寻常，但是娓娓道来，很多细节小事让节奏不拖沓。故事人物有主角爸爸的理性和坚持、主角妈妈的守诺不干预、女儿们自我的成长、侄子的酱油加醋、无能教练损人为己&lt;/p&gt;
&lt;p&gt;电影给我也留下了几个现实问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;父母的认识经验一定比子女高的多，主角爸爸在看到女儿们的摔跤天赋时，狠心训练与女儿本身的意愿如何选择？&lt;/li&gt;
&lt;li&gt;父母逐渐变老，他们真的跟不上我们了吗？&lt;/li&gt;
&lt;li&gt;如何辨别无能教练的瞎指挥？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>周末看了《摔跤吧，爸爸》，也是第一次独自电影院看电影，试写一篇影评纪念下。</p>
<p>注意：<strong>严重剧透预警。</strong></p>
<p>主角爸爸是印度全国摔跤冠军，一心想着为国家赢取一枚金牌。可自己没能实现梦想，把梦想转移给了自己还未出世的孩子。可事与愿违，想要男孩的主角爸爸的前三个孩子都是女孩，第四个还是女孩。主角爸爸近乎要放弃为国争取金牌的梦想时，却意外看的了大女儿、二女儿身上的摔跤天赋。</p>
<!-- more -->
<p>主角妈妈在得知主角爸爸准备将女儿们训练为拳击手时，道出了 “你不能将你的梦想施加在女儿们身上”，主角爸爸沉思片刻 “给我一年时间，其间你不要插手，如果没成功，我将有永远放弃我的梦想”。在主角爸爸说出这句话之前，我是反对父母将自己的梦想让孩子去实现的，也更没有去争取过孩子们的想法，现实中这样做的父母我是常为 Loser. 但是主角爸爸的回答让我看到的不是一个：被自己个人梦想冲昏头，用孩子的全部去成就自己的父亲。主角爸爸是讲道理的，一年后如果失败也会甘愿放弃。坚持与固执的区别也许就在此。</p>
<p>主角爸爸变身为女儿们的魔鬼摔跤教练，每天除了上学就是训练，跑步时衣服不合适换男生衣服，找侄子当陪练，没有力量加餐牛奶、鸡肉，长头发难清理剪成了寸头。受苦中的女儿们找妈妈求情，主角妈妈遵守承诺，不干预主角爸爸的训练。这里真的要给主角妈妈点赞，后面女儿们的成功，女儿们、主角爸爸的努力在明面放着，而主角妈妈的守诺不干预，对主角爸爸的信赖，是同样的伟大。不然那柔弱的耳边风不知乱了多少坚定的意志。</p>
<p>训练太辛苦的女儿们采用消极怠工对抗主角爸爸。破坏闹钟、破坏场地、假摔示弱。而旷工一天参加好友的婚礼现场被主角爸爸收拾。在与好友诉苦时，才发现了：对比其他女孩一望到底的家庭妇女人生，主角爸爸的狠心是那样让人羡慕。</p>
<p>观念改变的女儿们开始主动训练。大女儿首次参赛就初露锋芒，差一点将男孩对手打败。逐渐成长的大女儿愈战愈勇的直到赢得全国冠军。全国冠军都会进入国家体育学校学习，大女儿也要离开主角爸爸接受新教练的训练，备战世界大赛。从小镇走出来的大女儿，没了主角爸爸的严格管束，开始着迷于这外面多彩的世界。吃油炸食品、留起头发、逛街、电影院。假期回家的大女儿用自己新学的技巧击败了主角爸爸，更是产生自我膨胀，对主角爸爸传统技术技巧的不屑与不信任。</p>
<p>国际大赛上大女儿频频失利，无能教练将此归为命运。与此同时坚信主角爸爸训练方式的二儿女也获得全国冠军，进入国家队。在二儿女和主角妈妈的劝解下，一通电话化解了大女儿和主角爸爸的隔阂。主角爸爸亲自来到体育学校，为大女儿备战下次的国际大赛。</p>
<p>主角爸爸在仔细研究大女儿对手后，制定了针对性的战术帮助大女儿杀入决赛。无能教练为了不让主角爸爸抢了自己的功劳，在决赛开始前将主角爸爸骗入小黑屋，无法让主角爸爸指导大女儿比赛。决赛第二局大女儿没能把握赛点，让对手追平。主角爸爸继续被困，只能默默祈祷。决赛最后一局，大女儿大比分落后，最后 10 秒大女儿脑海闪现着主角爸爸的叮咛，一击 5 分绝杀翻盘，赢得世界冠军。冠军的国歌声奏起，小黑屋中的主角爸爸也被路人解救，冲入赛场的主角爸爸与大女儿四目相对，此时这已经不是大女儿或主角爸爸哪一个人的冠军，而是所有正在与命运抗战者的胜利。</p>
<p>主角爸爸：“你是我的骄傲”</p>
<hr>
<p>电影的专业手法和技术自己不懂，但是自己有个感觉：影片后期有大量的摔跤比赛，时不时有种看奥运赛的感觉，但是自己并没有感到乏味。电影故事情节很寻常，但是娓娓道来，很多细节小事让节奏不拖沓。故事人物有主角爸爸的理性和坚持、主角妈妈的守诺不干预、女儿们自我的成长、侄子的酱油加醋、无能教练损人为己</p>
<p>电影给我也留下了几个现实问题：</p>
<ul>
<li>父母的认识经验一定比子女高的多，主角爸爸在看到女儿们的摔跤天赋时，狠心训练与女儿本身的意愿如何选择？</li>
<li>父母逐渐变老，他们真的跟不上我们了吗？</li>
<li>如何辨别无能教练的瞎指挥？</li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>MySQL 5.6 5.7 组内排序的区别</title>
      <link>https://zyf.im/2017/04/20/mysql-group-by-and-order-by-difference-between-56-57/</link>
      <pubDate>Thu, 20 Apr 2017 20:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/04/20/mysql-group-by-and-order-by-difference-between-56-57/</guid>
      <description>&lt;p&gt;MySQL 5.7 对比 5.6 有很多的变化。一个常见的需求：按条件分组后，取出每组中某字段最大值的那条记录。其实就是组内排序的问题，我的做法是：子查询先进行倒序排序，外层查询分组。&lt;/p&gt;
&lt;h2 id=&#34;示例&#34;&gt;示例&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-md&#34; data-lang=&#34;md&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+----+----+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| id | no | name |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+----+----+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 5 | 5 | Mike |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 4 | 4 | Herry |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 3 | 3 | wyett |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 2 | 2 | John |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 7 | 2 | John |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 1 | 1 | Mike |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 6 | 1 | John |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 8 | 1 | Mike |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 9 | 1 | Mike |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+----+----+-------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要求：取出每人（按 name），最大 no 的记录。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>MySQL 5.7 对比 5.6 有很多的变化。一个常见的需求：按条件分组后，取出每组中某字段最大值的那条记录。其实就是组内排序的问题，我的做法是：子查询先进行倒序排序，外层查询分组。</p>
<h2 id="示例">示例</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">+----+----+-------+
</span></span><span class="line"><span class="cl">| id | no | name |
</span></span><span class="line"><span class="cl">+----+----+-------+
</span></span><span class="line"><span class="cl">| 5 | 5 | Mike |
</span></span><span class="line"><span class="cl">| 4 | 4 | Herry |
</span></span><span class="line"><span class="cl">| 3 | 3 | wyett |
</span></span><span class="line"><span class="cl">| 2 | 2 | John |
</span></span><span class="line"><span class="cl">| 7 | 2 | John |
</span></span><span class="line"><span class="cl">| 1 | 1 | Mike |
</span></span><span class="line"><span class="cl">| 6 | 1 | John |
</span></span><span class="line"><span class="cl">| 8 | 1 | Mike |
</span></span><span class="line"><span class="cl">| 9 | 1 | Mike |
</span></span><span class="line"><span class="cl">+----+----+-------+
</span></span></code></pre></div><p>要求：取出每人（按 name），最大 no 的记录。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">select</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="k">no</span><span class="p">,</span><span class="n">name</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">testorder</span><span class="w"> </span><span class="k">order</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="k">no</span><span class="w"> </span><span class="k">desc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="n">a</span><span class="w"> </span><span class="k">group</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">name</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">+----+----+-------+
</span></span><span class="line"><span class="cl">| id | no | name |
</span></span><span class="line"><span class="cl">+----+----+-------+
</span></span><span class="line"><span class="cl">| 4 | 4 | Herry |
</span></span><span class="line"><span class="cl">| 2 | 2 | John |
</span></span><span class="line"><span class="cl">| 5 | 5 | Mike |
</span></span><span class="line"><span class="cl">| 3 | 3 | wyett |
</span></span><span class="line"><span class="cl">+----+----+-------+
</span></span></code></pre></div><p>但是在 5.7 中，首先需要关闭 <code>ql_mode = ONLY_FULL_GROUP_BY</code>；相同的 <code>name</code> 值，返回则是取了 <strong>最早写入的数据行</strong> ，<strong>忽略了 <code>order by no desc</code>，按照数据的逻辑存储顺序来返回</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">+----+----+-------+
</span></span><span class="line"><span class="cl">| id | no | name |
</span></span><span class="line"><span class="cl">+----+----+-------+
</span></span><span class="line"><span class="cl">| 4 | 4 | Herry |
</span></span><span class="line"><span class="cl">| 2 | 2 | John |
</span></span><span class="line"><span class="cl">| 1 | 1 | Mike |
</span></span><span class="line"><span class="cl">| 3 | 3 | wyett |
</span></span><span class="line"><span class="cl">+----+----+-------+
</span></span></code></pre></div><p>等价于</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="k">no</span><span class="p">,</span><span class="n">name</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">testorder</span><span class="w"> </span><span class="k">group</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="n">name</span><span class="w">
</span></span></span></code></pre></div><!--more -->
<p>A query such as</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">field1</span><span class="p">,</span><span class="w"> </span><span class="n">field2</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="n">field1</span><span class="p">,</span><span class="w"> </span><span class="n">field2</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">table1</span><span class="w"> </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">field2</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="k">alias</span><span class="w">
</span></span></span></code></pre></div><p>returns a result set that is not necessarily ordered by field2. This is not a bug.
A &ldquo;table&rdquo; (and subquery in the FROM clause too) is - according to the SQL standard - an unordered set of rows.
Rows in a table (or in a subquery in the FROM clause) do not come in any specific order.</p>
<p>可以总结为：</p>
<ul>
<li>在 FROM 后的 subquery 中的 ORDER BY 会被忽略</li>
<li>GROUP BY cloumn 返回的行是无序的</li>
</ul>
<h2 id="解决方案">解决方案</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">id</span><span class="p">,</span><span class="n">a</span><span class="p">.</span><span class="k">no</span><span class="p">,</span><span class="n">a</span><span class="p">.</span><span class="n">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">from</span><span class="w"> </span><span class="n">testorder</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">inner</span><span class="w"> </span><span class="k">join</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">select</span><span class="w"> </span><span class="k">max</span><span class="p">(</span><span class="k">no</span><span class="p">)</span><span class="w"> </span><span class="k">no</span><span class="p">,</span><span class="n">name</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="n">testorder</span><span class="w"> </span><span class="k">group</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="n">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="k">no</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="k">no</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">group</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="k">no</span><span class="w">
</span></span></span></code></pre></div><p>其他方案：</p>
<ol>
<li>对于不符合 ONLY_FULL_GROUP_BY 限制的字段，添加 unique 索引。</li>
<li>使用 ANY_VALUE()，让 MySQL 跳过 ONLY_FULL_GROUP_BY 检测。</li>
</ol>
<h2 id="小结">小结</h2>
<blockquote>
<p><a href="https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html">MySQL 5.6 Handling of GROUP BY</a></p>
</blockquote>
<p>In standard SQL, a query that includes a GROUP BY clause cannot refer to nonaggregated columns in the select list that are not named in the GROUP BY clause.</p>
<p>在标准 SQL 中，包含 GROUP BY 子句的查询 <strong>不能引用</strong> select 列表中未在 GROUP BY 子句中命名的列。</p>
<p>MySQL extends the standard SQL use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. This means that the preceding query is legal in MySQL.</p>
<p>MySQL 扩展了 GROUP BY 的标准 SQL 使用，以便选择列表可以引用 GROUP BY 子句中未命名的非集合列。这意味着前面的查询在 MySQL 中是合法的。</p>
<p>However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate.</p>
<p>但是，主要是在 GROUP BY 中 <strong>未命名的每个非分组列中的所有值对于每个组是相同的</strong>，这是有用的。服务器可以自由选择每个组中的任何值，因此除非它们相同，所选择的值是 <strong>不确定的</strong>。</p>
<p>Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. Result set sorting occurs after values have been chosen, and ORDER BY does not affect which values within each group the server chooses.</p>
<p>此外，通过添加 ORDER BY 子句不会影响来自每个组的值的选择。结果集排序发生在选择值后，ORDER BY <strong>不影响</strong> 服务选择的每个组中的哪些值。</p>
<blockquote>
<p><a href="https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html">MySQL 5.7 Handling of GROUP BY</a></p>
</blockquote>
<p>MySQL 5.7.5 and up implements detection of functional dependence. If the ONLY_FULL_GROUP_BY SQL mode is enabled (which it is by default), MySQL rejects queries for which the select list, HAVING condition, or ORDER BY list refer to nonaggregated columns that are neither named in the GROUP BY clause nor are functionally dependent on them. (Before 5.7.5, MySQL does not detect functional dependency and ONLY_FULL_GROUP_BY is not enabled by default.)</p>
<p>MySQL 5.7.5 及以上功能依赖检测功能。如果启用了 ONLY_FULL_GROUP_BY SQL 模式（默认情况下），MySQL 将拒绝对列表，HAVING 条件或 ORDER BY 列表的查询引用在 GROUP BY 子句中既未命名的非集合列，也不在功能上依赖于它们。（5.7.5 之前，MySQL 没有检测到功能依赖关系，默认情况下不启用 ONLY_FULL_GROUP_BY）</p>
<p>You can achieve the same effect without disabling ONLY_FULL_GROUP_BY by using ANY_VALUE() to refer to the nonaggregated column.</p>
<p>你可以通过使用 <code>ANY_VALUE()</code> 使禁用了 ONLY_FULL_GROUP_BY 的 SQL，来实现相同的效果来引用非聚合列。</p>
<h2 id="56-与-57-的区别">5.6 与 5.7 的区别</h2>
<p>5.6 升级到 5.7 版本要注意：</p>
<ol>
<li>sql_mode 默认值的改变。</li>
<li>optimizer_switch 值的改变。</li>
<li>备库升级影响主备复制。</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">@@</span><span class="n">SQL_MODE</span><span class="p">,</span><span class="w"> </span><span class="o">@@</span><span class="k">GLOBAL</span><span class="p">.</span><span class="n">SQL_MODE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 5.6
</span></span></span><span class="line"><span class="cl"><span class="c1">-- NO_ENGINE_SUBSTITUTION
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 5.7
</span></span></span><span class="line"><span class="cl"><span class="c1">-- ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
</span></span></span></code></pre></div><ul>
<li><code>ONLY_FULL_GROUP_BY</code> SELECT 查询的字段必须是 GROUP BY 中出现的或者使用聚合函数的或者是具有唯一索引的。</li>
<li><code>STRICT_TRANS_TABLES</code> 在该模式下，如果一个值不能插入到一个事务表中，则中断当前的操作，对非事务表不做任何限制。</li>
<li><code>NO_ZERO_IN_DATE</code> 在严格模式，不接受月或日部分为 0 的日期。如果使用 IGNORE 选项，我们为类似的日期插入'0000-00-00&rsquo;。在非严格模式，可以接受该日期，但会生成警告。</li>
<li><code>NO_ZERO_DATE</code> 在严格模式，不要将 &lsquo;0000-00-00&rsquo;做为合法日期。你仍然可以用 IGNORE 选项插入零日期。在非严格模式，可以接受该日期，但会生成警告。</li>
<li><code>ERROR_FOR_DIVISION_BY_ZERO</code> 在严格模式，在 INSERT 或 UPDATE 过程中，如果被零除(或 MOD(X，0))，则产生错误(否则为警告)。如果未给出该模式，被零除时 MySQL 返回 NULL。如果用到 INSERT IGNORE 或 UPDATE IGNORE 中，MySQL 生成被零除警告，但操作结果为 NULL。</li>
<li><code>NO_AUTO_CREATE_USER</code> 防止 GRANT 自动创建新用户，除非还指定了密码。</li>
<li><code>NO_ENGINE_SUBSTITUTION</code> 如果需要的存储引擎被禁用或未编译，那么抛出错误。不设置此值时，用默认的存储引擎替代，并抛出一个异常。</li>
</ul>
<h3 id="strict_trans_tables-和-strict_all_tables-的区别">STRICT_TRANS_TABLES 和 STRICT_ALL_TABLES 的区别</h3>
<p>唯一的区别是：对于不支持事务的表，若开启 STRICT_TRANS_TABLES，MySQL 会尝试将一个不合法的字段值转换成一个值最近的合法值插入表中；而开启 STRICT_ALL_TABLES 后，则表现为不写入数据，且抛出错误。</p>
<p>因为现在绝大部分用的 InnoDB 引擎，是支持事务的，所以基本不用关心这种区别。</p>
<h3 id="严格模式和非严格模式的区别">严格模式和非严格模式的区别</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">sql_mode</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;STRICT_TRANS_TABLES&#39;</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>严格模式下不能在无符号整数字段插入负值。</p>
<ul>
<li>非严格模式下，会存储为 0。</li>
<li>严格模式下，报错。</li>
</ul>
<p>严格模式下，无默认值的 NOT NULL 字段在插入数据时必须指定值。</p>
<ul>
<li>非严格模式下，若不插入数据会存储字段类型的默认值。</li>
<li>严格模式下，报错。</li>
</ul>
<p>严格模式下，插入字符串不能超出定义长度</p>
<ul>
<li>非严格模式下，会成功插入数据，但是内容被截断。</li>
<li>严格模式下，报错。</li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="http://mysqlwyett.com/blog/2017/01/17/max_values_in_group_by/">MySQL 组内排序取最大值 | mysqlwyett</a></li>
<li><a href="http://stackoverflow.com/questions/1066453/mysql-group-by-and-order-by">sql - MySQL Group By and Order By; - Stack Overflow</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/50278304">MySQL5.7 中的 sql_mode 默认值 | zhihu</a></li>
<li><a href="https://www.letianbiji.com/mysql/mysql-strict-mode.html">MySQL: 严格模式 | letianbiji</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>MySQL 管理用户与访问授权</title>
      <link>https://zyf.im/2017/04/11/mysql-manage-user-and-grant/</link>
      <pubDate>Tue, 11 Apr 2017 11:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/04/11/mysql-manage-user-and-grant/</guid>
      <description>&lt;p&gt;MySQL 创建用户、修改密码、删除用户；查看、授予、撤销用户权限；对 MySQL 远程访问的新理解。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 创建用户 + 授权
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;GRANT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ALL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIVILEGES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;tom&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IDENTIFIED&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;pwd123&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WITH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;GRANT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;OPTION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 查询权限
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SHOW&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GRANTS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;tom&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 授权
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;GRANT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;tom&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 撤权
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;REVOKE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ALL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIVILEGES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;tom&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;REVOKE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;GRANT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;OPTION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;tom&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- not necessary
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;FLUSH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIVILEGES&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;管理用户&#34;&gt;管理用户&lt;/h2&gt;
&lt;h3 id=&#34;创建用户&#34;&gt;创建用户&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;CREATE USER &lt;span class=&#34;s1&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;@&lt;span class=&#34;s1&#34;&gt;&amp;#39;host&amp;#39;&lt;/span&gt; IDENTIFIED BY &lt;span class=&#34;s1&#34;&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;username&lt;/code&gt;：创建的用户名&lt;/li&gt;
&lt;li&gt;&lt;code&gt;host&lt;/code&gt;：该用户在哪个主机上可以登陆。如果是本地用户可用 &lt;code&gt;localhost&lt;/code&gt;；如果想让该用户可以从任意远程主机登陆，可以使用通配符 &lt;code&gt;%&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;password&lt;/code&gt;：该用户的登陆密码。密码可以为空，如果为空则该用户可以不需要密码登陆服务器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例子：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>MySQL 创建用户、修改密码、删除用户；查看、授予、撤销用户权限；对 MySQL 远程访问的新理解。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 创建用户 + 授权
</span></span></span><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">ALL</span><span class="w"> </span><span class="k">PRIVILEGES</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">_</span><span class="p">.</span><span class="n">_</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="s1">&#39;tom&#39;</span><span class="o">@</span><span class="s1">&#39;%&#39;</span><span class="w"> </span><span class="n">IDENTIFIED</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="s1">&#39;pwd123&#39;</span><span class="w"> </span><span class="k">WITH</span><span class="w"> </span><span class="k">GRANT</span><span class="w"> </span><span class="k">OPTION</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 查询权限
</span></span></span><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">GRANTS</span><span class="w"> </span><span class="k">FOR</span><span class="w"> </span><span class="s1">&#39;tom&#39;</span><span class="o">@</span><span class="s1">&#39;%&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 授权
</span></span></span><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="o">`</span><span class="n">my_db</span><span class="o">`</span><span class="p">.</span><span class="o">*</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="s1">&#39;tom&#39;</span><span class="o">@</span><span class="s1">&#39;%&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 撤权
</span></span></span><span class="line"><span class="cl"><span class="k">REVOKE</span><span class="w"> </span><span class="k">ALL</span><span class="w"> </span><span class="k">PRIVILEGES</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="o">`</span><span class="n">my_db</span><span class="o">`</span><span class="p">.</span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w">  </span><span class="s1">&#39;tom&#39;</span><span class="o">@</span><span class="s1">&#39;%&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">REVOKE</span><span class="w"> </span><span class="k">GRANT</span><span class="w"> </span><span class="k">OPTION</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="o">`</span><span class="n">my_db</span><span class="o">`</span><span class="p">.</span><span class="o">*</span><span class="w"> </span><span class="k">from</span><span class="w"> </span><span class="s1">&#39;tom&#39;</span><span class="o">@</span><span class="s1">&#39;%&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- not necessary
</span></span></span><span class="line"><span class="cl"><span class="n">FLUSH</span><span class="w"> </span><span class="k">PRIVILEGES</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h2 id="管理用户">管理用户</h2>
<h3 id="创建用户">创建用户</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">CREATE USER <span class="s1">&#39;username&#39;</span>@<span class="s1">&#39;host&#39;</span> IDENTIFIED BY <span class="s1">&#39;password&#39;</span><span class="p">;</span>
</span></span></code></pre></div><ul>
<li><code>username</code>：创建的用户名</li>
<li><code>host</code>：该用户在哪个主机上可以登陆。如果是本地用户可用 <code>localhost</code>；如果想让该用户可以从任意远程主机登陆，可以使用通配符 <code>%</code></li>
<li><code>password</code>：该用户的登陆密码。密码可以为空，如果为空则该用户可以不需要密码登陆服务器</li>
</ul>
<p>例子：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">CREATE USER <span class="s1">&#39;dog&#39;</span>@<span class="s1">&#39;localhost&#39;</span> IDENTIFIED BY <span class="s1">&#39;123456&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">CREATE USER <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;192.168.1.%&#39;</span> IDENDIFIED BY <span class="s1">&#39;123456&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">CREATE USER <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span> IDENTIFIED BY <span class="s1">&#39;123456&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">CREATE USER <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span> IDENTIFIED BY <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">CREATE USER <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="修改用户密码">修改用户密码</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">SET PASSWORD FOR <span class="s1">&#39;username&#39;</span>@<span class="s1">&#39;host&#39;</span> <span class="o">=</span> PASSWORD<span class="o">(</span><span class="s1">&#39;newpassword&#39;</span><span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">SET PASSWORD FOR <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span> <span class="o">=</span> PASSWORD<span class="o">(</span><span class="s2">&#34;123456&#34;</span><span class="o">)</span><span class="p">;</span>
</span></span></code></pre></div><p>如果修改当前登陆用户密码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">SET <span class="nv">PASSWORD</span> <span class="o">=</span> PASSWORD<span class="o">(</span><span class="s2">&#34;newpassword&#34;</span><span class="o">)</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="删除用户">删除用户</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">DROP USER <span class="s1">&#39;username&#39;</span>@<span class="s1">&#39;host&#39;</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="访问授权">访问授权</h2>
<h3 id="查看用户权限">查看用户权限</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">SHOW GRANTS FOR <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="授予用户权限">授予用户权限</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">GRANT PRIVILEGES ON databasename.tablename TO <span class="s1">&#39;username&#39;</span>@<span class="s1">&#39;host&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">GRANT SELECT, INSERT ON test.user TO <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">GRANT ALL ON *.* TO <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span></code></pre></div><ul>
<li><code>privileges</code>：用户的操作权限，如 <code>SELECT</code> <code>INSERT</code> <code>UPDATE</code> 等，如果要授予所的权限则使用 <code>ALL</code></li>
<li><code>databasename</code>：数据库名</li>
<li><code>tablename</code>：表名，如果要授予该用户对所有数据库和表的相应操作权限则可用 <code>*</code> 表示</li>
</ul>
<p><strong>注意：</strong> 用以上命令授权的用户不能给其它用户授权，如果想让该用户可以授权，用 <code>WITH GRANT OPTION</code> 命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">GRANT PRIVILEGES ON databasename.tablename TO <span class="s1">&#39;username&#39;</span>@<span class="s1">&#39;host&#39;</span> WITH GRANT OPTION<span class="p">;</span>
</span></span></code></pre></div><h3 id="撤销用户权限">撤销用户权限</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">REVOKE PRIVILEGE ON databasename.tablename FROM <span class="s1">&#39;username&#39;</span>@<span class="s1">&#39;host&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">REVOKE SELECT ON *.* FROM <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span></code></pre></div><p><strong>注意：</strong> 假如你在给用户&rsquo;pig&rsquo;@&rsquo;%&lsquo;授权的时候是这样的（或类似的）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">GRANT SELECT ON test.user TO <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span></code></pre></div><p>则在使用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">REVOKE SELECT ON _._ FROM <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span></code></pre></div><p>命令并 <strong>不能</strong> 撤销该用户对 test 数据库中 user 表的 SELECT 操作。</p>
<p>相反，如果授权使用的是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">GRANT SELECT ON _._ TO <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span></code></pre></div><p>则：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">REVOKE SELECT ON test.user FROM <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span></code></pre></div><p>命令 <strong>也不能</strong> 撤销该用户对 test 数据库中 user 表的 SELECT 权限。</p>
<p>具体信息可以用查看命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">SHOW GRANTS FOR <span class="s1">&#39;pig&#39;</span>@<span class="s1">&#39;%&#39;</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="刷新权限">刷新权限</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">FLUSH PRIVILEGES<span class="p">;</span>
</span></span></code></pre></div><h3 id="开启远程访问">开启远程访问</h3>
<p>通过上面的配置，可以发现：开启 MySQL 远程访问，其实就是修改用户权限：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">-- 只允许 192.168.1.100 连接
</span></span><span class="line"><span class="cl">GRANT ALL PRIVILEGES ON _._ TO <span class="s1">&#39;root&#39;</span>@<span class="s1">&#39;192.168.1.100&#39;</span> IDENTIFIED BY <span class="s1">&#39;pwd123&#39;</span> WITH GRANT OPTION<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">-- 允许所有 ip 访问
</span></span><span class="line"><span class="cl">GRANT ALL PRIVILEGES ON _._ TO <span class="s1">&#39;root&#39;</span>@<span class="s1">&#39;%&#39;</span> IDENTIFIED BY <span class="s1">&#39;pwd123&#39;</span> WITH GRANT OPTION<span class="p">;</span>
</span></span></code></pre></div><p><strong>注意：</strong> Ubuntu 上还需要修改 MySQL 配置文件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
</span></span></code></pre></div><p>将 <code>bind-address = 127.0.0.1</code> 这一行注释掉, 即:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#bind-address = 127.0.0.1</span>
</span></span></code></pre></div><p>重启 MySQL</p>
<h2 id="补充">补充</h2>
<p>查询当前用户：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mysql&gt; SELECT CURRENT_USER<span class="o">()</span><span class="p">;</span>
</span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>HOST</th>
          <th>User</th>
          <th>被条目匹配的连接</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>&rsquo;thomas.loc.gov&rsquo;</td>
          <td>&lsquo;fred&rsquo;</td>
          <td>fred, 从 thomas.loc.gov 连接</td>
      </tr>
      <tr>
          <td>&rsquo;thomas.loc.gov'</td>
          <td>''</td>
          <td>任何用户, 从 thomas.loc.gov 连接</td>
      </tr>
      <tr>
          <td>&lsquo;%&rsquo;</td>
          <td>&lsquo;fred&rsquo;</td>
          <td>fred, 从任何主机连接</td>
      </tr>
      <tr>
          <td>&lsquo;%&rsquo;</td>
          <td>''</td>
          <td>任何用户, 从任何主机连接</td>
      </tr>
      <tr>
          <td>&lsquo;%.loc.gov&rsquo;</td>
          <td>&lsquo;fred&rsquo;</td>
          <td>fred, 从在 loc.gov 域的任何主机连接</td>
      </tr>
      <tr>
          <td>&lsquo;x.y.%&rsquo;</td>
          <td>&lsquo;fred&rsquo;</td>
          <td>fred, 从 x.y.net、x.y.com,x.y.edu 等联接（这或许无用）</td>
      </tr>
      <tr>
          <td>&lsquo;144.155.166.177&rsquo;</td>
          <td>&lsquo;fred&rsquo;</td>
          <td>fred, 从有 144.155.166.177 的主机连接</td>
      </tr>
      <tr>
          <td>&lsquo;144.155.166.%&rsquo;</td>
          <td>&lsquo;fred&rsquo;</td>
          <td>fred, 从 144.155.166 C 类子网的任何主机连接</td>
      </tr>
  </tbody>
</table>
<p>可以在 Host 字段使用 IP 通配符值（例如，<code>'144.155.166.%'</code> 匹配在一个子网上的每台主机），有可能某人可能企图探究这种能力，通过命名一台主机为 144.155.166.somewhere.com。为了阻止这样的企图，MySQL 不允许匹配以数字和一个点起始的主机名，这样，如果你用一个命名为类似 1.2.foo.com 的主机，它的名字决不会匹配授权表中的 Host 列。只有一个 IP 数字能匹配 IP 通配符值。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://www.jianshu.com/p/d7b9c468f20d">MySQL 创建用户与授权 - 简书</a></li>
<li><a href="http://www.iteedu.com/database/mysql/mysqlmanualcn/database-administration/connection-access.php">5.7.5. 访问控制, 阶段 1：连接核实</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Git pull rebase 和 merge no-ff 保持提交线图整洁</title>
      <link>https://zyf.im/2017/03/17/git-pull-rebase-and-merge-no-ff-to-keep-clear-commit-graph/</link>
      <pubDate>Fri, 17 Mar 2017 16:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/03/17/git-pull-rebase-and-merge-no-ff-to-keep-clear-commit-graph/</guid>
      <description>&lt;p&gt;git log 中的一个清晰的提交线图是很方便进行 code review 和代码回退
&lt;code&gt;git pull --rebase&lt;/code&gt; 主要是为是将提交约线图平坦化，而 &lt;code&gt;git merge --no-ff&lt;/code&gt; 则是刻意制造分叉&lt;/p&gt;
&lt;h2 id=&#34;pull-rebase&#34;&gt;pull rebase&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;perform a rebase after fetching&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;状况&#34;&gt;状况&lt;/h3&gt;
&lt;p&gt;Git 作为分布式版本控制系统，所有修改操作都是基于本地的，在团队协作过程中，假设你和你的同伴在本地中分别有各自的新提交，而你的同伴先于你 push 了代码到远程分支上，所以你必须先执行 &lt;code&gt;git pull&lt;/code&gt; 来获取同伴的提交，然后才能 push 自己的提交到远程分支。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;170317-git-pull-rebase-and-merge-no-ff-to-keep-clear-commit-graph-01&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/80202129-c1cdfb00-8657-11ea-814e-49f8618f301c.jpg&#34;&gt;&lt;/p&gt;
&lt;p&gt;按照 Git 的默认策略，如果远程分支和本地分支之间的提交线图有分叉的话（即不是 fast-forwarded），Git 会执行一次 merge 操作，因此产生&lt;strong&gt;一次没意义的提交记录&lt;/strong&gt;，从而造成了像上图那样的混乱。&lt;/p&gt;
&lt;h3 id=&#34;解决&#34;&gt;解决&lt;/h3&gt;
&lt;p&gt;其实在 pull 操作的时候，使用 &lt;code&gt;git pull --rebase&lt;/code&gt; 选项即可很好地解决上述问题。 加上 &lt;code&gt;--rebase&lt;/code&gt; 参数的作用是，提交线图有分叉的话，Git 会 &lt;code&gt;rebase&lt;/code&gt; 策略来代替默认的 &lt;code&gt;merge&lt;/code&gt; 策略。
假设提交线图在执行 pull 前是这样的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 A---B---C  remotes/origin/master
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           D---E---F---G  master
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果是执行 &lt;code&gt;git pull&lt;/code&gt; 后，结果多出了 H 这个没必要的提交记录。提交线图会变成这样：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>git log 中的一个清晰的提交线图是很方便进行 code review 和代码回退
<code>git pull --rebase</code> 主要是为是将提交约线图平坦化，而 <code>git merge --no-ff</code> 则是刻意制造分叉</p>
<h2 id="pull-rebase">pull rebase</h2>
<blockquote>
<p>perform a rebase after fetching</p>
</blockquote>
<h3 id="状况">状况</h3>
<p>Git 作为分布式版本控制系统，所有修改操作都是基于本地的，在团队协作过程中，假设你和你的同伴在本地中分别有各自的新提交，而你的同伴先于你 push 了代码到远程分支上，所以你必须先执行 <code>git pull</code> 来获取同伴的提交，然后才能 push 自己的提交到远程分支。</p>
<p><img alt="170317-git-pull-rebase-and-merge-no-ff-to-keep-clear-commit-graph-01" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80202129-c1cdfb00-8657-11ea-814e-49f8618f301c.jpg"></p>
<p>按照 Git 的默认策略，如果远程分支和本地分支之间的提交线图有分叉的话（即不是 fast-forwarded），Git 会执行一次 merge 操作，因此产生<strong>一次没意义的提交记录</strong>，从而造成了像上图那样的混乱。</p>
<h3 id="解决">解决</h3>
<p>其实在 pull 操作的时候，使用 <code>git pull --rebase</code> 选项即可很好地解决上述问题。 加上 <code>--rebase</code> 参数的作用是，提交线图有分叉的话，Git 会 <code>rebase</code> 策略来代替默认的 <code>merge</code> 策略。
假设提交线图在执行 pull 前是这样的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">                 A---B---C  remotes/origin/master
</span></span><span class="line"><span class="cl">                /
</span></span><span class="line"><span class="cl">           D---E---F---G  master
</span></span></code></pre></div><p>如果是执行 <code>git pull</code> 后，结果多出了 H 这个没必要的提交记录。提交线图会变成这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">                 A---B---C remotes/origin/master
</span></span><span class="line"><span class="cl">                /         <span class="se">\
</span></span></span><span class="line"><span class="cl">           D---E---F---G---H master
</span></span></code></pre></div><p>如果是执行 <code>git pull --rebase</code> 的话，提交线图就会变成这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">                       remotes/origin/master
</span></span><span class="line"><span class="cl">                           <span class="p">|</span>
</span></span><span class="line"><span class="cl">           D---E---A---B---C---F<span class="s1">&#39;---G&#39;</span>  master
</span></span></code></pre></div><p>F G 两个提交通过 <code>rebase</code> 方式重新拼接在 C 之后，多余的分叉去掉了，目的达到。</p>
<h3 id="注意">注意</h3>
<p>使用 <code>git log --graph</code> 可查看提交线图
使用 <code>git pull --rebase</code> 是为了使提交线图更好看，从而方便 code review 和代码回退
使用 <code>git pull --rebase</code> 比直接 pull 容易导致冲突的产生，如果预期冲突比较多的话，建议还是直接 pull</p>
<!--more -->
<h2 id="merge-no-ff">merge no-ff</h2>
<blockquote>
<p>generate a merge commit even if the merge resolve</p>
</blockquote>
<h3 id="例子-1">例子 1</h3>
<p><code>git pull --rebase</code> 策略目的是修整提交线图，使其形成一条直线，而即将要用到的 <code>git merge --no-ff &lt;branch-name&gt;</code> 策略偏偏是反行其道，刻意地弄出提交线图分叉出来。</p>
<p>假设你在本地准备合并两个分支，而刚好这两个分支是 fast-forwarded 的，那么直接合并后你得到一个直线的提交线图，当然这样没什么坏处，但如果你想更清晰地告诉你同伴：这一系列的提交都是为了实现同一个目的，那么你可以刻意地将这次提交内容弄成一次提交线图分叉。</p>
<p>执行 <code>git merge --no-ff &lt;branch-name&gt;</code> 的结果大概会是这样的：</p>
<p><img alt="170317-git-pull-rebase-and-merge-no-ff-to-keep-clear-commit-graph-02" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80202132-c397be80-8657-11ea-8135-781a36fc64e5.jpg"></p>
<p>中间的分叉线路图很清晰的显示这些提交都是为了实现：<strong>complete adjusting user domains and tags</strong></p>
<h3 id="例子-2">例子 2</h3>
<p>往往在合并分支之前（假设要在本地将 feature 分支合并到 dev 分支），会先检查 feature 分支是否部分落后于远程 dev 分支：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout dev
</span></span><span class="line"><span class="cl">git pull <span class="c1"># 更新 dev 分支</span>
</span></span><span class="line"><span class="cl">git log feature..dev <span class="c1"># 对比</span>
</span></span></code></pre></div><p>如果没有输出任何提交信息的话，即表示 feature 对于 dev 分支是 up-to-date 的。如果有输出的话而马上执行了 <code>git merge --no-ff</code> 的话，提交线图会变成这样：</p>
<p><img alt="170317-git-pull-rebase-and-merge-no-ff-to-keep-clear-commit-graph-03" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80202134-c4305500-8657-11ea-8c4b-52f858f669ec.jpg"></p>
<p>所以这时在合并前，通常先执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout feature
</span></span><span class="line"><span class="cl">git rebase dev
</span></span></code></pre></div><p>这样就可以将 feature 重新拼接到更新了的 dev 之后，然后就可以合并了。这时分叉点将上移，最终得到一个干净舒服的提交线图</p>
<h2 id="总结">总结</h2>
<ul>
<li>使用 <code>git pull --rebase</code> 和 <code>git merge --no-ff</code> 其实和直接使用 <code>git pull</code> <code>git merge</code> 得到的代码应该是一样。</li>
<li>使用 <code>git pull --rebase</code> 主要是为是将提交约线图平坦化，而 <code>git merge --no-ff</code> 则是刻意制造分叉。</li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="http://hungyuhei.github.io/2012/08/07/better-git-commit-graph-using-pull---rebase-and-merge---no-ff.html">洁癖者用 Git：pull &ndash;rebase 和 merge &ndash;no-ff</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Git 修改提交历史</title>
      <link>https://zyf.im/2017/03/13/git-modify-commits-history/</link>
      <pubDate>Mon, 13 Mar 2017 17:00:00 +0000</pubDate>
      <guid>https://zyf.im/2017/03/13/git-modify-commits-history/</guid>
      <description>&lt;p&gt;在使用 Git 时，我们经常需要修改本地提交记录，比如：修改最近一次提交、将多次小的 commit 合并成一个、或者批量修改提交者信息。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;以下操作会修改 Git 历史记录（SHA 值会变化），请确保不要对已推送到共享仓库的提交执行这些操作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;修改最近一次提交&#34;&gt;修改最近一次提交&lt;/h2&gt;
&lt;h3 id=&#34;修改提交说明&#34;&gt;修改提交说明&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit --amend
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;进入编辑器修改提交说明，保存退出即可。&lt;/p&gt;
&lt;h3 id=&#34;追加文件到最近一次提交&#34;&gt;追加文件到最近一次提交&lt;/h3&gt;
&lt;p&gt;先将修改的文件添加到暂存区，再运行 &lt;code&gt;--amend&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git add new_file.cpp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit --amend
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也可以用 &lt;code&gt;-a&lt;/code&gt; 自动暂存所有已跟踪文件的修改，但要注意不要提交多余文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit -a --amend
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;将文件从本次提交中移除&#34;&gt;将文件从本次提交中移除&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git reset HEAD~1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git checkout -- filename  &lt;span class=&#34;c1&#34;&gt;# 要从本次提交移除的文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;new commit&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;修改多个提交记录交互式-rebase&#34;&gt;修改多个提交记录（交互式 Rebase）&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;git rebase -i&lt;/code&gt; 可以对历史提交进行修改、重排、合并、拆分等操作。&lt;/p&gt;
&lt;p&gt;例如修改最近 3 次提交：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git rebase -i HEAD~3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;进入交互界面后，会看到提交列表（注意顺序与 &lt;code&gt;git log&lt;/code&gt; 相反，最早的在上面）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pick fecb551 Init the view model
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pick bb199a0 Update the version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pick bc5cd9d Add new method
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Commands:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#  p, pick = use commit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#  r, reword = use commit, but edit the commit message&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#  e, edit = use commit, but stop for amending&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#  s, squash = use commit, but meld into previous commit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#  f, fixup = like &amp;#34;squash&amp;#34;, but discard this commit&amp;#39;s log message&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#  x, exec = run command (the rest of the line) using shell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;修改指定提交说明&#34;&gt;修改指定提交说明&lt;/h3&gt;
&lt;p&gt;将 &lt;code&gt;pick&lt;/code&gt; 改为 &lt;code&gt;reword&lt;/code&gt;：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>在使用 Git 时，我们经常需要修改本地提交记录，比如：修改最近一次提交、将多次小的 commit 合并成一个、或者批量修改提交者信息。</p>
<blockquote>
<p>以下操作会修改 Git 历史记录（SHA 值会变化），请确保不要对已推送到共享仓库的提交执行这些操作。</p>
</blockquote>
<h2 id="修改最近一次提交">修改最近一次提交</h2>
<h3 id="修改提交说明">修改提交说明</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit --amend
</span></span></code></pre></div><p>进入编辑器修改提交说明，保存退出即可。</p>
<h3 id="追加文件到最近一次提交">追加文件到最近一次提交</h3>
<p>先将修改的文件添加到暂存区，再运行 <code>--amend</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add new_file.cpp
</span></span><span class="line"><span class="cl">git commit --amend
</span></span></code></pre></div><p>也可以用 <code>-a</code> 自动暂存所有已跟踪文件的修改，但要注意不要提交多余文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit -a --amend
</span></span></code></pre></div><h3 id="将文件从本次提交中移除">将文件从本次提交中移除</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset HEAD~1
</span></span><span class="line"><span class="cl">git checkout -- filename  <span class="c1"># 要从本次提交移除的文件</span>
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;new commit&#34;</span>
</span></span></code></pre></div><h2 id="修改多个提交记录交互式-rebase">修改多个提交记录（交互式 Rebase）</h2>
<p>使用 <code>git rebase -i</code> 可以对历史提交进行修改、重排、合并、拆分等操作。</p>
<p>例如修改最近 3 次提交：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rebase -i HEAD~3
</span></span></code></pre></div><p>进入交互界面后，会看到提交列表（注意顺序与 <code>git log</code> 相反，最早的在上面）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pick fecb551 Init the view model
</span></span><span class="line"><span class="cl">pick bb199a0 Update the version
</span></span><span class="line"><span class="cl">pick bc5cd9d Add new method
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Commands:</span>
</span></span><span class="line"><span class="cl"><span class="c1">#  p, pick = use commit</span>
</span></span><span class="line"><span class="cl"><span class="c1">#  r, reword = use commit, but edit the commit message</span>
</span></span><span class="line"><span class="cl"><span class="c1">#  e, edit = use commit, but stop for amending</span>
</span></span><span class="line"><span class="cl"><span class="c1">#  s, squash = use commit, but meld into previous commit</span>
</span></span><span class="line"><span class="cl"><span class="c1">#  f, fixup = like &#34;squash&#34;, but discard this commit&#39;s log message</span>
</span></span><span class="line"><span class="cl"><span class="c1">#  x, exec = run command (the rest of the line) using shell</span>
</span></span></code></pre></div><h3 id="修改指定提交说明">修改指定提交说明</h3>
<p>将 <code>pick</code> 改为 <code>reword</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">reword fecb551 Init the view model
</span></span><span class="line"><span class="cl">pick bb199a0 Update the version
</span></span><span class="line"><span class="cl">pick bc5cd9d Add new method
</span></span></code></pre></div><p>保存退出后，rebase 会在该提交处打开编辑器让你修改说明。</p>
<p>如果还需要修改提交内容，使用 <code>edit</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">edit fecb551 Init the view model
</span></span><span class="line"><span class="cl">pick bb199a0 Update the version
</span></span><span class="line"><span class="cl">pick bc5cd9d Add new method
</span></span></code></pre></div><p>rebase 会在该提交处暂停，完成修改后运行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git commit --amend
</span></span><span class="line"><span class="cl">git rebase --continue
</span></span></code></pre></div><h3 id="重排或删除提交">重排或删除提交</h3>
<p>直接调整行的顺序或删除对应行即可。例如删除 &ldquo;Update the version&rdquo; 并交换其余两次提交的顺序：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pick bc5cd9d Add new method
</span></span><span class="line"><span class="cl">pick fecb551 Init the view model
</span></span></code></pre></div><h3 id="合并提交squash">合并提交（Squash）</h3>
<p>将 <code>pick</code> 改为 <code>squash</code>，该提交会被合并到上一个提交中：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pick fecb551 Init the view model
</span></span><span class="line"><span class="cl">squash bb199a0 Update the version
</span></span><span class="line"><span class="cl">squash bc5cd9d Add new method
</span></span></code></pre></div><p>保存退出后，rebase 会让你编辑合并后的提交说明。</p>
<h3 id="拆分提交">拆分提交</h3>
<p>使用 <code>edit</code> 暂停在要拆分的提交处，然后重置并分次提交：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pick fecb551 Init the view model
</span></span><span class="line"><span class="cl">edit bb199a0 Update the version
</span></span><span class="line"><span class="cl">pick bc5cd9d Add new method
</span></span></code></pre></div><p>rebase 暂停后执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git reset HEAD^
</span></span><span class="line"><span class="cl">git add file1
</span></span><span class="line"><span class="cl">git commit -m <span class="s1">&#39;Update the version1&#39;</span>
</span></span><span class="line"><span class="cl">git add file2
</span></span><span class="line"><span class="cl">git commit -m <span class="s1">&#39;Update the version2&#39;</span>
</span></span><span class="line"><span class="cl">git rebase --continue
</span></span></code></pre></div><h2 id="批量修改提交者信息">批量修改提交者信息</h2>
<p>查看当前仓库中所有提交者信息：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log --format<span class="o">=</span><span class="s1">&#39;%aN %aE&#39;</span> <span class="p">|</span> sort -u
</span></span></code></pre></div><p>使用 <code>filter-branch</code> 批量替换（如果 email 从未设置过，<code>OLD_EMAIL</code> 可以填 <code>user.name</code>）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git filter-branch -f --env-filter <span class="s1">&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">OLD_EMAIL=&#34;old@qq.com&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">CORRECT_NAME=&#34;new_name&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">CORRECT_EMAIL=&#34;new@gmail.com&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">if [ &#34;$GIT_COMMITTER_EMAIL&#34; = &#34;$OLD_EMAIL&#34; ]
</span></span></span><span class="line"><span class="cl"><span class="s1">then
</span></span></span><span class="line"><span class="cl"><span class="s1">    export GIT_COMMITTER_NAME=&#34;$CORRECT_NAME&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">    export GIT_COMMITTER_EMAIL=&#34;$CORRECT_EMAIL&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">fi
</span></span></span><span class="line"><span class="cl"><span class="s1">if [ &#34;$GIT_AUTHOR_EMAIL&#34; = &#34;$OLD_EMAIL&#34; ]
</span></span></span><span class="line"><span class="cl"><span class="s1">then
</span></span></span><span class="line"><span class="cl"><span class="s1">    export GIT_AUTHOR_NAME=&#34;$CORRECT_NAME&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">    export GIT_AUTHOR_EMAIL=&#34;$CORRECT_EMAIL&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">fi
</span></span></span><span class="line"><span class="cl"><span class="s1">&#39;</span> --tag-name-filter cat -- --branches --tags
</span></span></code></pre></div><p>修改历史后需要强制推送：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git push -f --tags
</span></span></code></pre></div><p>如果远程分支有保护策略（如 GitLab），需要先在 Project → Settings → Repository → Protected branches 中取消保护。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://glgjing.github.io/blog/2015/01/06/git-xiu-gai-ti-jiao-li-shi/">Git 修改提交历史 - GLGJing&rsquo;s Blog</a></li>
<li><a href="https://segmentfault.com/a/1190000008032330">Git 批量修改历史 commit 中的 user.name 和 user.email | segmentfault</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>回顾 2016</title>
      <link>https://zyf.im/2016/12/31/review-2016/</link>
      <pubDate>Sat, 31 Dec 2016 22:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/12/31/review-2016/</guid>
      <description>&lt;p&gt;2016 肯定是很特别的一年。年初离开了老师的队伍，来北京找实习。年中和 318 告别，大学毕业了。年末换房学做饭，开始另种生活。对于 2017 更多了对自己的期待。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;iframe frameborder=&#34;no&#34; border=&#34;0&#34; marginwidth=&#34;0&#34; marginheight=&#34;0&#34; width=260 height=52 src=&#34;//music.163.com/outchain/player?type=2&amp;id=31445772&amp;auto=0&amp;height=32&#34;&gt;&lt;/iframe&gt;
&lt;p&gt;2016 是在保定的雾霾中开始。结束了大为，决定离开老师的队伍，来北京实习，这对我来说是一个不大不小的决定。老师带着，跟学长一起走到这里，这里有了很多宝贵的东西，有很多回忆，很多故事可以讲。选择离开，主要还是想看看在这之外的是什么样子。&lt;/p&gt;
&lt;p&gt;朋友、老师、爸妈都为我的实习操过心，不过推荐的地方在各种奇怪的理由下都没有成。自己开始在拉勾上投简历，一次次来返在京石铁路上。面试一直是不顺的。完全没有想到对实习生有如此高的要求，很多技术问题正中下怀。心里是有落差的，在我当时的想法里，那些在工程中是用不易用到的，但也感到自己技术知识的浅薄。面试了俩三家后也发现了套路，回答不好的、答不上的问题，回去查查准备准备，下回就能用上。&lt;/p&gt;
&lt;p&gt;没什么难度的大学毕业。大学里更多的收获是跟着老师、学长在课堂外搞事，是和宿舍的、好朋友在一起的时间。真的好幸运啊，遇到 318 的舍友们。没想到和你们每一个人都超级聊的来。大四的时候人就渐渐不齐，但只要宿舍在，不到最后毕业真的不知道要分别了。我们相互影响着成长，至少除去了我很多很 low 的思想。我们相互练着酒量、每天演着戏、日常说着骚话。&lt;/p&gt;
&lt;p&gt;德育答辩上一个同学说：我常翘课、考试挂科，但我大学四年过的很坦荡，我会因为和舍友游戏上的一个好玩的套路高兴很久。我收获朋友。我没什么后悔的。
这很戳中我的泪点。没什么后悔的，也是对我大学的注脚。愿你们成家的幸福，工作的有成。&lt;/p&gt;
&lt;p&gt;从上学到工作，过渡的很平稳。工作中有一些挑战，但总之是还应对的来。可没有什么出众的地方，我不想过的太平凡，愿有一技之长，独挡一面。我常说自己：菜啊、傻啊、什么的，我确实不想用这些来词来逃避问题，我想尽快的甩掉这些。我也愿自己可以更有底气和勇气说出，现在不能说出的话，愿更决绝的对大事小情做决定。&lt;/p&gt;
&lt;p&gt;年末随着公司搬家，自己也搬了。炒菜做饭成了每天另一部分。也许才是真正生活二字的开始。&lt;/p&gt;
&lt;p&gt;突然想到学长提的一个问题：你们有谁把技术当做以后安身立命的本事？
现在对于我来说，这问题的答案比任何时候都更加清晰。&lt;/p&gt;
&lt;p&gt;最后，还是怀揣着 2014 末的敬请期待，让一切皆有可能，开始 2017。&lt;/p&gt;
&lt;p&gt;—— 送给 木林木丶&lt;/p&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>2016 肯定是很特别的一年。年初离开了老师的队伍，来北京找实习。年中和 318 告别，大学毕业了。年末换房学做饭，开始另种生活。对于 2017 更多了对自己的期待。</p>
<!-- more -->
<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=260 height=52 src="//music.163.com/outchain/player?type=2&id=31445772&auto=0&height=32"></iframe>
<p>2016 是在保定的雾霾中开始。结束了大为，决定离开老师的队伍，来北京实习，这对我来说是一个不大不小的决定。老师带着，跟学长一起走到这里，这里有了很多宝贵的东西，有很多回忆，很多故事可以讲。选择离开，主要还是想看看在这之外的是什么样子。</p>
<p>朋友、老师、爸妈都为我的实习操过心，不过推荐的地方在各种奇怪的理由下都没有成。自己开始在拉勾上投简历，一次次来返在京石铁路上。面试一直是不顺的。完全没有想到对实习生有如此高的要求，很多技术问题正中下怀。心里是有落差的，在我当时的想法里，那些在工程中是用不易用到的，但也感到自己技术知识的浅薄。面试了俩三家后也发现了套路，回答不好的、答不上的问题，回去查查准备准备，下回就能用上。</p>
<p>没什么难度的大学毕业。大学里更多的收获是跟着老师、学长在课堂外搞事，是和宿舍的、好朋友在一起的时间。真的好幸运啊，遇到 318 的舍友们。没想到和你们每一个人都超级聊的来。大四的时候人就渐渐不齐，但只要宿舍在，不到最后毕业真的不知道要分别了。我们相互影响着成长，至少除去了我很多很 low 的思想。我们相互练着酒量、每天演着戏、日常说着骚话。</p>
<p>德育答辩上一个同学说：我常翘课、考试挂科，但我大学四年过的很坦荡，我会因为和舍友游戏上的一个好玩的套路高兴很久。我收获朋友。我没什么后悔的。
这很戳中我的泪点。没什么后悔的，也是对我大学的注脚。愿你们成家的幸福，工作的有成。</p>
<p>从上学到工作，过渡的很平稳。工作中有一些挑战，但总之是还应对的来。可没有什么出众的地方，我不想过的太平凡，愿有一技之长，独挡一面。我常说自己：菜啊、傻啊、什么的，我确实不想用这些来词来逃避问题，我想尽快的甩掉这些。我也愿自己可以更有底气和勇气说出，现在不能说出的话，愿更决绝的对大事小情做决定。</p>
<p>年末随着公司搬家，自己也搬了。炒菜做饭成了每天另一部分。也许才是真正生活二字的开始。</p>
<p>突然想到学长提的一个问题：你们有谁把技术当做以后安身立命的本事？
现在对于我来说，这问题的答案比任何时候都更加清晰。</p>
<p>最后，还是怀揣着 2014 末的敬请期待，让一切皆有可能，开始 2017。</p>
<p>—— 送给 木林木丶</p>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Shell 解题集合</title>
      <link>https://zyf.im/2016/09/19/leetcode-shell-solutions/</link>
      <pubDate>Mon, 19 Sep 2016 16:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/09/19/leetcode-shell-solutions/</guid>
      <description>&lt;p&gt;LeetCode Shell 的试题多为文本操作，&lt;a href=&#34;https://leetcode.com/problems/tenth-line/&#34;&gt;195. Tenth Line&lt;/a&gt;、&lt;a href=&#34;https://leetcode.com/problems/valid-phone-numbers/&#34;&gt;193. Valid Phone Numbers&lt;/a&gt;、&lt;a href=&#34;https://leetcode.com/problems/word-frequency/&#34;&gt;192. Word Frequency&lt;/a&gt;、&lt;a href=&#34;https://leetcode.com/problems/transpose-file/&#34;&gt;194. Transpose File&lt;/a&gt; 暂时只有 4 道题，就整合在这一起了&lt;/p&gt;
&lt;p&gt;Shell 中文本处理的事情基本 &lt;code&gt;awk&lt;/code&gt; &lt;code&gt;sed&lt;/code&gt; &lt;code&gt;grep&lt;/code&gt; &lt;code&gt;sort&lt;/code&gt; &lt;code&gt;uniq&lt;/code&gt; &lt;code&gt;tail&lt;/code&gt; &lt;code&gt;head&lt;/code&gt; 几个命令组合组合就搞定了&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;195-tenth-line&#34;&gt;&lt;a href=&#34;https://leetcode.com/problems/tenth-line/&#34;&gt;195. Tenth Line&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&#34;大体意思&#34;&gt;大体意思&lt;/h3&gt;
&lt;p&gt;How would you print just the 10th line of a file?
For example, assume that file.txt has the following content:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Your script should output the tenth line, which is:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>LeetCode Shell 的试题多为文本操作，<a href="https://leetcode.com/problems/tenth-line/">195. Tenth Line</a>、<a href="https://leetcode.com/problems/valid-phone-numbers/">193. Valid Phone Numbers</a>、<a href="https://leetcode.com/problems/word-frequency/">192. Word Frequency</a>、<a href="https://leetcode.com/problems/transpose-file/">194. Transpose File</a> 暂时只有 4 道题，就整合在这一起了</p>
<p>Shell 中文本处理的事情基本 <code>awk</code> <code>sed</code> <code>grep</code> <code>sort</code> <code>uniq</code> <code>tail</code> <code>head</code> 几个命令组合组合就搞定了</p>
<!-- more -->
<h2 id="195-tenth-line"><a href="https://leetcode.com/problems/tenth-line/">195. Tenth Line</a></h2>
<h3 id="大体意思">大体意思</h3>
<p>How would you print just the 10th line of a file?
For example, assume that file.txt has the following content:</p>
<pre tabindex="0"><code>Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
</code></pre><p>Your script should output the tenth line, which is:</p>
<pre tabindex="0"><code>Line 10
</code></pre><p>打印出文本文件中第 10 行的内容。</p>
<h3 id="自己的解法">自己的解法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat  file.txt <span class="p">|</span> tail -n +10 <span class="p">|</span> head -n <span class="m">1</span>
</span></span></code></pre></div><p>这是学习了：<a href="http://www.cnblogs.com/xianghang123/archive/2011/08/03/2125977.html">linux 如何显示一个文件的某几行(中间几行) - 香格里拉(^o^)/</a></p>
<h3 id="别人的解法">别人的解法</h3>
<p>方法一：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">awk <span class="s1">&#39;NR==10&#39;</span> file.txt
</span></span><span class="line"><span class="cl"><span class="c1"># awk的默认动作就是打印$0，所以NR==10后面可以不用加{print $0}</span>
</span></span></code></pre></div><p>方法二：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sed -n <span class="s1">&#39;10p&#39;</span> file.txt
</span></span><span class="line"><span class="cl"><span class="c1"># 如果不够10行，则什么也不打印</span>
</span></span></code></pre></div><p>方法三：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">line</span><span class="o">=</span><span class="k">$(</span>cat file.txt <span class="p">|</span> wc -l<span class="k">)</span>     <span class="c1"># 千万注意，等号前后一定不要有空格</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$line</span><span class="s2">&#34;</span> -ge <span class="m">10</span> <span class="o">]</span> <span class="p">;</span> <span class="k">then</span>     <span class="c1"># $line的双引号也可以不用加</span>
</span></span><span class="line"><span class="cl">  cat file.txt <span class="p">|</span> head -n <span class="m">10</span> <span class="p">|</span> tail -n <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><p>Reference: <a href="http://blog.csdn.net/sole_cc/article/details/44977821">leetcode-195 Tenth Line - 2&gt; /dev/null</a></p>
<h2 id="193-valid-phone-numbers"><a href="https://leetcode.com/problems/valid-phone-numbers/">193. Valid Phone Numbers</a></h2>
<h3 id="大体意思-1">大体意思</h3>
<p>Given a text file <code>file.txt</code> that contains list of phone numbers (one per line), write a one liner bash script to print all valid phone numbers.
You may assume that a valid phone number must appear in one of the following two formats: (xxx) xxx-xxxx or xxx-xxx-xxxx. (x means a digit)
You may also assume each line in the text file must not contain leading or trailing white spaces.</p>
<p>For example, assume that <code>file.txt</code> has the following content:</p>
<pre tabindex="0"><code>987-123-4567
123 456 7890
(123) 456-7890
</code></pre><p>Your script should output the following valid phone numbers:</p>
<pre tabindex="0"><code>987-123-4567
(123) 456-7890
</code></pre><p>匹配有效的电话号码</p>
<h3 id="自己的解法-1">自己的解法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat file.txt <span class="p">|</span> grep <span class="s1">&#39;^[0-9]\{3\}-[0-9]\{3\}-[0-9]\{4\}$\|^([0-9]\{3\}) [0-9]\{3\}-[0-9]\{4\}$&#39;</span>
</span></span></code></pre></div><h3 id="别人的解法-1">别人的解法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">grep -P <span class="s1">&#39;^(\(\d{3}\)|\d{3}-)\d{3}-\d{4}$&#39;</span> file.txt
</span></span></code></pre></div><p>Reference: <a href="https://ruixublog.wordpress.com/2015/05/13/leetcode-193-valid-phone-numbers/">LeetCode 193: Valid Phone Numbers – Revo</a></p>
<h2 id="192-word-frequency"><a href="https://leetcode.com/problems/word-frequency/">192. Word Frequency</a></h2>
<h3 id="大体意思-2">大体意思</h3>
<p>Write a bash script to calculate the frequency of each word in a text file <code>words.txt</code>.</p>
<p>For simplicity sake, you may assume:</p>
<p><code>words.txt</code> contains only lowercase characters and space <code>' '</code> characters.
Each word must consist of lowercase characters only.
Words are separated by one or more whitespace characters.
For example, assume that <code>words.txt</code> has the following content:</p>
<pre tabindex="0"><code>the day is sunny the the
the sunny is is
</code></pre><p>Your script should output the following, sorted by descending frequency:</p>
<pre tabindex="0"><code>the 4
is 3
sunny 2
day 1
</code></pre><p>Note:
Don&rsquo;t worry about handling ties, it is guaranteed that each word&rsquo;s frequency count is unique.</p>
<p>统计单词出现的频次，然后倒序排列</p>
<h3 id="别人的解法-2">别人的解法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">awk <span class="s1">&#39;{i=1;while(i&lt;=NF){print $i;i++}}&#39;</span> words.txt  <span class="p">|</span> sort <span class="p">|</span> uniq -c  <span class="p">|</span> sort -k1nr  <span class="p">|</span> awk <span class="s1">&#39;{print $2 &#34; &#34; $1}&#39;</span>
</span></span></code></pre></div><p>1、利用 awk 默认一行一条记录，默认以空格划分每条记录，NF 为划分的总块数先打印出所有单词
2、排序 + 统计 + 消除重复
3、输出</p>
<p>Reference: <a href="http://blog.csdn.net/wangxiaobupt/article/details/45201817">LeetCode 192 Word Frequency - wangxiaobupt 的专栏</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">awk <span class="s1">&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">{ for (i=1; i&lt;=NF; i++) { ++S[$i]; } }
</span></span></span><span class="line"><span class="cl"><span class="s1">END { for (i in S) { print i, S[i] } }
</span></span></span><span class="line"><span class="cl"><span class="s1">&#39;</span> words.txt <span class="p">|</span> sort -nr -k <span class="m">2</span>
</span></span></code></pre></div><p>Reference: <a href="https://github.com/illuz/leetcode/tree/master/solutions/192.Word_Frequency">leetcode/solutions/192.Word_Frequency at master · illuz/leetcode</a></p>
<h2 id="194-transpose-file"><a href="https://leetcode.com/problems/transpose-file/">194. Transpose File</a></h2>
<h3 id="大体意思-3">大体意思</h3>
<p>Given a text file <code>file.txt</code>, transpose its content.</p>
<p>You may assume that each row has the same number of columns and each field is separated by the <code>' '</code> character.</p>
<p>For example, if <code>file.txt</code> has the following content:</p>
<pre tabindex="0"><code>name age
alice 21
ryan 30
</code></pre><p>Output the following:</p>
<pre tabindex="0"><code>name alice ryan
age 21 30
</code></pre><h3 id="别人的解法-3">别人的解法</h3>
<pre tabindex="0"><code>awk &#39;
{
    for (i = 1; i &lt;= NF; i++) {
        if(NR == 1) {
            s[i] = $i;
        } else {
            s[i] = s[i] &#34; &#34; $i;
        }
    }
}
END {
    for (i = 1; s[i] != &#34;&#34;; i++) {
        print s[i];
    }
}&#39; file.txt
</code></pre><p>Reference: <a href="https://github.com/illuz/leetcode/tree/master/solutions/194.Transpose_File">leetcode/solutions/194.Transpose_File at master · illuz/leetcode</a></p>
<h2 id="总结">总结</h2>
<p>Shell 中文本处理的事情基本 <code>awk</code> <code>sed</code> <code>grep</code> <code>sort</code> <code>uniq</code> <code>tail</code> <code>head</code> 几个命令组合组合就搞定了，各命令的常用方法之后总结</p>
]]></content:encoded>
    </item>
    <item>
      <title>MySQL LIMIT 查询优化</title>
      <link>https://zyf.im/2016/09/18/mysql-limit-query-optimization/</link>
      <pubDate>Sun, 18 Sep 2016 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/09/18/mysql-limit-query-optimization/</guid>
      <description>&lt;p&gt;最近常在 SQL 中使用到 &lt;code&gt;LIMIT ? ?&lt;/code&gt;，在执行 &lt;code&gt;LIMIT 0, 1000&lt;/code&gt; 与 &lt;code&gt;LIMIT 100000, 1000&lt;/code&gt; 时，查询速度明显有很大的区别，而且随着 LIMIT 的偏移量的增加，查询速度越来越慢。是否有办法对 SQL 中 LIMIT 查询进行优化呢？&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;limit-速度慢的原因&#34;&gt;LIMIT 速度慢的原因&lt;/h2&gt;
&lt;p&gt;LIMIT 100000, 1000 的意思扫描满足条件的 101000 行，扔掉前面的 100000 行，返回最后的 1000 行，问题就在这里。&lt;/p&gt;
&lt;h2 id=&#34;limit-优化思路&#34;&gt;LIMIT 优化思路&lt;/h2&gt;
&lt;p&gt;1、尽可能从索引中直接获取数据，避免或减少直接扫描行数据的频率
2、尽可能减少扫描的记录数，也就是先确定起始的范围，再往后取 N 条记录即可&lt;/p&gt;
&lt;h2 id=&#34;limit-优化示例&#34;&gt;LIMIT 优化示例&lt;/h2&gt;
&lt;h3 id=&#34;原始-sql&#34;&gt;原始 SQL&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 含条件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ftype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;LIMIT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 不含 WHERE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;LIMIT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;子查询优化-sql&#34;&gt;子查询优化 SQL&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 采用子查询的方式优化，在子查询里先从索引获取到最大 id，然后倒序排，再取 10 行结果集
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 注意这里采用了 2 次倒序排，因此在取 LIMIT 的 start 值时，比原来的值加了 10，即 935510，否则结果将和原来的不一致
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ftype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;LIMIT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;935510&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;LIMIT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;inner-优化-sql&#34;&gt;INNER 优化 SQL&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 采用 INNER JOIN 优化，JOIN 子句里也优先从索引获取 ID 列表，然后直接关联查询获得最终结果，这里不需要加 10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INNER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ftype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;LIMIT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;935500&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;USING&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;1、要学着使用 &lt;code&gt;EXPLAIN&lt;/code&gt; 对 SQL 进行优化调整
2、推荐使用 INNER 优化 SQL
3、发现了一个 ubuntu 中监控 MySQL 的 &lt;code&gt;mytop&lt;/code&gt; 命令，刚刚安装还没细研究&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>最近常在 SQL 中使用到 <code>LIMIT ? ?</code>，在执行 <code>LIMIT 0, 1000</code> 与 <code>LIMIT 100000, 1000</code> 时，查询速度明显有很大的区别，而且随着 LIMIT 的偏移量的增加，查询速度越来越慢。是否有办法对 SQL 中 LIMIT 查询进行优化呢？</p>
<!-- more -->
<h2 id="limit-速度慢的原因">LIMIT 速度慢的原因</h2>
<p>LIMIT 100000, 1000 的意思扫描满足条件的 101000 行，扔掉前面的 100000 行，返回最后的 1000 行，问题就在这里。</p>
<h2 id="limit-优化思路">LIMIT 优化思路</h2>
<p>1、尽可能从索引中直接获取数据，避免或减少直接扫描行数据的频率
2、尽可能减少扫描的记录数，也就是先确定起始的范围，再往后取 N 条记录即可</p>
<h2 id="limit-优化示例">LIMIT 优化示例</h2>
<h3 id="原始-sql">原始 SQL</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 含条件
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="o">`</span><span class="n">t1</span><span class="o">`</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">ftype</span><span class="o">=</span><span class="mi">1</span><span class="w"> </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">DESC</span><span class="w"> </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="mi">10</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 不含 WHERE
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="o">`</span><span class="n">t1</span><span class="o">`</span><span class="w"> </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">DESC</span><span class="w"> </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="mi">10</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h3 id="子查询优化-sql">子查询优化 SQL</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 采用子查询的方式优化，在子查询里先从索引获取到最大 id，然后倒序排，再取 10 行结果集
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 注意这里采用了 2 次倒序排，因此在取 LIMIT 的 start 值时，比原来的值加了 10，即 935510，否则结果将和原来的不一致
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">(</span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="o">`</span><span class="n">t1</span><span class="o">`</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">(</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="o">`</span><span class="n">t1</span><span class="o">`</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">ftype</span><span class="o">=</span><span class="mi">1</span><span class="w"> </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">DESC</span><span class="w"> </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">935510</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="n">T</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">DESC</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h3 id="inner-优化-sql">INNER 优化 SQL</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 采用 INNER JOIN 优化，JOIN 子句里也优先从索引获取 ID 列表，然后直接关联查询获得最终结果，这里不需要加 10
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="o">`</span><span class="n">t1</span><span class="o">`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">INNER</span><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="o">`</span><span class="n">t1</span><span class="o">`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">WHERE</span><span class="w"> </span><span class="n">ftype</span><span class="o">=</span><span class="mi">1</span><span class="w"> </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">DESC</span><span class="w"> </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">935500</span><span class="p">,</span><span class="mi">10</span><span class="p">)</span><span class="w"> </span><span class="n">t2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">USING</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>1、要学着使用 <code>EXPLAIN</code> 对 SQL 进行优化调整
2、推荐使用 INNER 优化 SQL
3、发现了一个 ubuntu 中监控 MySQL 的 <code>mytop</code> 命令，刚刚安装还没细研究</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get install mytop
</span></span></code></pre></div><p>4、<code>USING (id)</code> 如果两个表的字段名都一样，那么可以用 using(字段名) 来协商条件，效果跟 <code>on a.id = b.id</code> 一样</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://imysql.com/2014/07/26/mysql-optimization-case-paging-optimize.shtml">MySQL 优化案例系列 — 分页优化 | iMySQL</a></li>
<li><a href="http://www.fienda.com/archives/110">MYSQL 分页 limit 速度太慢优化方法 | Fienda blog</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Database Trips and Users 262</title>
      <link>https://zyf.im/2016/09/14/leetcode-database-trips-and-users-262/</link>
      <pubDate>Wed, 14 Sep 2016 13:45:00 +0000</pubDate>
      <guid>https://zyf.im/2016/09/14/leetcode-database-trips-and-users-262/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/trips-and-users/&#34;&gt;262. Trips and Users&lt;/a&gt;
  The &lt;code&gt;Trips&lt;/code&gt; table holds all taxi trips. Each trip has a unique Id, while Client_Id and Driver_Id are both foreign keys to the Users_Id at the &lt;code&gt;Users&lt;/code&gt; table. Status is an ENUM type of (‘completed’, ‘cancelled_by_driver’, ‘cancelled_by_client’).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-----------+-----------+---------+--------------------+----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Client_Id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Driver_Id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;City_Id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Request_at&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-----------+-----------+---------+--------------------+----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;completed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;01&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cancelled_by_driver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;01&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;completed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;01&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cancelled_by_client&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;01&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;completed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;02&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;completed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;02&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;completed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;02&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;completed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;03&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;completed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;03&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cancelled_by_driver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2013&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;03&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-----------+-----------+---------+--------------------+----------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;  The &lt;code&gt;Users&lt;/code&gt; table holds all users. Each user has an unique Users_Id, and Role is an ENUM type of (‘client’, ‘driver’, ‘partner’).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/trips-and-users/">262. Trips and Users</a>
  The <code>Trips</code> table holds all taxi trips. Each trip has a unique Id, while Client_Id and Driver_Id are both foreign keys to the Users_Id at the <code>Users</code> table. Status is an ENUM type of (‘completed’, ‘cancelled_by_driver’, ‘cancelled_by_client’).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-----------+-----------+---------+--------------------+----------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Client_Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Driver_Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">City_Id</span><span class="w"> </span><span class="o">|</span><span class="w">        </span><span class="n">Status</span><span class="w">      </span><span class="o">|</span><span class="n">Request_at</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-----------+-----------+---------+--------------------+----------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">1</span><span class="w">  </span><span class="o">|</span><span class="w">     </span><span class="mi">1</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">10</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">1</span><span class="w">    </span><span class="o">|</span><span class="w">     </span><span class="n">completed</span><span class="w">      </span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">01</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">2</span><span class="w">  </span><span class="o">|</span><span class="w">     </span><span class="mi">2</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">11</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">1</span><span class="w">    </span><span class="o">|</span><span class="w"> </span><span class="n">cancelled_by_driver</span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">01</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">3</span><span class="w">  </span><span class="o">|</span><span class="w">     </span><span class="mi">3</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">12</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">6</span><span class="w">    </span><span class="o">|</span><span class="w">     </span><span class="n">completed</span><span class="w">      </span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">01</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">4</span><span class="w">  </span><span class="o">|</span><span class="w">     </span><span class="mi">4</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">13</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">6</span><span class="w">    </span><span class="o">|</span><span class="w"> </span><span class="n">cancelled_by_client</span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">01</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">5</span><span class="w">  </span><span class="o">|</span><span class="w">     </span><span class="mi">1</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">10</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">1</span><span class="w">    </span><span class="o">|</span><span class="w">     </span><span class="n">completed</span><span class="w">      </span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">02</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">6</span><span class="w">  </span><span class="o">|</span><span class="w">     </span><span class="mi">2</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">11</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">6</span><span class="w">    </span><span class="o">|</span><span class="w">     </span><span class="n">completed</span><span class="w">      </span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">02</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">7</span><span class="w">  </span><span class="o">|</span><span class="w">     </span><span class="mi">3</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">12</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">6</span><span class="w">    </span><span class="o">|</span><span class="w">     </span><span class="n">completed</span><span class="w">      </span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">02</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">8</span><span class="w">  </span><span class="o">|</span><span class="w">     </span><span class="mi">2</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">12</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">12</span><span class="w">   </span><span class="o">|</span><span class="w">     </span><span class="n">completed</span><span class="w">      </span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">03</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">9</span><span class="w">  </span><span class="o">|</span><span class="w">     </span><span class="mi">3</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">10</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">12</span><span class="w">   </span><span class="o">|</span><span class="w">     </span><span class="n">completed</span><span class="w">      </span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">03</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="o">|</span><span class="w">     </span><span class="mi">4</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">13</span><span class="w">     </span><span class="o">|</span><span class="w">    </span><span class="mi">12</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">cancelled_by_driver</span><span class="o">|</span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">03</span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-----------+-----------+---------+--------------------+----------+
</span></span></span></code></pre></div><p>  The <code>Users</code> table holds all users. Each user has an unique Users_Id, and Role is an ENUM type of (‘client’, ‘driver’, ‘partner’).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----------+--------+--------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Users_Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Banned</span><span class="w"> </span><span class="o">|</span><span class="w">  </span><span class="k">Role</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----------+--------+--------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">    </span><span class="mi">1</span><span class="w">     </span><span class="o">|</span><span class="w">   </span><span class="k">No</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">client</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">    </span><span class="mi">2</span><span class="w">     </span><span class="o">|</span><span class="w">   </span><span class="n">Yes</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">client</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">    </span><span class="mi">3</span><span class="w">     </span><span class="o">|</span><span class="w">   </span><span class="k">No</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">client</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">    </span><span class="mi">4</span><span class="w">     </span><span class="o">|</span><span class="w">   </span><span class="k">No</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">client</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">    </span><span class="mi">10</span><span class="w">    </span><span class="o">|</span><span class="w">   </span><span class="k">No</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">driver</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">    </span><span class="mi">11</span><span class="w">    </span><span class="o">|</span><span class="w">   </span><span class="k">No</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">driver</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">    </span><span class="mi">12</span><span class="w">    </span><span class="o">|</span><span class="w">   </span><span class="k">No</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">driver</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">    </span><span class="mi">13</span><span class="w">    </span><span class="o">|</span><span class="w">   </span><span class="k">No</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="n">driver</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----------+--------+--------+
</span></span></span></code></pre></div><p>  Write a SQL query to find the cancellation rate of requests made by unbanned clients between Oct 1, 2013 and Oct 3, 2013. For the above tables, your SQL query should return the following rows with the cancellation rate being rounded to two decimal places.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">------------+-------------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">     </span><span class="k">Day</span><span class="w">    </span><span class="o">|</span><span class="w"> </span><span class="n">Cancellation</span><span class="w"> </span><span class="n">Rate</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">------------+-------------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">01</span><span class="w"> </span><span class="o">|</span><span class="w">       </span><span class="mi">0</span><span class="p">.</span><span class="mi">33</span><span class="w">        </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">02</span><span class="w"> </span><span class="o">|</span><span class="w">       </span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="w">        </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">2013</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">03</span><span class="w"> </span><span class="o">|</span><span class="w">       </span><span class="mi">0</span><span class="p">.</span><span class="mi">50</span><span class="w">        </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">------------+-------------------+
</span></span></span></code></pre></div><!-- more -->
<h3 id="大体意思">大体意思</h3>
<p>写一个 SQL 语句，查询非禁止客户（Users 表中 Banned 列为 No 的客户）在 2013-10-1 至 2013-10-3 间的单据取消率，结果为四舍五入后的两位有效数字。</p>
<h3 id="别人的解法">别人的解法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">Request_at</span><span class="w"> </span><span class="k">DAY</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="n">ROUND</span><span class="p">(</span><span class="k">SUM</span><span class="p">(</span><span class="k">IF</span><span class="p">(</span><span class="n">Status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;completed&#39;</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">),</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="s1">&#39;Cancellation Rate&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w">   </span><span class="n">Trips</span><span class="w"> </span><span class="n">t</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">LEFT</span><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="n">Users</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">t</span><span class="p">.</span><span class="n">Client_Id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">t1</span><span class="p">.</span><span class="n">Users_Id</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w">  </span><span class="n">t1</span><span class="p">.</span><span class="n">Banned</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;No&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">Request_at</span><span class="w"> </span><span class="k">BETWEEN</span><span class="w"> </span><span class="s1">&#39;2013-10-01&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="s1">&#39;2013-10-03&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GROUP</span><span class="w">  </span><span class="k">BY</span><span class="w"> </span><span class="n">t</span><span class="p">.</span><span class="n">Re</span><span class="w"> </span><span class="n">quest_at</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>这道题主要是统计计算</p>
<p>笔记：
1、函数：ROUND(column_name,decimals)
<code>column_name</code> 必需。要舍入的字段
<code>decimals</code> 必需。规定要返回的小数位数</p>
<p>2、<code>SUM(IF(Status = 'completed', 0, 1))</code>
统计 SUM 这里也好巧妙</p>
<p>3、<code>having</code> 与 <code>where</code> 的区别
having 字句可以让我们筛选成组后的各种数据，where 字句在聚合前先筛选记录，也就是说作用在 group by 和 having 字句前。而 having 子句在聚合后对组记录进行筛选。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">region</span><span class="p">,</span><span class="w"> </span><span class="k">SUM</span><span class="p">(</span><span class="n">population</span><span class="p">),</span><span class="w"> </span><span class="k">SUM</span><span class="p">(</span><span class="n">area</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="n">bbc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">region</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">HAVING</span><span class="w"> </span><span class="k">SUM</span><span class="p">(</span><span class="n">area</span><span class="p">)</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">1000000</span><span class="w">
</span></span></span></code></pre></div><p>在这里，我们不能用 where 来筛选超过 1000000 的地区，因为表中不存在这样一条记录。相反，having 子句可以让我们筛选成组后的各组数据</p>
<blockquote>
<p>Reference:
<a href="https://my.oschina.net/Tsybius2014/blog/496047">LeetCode：Trips and Users - 出租车接单取消率 - Tsybius2014</a> &gt; <a href="http://www.blogjava.net/Johnny-Ajun/archive/2011/08/28/357445.html">mysql 中的 where 和 having 子句的区别 - Hukin</a></p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Database Department Highest Salary 184</title>
      <link>https://zyf.im/2016/09/12/leetcode-database-department-highest-salary-184/</link>
      <pubDate>Mon, 12 Sep 2016 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/09/12/leetcode-database-department-highest-salary-184/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/department-highest-salary/&#34;&gt;184. Department Highest Salary&lt;/a&gt;
  The &lt;code&gt;Employee&lt;/code&gt; table holds all employees. Every employee has an Id, a salary, and there is also a column for the department Id.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-------+--------+--------------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Salary&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DepartmentId&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-------+--------+--------------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Joe&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;70000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Henry&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;80000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Sam&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;60000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;Max&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;90000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-------+--------+--------------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;  The &lt;code&gt;Department&lt;/code&gt; table holds all departments of the company.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/department-highest-salary/">184. Department Highest Salary</a>
  The <code>Employee</code> table holds all employees. Every employee has an Id, a salary, and there is also a column for the department Id.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------+--------+--------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Name</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">Salary</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">DepartmentId</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------+--------+--------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">1</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">Joe</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="mi">70000</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="mi">1</span><span class="w">            </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">2</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">Henry</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">80000</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="mi">2</span><span class="w">            </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">3</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">Sam</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="mi">60000</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="mi">2</span><span class="w">            </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">4</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="k">Max</span><span class="w">   </span><span class="o">|</span><span class="w"> </span><span class="mi">90000</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="mi">1</span><span class="w">            </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-------+--------+--------------+
</span></span></span></code></pre></div><p>  The <code>Department</code> table holds all departments of the company.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+----------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Name</span><span class="w">     </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+----------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">1</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">IT</span><span class="w">       </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">2</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="n">Sales</span><span class="w">    </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+----------+
</span></span></span></code></pre></div><p>  Write a SQL query to find employees who have the highest salary in each of the departments. For the above tables, Max has the highest salary in the IT department and Henry has the highest salary in the Sales department.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">------------+----------+--------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Department</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Employee</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Salary</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">------------+----------+--------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">IT</span><span class="w">         </span><span class="o">|</span><span class="w"> </span><span class="k">Max</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="mi">90000</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Sales</span><span class="w">      </span><span class="o">|</span><span class="w"> </span><span class="n">Henry</span><span class="w">    </span><span class="o">|</span><span class="w"> </span><span class="mi">80000</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">------------+----------+--------+
</span></span></span></code></pre></div><!-- more -->
<h3 id="大体意思">大体意思</h3>
<p>查询出每一个部门中收入最后的员工的信息</p>
<h3 id="自己的解法">自己的解法</h3>
<p>一开始时自己没有考虑收入最高可能有并列的情况，就直接 MAX()，加 join in 了；同时也要考虑到：所属部门不存在的情况；修改后 SQL 为</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">d</span><span class="p">.</span><span class="n">NAME</span><span class="w"> </span><span class="n">Department</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">e</span><span class="p">.</span><span class="n">NAME</span><span class="w"> </span><span class="n">Employee</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">e</span><span class="p">.</span><span class="n">Salary</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">Employee</span><span class="w"> </span><span class="n">e</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">JOIN</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">max</span><span class="p">(</span><span class="w"> </span><span class="n">salary</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="n">salary</span><span class="p">,</span><span class="w"> </span><span class="n">departmentId</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">Employee</span><span class="w"> </span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">departmentId</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="k">USING</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">salary</span><span class="p">,</span><span class="w"> </span><span class="n">departmentId</span><span class="w"> </span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">JOIN</span><span class="w"> </span><span class="n">Department</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">e</span><span class="p">.</span><span class="n">departmentId</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">d</span><span class="p">.</span><span class="n">id</span><span class="w">
</span></span></span></code></pre></div><h3 id="别人的解法">别人的解法</h3>
<p>基本都是连表查询的套路，但是有一段总结还是有些意思的。</p>
<p>1、聚合函数 max() 的效率不如嵌套子查询
2、in 与 exists 效率差不多，当时在网上查的是：</p>
<blockquote>
<p>in 和 not in 也要慎用，否则会导致全表扫描
很多时候用 exists 代替 in 是一个好的选择</p>
</blockquote>
<p>3、将 join on 代替了 where 判断，效率提升很多，后来有个看过 MYSQL 源码的大神说：</p>
<blockquote>
<p>在 MySQL 的 SELECT 查询当中，其核心算法就是 JOIN 查询算法。其他的查询语句都相应向 JOIN 靠拢：单表查询被当作 JOIN 的特例；子查询被尽量转换为 JOIN 查询</p>
</blockquote>
<p>4、将 join 替换为了 straight_join，还是源码大神说的：</p>
<blockquote>
<p>对于多表查询，如果可以确定表按照某一固定次序处理可以获得较好的效率，则建议加上 STRAIGHT_JOIN 子句，以减少优化器对表进行重排序优化的过程。
该子句一方面可以用于优化器无法给出最优排列的 SQL 语句；另一方面同样适用于优化器可以给出最优排列的 SQL 语句，因为 MySQL 算出最优排列也需要耗费较长的流程。
对于后一状况，可以根据 EXPLAIN 的提示选定表的顺序，并加上 STRAIGHT_JOIN 子句固定该顺序。该状况下的使用前提是几个表之间的数据量比例会一直保持在某一顺序，否则在各表数据此消彼长之后会适得其反。</p>
</blockquote>
<p>对于经常调用的 SQL 语句，这一方法效果较好；同时操作的表越多，效果越好。</p>
<blockquote>
<p>Reference:
<a href="http://www.cnblogs.com/zhangyunhao/p/4896055.html">leetcode-184-Department Highest Salary 优化记录 - M-zyh</a></p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Database Consecutive Numbers 180</title>
      <link>https://zyf.im/2016/09/11/leetcode-database-consecutive-numbers-180/</link>
      <pubDate>Sun, 11 Sep 2016 20:45:00 +0000</pubDate>
      <guid>https://zyf.im/2016/09/11/leetcode-database-consecutive-numbers-180/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/consecutive-numbers/&#34;&gt;180. Consecutive Numbers&lt;/a&gt;
  Write a SQL query to find all numbers that appear at least three times consecutively.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-----+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Num&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-----+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+-----+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For example, given the above &lt;code&gt;Logs&lt;/code&gt; table, &lt;code&gt;1&lt;/code&gt; is the only number that appears consecutively for at least three times.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/consecutive-numbers/">180. Consecutive Numbers</a>
  Write a SQL query to find all numbers that appear at least three times consecutively.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-----+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Num</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-----+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">1</span><span class="w">  </span><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">2</span><span class="w">  </span><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">3</span><span class="w">  </span><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">4</span><span class="w">  </span><span class="o">|</span><span class="w">  </span><span class="mi">2</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">5</span><span class="w">  </span><span class="o">|</span><span class="w">  </span><span class="mi">1</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">6</span><span class="w">  </span><span class="o">|</span><span class="w">  </span><span class="mi">2</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">7</span><span class="w">  </span><span class="o">|</span><span class="w">  </span><span class="mi">2</span><span class="w">  </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+-----+
</span></span></span></code></pre></div><p>For example, given the above <code>Logs</code> table, <code>1</code> is the only number that appears consecutively for at least three times.</p>
<!-- more -->
<h3 id="大体意思">大体意思</h3>
<p>写 SQL 查询出连续出现至少 3 次的 Num</p>
<h3 id="自己的解法">自己的解法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">DISTINCT</span><span class="w"> </span><span class="n">L1</span><span class="p">.</span><span class="n">Num</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">ConsecutiveNums</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="n">Logs</span><span class="w"> </span><span class="n">L1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="n">Logs</span><span class="w"> </span><span class="n">L2</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">L1</span><span class="p">.</span><span class="n">Id</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">L2</span><span class="p">.</span><span class="n">Id</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="n">Logs</span><span class="w"> </span><span class="n">L3</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">L1</span><span class="p">.</span><span class="n">Id</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">L3</span><span class="p">.</span><span class="n">Id</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w"> </span><span class="n">L1</span><span class="p">.</span><span class="n">Num</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">L2</span><span class="p">.</span><span class="n">Num</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">L1</span><span class="p">.</span><span class="n">Num</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">L3</span><span class="p">.</span><span class="n">Num</span><span class="w">
</span></span></span></code></pre></div><p>很传统连表查询，但是有坑的地方，就是依靠的时 Id，所以局限是 Id 要连贯</p>
<h3 id="别人的解法">别人的解法</h3>
<p>首先，增加一个 rank 字段，记录序号，初始值为 1，当后一个值与前一个值相等时，序号加 1。之后，把所有 rank 值大于等于 3 的都检索出来，再去重即可。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">DISTINCT</span><span class="p">(</span><span class="n">Num</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">ConsecutiveNums</span><span class="w"> </span><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">SELECT</span><span class="w"> </span><span class="n">Id</span><span class="p">,</span><span class="n">Num</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">@</span><span class="n">Rank</span><span class="p">:</span><span class="o">=</span><span class="k">IF</span><span class="p">(</span><span class="o">@</span><span class="n">prevNum</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">Num</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="o">@</span><span class="n">Rank</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">Rank</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">@</span><span class="n">prevNum</span><span class="p">:</span><span class="o">=</span><span class="n">Num</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="n">Logs</span><span class="p">)</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">SELECT</span><span class="w"> </span><span class="o">@</span><span class="n">Rank</span><span class="p">:</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span><span class="o">@</span><span class="n">prevNum</span><span class="p">:</span><span class="o">=</span><span class="k">NULL</span><span class="p">)</span><span class="w"> </span><span class="n">r</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w"> </span><span class="n">t</span><span class="p">.</span><span class="n">Rank</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">3</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h3 id="自己的理解">自己的理解</h3>
<pre tabindex="0"><code>(SELECT @Rank:=0,@prevNum:=NULL) r
</code></pre><p>是个巧妙的地方，对变量进行了初始化</p>
<blockquote>
<p>Reference:
<a href="http://blog.csdn.net/Kevin_zhai/article/details/52152289">【leetcode Database】180. Consecutive Numbers - Kevin_zhai 的博客</a></p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Database Second Highest Salary 176</title>
      <link>https://zyf.im/2016/09/11/leetcode-database-second-highest-salary-176/</link>
      <pubDate>Sun, 11 Sep 2016 19:45:00 +0000</pubDate>
      <guid>https://zyf.im/2016/09/11/leetcode-database-second-highest-salary-176/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/second-highest-salary/&#34;&gt;176. Second Highest Salary&lt;/a&gt;
  Write a SQL query to get the second highest salary from the &lt;code&gt;Employee&lt;/code&gt; table.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+--------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Salary&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+--------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;300&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;----+--------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For example, given the above Employee table, the second highest salary is &lt;code&gt;200&lt;/code&gt;. If there is no second highest salary, then the query should return &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h3 id=&#34;大体意思&#34;&gt;大体意思&lt;/h3&gt;
&lt;p&gt;写 SQL 查询出第二高薪水的 Id。如何没有第二高，则返回 &lt;code&gt;null&lt;/code&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/second-highest-salary/">176. Second Highest Salary</a>
  Write a SQL query to get the second highest salary from the <code>Employee</code> table.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+--------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Salary</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+--------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">1</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="mi">100</span><span class="w">    </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">2</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="mi">200</span><span class="w">    </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">3</span><span class="w">  </span><span class="o">|</span><span class="w"> </span><span class="mi">300</span><span class="w">    </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+--------+
</span></span></span></code></pre></div><p>For example, given the above Employee table, the second highest salary is <code>200</code>. If there is no second highest salary, then the query should return <code>null</code>.</p>
<!-- more -->
<h3 id="大体意思">大体意思</h3>
<p>写 SQL 查询出第二高薪水的 Id。如何没有第二高，则返回 <code>null</code></p>
<h3 id="自己的解法">自己的解法</h3>
<pre tabindex="0"><code>SELECT
    Salary
FROM
    Employee
ORDER BY Salary DESC
LIMIT 1,1
</code></pre><p>但是在没有第二高的时候将没有返回值，不符合题意；看了别人的，发现自己也少考虑 <code>DISTINCT</code></p>
<h3 id="别人的解法">别人的解法</h3>
<p>多加一层 SELECT 并添加一个 IF 条件判断。如果结果有 0 行则返回 NULL，有 1 行返回正常结果。由于可以预期上一步结果只有一个，所以这里可以用 COUNT 而不用 GROUP BY。</p>
<p>构造测试数据：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">Employee</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Id</span><span class="w"> </span><span class="nb">INT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Salary</span><span class="w"> </span><span class="nb">INT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">DELETE</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">Employee</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">Employee</span><span class="w"> </span><span class="k">VALUES</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">200</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">300</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>预期结果：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">---------------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">SecondHighestSalary</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">---------------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="mi">200</span><span class="w">                 </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">---------------------+
</span></span></span></code></pre></div><p>解法一：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">IF</span><span class="p">(</span><span class="k">COUNT</span><span class="p">(</span><span class="n">Salary</span><span class="p">)</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">Salary</span><span class="p">,</span><span class="w"> </span><span class="k">NULL</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">SecondHighestSalary</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">(</span><span class="k">SELECT</span><span class="w"> </span><span class="k">DISTINCT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Salary</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Employee</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">Salary</span><span class="w"> </span><span class="k">DESC</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="n">tmp</span><span class="w">
</span></span></span></code></pre></div><p>解法二：
正常 Ranking 类问题解法，使用自定义变量计算排名。接着和上面一种解法类似需要对结果进行处理，没有第 2 名的返回 NULL：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">IF</span><span class="p">(</span><span class="k">COUNT</span><span class="p">(</span><span class="n">Salary</span><span class="p">)</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">Salary</span><span class="p">,</span><span class="w"> </span><span class="k">NULL</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">SecondHighestSalary</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">(</span><span class="k">SELECT</span><span class="w"> </span><span class="k">DISTINCT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Salary</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">(</span><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">         </span><span class="n">Id</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">Salary</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="o">@</span><span class="n">rank</span><span class="p">:</span><span class="o">=</span><span class="k">IF</span><span class="p">(</span><span class="o">@</span><span class="n">prevVal</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">Salary</span><span class="p">,</span><span class="w"> </span><span class="o">@</span><span class="n">rank</span><span class="p">:</span><span class="o">=@</span><span class="n">rank</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="o">@</span><span class="n">rank</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">Rank</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="o">@</span><span class="n">prevVal</span><span class="p">:</span><span class="o">=</span><span class="n">Salary</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">         </span><span class="n">Employee</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="k">SELECT</span><span class="w"> </span><span class="o">@</span><span class="n">prevVal</span><span class="p">:</span><span class="o">=</span><span class="k">NULL</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="k">SELECT</span><span class="w"> </span><span class="o">@</span><span class="n">rank</span><span class="p">:</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="n">y</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">Salary</span><span class="w"> </span><span class="k">DESC</span><span class="p">)</span><span class="w"> </span><span class="n">tmp</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">WHERE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">tmp</span><span class="p">.</span><span class="n">Rank</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="n">tmp2</span><span class="w">
</span></span></span></code></pre></div><p>解法三：
上面两种解法都是可以扩展到任意排名的，如果想偏一点可以得到其他解法。排名第 2 可以看做是除了 MAX 之外的 MAX，可以得到这两种类似的解法。由于 MAX 函数可以返回 NULL 结果，就不用在进一步加工结果。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">MAX</span><span class="p">(</span><span class="n">Salary</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Employee</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Salary</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="p">(</span><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">MAX</span><span class="p">(</span><span class="n">Salary</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">Employee</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">MAX</span><span class="p">(</span><span class="n">Salary</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Employee</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Salary</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">MAX</span><span class="p">(</span><span class="n">Salary</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">FROM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">Employee</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><blockquote>
<p>Reference:
<a href="http://tsuinte.ru/2015/04/05/leetcode-database-176-second-highest-salary/">Leetcode Database: #176 Second Highest Salary | tsuinteru</a></p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Database Rising Temperature 197</title>
      <link>https://zyf.im/2016/09/11/leetcode-database-rising-temperature-197/</link>
      <pubDate>Sun, 11 Sep 2016 19:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/09/11/leetcode-database-rising-temperature-197/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/rising-temperature/&#34;&gt;197. Rising Temperature&lt;/a&gt;
  Given a &lt;code&gt;Weather&lt;/code&gt; table, write a SQL query to find all dates&amp;rsquo; Ids with higher temperature compared to its previous (yesterday&amp;rsquo;s) dates.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;---------+------------+------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;INT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;DATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Temperature&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;INT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;---------+------------+------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2015&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;01&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;01&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2015&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;01&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;02&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2015&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;01&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;03&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2015&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;01&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;04&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;---------+------------+------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- more --&gt;
&lt;p&gt;For example, return the following Ids for the above Weather table:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/rising-temperature/">197. Rising Temperature</a>
  Given a <code>Weather</code> table, write a SQL query to find all dates&rsquo; Ids with higher temperature compared to its previous (yesterday&rsquo;s) dates.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">---------+------------+------------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="p">(</span><span class="nb">INT</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nb">Date</span><span class="p">(</span><span class="nb">DATE</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Temperature</span><span class="p">(</span><span class="nb">INT</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">---------+------------+------------------+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">       </span><span class="mi">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">2015</span><span class="o">-</span><span class="mi">01</span><span class="o">-</span><span class="mi">01</span><span class="w"> </span><span class="o">|</span><span class="w">               </span><span class="mi">10</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">       </span><span class="mi">2</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">2015</span><span class="o">-</span><span class="mi">01</span><span class="o">-</span><span class="mi">02</span><span class="w"> </span><span class="o">|</span><span class="w">               </span><span class="mi">25</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">       </span><span class="mi">3</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">2015</span><span class="o">-</span><span class="mi">01</span><span class="o">-</span><span class="mi">03</span><span class="w"> </span><span class="o">|</span><span class="w">               </span><span class="mi">20</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">       </span><span class="mi">4</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="mi">2015</span><span class="o">-</span><span class="mi">01</span><span class="o">-</span><span class="mi">04</span><span class="w"> </span><span class="o">|</span><span class="w">               </span><span class="mi">30</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">---------+------------+------------------+
</span></span></span></code></pre></div><!-- more -->
<p>For example, return the following Ids for the above Weather table:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w"> </span><span class="n">Id</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">2</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">|</span><span class="w">  </span><span class="mi">4</span><span class="w"> </span><span class="o">|</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">----+
</span></span></span></code></pre></div><hr>
<h3 id="大体意思">大体意思</h3>
<p>写 SQL 查询出温度比昨天大的日期 Id</p>
<h3 id="别人的解法">别人的解法</h3>
<p>The <code>DATEDIFF()</code> function returns the time between two dates</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">w1</span><span class="p">.</span><span class="n">Id</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">Weather</span><span class="w"> </span><span class="n">w1</span><span class="p">,</span><span class="w"> </span><span class="n">Weather</span><span class="w"> </span><span class="n">w2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w"> </span><span class="n">w1</span><span class="p">.</span><span class="n">Temperature</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">w2</span><span class="p">.</span><span class="n">Temperature</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="p">(</span><span class="n">DATEDIFF</span><span class="p">(</span><span class="n">w1</span><span class="p">.</span><span class="nb">Date</span><span class="p">,</span><span class="w"> </span><span class="n">w2</span><span class="p">.</span><span class="nb">Date</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><blockquote>
<p>Reference:
<a href="https://leijiangcoding.wordpress.com/2015/05/01/leetcode-q197-rising-temperature/">LeetCode Q197 Rising Temperature | Lei Jiang Coding</a></p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>MyBatis Generator 使用配置</title>
      <link>https://zyf.im/2016/09/10/mybatis-generator-tutorial/</link>
      <pubDate>Sat, 10 Sep 2016 19:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/09/10/mybatis-generator-tutorial/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 内容声明&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;本文写于 2016 年，内容已不再维护更新。文章中的部分内容已过时：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MyBatis Generator 版本&lt;/strong&gt;：文中使用的是 1.3.5 版本，当前最新版本已更新至 1.4.x，提供了更多功能和改进&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL 驱动&lt;/strong&gt;：文中使用的 &lt;code&gt;com.mysql.jdbc.Driver&lt;/code&gt; 已被弃用，应使用 &lt;code&gt;com.mysql.cj.jdbc.Driver&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Eclipse IDE&lt;/strong&gt;：现代 Java 开发更多使用 IntelliJ IDEA，且 IDE 对 MyBatis 的支持已有专门的插件（如 MyBatisX）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;当前最佳实践&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 MyBatis Generator 最新版本（1.4.x）&lt;/li&gt;
&lt;li&gt;配合现代构建工具（Maven/Gradle）和 IDE 插件使用&lt;/li&gt;
&lt;li&gt;考虑使用 MyBatis-Plus 等增强框架，提供更便捷的代码生成和 CRUD 操作&lt;/li&gt;
&lt;li&gt;对于新项目，建议评估是否需要代码生成器，或考虑使用 JPA/Spring Data JPA 等现代 ORM 方案&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;MyBatis Generator (MBG) 是一个 MyBatis 的代码生成器。MBG 可以内省数据库的表（或多个表），然后生成可以用来访问（多个）表的基础对象。这样在与数据库表进行交互时，不需要手动创建对象和配置文件。MBG 解决了对数据库操作影响最大的一些简单 CRUD（插入、查询、更新、删除）操作。&lt;/p&gt;
&lt;h2 id=&#34;mybatis-generator-文档&#34;&gt;MyBatis Generator 文档&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://www.mybatis.org/generator/index.html&#34;&gt;MyBatis Generator 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://mbg.cndocs.tk/index.html&#34;&gt;MyBatis Generator 中文文档&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;通过-maven-运行-mbg&#34;&gt;通过 Maven 运行 MBG&lt;/h2&gt;
&lt;p&gt;MyBatis Generator (MBG) 包含了一个可以集成到 Maven 构建的 Maven 插件，按照 Maven 的配置惯例，将 MBG 集成到 Maven 很容易。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>⚠️ 内容声明</strong></p>
<p>本文写于 2016 年，内容已不再维护更新。文章中的部分内容已过时：</p>
<ul>
<li><strong>MyBatis Generator 版本</strong>：文中使用的是 1.3.5 版本，当前最新版本已更新至 1.4.x，提供了更多功能和改进</li>
<li><strong>MySQL 驱动</strong>：文中使用的 <code>com.mysql.jdbc.Driver</code> 已被弃用，应使用 <code>com.mysql.cj.jdbc.Driver</code></li>
<li><strong>Eclipse IDE</strong>：现代 Java 开发更多使用 IntelliJ IDEA，且 IDE 对 MyBatis 的支持已有专门的插件（如 MyBatisX）</li>
</ul>
<p><strong>当前最佳实践</strong>：</p>
<ul>
<li>使用 MyBatis Generator 最新版本（1.4.x）</li>
<li>配合现代构建工具（Maven/Gradle）和 IDE 插件使用</li>
<li>考虑使用 MyBatis-Plus 等增强框架，提供更便捷的代码生成和 CRUD 操作</li>
<li>对于新项目，建议评估是否需要代码生成器，或考虑使用 JPA/Spring Data JPA 等现代 ORM 方案</li>
</ul>
</blockquote>
<p>MyBatis Generator (MBG) 是一个 MyBatis 的代码生成器。MBG 可以内省数据库的表（或多个表），然后生成可以用来访问（多个）表的基础对象。这样在与数据库表进行交互时，不需要手动创建对象和配置文件。MBG 解决了对数据库操作影响最大的一些简单 CRUD（插入、查询、更新、删除）操作。</p>
<h2 id="mybatis-generator-文档">MyBatis Generator 文档</h2>
<ul>
<li><a href="http://www.mybatis.org/generator/index.html">MyBatis Generator 官方文档</a></li>
<li><a href="http://mbg.cndocs.tk/index.html">MyBatis Generator 中文文档</a></li>
</ul>
<h2 id="通过-maven-运行-mbg">通过 Maven 运行 MBG</h2>
<p>MyBatis Generator (MBG) 包含了一个可以集成到 Maven 构建的 Maven 插件，按照 Maven 的配置惯例，将 MBG 集成到 Maven 很容易。</p>
<!-- more -->
<h3 id="pomxml-配置"><code>pom.xml</code> 配置</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl"><span class="nt">&lt;properties&gt;</span>
</span></span><span class="line"><span class="cl">  ...
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 设置一些变量 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- plugin versions --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;plugin.mybatis.generator&gt;</span>1.3.5<span class="nt">&lt;/plugin.mybatis.generator&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- plugin setting --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;mybatis.generator.generatorConfig.xml&gt;</span>${basedir}/src/test/resources/generatorConfig.xml<span class="nt">&lt;/mybatis.generator.generatorConfig.xml&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;mybatis.generator.generatorConfig.properties&gt;</span>file:///${basedir}/src/test/resources/generatorConfig.properties<span class="nt">&lt;/mybatis.generator.generatorConfig.properties&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/properties&gt;</span>
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl"><span class="nt">&lt;build&gt;</span>
</span></span><span class="line"><span class="cl">  ...
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;plugins&gt;</span>
</span></span><span class="line"><span class="cl">    ...
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- MyBatis Generator 代码生成插件配置 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;plugin&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;groupId&gt;</span>org.mybatis.generator<span class="nt">&lt;/groupId&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;artifactId&gt;</span>mybatis-generator-maven-plugin<span class="nt">&lt;/artifactId&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;version&gt;</span>${plugin.mybatis.generator}<span class="nt">&lt;/version&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;configuration&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;configurationFile&gt;</span>${mybatis.generator.generatorConfig.xml}<span class="nt">&lt;/configurationFile&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;overwrite&gt;</span>true<span class="nt">&lt;/overwrite&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;verbose&gt;</span>true<span class="nt">&lt;/verbose&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;/configuration&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/plugin&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/plugins&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/build&gt;</span>
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><h3 id="generatorconfigxml-核心配置"><code>generatorConfig.xml</code> 核心配置</h3>
<p>非常完整的 MBG 核心配置文件，配合文档效果更佳。因为有大量的注释篇幅较长，建议复制到 XML 文件中查看。</p>
<p><code>generatorConfig.xml</code> 的文件位置要对应 <code>pom.xml</code> 中的配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;mybatis.generator.generatorConfig.xml&gt;</span>${basedir}/src/test/resources/generatorConfig.xml<span class="nt">&lt;/mybatis.generator.generatorConfig.xml&gt;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE generatorConfiguration
</span></span></span><span class="line"><span class="cl"><span class="cp">  PUBLIC &#34;-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">&#34;http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd&#34;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 配置生成器 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;generatorConfiguration&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 可以用于加载配置项或者配置文件，在整个配置文件中就可以使用 ${propertyKey} 的方式来引用配置项 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- resource：配置资源加载地址，使用 resource，MBG 从 classpath 开始找，比如 com/myproject/generatorConfig.properties --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- url：配置资源加载地址，使用 URL 的方式，比如 file:///C:/myfolder/generatorConfig.properties
</span></span></span><span class="line"><span class="cl"><span class="c">    注意，两个属性只能选择一个 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 另外，如果使用了 mybatis-generator-maven-plugin，那么在 pom.xml 中定义的 properties 都可以直接在 generatorConfig.xml 中使用 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- &lt;properties resource=&#34;&#34; url=&#34;&#34; /&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 配置文件路径 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;properties</span> <span class="na">url=</span><span class="s">&#34;${mybatis.generator.generatorConfig.properties}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 在 MBG 工作的时候，需要额外加载的依赖包，location 属性指明加载 jar/zip 包的全路径 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 数据库访问的驱动包 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;classPathEntry</span> <span class="na">location=</span><span class="s">&#34;${mbg.drive.class.path}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- context：生成一组对象的环境，id：必选，上下文 id，用于在生成错误时提示 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- defaultModelType：指定生成对象的样式
</span></span></span><span class="line"><span class="cl"><span class="c">    1. conditional：类似 hierarchical
</span></span></span><span class="line"><span class="cl"><span class="c">    2. flat：所有内容（主键、blob）等全部生成在一个对象中
</span></span></span><span class="line"><span class="cl"><span class="c">    3. hierarchical：主键生成一个 XXKey 对象（key class），Blob 等单独生成一个对象，其他简单属性在一个对象中（record class） --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- targetRuntime：
</span></span></span><span class="line"><span class="cl"><span class="c">    1. MyBatis3：默认值，生成基于 MyBatis 3.x 以上版本的内容，包括 XXXBySample
</span></span></span><span class="line"><span class="cl"><span class="c">    2. MyBatis3Simple：类似 MyBatis3，只是不生成 XXXBySample
</span></span></span><span class="line"><span class="cl"><span class="c">    introspectedColumnImpl：类全限定名，用于扩展 MBG --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;context</span> <span class="na">id=</span><span class="s">&#34;mysql_mbg&#34;</span> <span class="na">defaultModelType=</span><span class="s">&#34;flat&#34;</span> <span class="na">targetRuntime=</span><span class="s">&#34;MyBatis3Simple&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 自动识别数据库关键字，默认 false，如果设置为 true，根据 SqlReservedWords 中定义的关键字列表 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 一般保留默认值，遇到数据库关键字（Java 关键字），使用 columnOverride 覆盖 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;autoDelimitKeywords&#34;</span> <span class="na">value=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 生成的 Java 文件的编码 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;javaFileEncoding&#34;</span> <span class="na">value=</span><span class="s">&#34;UTF-8&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 格式化 Java 代码 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;javaFormatter&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">value=</span><span class="s">&#34;org.mybatis.generator.api.dom.DefaultJavaFormatter&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 格式化 XML 代码 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;xmlFormatter&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">value=</span><span class="s">&#34;org.mybatis.generator.api.dom.DefaultXmlFormatter&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- beginningDelimiter 和 endingDelimiter：指明数据库用于标记数据库对象名的符号 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 比如 ORACLE 就是双引号，MySQL 默认是反引号 ` --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;beginningDelimiter&#34;</span> <span class="na">value=</span><span class="s">&#34;`&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;endingDelimiter&#34;</span> <span class="na">value=</span><span class="s">&#34;`&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 关闭注释 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;commentGenerator&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;suppressAllComments&#34;</span> <span class="na">value=</span><span class="s">&#34;true&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/commentGenerator&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 必须要有的，使用这个配置链接数据库 @TODO:是否可以扩展 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;jdbcConnection</span> <span class="na">driverClass=</span><span class="s">&#34;com.mysql.jdbc.Driver&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">connectionURL=</span><span class="s">&#34;${mbg.jdbc.url}&#34;</span> <span class="na">userId=</span><span class="s">&#34;${mbg.jdbc.username}&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">password=</span><span class="s">&#34;${mbg.jdbc.password}&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 这里面可以设置property属性，每一个property属性都设置到配置的Driver上 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/jdbcConnection&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- java类型处理器 用于处理DB中的类型到Java中的类型，默认使用JavaTypeResolverDefaultImpl; 注意一点，默认会先尝试使用Integer，Long，Short等来对应DECIMAL和
</span></span></span><span class="line"><span class="cl"><span class="c">      NUMERIC数据类型; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;javaTypeResolver</span>
</span></span><span class="line"><span class="cl">      <span class="na">type=</span><span class="s">&#34;org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- true：使用BigDecimal对应DECIMAL和 NUMERIC数据类型 false：默认, scale&gt;0;length&gt;18：使用BigDecimal;
</span></span></span><span class="line"><span class="cl"><span class="c">        scale=0;length[10,18]：使用Long; scale=0;length[5,9]：使用Integer; scale=0;length&lt;5：使用Short; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;forceBigDecimals&#34;</span> <span class="na">value=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/javaTypeResolver&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- java模型创建器，是必须要的元素 负责：1，key类（见context的defaultModelType）;2，java类;3，查询类 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- targetPackage：生成的类要放的包，真实的包受enableSubPackages属性控制; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- targetProject：目标项目，指定一个存在的目录下，生成的内容会放到指定目录中，如果目录不存在，MBG不会自动建目录 --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;javaModelGenerator</span> <span class="na">targetPackage=</span><span class="s">&#34;${mbg.model.package}&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">targetProject=</span><span class="s">&#34;src/test/java&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- for MyBatis3/MyBatis3Simple 自动为每一个生成的类创建一个构造方法，构造方法包含了所有的field;而不是使用setter; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;constructorBased&#34;</span> <span class="na">value=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 在targetPackage的基础上，根据数据库的schema再生成一层package，最终生成的类放在这个package下，默认为false --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;enableSubPackages&#34;</span> <span class="na">value=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- for MyBatis3 / MyBatis3Simple 是否创建一个不可变的类，如果为true， 那么MBG会创建一个没有setter方法的类，取而代之的是类似constructorBased的类 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;immutable&#34;</span> <span class="na">value=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 设置一个根对象，如果设置了这个根对象，那么生成的keyClass或者recordClass会继承这个类;在Table的rootClass属性中可以覆盖该选项 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 注意：如果在key class或者record class中有root class相同的属性，MBG就不会重新生成这些属性了， 包括：
</span></span></span><span class="line"><span class="cl"><span class="c">        1，属性名相同，类型相同，有相同的getter/setter方法; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;property name=&#34;rootClass&#34; value=&#34;com.domain.BaseDomain&#34; /&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 设置是否在getter方法中，对String类型字段调用trim()方法 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;trimStrings&#34;</span> <span class="na">value=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/javaModelGenerator&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 生成SQL map的XML文件生成器，注意，在Mybatis3之后，我们可以使用mapper.xml文件+Mapper接口（或者不用mapper接口），
</span></span></span><span class="line"><span class="cl"><span class="c">      或者只使用Mapper接口+Annotation，所以，如果 javaClientGenerator配置中配置了需要生成XML的话，这个元素就必须配置
</span></span></span><span class="line"><span class="cl"><span class="c">      targetPackage/targetProject:同javaModelGenerator --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;sqlMapGenerator</span> <span class="na">targetPackage=</span><span class="s">&#34;${mbg.xml.mapper.package}&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">targetProject=</span><span class="s">&#34;src/test/java&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 在targetPackage的基础上，根据数据库的schema再生成一层package，最终生成的类放在这个package下，默认为false --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;enableSubPackages&#34;</span> <span class="na">value=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/sqlMapGenerator&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 对于mybatis来说，即生成Mapper接口，注意，如果没有配置该元素，那么默认不会生成Mapper接口 targetPackage/targetProject:同javaModelGenerator --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- type：选择怎么生成mapper接口（在MyBatis3/MyBatis3Simple下）： --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 1，ANNOTATEDMAPPER：会生成使用Mapper接口+Annotation的方式创建（SQL生成在annotation中），不会生成对应的XML; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 2，MIXEDMAPPER：使用混合配置，会生成Mapper接口，并适当添加合适的Annotation，但是XML会生成在XML中; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 3，XMLMAPPER：会生成Mapper接口，接口完全依赖XML; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 注意，如果context是MyBatis3Simple：只支持ANNOTATEDMAPPER和XMLMAPPER --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;javaClientGenerator</span> <span class="na">targetPackage=</span><span class="s">&#34;${mbg.xml.mapper.package}&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">type=</span><span class="s">&#34;XMLMAPPER&#34;</span> <span class="na">targetProject=</span><span class="s">&#34;src/test/java&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 在targetPackage的基础上，根据数据库的schema再生成一层package，最终生成的类放在这个package下，默认为false --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;enableSubPackages&#34;</span> <span class="na">value=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 可以为所有生成的接口添加一个父接口，但是MBG只负责生成，不负责检查 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;property name=&#34;rootInterface&#34; value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/javaClientGenerator&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 选择一个table来生成相关文件，可以有一个或多个table，必须要有table元素 选择的table会生成一下文件： --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 1，SQL map文件 2，生成一个主键类; 3，除了BLOB和主键的其他字段的类; 4，包含BLOB的类; 5，一个用户生成动态查询的条件类（selectByExample,
</span></span></span><span class="line"><span class="cl"><span class="c">      deleteByExample），可选; 6，Mapper接口（可选） --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 必要： --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- tableName ：要生成对象的表名; 注意：大小写敏感问题。正常情况下，MBG会自动的去识别数据库标识符的大小写敏感度，在一般情况下，MBG会
</span></span></span><span class="line"><span class="cl"><span class="c">      根据设置的schema，catalog或tablename去查询数据表，按照下面的流程： 1，如果schema，catalog或tablename中有空格，那么设置的是什么格式，就精确的使用指定的大小写格式去查询;
</span></span></span><span class="line"><span class="cl"><span class="c">      2，否则，如果数据库的标识符使用大写的，那么MBG自动把表名变成大写再查找; 3，否则，如果数据库的标识符使用小写的，那么MBG自动把表名变成小写再查找;
</span></span></span><span class="line"><span class="cl"><span class="c">      4，否则，使用指定的大小写格式查询; 另外的，如果在创建表的时候，使用的&#34;&#34;把数据库对象规定大小写，就算数据库标识符是使用的大写，在这种情况下也会使用给定的大小写来创建表名;
</span></span></span><span class="line"><span class="cl"><span class="c">      这个时候，请设置delimitIdentifiers=&#34;true&#34;即可保留大小写格式; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 可选： --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 1，schema：数据库的schema; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 2，catalog：数据库的catalog; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 3，alias：为数据表设置的别名，如果设置了alias，那么生成的所有的SELECT SQL语句中，列名会变成：alias_actualColumnName --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 4，domainObjectName：生成的domain类的名字，如果不设置，直接使用表名作为domain类的名字;可以设置为somepck.domainName，那么会自动把domainName类再放到somepck包里面; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 5，enableInsert（默认true）：指定是否生成insert语句; 6，enableSelectByPrimaryKey（默认true）：指定是否生成按照主键查询对象的语句（就是getById或get）; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 7，enableSelectByExample（默认true）：MyBatis3Simple为false，指定是否生成动态查询语句; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 8，enableUpdateByPrimaryKey（默认true）：指定是否生成按照主键修改对象的语句（即update); --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 9，enableDeleteByPrimaryKey（默认true）：指定是否生成按照主键删除对象的语句（即delete）; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 10，enableDeleteByExample（默认true）：MyBatis3Simple为false，指定是否生成动态删除语句; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 11，enableCountByExample（默认true）：MyBatis3Simple为false，指定是否生成动态查询总条数语句（用于分页的总条数查询）; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 12，enableUpdateByExample（默认true）：MyBatis3Simple为false，指定是否生成动态修改语句（只修改对象中不为空的属性）; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 13，modelType：参考context元素的defaultModelType，相当于覆盖; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 14，delimitIdentifiers：参考tableName的解释，注意，默认的delimitIdentifiers是双引号，如果类似MYSQL这样的数据库，使用的是`（反引号，那么还需要设置context的beginningDelimiter和endingDelimiter属性） --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 15，delimitAllColumns：设置是否所有生成的SQL中的列名都使用标识符引起来。默认为false，delimitIdentifiers参考context的属性
</span></span></span><span class="line"><span class="cl"><span class="c">      注意，table里面很多参数都是对javaModelGenerator，context等元素的默认属性的一个复写; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;table</span> <span class="na">tableName=</span><span class="s">&#34;test_handbook&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 参考 javaModelGenerator 的 constructorBased属性 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;property name=&#34;constructorBased&#34; value=&#34;false&#34; /&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 默认为false，如果设置为true，在生成的SQL中，table名字不会加上catalog或schema; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;property name=&#34;ignoreQualifiersAtRuntime&#34; value=&#34;false&#34; /&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 参考 javaModelGenerator 的 immutable 属性 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;property name=&#34;immutable&#34; value=&#34;false&#34; /&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 指定是否只生成domain类，如果设置为true，只生成domain类，如果还配置了sqlMapGenerator，那么在mapper
</span></span></span><span class="line"><span class="cl"><span class="c">        XML文件中，只生成resultMap元素 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;property name=&#34;modelOnly&#34; value=&#34;false&#34; /&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 参考 javaModelGenerator 的 rootClass 属性 &lt;property name=&#34;rootClass&#34; value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 参考javaClientGenerator 的 rootInterface 属性 &lt;property name=&#34;rootInterface&#34;
</span></span></span><span class="line"><span class="cl"><span class="c">        value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 如果设置了runtimeCatalog，那么在生成的SQL中，使用该指定的catalog，而不是table元素上的catalog
</span></span></span><span class="line"><span class="cl"><span class="c">        &lt;property name=&#34;runtimeCatalog&#34; value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 如果设置了runtimeSchema，那么在生成的SQL中，使用该指定的schema，而不是table元素上的schema &lt;property
</span></span></span><span class="line"><span class="cl"><span class="c">        name=&#34;runtimeSchema&#34; value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 如果设置了runtimeTableName，那么在生成的SQL中，使用该指定的tablename，而不是table元素上的tablename
</span></span></span><span class="line"><span class="cl"><span class="c">        &lt;property name=&#34;runtimeTableName&#34; value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 注意，该属性只针对MyBatis3Simple有用; 如果选择的runtime是MyBatis3Simple，那么会生成一个SelectAll方法，如果指定了selectAllOrderByClause，那么会在该SQL中添加指定的这个order条件; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;property name=&#34;selectAllOrderByClause&#34; value=&#34;age desc,username
</span></span></span><span class="line"><span class="cl"><span class="c">        asc&#34; /&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 如果设置为true，生成的model类会直接使用column本身的名字，而不会再使用驼峰命名方法，比如BORN_DATE，生成的属性名字就是BORN_DATE,而不会是bornDate --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;useActualColumnNames&#34;</span> <span class="na">value=</span><span class="s">&#34;true&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- generatedKey用于生成生成主键的方法 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 如果设置了该元素，MBG会在生成的&lt;insert&gt;元素中生成一条正确的&lt;selectKey&gt;元素， --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 该元素可选 column:主键的列名; sqlStatement：要生成的selectKey语句， --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 有以下可选项： --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- Cloudscape:相当于selectKey的SQL为： VALUES IDENTITY_VAL_LOCAL() --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- DB2 :相当于selectKey的SQL为： VALUES IDENTITY_VAL_LOCAL() --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- DB2_MF :相当于selectKey的SQL为：SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- Derby :相当于selectKey的SQL为：VALUES IDENTITY_VAL_LOCAL() --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- HSQLDB :相当于selectKey的SQL为：CALL IDENTITY() --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- Informix :相当于selectKey的SQL为：select dbinfo(&#39;sqlca.sqlerrd1&#39;) from
</span></span></span><span class="line"><span class="cl"><span class="c">        systables where tabid=1 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- MySql :相当于selectKey的SQL为：SELECT LAST_INSERT_ID() --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- SqlServer :相当于selectKey的SQL为：SELECT SCOPE_IDENTITY() --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- SYBASE :相当于selectKey的SQL为：SELECT @@IDENTITY --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- JDBC :相当于在生成的insert元素上添加useGeneratedKeys=&#34;true&#34;和keyProperty属性 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;generatedKey column=&#34;&#34; sqlStatement=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 该元素会在根据表中列名计算对象属性名之前先重命名列名，非常适合用于表中的列都有公用的前缀字符串的时候， --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 比如列名为：CUST_ID,CUST_NAME,CUST_EMAIL,CUST_ADDRESS等; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 那么就可以设置searchString为&#34;^CUST_&#34;，并使用空白替换，那么生成的Customer对象中的属性名称就不是 custId,custName等，而是先被替换为ID,NAME,EMAIL,然后变成属性：id，name，email; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 注意，MBG是使用java.util.regex.Matcher.replaceAll来替换searchString和replaceString的，
</span></span></span><span class="line"><span class="cl"><span class="c">        如果使用了columnOverride元素，该属性无效; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;columnRenamingRule searchString=&#34;&#34; replaceString=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 用来修改表中某个列的属性，MBG会使用修改后的列来生成domain的属性; column:要重新设置的列名; 注意，一个table元素中可以有多个columnOverride元素 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;columnOverride column=&#34;username&#34;&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 使用property属性来指定列要生成的属性名称 --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;property name=&#34;property&#34; value=&#34;userName&#34; /&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- javaType用于指定生成的domain的属性类型，使用类型的全限定名 &lt;property name=&#34;javaType&#34; value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- jdbcType用于指定该列的JDBC类型 &lt;property name=&#34;jdbcType&#34; value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- typeHandler 用于指定该列使用到的TypeHandler，如果要指定，配置类型处理器的全限定名 注意，mybatis中，不会生成到mybatis-config.xml中的typeHandler
</span></span></span><span class="line"><span class="cl"><span class="c">        只会生成类似：where id = #{id,jdbcType=BIGINT,typeHandler=com._520it.mybatis.MyTypeHandler}的参数描述
</span></span></span><span class="line"><span class="cl"><span class="c">        &lt;property name=&#34;jdbcType&#34; value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- 参考table元素的delimitAllColumns配置，默认为false &lt;property name=&#34;delimitedColumnName&#34;
</span></span></span><span class="line"><span class="cl"><span class="c">        value=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- &lt;/columnOverride&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c">&lt;!-- ignoreColumn设置一个MGB忽略的列，如果设置了改列，那么在生成的domain中，生成的SQL中，都不会有该列出现 column:指定要忽略的列的名字;
</span></span></span><span class="line"><span class="cl"><span class="c">        delimitedColumnName：参考table元素的delimitAllColumns配置，默认为false 注意，一个table元素中可以有多个ignoreColumn元素
</span></span></span><span class="line"><span class="cl"><span class="c">        &lt;ignoreColumn column=&#34;deptId&#34; delimitedColumnName=&#34;&#34;/&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/table&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/context&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/generatorConfiguration&gt;</span>
</span></span></code></pre></div><h3 id="相关的-properties-文件-generatorconfigproperties">相关的 properties 文件 <code>generatorConfig.properties</code></h3>
<p>generatorConfig.properties 要对应在 pom.xml 中的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;mybatis.generator.generatorConfig.properties&gt;</span>file:///${basedir}/src/test/resources/generatorConfig.properties<span class="nt">&lt;/mybatis.generator.generatorConfig.properties&gt;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"># 数据库驱动 jar 路径
</span></span><span class="line"><span class="cl">mbg.drive.class.path=/home/yifan/.m2/repository/mysql/mysql-connector-java/5.1.39/mysql-connector-java-5.1.39.jar
</span></span><span class="line"><span class="cl"># 数据库连接参数
</span></span><span class="line"><span class="cl">mbg.jdbc.driver=com.mysql.jdbc.Driver
</span></span><span class="line"><span class="cl">mbg.jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true<span class="err">&amp;</span>characterEncoding=utf-8
</span></span><span class="line"><span class="cl">mbg.jdbc.username=root
</span></span><span class="line"><span class="cl">mbg.jdbc.password=root
</span></span><span class="line"><span class="cl"># 包路径配置
</span></span><span class="line"><span class="cl">mbg.model.package=com.test.demo.mybatis
</span></span><span class="line"><span class="cl">mbg.dao.package=com.test.demo.mybatis
</span></span><span class="line"><span class="cl">mbg.xml.mapper.package=com.test.demo.mybatis
</span></span></code></pre></div><h3 id="生成代码-方法一eclipse-maven-运行">生成代码 方法一：Eclipse Maven 运行</h3>
<p>如果是在 Eclipse 中，选择 <code>pom.xml</code> 文件，右键选择：</p>
<p>Run AS &gt; Maven Build… &gt; 在 Goals 框中输入：<code>mybatis-generator:generate</code></p>
<p>在 <code>Console</code> 中可以看到日志。</p>
<h3 id="生成代码-方法二shell-运行">生成代码 方法二：Shell 运行</h3>
<p>在命令行输入 Maven 命令即可。注意：一定要在当前项目目录下运行该命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn mybatis-generator:generate
</span></span></code></pre></div><h2 id="eclipse-plugin-运行-mbg">Eclipse Plugin 运行 MBG</h2>
<p>详细见：<a href="http://www.mybatis.org/generator/running/runningWithEclipse.html">MyBatis Generator - Running MyBatis Generator with Eclipse</a></p>
<ol>
<li>如果使用这种方法，将不再依靠 Maven，<code>pom.xml</code> 中的配置将可以省去</li>
<li>加载 properties 文件需变化，自己一直没搞清楚 properties 的路径应该怎么写，后来变量就直接写在 <code>generatorConfig.xml</code> 中了。要是有明白的小伙伴，可以在下面留言</li>
<li>需要下载 plugin：Help &gt; Eclipse Marketplace&hellip; &gt; Search for &ldquo;MyBatis Generator&rdquo;。这个也是个局限，就是使用的人还需要下载 Eclipse plugin，所以推荐上面使用 Maven 的方法</li>
</ol>
<h2 id="references">References</h2>
<ul>
<li><a href="http://www.mybatis.org/generator/index.html">MyBatis Generator 官方文档</a></li>
<li><a href="http://mbg.cndocs.tk/index.html">MyBatis Generator 中文文档</a></li>
<li><a href="http://www.jianshu.com/p/e09d2370b796">MyBatis Generator 最完整配置详解 - 简书</a></li>
<li><a href="http://my.oschina.net/lilw/blog/168304">用 Maven 插件生成 MyBatis 代码 - 边城刀客的博客</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>SpringMVC 入门使用</title>
      <link>https://zyf.im/2016/09/07/springmvc-getting-started-tutorial/</link>
      <pubDate>Wed, 07 Sep 2016 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/09/07/springmvc-getting-started-tutorial/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 内容声明&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;本文写于 &lt;strong&gt;2016 年&lt;/strong&gt;，部分内容已过时，仅供历史参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XML 配置方式的 SpringMVC 已不再推荐&lt;/li&gt;
&lt;li&gt;web.xml 配置前端控制器的方式已过时&lt;/li&gt;
&lt;li&gt;JSP 视图技术已较少使用&lt;/li&gt;
&lt;li&gt;文中提及的 Struts2 框架已基本淘汰&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;当前最佳实践（2024+）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;项目创建&lt;/strong&gt;：使用 &lt;a href=&#34;https://start.spring.io/&#34;&gt;Spring Initializr&lt;/a&gt; 生成 Spring Boot 项目，自动配置 DispatcherServlet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;控制器&lt;/strong&gt;：使用 &lt;code&gt;@RestController&lt;/code&gt; 直接返回 JSON，无需 &lt;code&gt;@ResponseBody&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路由注解&lt;/strong&gt;：使用 &lt;code&gt;@GetMapping&lt;/code&gt;、&lt;code&gt;@PostMapping&lt;/code&gt; 等简化注解替代 &lt;code&gt;@RequestMapping&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数绑定&lt;/strong&gt;：MVC 基础概念仍适用，但推荐结合 Bean Validation（&lt;code&gt;@Valid&lt;/code&gt;）进行校验&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;视图层&lt;/strong&gt;：前后端分离（REST API + Vue/React），或使用 Thymeleaf 模板&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置方式&lt;/strong&gt;：Java Config + &lt;code&gt;application.yml&lt;/code&gt;，无需 XML 配置&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;本文主要参考了 &lt;a href=&#34;http://www.imooc.com/video/7237&#34;&gt;imooc-SpringMVC 起步&lt;/a&gt; 视频教程和 &lt;a href=&#34;http://www.imooc.com/article/3804&#34;&gt;SpringMVC 从入门到精通 系列 - HansonQ&lt;/a&gt; ，还有自己的一些总结。&lt;/p&gt;
&lt;p&gt;主要内容：MVC 简介、前端控制器模式、SpringMVC 基本概念、SpringMVC 配置、SpringMVC 中的注解、SpringMVC 数据绑定。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;mvc-简介&#34;&gt;MVC 简介&lt;/h2&gt;
&lt;p&gt;1、MVC 是一种架构模式&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>⚠️ 内容声明</strong></p>
<p>本文写于 <strong>2016 年</strong>，部分内容已过时，仅供历史参考：</p>
<ul>
<li>XML 配置方式的 SpringMVC 已不再推荐</li>
<li>web.xml 配置前端控制器的方式已过时</li>
<li>JSP 视图技术已较少使用</li>
<li>文中提及的 Struts2 框架已基本淘汰</li>
</ul>
<p><strong>当前最佳实践（2024+）：</strong></p>
<ul>
<li><strong>项目创建</strong>：使用 <a href="https://start.spring.io/">Spring Initializr</a> 生成 Spring Boot 项目，自动配置 DispatcherServlet</li>
<li><strong>控制器</strong>：使用 <code>@RestController</code> 直接返回 JSON，无需 <code>@ResponseBody</code></li>
<li><strong>路由注解</strong>：使用 <code>@GetMapping</code>、<code>@PostMapping</code> 等简化注解替代 <code>@RequestMapping</code></li>
<li><strong>参数绑定</strong>：MVC 基础概念仍适用，但推荐结合 Bean Validation（<code>@Valid</code>）进行校验</li>
<li><strong>视图层</strong>：前后端分离（REST API + Vue/React），或使用 Thymeleaf 模板</li>
<li><strong>配置方式</strong>：Java Config + <code>application.yml</code>，无需 XML 配置</li>
</ul>
</blockquote>
<hr>
<p>本文主要参考了 <a href="http://www.imooc.com/video/7237">imooc-SpringMVC 起步</a> 视频教程和 <a href="http://www.imooc.com/article/3804">SpringMVC 从入门到精通 系列 - HansonQ</a> ，还有自己的一些总结。</p>
<p>主要内容：MVC 简介、前端控制器模式、SpringMVC 基本概念、SpringMVC 配置、SpringMVC 中的注解、SpringMVC 数据绑定。</p>
<!-- more -->
<h2 id="mvc-简介">MVC 简介</h2>
<p>1、MVC 是一种架构模式</p>
<p>程序分层，分工合作，既相互独立，又协同工作，分为三层：模型层、视图层和控制层</p>
<p>2、MVC 是一种思考方式</p>
<ul>
<li>View：视图层，为用户提供 UI，重点关注数据的呈现，为用户提供界面</li>
<li>Model：模型层，业务数据的信息表示，关注支撑业务的信息构成，通常是多个业务实体的组合</li>
<li>Controller：控制层，调用业务逻辑产生合适的数据（Model），传递数据给视图用于呈现</li>
</ul>
<p>MVC 设计模式在 B/S 下的应用：</p>
<p><img alt="160907-springmvc-getting-started-tutorial-mvc" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199601-1cfdee80-8654-11ea-8328-9d74c174b2bc.gif"></p>
<p>①：浏览器发送请求到控制器(这里要知道控制器的作用)
②：控制器不能处理请求必须交给模型层来处理接着去访问数据库
③：模型层将处理好的结果返回给控制层
④：控制层将逻辑视图响应给浏览器(浏览器显示的是渲染过的视图)</p>
<p>MVC 本质：<strong>MVC 的核心思想是业务数据抽取同业务数据呈现相分离；分离有利于程序简化，方便编程</strong></p>
<h2 id="前端控制器模式">前端控制器模式</h2>
<p>前端控制器模式（Front Controller Pattern）是用来提供一个集中的请求处理机制，所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志，或者跟踪请求，然后把请求传给相应的处理程序。</p>
<ul>
<li>前端控制器（Front Controller）- 处理应用程序所有类型请求的单个处理程序，应用程序可以是基于 web 的应用程序，也可以是基于桌面的应用程序。</li>
<li>调度器（Dispatcher） - 前端控制器可能使用一个调度器对象来调度请求到相应的具体处理程序。</li>
<li>视图（View） - 视图是为请求而创建的对象。</li>
</ul>
<p>前端控制器的主要作用：</p>
<ul>
<li>指前端控制器将我们的请求分发给我们的控制器去生成业务数据</li>
<li>将生成的业务数据分发给恰当的视图模版来生成最终的视图界面</li>
</ul>
<p><img alt="160907-springmvc-getting-started-tutorial-front-controller" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80201864-63a11800-8657-11ea-91cb-2b2a2f55f6a6.jpg"></p>
<h2 id="springmvc-基本概念">SpringMVC 基本概念</h2>
<p><img alt="160907-springmvc-getting-started-tutorial-springmvc01" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199605-1d968500-8654-11ea-8f05-57d4685934bb.jpg"></p>
<p>对组件说明：</p>
<ol>
<li>DispatherServlet：前端控制器 用户请求到达前端控制器，相当于 MVC 中的 C，而 DispatherServlet 是整个流程的核心，它来调用其他组件来处理用户的请求，前端控制器的存在降低了其他组件之间的耦合度。</li>
<li>HandlerMapping：处理器映射器 它的作用就好比去看电影要拿着电影票根据电影票上面的座位号找到座位其中座位就是 Handler，电影票以及上面的座位号就是 URL HandlerMapping 负责根据用户请求找到 Handler 即处理器，SpringMVC 提供了不同的映射器实现不同的映射方式，例如：配置文件方式，实现接口方式，注解方式等。</li>
<li>Handler：处理器 Handler 是后端控制器，在前端控制器的控制下后端控制器对具体的用户请求进行处理，Handler 涉及到具体的用户业务请求，所以一般情况下需要程序员根据业务需求开发。</li>
<li>HandlerAdapter：处理器适配器 通过 HandlerAdapter 对处理器进行执行，这是适配器模式的应用，通过适配器可以对更多类型的处理器进行执行。播放的电影是 3D 的你看不清楚，因此电影院跟你说你要想看清电影就必须戴 3D 眼镜。也就是说 Handler 满足一定的要求才可以被执行。</li>
<li>ViewResolver：视图解析器 ViewResolver 负责将处理结果生成 View 视图，ViewResolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址，再生成 View 视图对象，最后对 View 进行渲染将处理结果通过页面展示给用户。</li>
</ol>
<p><img alt="160907-springmvc-getting-started-tutorial-springmvc02" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199609-1d968500-8654-11ea-997b-67a5862bfd25.jpg"></p>
<p>工作原理解释说明：
1、用户发送请求到 SpringMVC 框架提供的 DispatcherServlet 这个前端控制器（了解 struts2 的朋友也都知道其实 struts2 也有一个前端控制器 web.xml 中的 filter 标签就是）。
2、前端控制器会去找处理器映射器（HandlerMapping），处理器映射器根据请求 url 找到具体的处理器，生成处理器对象及处理器拦截器（如果有则生成）一并返回给 DispatcherServlet 。
3、根据处理器映射器返回的处理器，DispatcherServlet 会找“合适”的处理器适配器（HandlerAdapter）
4、处理器适配器 HandlerAdpater 会去执行处理器（Handler 开发的时候会被叫成 Controller 也叫后端控制器在 struts2 中 action 也是一个后端控制器）执行之前会有转换器、数据绑定、校验器等等完成上面这些才会去正在执行 Handler
5、后端控制器 Handler 执行完成之后返回一个 ModelAndView 对象 。
6、处理器适配器 HandlerAdpater 会将这个 ModelAndView 返回前端控制器 DispatcherServlet。前端控制器会将 ModelAndView 对象交给视图解析器 ViewResolver。
7、视图解析器 ViewResolver 解析 ModelAndView 对象之后返回逻辑视图。
8、前端控制器 DispatcherServlet 对逻辑视图进行渲染（数据填充）之后返回真正的物理 View 并响应给浏览器。</p>
<p><img alt="160907-springmvc-getting-started-tutorial-springmvc03" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199612-1e2f1b80-8654-11ea-9245-65a4470bc5f5.jpg"></p>
<h2 id="springmvc-配置">SpringMVC 配置</h2>
<p>1、前端控制器需要在 web.xml 中配置</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="c">&lt;!-- 配置前端控制器 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;servlet&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;servlet-name&gt;</span>web-dispatcher<span class="nt">&lt;/servlet-name&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;servlet-class&gt;</span>org.springframework.web.servlet.DispatcherServlet<span class="nt">&lt;/servlet-class&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!--加载前端控制器配置文件 上下文配置位置--&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;init-param&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- 备注：
</span></span></span><span class="line"><span class="cl"><span class="c">            contextConfigLocation：指定 SpringMVC 配置的加载位置，如果不指定则默认加载
</span></span></span><span class="line"><span class="cl"><span class="c">            WEB-INF/[DispatcherServlet 的 Servlet 名字]-servlet.xml
</span></span></span><span class="line"><span class="cl"><span class="c">          --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;param-name&gt;</span>contextConfigLocation<span class="nt">&lt;/param-name&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;param-value&gt;</span>classpath:spring/spring-*.xml<span class="nt">&lt;/param-value&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/init-param&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 表示随WEB服务器启动 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;load-on-startup&gt;</span>1<span class="nt">&lt;/load-on-startup&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/servlet&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;servlet-mapping&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;servlet-name&gt;</span>web-dispatcher<span class="nt">&lt;/servlet-name&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 备注：可以拦截三种请求
</span></span></span><span class="line"><span class="cl"><span class="c">        第一种：拦截固定后缀的url，比如设置为 *.do、*.action， 例如：/user/add.action 此方法最简单,不会导致静态资源（jpg,js,css）被拦截
</span></span></span><span class="line"><span class="cl"><span class="c">        第二种：拦截所有,设置为/，例如：/user/add  /user/add.action此方法可以实现REST风格的url,
</span></span></span><span class="line"><span class="cl"><span class="c">        很多互联网类型的应用使用这种方式.但是此方法会导致静态文件(jpg,js,css)被拦截后不能正常显示.需要特殊处理
</span></span></span><span class="line"><span class="cl"><span class="c">        第三种：拦截所有,设置为/*，此设置方法错误,因为请求到Action,当action转到jsp时再次被拦截,提示不能根据jsp路径mapping成功
</span></span></span><span class="line"><span class="cl"><span class="c">    --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 默认匹配所有的请求 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;url-pattern&gt;</span>/<span class="nt">&lt;/url-pattern&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/servlet-mapping&gt;</span>
</span></span></code></pre></div><p>2、在 <code>spring/spring-web.xml</code> 配置视图解析器</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="c">&lt;!-- 配置视图解析器 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- InternalResourceViewResolver：支持JSP视图解析 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- viewClass：JstlView 表示JSP模板页面需要使用JSTL标签库，所以classpath中必须包含jstl的相关jar包； --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- prefix 和 suffix：查找视图页面的前缀和后缀，最终视图的址为： --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 前缀+逻辑视图名+后缀，逻辑视图名需要在controller中返回ModelAndView指定，比如逻辑视图名为hello，--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 则最终返回的jsp视图地址 &#34;WEB-INF/jsp/hello.jsp&#34; --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;bean</span> <span class="na">class=</span><span class="s">&#34;org.springframework.web.servlet.view.InternalResourceViewResolver&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 决定视图类型，如果添加了jstl支持（即有jstl.jar），那么默认就是解析为jstl视图 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;viewClass&#34;</span> <span class="na">value=</span><span class="s">&#34;org.springframework.web.servlet.view.JstlView&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 视图前缀 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;prefix&#34;</span> <span class="na">value=</span><span class="s">&#34;/WEB-INF/jsp/&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 视图后缀 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;suffix&#34;</span> <span class="na">value=</span><span class="s">&#34;.jsp&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/bean&gt;</span>
</span></span></code></pre></div><p>3、在 <code>spring/spring-web.xml</code> 配置 注解模式</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="c">&lt;!-- 自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter， --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 可用在xml配置文件中使用&lt;mvc:annotation-driven&gt;替代注解处理器和适配器的配置。 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;mvc:annotation-driven/&gt;</span>
</span></span></code></pre></div><p>4、在 <code>spring/spring-web.xml</code> 配置 扫描 web 相关的 bean</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="c">&lt;!-- 组件扫描器：可以扫描 @Controller、@Service、@Repository 等等 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;context:component-scan</span> <span class="na">base-package=</span><span class="s">&#34;com.controller&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><h2 id="springmvc-中的注解">SpringMVC 中的注解</h2>
<h3 id="controller"><code>@Controller</code></h3>
<p>@Controller 注解，用于标识这个类是一个后端控制器（类似 struts 中的 action），主要作用就是接受页面的参数，转发页面。
@Controller 源码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Target</span><span class="p">({</span><span class="n">ElementType</span><span class="p">.</span><span class="na">TYPE</span><span class="p">})</span><span class="w"> </span><span class="c1">// 表明只能定义在类上面</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Retention</span><span class="p">(</span><span class="n">RetentionPolicy</span><span class="p">.</span><span class="na">RUNTIME</span><span class="p">)</span><span class="w"> </span><span class="c1">//保留策略是RUNTIME，在JVM加载类时，会把注解加载到JVM内存中（它是唯一可以用反射来读取注解的策略）</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Documented</span><span class="w"> </span><span class="c1">//@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API，因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解，没有成员。</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Component</span><span class="w"> </span><span class="c1">//spring框架规定当一个类不好归类（service、dao、controller）的时候可以使用这个注解，由此可见即便好归类内部还是使用的@Component注解</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="nd">@interface</span><span class="w"> </span><span class="n">Controller</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">    * The value may indicate a suggestion for a logical component name,
</span></span></span><span class="line"><span class="cl"><span class="cm">    * to be turned into a Spring bean in case of an autodetected component.
</span></span></span><span class="line"><span class="cl"><span class="cm">    * @return the suggested component name, if any
</span></span></span><span class="line"><span class="cl"><span class="cm">    */</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">String</span><span class="w"> </span><span class="nf">value</span><span class="p">()</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="requestmapping"><code>@RequestMapping</code></h3>
<p>这个注解的作用目标就跟 @Controller 不一样了，这个注解可以定义在类上面也可以定义在方法上面。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">* 1.@RequestMapping：除了修饰方法,还可以修饰类
</span></span></span><span class="line"><span class="cl"><span class="cm">* 2.类定义处：提供初步的请求信息映射.相对于WEB应用的根目录(窄化请求)
</span></span></span><span class="line"><span class="cl"><span class="cm">* 3.方法处：提供进一步的细分映射信息。相对于类定义处的URL。
</span></span></span><span class="line"><span class="cl"><span class="cm">*      若类定义处为标注@RequestMapping,则方法出的URL相对于WEB应用的根目录
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Target</span><span class="p">({</span><span class="n">ElementType</span><span class="p">.</span><span class="na">METHOD</span><span class="p">,</span><span class="w"> </span><span class="n">ElementType</span><span class="p">.</span><span class="na">TYPE</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Retention</span><span class="p">(</span><span class="n">RetentionPolicy</span><span class="p">.</span><span class="na">RUNTIME</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Documented</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Mapping</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="nd">@interface</span><span class="w"> </span><span class="n">RequestMapping</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="nf">value</span><span class="p">()</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="p">{};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">RequestMethod</span><span class="o">[]</span><span class="w"> </span><span class="nf">method</span><span class="p">()</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="p">{};</span><span class="w"> </span><span class="c1">//限制请求方式</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="nf">params</span><span class="p">()</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="p">{};</span><span class="w"> </span><span class="c1">//要求请求的URL包含指定的参数</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>代码实例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Controller</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@RequestMapping</span><span class="p">(</span><span class="s">&#34;/demo&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">IndexController</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nd">@RequestMapping</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;/test&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RequestMethod</span><span class="p">.</span><span class="na">GET</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">index</span><span class="p">(</span><span class="n">Model</span><span class="w"> </span><span class="n">model</span><span class="p">,</span><span class="w"> </span><span class="n">HttpServletRequest</span><span class="w"> </span><span class="n">request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 在游览器访问 http://localhost:8080/demo/test 将进入这里</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;originURL&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerName&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;index&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;index&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>@RequestMapping 还支持 Ant 方格的请求</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">?：匹配文件中的一个字符
</span></span><span class="line"><span class="cl">*：匹配文件中任意字符
</span></span><span class="line"><span class="cl">**：**匹配多层路径
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/user/*/createUser : 匹配 -/user/aa/createUser 或者 -/user/aa/createUser
</span></span><span class="line"><span class="cl">/user/**/createUser : 匹配 -/user/aa/createUser 或者 -/user/createUser 或者 -/user/aa/cc/createUser
</span></span><span class="line"><span class="cl">/user/createUser?? : 匹配 -/user/aa/createUseraa
</span></span></code></pre></div><h3 id="pathvariable"><code>@PathVariable</code></h3>
<p>@PathVariable 这个注解支持现在当下较为流行的 Restful 风格的 URL。 先说说这个注解的作用，支持将 url 中的占位符参数绑定到目标方法的参数上， 该功能也是 SpringMVC 实现 Restful 风格 url 的重要措施。</p>
<p>代码实例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// http://localhost:8080/demo/sss</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@RequestMapping</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;/{slug:.+}&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RequestMethod</span><span class="p">.</span><span class="na">GET</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">index2</span><span class="p">(</span><span class="nd">@PathVariable</span><span class="p">(</span><span class="s">&#34;slug&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">slug</span><span class="p">,</span><span class="w"> </span><span class="n">Model</span><span class="w"> </span><span class="n">model</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">LOG</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">&#34;DemoController index2 slug  &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">slug</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c1">// common</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;originURL&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo/&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerName&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerMethod&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;index2&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;slug&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">slug</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">//slug = sss</span><span class="w">
</span></span></span></code></pre></div><p>我们熟悉的请求应该是 POST 和 GET 请求，这两个请求也是最常用的而实际上 HTTP1.1 请求还有 PUT、DELETE 等 8 种来表名请求的动作。</p>
<p>在 SpringMVC 中要实现 PUT 和 DELETE 请求需要在 web.xml 额外配置一个过滤器，这个过滤器的作用就是把 POST 请求变为 PUT 和 DELETE 请求。
<em>关于 Restful 的内容计划单独写。</em></p>
<h3 id="requestparam"><code>@RequestParam</code></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Target</span><span class="p">(</span><span class="n">ElementType</span><span class="p">.</span><span class="na">PARAMETER</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Retention</span><span class="p">(</span><span class="n">RetentionPolicy</span><span class="p">.</span><span class="na">RUNTIME</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Documented</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="nd">@interface</span><span class="w"> </span><span class="n">RequestParam</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">String</span><span class="w"> </span><span class="nf">value</span><span class="p">()</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">;</span><span class="c1">//值即为请求参数的参数名</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">required</span><span class="p">()</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="c1">//该参数是否是必须。默认值为true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">String</span><span class="w"> </span><span class="nf">defaultValue</span><span class="p">()</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">ValueConstants</span><span class="p">.</span><span class="na">DEFAULT_NONE</span><span class="p">;</span><span class="c1">//请求参数的默认值</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// http://localhost:8080/demo/para?slug=google</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@RequestMapping</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;/para&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RequestMethod</span><span class="p">.</span><span class="na">GET</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">index3</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;slug&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">slug</span><span class="p">,</span><span class="w"> </span><span class="n">Model</span><span class="w"> </span><span class="n">model</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;originURL&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo/&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerName&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerMethod&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;index3&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;slug&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">slug</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">slug</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">google</span><span class="w">
</span></span></span></code></pre></div><p>另外还有一点要提示一下，参数没有加这个注解也能映射成功，这是应为 SpringMVC 框架支持请求参数和目标方法参数一致的时候可以省略这个注解。</p>
<h3 id="responsebody"><code>@ResponseBody</code></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Annotation that indicates a method return value should be bound to the web
</span></span></span><span class="line"><span class="cl"><span class="cm"> * response body. Supported for annotated handler methods in Servlet environments.
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 这个注解指明一个方法的返回值应该绑定在 web response body 中，在 Servlet 环境中支持注解处理方法
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * &lt;p&gt;As of version 4.0 this annotation can also be added on the type level in
</span></span></span><span class="line"><span class="cl"><span class="cm"> * which case it is inherited and does not need to be added on the method level.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Target</span><span class="p">({</span><span class="n">ElementType</span><span class="p">.</span><span class="na">TYPE</span><span class="p">,</span><span class="w"> </span><span class="n">ElementType</span><span class="p">.</span><span class="na">METHOD</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Retention</span><span class="p">(</span><span class="n">RetentionPolicy</span><span class="p">.</span><span class="na">RUNTIME</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Documented</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="nd">@interface</span><span class="w"> </span><span class="n">ResponseBody</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>代码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// http://localhost:8080/demo/json</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@RequestMapping</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;/json&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RequestMethod</span><span class="p">.</span><span class="na">POST</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="nd">@ResponseBody</span><span class="w"> </span><span class="n">Domain</span><span class="w"> </span><span class="nf">index7</span><span class="p">(</span><span class="n">HttpServletRequest</span><span class="w"> </span><span class="n">request</span><span class="p">,</span><span class="w"> </span><span class="n">Model</span><span class="w"> </span><span class="n">model</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">LOG</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">&#34;DemoController demo index7&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;originURL&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo/&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerName&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerMethod&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;index7&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">Domain</span><span class="w"> </span><span class="n">domain</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Domain</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">domain</span><span class="p">.</span><span class="na">setDomain</span><span class="p">(</span><span class="s">&#34;gggoogle.com&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">domain</span><span class="p">.</span><span class="na">setId</span><span class="p">(</span><span class="n">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="n">domain</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="cm">/* response body
</span></span></span><span class="line"><span class="cl"><span class="cm">{
</span></span></span><span class="line"><span class="cl"><span class="cm">  &#34;id&#34;: 100,
</span></span></span><span class="line"><span class="cl"><span class="cm">  &#34;domain&#34;: &#34;gggoogle.com&#34;
</span></span></span><span class="line"><span class="cl"><span class="cm">}
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span><span class="w">
</span></span></span></code></pre></div><h2 id="springmvc-数据绑定">SpringMVC 数据绑定</h2>
<p>简单说一下场景：
对于一个注册页面有很多信息譬如：用户名、密码、确认密码、邮箱、手机、兴趣等等。这时候就会想能不能将这些个参数包装在一个对象中（POJO），用这个 POJO 来做目标方法的形参上面。</p>
<p>可以说的是 SpringMVC 是支持将 POJO 作为目标参数的。当然也是要遵循一些规则的，就是表单的 name 属性值要和 POJO 的属性值要一致。当然了，这样又会有一个新的疑问支不支持级联属性答案是支持的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Address</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">city</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Persion</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kd">private</span><span class="w"> </span><span class="n">Address</span><span class="w"> </span><span class="n">address</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">form</span> <span class="na">action</span><span class="o">=</span><span class="s">&#34;/demo/pojo&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  NAME:<span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;name&#34;</span> <span class="p">/&gt;</span> CITY:<span class="p">&lt;</span><span class="nt">input</span>
</span></span><span class="line"><span class="cl">    <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">name</span><span class="o">=</span><span class="s">&#34;address.city&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@RequestMapping</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;/pojo&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RequestMethod</span><span class="p">.</span><span class="na">POST</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">index4</span><span class="p">(</span><span class="n">Persion</span><span class="w"> </span><span class="n">persion</span><span class="p">,</span><span class="w"> </span><span class="n">Model</span><span class="w"> </span><span class="n">model</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="err">`</span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;originURL&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo/&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerName&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerMethod&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;index4&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;persion&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">persion</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">;</span><span class="err">`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="springmvc-使用-servlet-api">SpringMVC 使用 Servlet API</h3>
<p>可以使用 Servlet 原生的 API 作为目标方法的参数。具体支持以下类型：HttpServletRequest、HttpServletResponse、HttpSession、java.security.Principal、Locale、InputStream、OutputStream、Reader、Writer</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// http://localhost:8080/demo/req?slug=facebook</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@RequestMapping</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;/req&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">RequestMethod</span><span class="p">.</span><span class="na">GET</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">index5</span><span class="p">(</span><span class="n">HttpServletRequest</span><span class="w"> </span><span class="n">request</span><span class="p">,</span><span class="w"> </span><span class="n">Model</span><span class="w"> </span><span class="n">model</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">String</span><span class="w"> </span><span class="n">slug</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">request</span><span class="p">.</span><span class="na">getParameter</span><span class="p">(</span><span class="s">&#34;slug&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;originURL&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo/&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerName&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;controllerMethod&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;index5&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">model</span><span class="p">.</span><span class="na">addAttribute</span><span class="p">(</span><span class="s">&#34;slug&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">slug</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="http://www.imooc.com/video/7237">IMOOC-SpringMVC 起步</a></li>
<li><a href="http://www.imooc.com/article/3804">SpringMVC 从入门到精通 系列 - HansonQ</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>刚刚毕业的两个月小结</title>
      <link>https://zyf.im/2016/08/31/2016-08-report/</link>
      <pubDate>Wed, 31 Aug 2016 23:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/31/2016-08-report/</guid>
      <description>&lt;p&gt;走出校园已经两个月了，因为之前的暑期也没怎么在家待过，大一在中康、大二在腾骏、大三在大为，大四毕业也就是现在，所以也没有什么特别的感觉。可以说，这两个月也做了些事情，学了些东西的。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;从学校毕业，最直接的影响就是自己更加专注于计算机知识，不用再为学业担心。确定了先走技术的道路，也让自己不那么迷茫做什么。工作规律，自己开始读读书，才觉的读书是件有意思的事。也逼着自己常常写点东西，主要是觉的：写东西的时候自己会主动的思考，文笔练着练着也就能进步吧。&lt;/p&gt;
&lt;p&gt;（一）&lt;/p&gt;
&lt;p&gt;DMV 是毕业后自己第一个项目，全栈开发。初版两周上线，后有花了一周多的时间编写了答题记录的功能。其中的收获是：项目框架的搭建和前端入门知识。原来的项目搭建都不是自己做的，也一直觉的是一件很难的事情。也许就是因为没有做过才觉的难，做过之后也觉的不过如此。项目的页面不多，但都是自己一点点写的，HTML JS 原来也就是自己改改，没有完整写过，这次算是一次不错的锻炼。写前端的时候才发现现在的前端真的是日新月异 AngularJS React 好多好多自己都没有听过的东西，也是从 Mengqi 那里了解到了很多。可惜的是自己没有实际的用上这些，如有机会一定尝试。&lt;/p&gt;
&lt;p&gt;DMV 项目流量平平，因为有 Domain 类项目的工作，DMV 的维护就暂时放置了，还是心有不舍的。期间和 John 聊过一次关于产品的事基本总结为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;产品项目是会有失败的，有流量的项目才有维护的意义。&lt;/li&gt;
&lt;li&gt;大型项目只有成功与失败之分，没有中间项，失败将代价很大。&lt;/li&gt;
&lt;li&gt;不一定要留住用户。&lt;/li&gt;
&lt;li&gt;更小的成本更高的流量，高流量后就可以做很多的事。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（二）&lt;/p&gt;
&lt;p&gt;Domain 信息类项目，使用的架构和 DMV 一致，逻辑更加简单。自己主要做了数据整理，收获是关于 SQL 命令。SQL 就可以直接处理很多的事情。使用了 RMI 做数据同步，用反射写了一段程序，是一次有突破的尝试。
一次看到自己年初的简历，笑了。自己是真敢写，能写个 Hello World 就敢标成了解。从会用到原理，是接下要走的路。&lt;/p&gt;
&lt;p&gt;（三）&lt;/p&gt;
&lt;p&gt;最近在看设计模式和算法的书，一直在做 LeetCode 的题。设计模式没用过，逮到能用机会绝不错过。算法健脑，刚刚开始接触觉的挺有意思，对数据结构也是种了解。&lt;/p&gt;
&lt;p&gt;（四）&lt;/p&gt;
&lt;p&gt;再次参加舍友的婚礼，又个结婚了。大一奶了一口，现在成真。祝福 Lu&amp;amp;Feifei。&lt;/p&gt;
&lt;p&gt;（五）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;来说说程序员那无处安放的创造力&lt;/p&gt;
&lt;p&gt;有了锤子想找钉子是很正常的原始冲动，但我们必须认识到，创造力对于程序员这个职业来讲，是锦上添花的东西。如果你没有强大的工程能力，那么创造力也不过是无本之木。所以扎扎实实的把工程基础打好，这是最根本的。&lt;/p&gt;
&lt;p&gt;在此基础上，我比较推荐程序员采用内外两条线来培养自己。在公司内的项目上采取相对保守的策略，尽力把稳定性做到最好，培养出自己卓越的工程能力；然后在公司外的开源项目和自己的独立项目上，采用一些新的技术、实践一些新的想法、充分发挥自己的创造力，梦想还是要有的，对吧。&lt;/p&gt;
&lt;p&gt;这样做最明显的好处是，你可以了解到新技术和激进方案的优缺点，从而在进行方案选型时，有更多的依据；还有一个职业发展上的好处：如果不是主负责人，公司的项目往往不能代表你的能力；但独立项目却可以作为一个非常好的能力证明出现在你的简历里边。&lt;/p&gt;
&lt;p&gt;你可以是一个身怀绝技的手艺人，在自己家里你尝试各种手法各种风格的个人作品；但当你参与颐和园这种级别的工程时，好好的把自己负责的石头雕成总设计师要求的样子就好 —— 毕竟这个时代一个人已经很难负责整个项目了。这就是我所理解的程序员的工匠精神。&lt;/p&gt;
&lt;p&gt;摘自：&lt;a href=&#34;http://mp.weixin.qq.com/s?__biz=MzI5OTI5Njg2Mg==&amp;amp;mid=2247483667&amp;amp;idx=1&amp;amp;sn=d6e5953c7a7835148e3822b919b82416#rd&#34;&gt;程序员到底是一个什么职业？&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;（尾）&lt;/p&gt;
&lt;p&gt;现在自己能听见进去一些原来听不去的话了。我不认为自己是被同化了，也许是心中少了些恶意。&lt;/p&gt;
&lt;p&gt;状态不错，继续前进。&lt;/p&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>走出校园已经两个月了，因为之前的暑期也没怎么在家待过，大一在中康、大二在腾骏、大三在大为，大四毕业也就是现在，所以也没有什么特别的感觉。可以说，这两个月也做了些事情，学了些东西的。</p>
<!-- more -->
<p>从学校毕业，最直接的影响就是自己更加专注于计算机知识，不用再为学业担心。确定了先走技术的道路，也让自己不那么迷茫做什么。工作规律，自己开始读读书，才觉的读书是件有意思的事。也逼着自己常常写点东西，主要是觉的：写东西的时候自己会主动的思考，文笔练着练着也就能进步吧。</p>
<p>（一）</p>
<p>DMV 是毕业后自己第一个项目，全栈开发。初版两周上线，后有花了一周多的时间编写了答题记录的功能。其中的收获是：项目框架的搭建和前端入门知识。原来的项目搭建都不是自己做的，也一直觉的是一件很难的事情。也许就是因为没有做过才觉的难，做过之后也觉的不过如此。项目的页面不多，但都是自己一点点写的，HTML JS 原来也就是自己改改，没有完整写过，这次算是一次不错的锻炼。写前端的时候才发现现在的前端真的是日新月异 AngularJS React 好多好多自己都没有听过的东西，也是从 Mengqi 那里了解到了很多。可惜的是自己没有实际的用上这些，如有机会一定尝试。</p>
<p>DMV 项目流量平平，因为有 Domain 类项目的工作，DMV 的维护就暂时放置了，还是心有不舍的。期间和 John 聊过一次关于产品的事基本总结为：</p>
<ul>
<li>产品项目是会有失败的，有流量的项目才有维护的意义。</li>
<li>大型项目只有成功与失败之分，没有中间项，失败将代价很大。</li>
<li>不一定要留住用户。</li>
<li>更小的成本更高的流量，高流量后就可以做很多的事。</li>
</ul>
<p>（二）</p>
<p>Domain 信息类项目，使用的架构和 DMV 一致，逻辑更加简单。自己主要做了数据整理，收获是关于 SQL 命令。SQL 就可以直接处理很多的事情。使用了 RMI 做数据同步，用反射写了一段程序，是一次有突破的尝试。
一次看到自己年初的简历，笑了。自己是真敢写，能写个 Hello World 就敢标成了解。从会用到原理，是接下要走的路。</p>
<p>（三）</p>
<p>最近在看设计模式和算法的书，一直在做 LeetCode 的题。设计模式没用过，逮到能用机会绝不错过。算法健脑，刚刚开始接触觉的挺有意思，对数据结构也是种了解。</p>
<p>（四）</p>
<p>再次参加舍友的婚礼，又个结婚了。大一奶了一口，现在成真。祝福 Lu&amp;Feifei。</p>
<p>（五）</p>
<blockquote>
<p>来说说程序员那无处安放的创造力</p>
<p>有了锤子想找钉子是很正常的原始冲动，但我们必须认识到，创造力对于程序员这个职业来讲，是锦上添花的东西。如果你没有强大的工程能力，那么创造力也不过是无本之木。所以扎扎实实的把工程基础打好，这是最根本的。</p>
<p>在此基础上，我比较推荐程序员采用内外两条线来培养自己。在公司内的项目上采取相对保守的策略，尽力把稳定性做到最好，培养出自己卓越的工程能力；然后在公司外的开源项目和自己的独立项目上，采用一些新的技术、实践一些新的想法、充分发挥自己的创造力，梦想还是要有的，对吧。</p>
<p>这样做最明显的好处是，你可以了解到新技术和激进方案的优缺点，从而在进行方案选型时，有更多的依据；还有一个职业发展上的好处：如果不是主负责人，公司的项目往往不能代表你的能力；但独立项目却可以作为一个非常好的能力证明出现在你的简历里边。</p>
<p>你可以是一个身怀绝技的手艺人，在自己家里你尝试各种手法各种风格的个人作品；但当你参与颐和园这种级别的工程时，好好的把自己负责的石头雕成总设计师要求的样子就好 —— 毕竟这个时代一个人已经很难负责整个项目了。这就是我所理解的程序员的工匠精神。</p>
<p>摘自：<a href="http://mp.weixin.qq.com/s?__biz=MzI5OTI5Njg2Mg==&amp;mid=2247483667&amp;idx=1&amp;sn=d6e5953c7a7835148e3822b919b82416#rd">程序员到底是一个什么职业？</a></p>
</blockquote>
<p>（尾）</p>
<p>现在自己能听见进去一些原来听不去的话了。我不认为自己是被同化了，也许是心中少了些恶意。</p>
<p>状态不错，继续前进。</p>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Number of 1 Bits 191</title>
      <link>https://zyf.im/2016/08/31/leetcode-number-of-1-bits-191/</link>
      <pubDate>Wed, 31 Aug 2016 13:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/31/leetcode-number-of-1-bits-191/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/number-of-1-bits/&#34;&gt;191. Number of 1 Bits&lt;/a&gt;
  Write a function that takes an unsigned integer and returns the number of ’1&amp;rsquo; bits it has (also known as the Hamming weight).
  For example, the 32-bit integer ’11&amp;rsquo; has binary representation &lt;code&gt;00000000000000000000000000001011&lt;/code&gt;, so the function should return 3.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;hr&gt;
&lt;h3 id=&#34;大体意思&#34;&gt;大体意思&lt;/h3&gt;
&lt;p&gt;写一个函数，输入一个无符号整数，返回其中值为 1 的比特位的个数（这个值也被称为数字汉明重量）&lt;/p&gt;
&lt;h3 id=&#34;自己的思路&#34;&gt;自己的思路&lt;/h3&gt;
&lt;p&gt;循环判断最后一位是否是 &lt;code&gt;1&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;hammingWeight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 Submit Solution&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/number-of-1-bits/">191. Number of 1 Bits</a>
  Write a function that takes an unsigned integer and returns the number of ’1&rsquo; bits it has (also known as the Hamming weight).
  For example, the 32-bit integer ’11&rsquo; has binary representation <code>00000000000000000000000000001011</code>, so the function should return 3.</p>
<!-- more -->
<hr>
<h3 id="大体意思">大体意思</h3>
<p>写一个函数，输入一个无符号整数，返回其中值为 1 的比特位的个数（这个值也被称为数字汉明重量）</p>
<h3 id="自己的思路">自己的思路</h3>
<p>循环判断最后一位是否是 <code>1</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">hammingWeight</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="p">((</span><span class="n">n</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="n">result</span><span class="o">++</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">result</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>在 Submit Solution</p>
<pre tabindex="0"><code>Submission Result: Time Limit Exceeded
Last executed input: 2147483648 (10000000000000000000000000000000)
</code></pre><h3 id="别人的算法">别人的算法</h3>
<p>2147483648 输入超过了 Java 语言中整型的上限</p>
<p>原来问题就出在输入可能是无符号数字上！当输入为 2147483648 时，Java 会将这个数字判断位 -1，而右移符号 &raquo; 在计算时会保持数字的符号位，即正数右移高位补 0，负数右移高位补 1。使用这种规则进行右移，会导致数字在右移过程中被不断补 1，这样循环永远无法停止！因此，如果输入为负数，也应该保持右移时高位补 0，位运算符 &raquo;&gt; 可以帮助我们解决这个问题。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">hammingWeight</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">counter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">2</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">counter</span><span class="o">++</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;&gt;&gt;</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">counter</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><ol>
<li><code>&gt;&gt;</code> 是带符号右移，负数高位补 1，正数补 0</li>
<li><code>&lt;&lt;</code> 左移不管负数还是正数，在低位永远补 0</li>
<li><code>&gt;&gt;&gt;</code> 是不带符号右移，不论负数还是正数，高位补 0</li>
</ol>
<blockquote>
<p>引用：<a href="http://my.oschina.net/Tsybius2014/blog/491381">LeetCode：Number of 1 Bits-整数的汉明重量-Tsybius2014</a></p>
</blockquote>
<h3 id="别人的算法-2">别人的算法 2</h3>
<ul>
<li>资料：<a href="http://www.cnblogs.com/xinsheng/p/3419202.html">Int 型数值存储</a></li>
<li>资料：<a href="http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html">原码, 反码, 补码</a></li>
</ul>
<p>补码：正数为其本身，负数为取反加一</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// you need to treat n as an unsigned value</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">hammingWeight</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Integer</span><span class="p">.</span><span class="na">toUnsignedLong</span><span class="p">(</span><span class="n">n</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(;</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span><span class="k">if</span><span class="w"> </span><span class="p">((</span><span class="n">l</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">++</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">count</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>所以在 Java 中需要将 <code>int</code> 转换为 <code>unsigned int</code> 的时候，可以将 <code>int</code> 转换到容量更大的 <code>long</code> 中就行了，直接 <code>((long) x) &amp; 0xffffffffL</code> 或者使用函数 <code>Integer.toUnsignedLong()</code>。</p>
<blockquote>
<p>引用：<a href="https://github.com/nekocode/leetcode-solutions/blob/master/solutions/191.%20Number%20of%201%20Bits.md">nekocode/leetcode-solutions-191. Number of 1 Bits</a></p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Happy Number 202</title>
      <link>https://zyf.im/2016/08/29/leetcode-happy-number-202/</link>
      <pubDate>Mon, 29 Aug 2016 14:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/29/leetcode-happy-number-202/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/happy-number/&#34;&gt;202. Happy Number&lt;/a&gt;
  Write an algorithm to determine if a number is &amp;ldquo;happy&amp;rdquo;.
  A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/happy-number/">202. Happy Number</a>
  Write an algorithm to determine if a number is &ldquo;happy&rdquo;.
  A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.</p>
<!-- more -->
<p>  <strong>Example</strong>: 19 is a happy number</p>
<pre tabindex="0"><code>1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
</code></pre><hr>
<h3 id="大体意思">大体意思</h3>
<p>题目说的是取任意一个正整数，不断各个数位上数字的平方和，若最终收敛为 1，则该数字为<code>happy number</code>，否则程序可能从某个数开始陷入循环。</p>
<h3 id="自己的思路">自己的思路</h3>
<p>关键：通过 <code>/</code> 和 <code>%</code> 取 int 各个位的数字；用 <code>set</code> 判重复</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">isHappy</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Set</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">HashSet</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">int</span><span class="w"> </span><span class="n">newN</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">do</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">do</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="kt">int</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">10</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">10</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="n">newN</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">Math</span><span class="p">.</span><span class="na">pow</span><span class="p">(</span><span class="n">end</span><span class="p">,</span><span class="w"> </span><span class="n">2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">newN</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">newN</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="na">contains</span><span class="p">(</span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">result</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">n</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="kc">true</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>套路：取各个位数字</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">do</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">int</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">10</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">10</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Roman to Integer 13</title>
      <link>https://zyf.im/2016/08/25/leetcode-roman-to-integer-13/</link>
      <pubDate>Thu, 25 Aug 2016 10:30:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/25/leetcode-roman-to-integer-13/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/roman-to-integer/&#34;&gt;13. Roman to Integer&lt;/a&gt;
  Given a roman numeral, convert it to an integer.
  Input is guaranteed to be within the range from 1 to 3999.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97&#34;&gt;wikipedia-罗马数字&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;罗马数字共有 7 个，即 I（1）、V（5）、X（10）、L（50）、C（100）、D（500）和 M（1000）。按照下述的规则可以表示任意正整数。需要注意的是罗马数字中没有“0”，与进位制无关。一般认为罗马数字只用来记数，而不作演算。&lt;/p&gt;
&lt;p&gt;重复数次：一个罗马数字重复几次，就表示这个数的几倍。&lt;/p&gt;
&lt;p&gt;右加左减：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在较大的罗马数字的右边记上较小的罗马数字，表示大数字加小数字。&lt;/li&gt;
&lt;li&gt;在较大的罗马数字的左边记上较小的罗马数字，表示大数字减小数字。&lt;/li&gt;
&lt;li&gt;左减的数字有限制，仅限于 I、X、C。比如 45 不可以写成 VL，只能是 XLV&lt;/li&gt;
&lt;li&gt;但是，左减时不可跨越一个位值。比如，99 不可以用 IC（100-1）表示，而是用 XCIX（[100-10]+[10-1]）表示。（等同于阿拉伯数字每位数字分别表示。）&lt;/li&gt;
&lt;li&gt;左减数字必须为一位，比如 8 写成 VIII，而非 IIX。&lt;/li&gt;
&lt;li&gt;右加数字不可连续超过三位，比如 14 写成 XIV，而非 XIIII。（见下方“数码限制”一项。）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;加线乘千：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在罗马数字的上方加上一条横线或者加上下标的 Ⅿ，表示将这个数乘以 1000，即是原数的 1000 倍。&lt;/li&gt;
&lt;li&gt;同理，如果上方有两条横线，即是原数的 1000000（1000^{{2}}）倍。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;数码限制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同一数码最多只能连续出现三次，如 40 不可表示为 XXXX，而要表示为 XL。&lt;/li&gt;
&lt;li&gt;例外：由于 IV 是古罗马神话主神朱庇特（即 IVPITER，古罗马字母里没有 J 和 U）的首字，因此有时用 IIII 代替 IV。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;自己的解法&#34;&gt;自己的解法&lt;/h3&gt;
&lt;p&gt;做一个数组对照的字典，遵守此条规定：在较大的罗马数字的左边记上较小的罗马数字，表示大数字减小数字。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/roman-to-integer/">13. Roman to Integer</a>
  Given a roman numeral, convert it to an integer.
  Input is guaranteed to be within the range from 1 to 3999.</p>
<!-- more -->
<blockquote>
<p><a href="https://zh.wikipedia.org/wiki/%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97">wikipedia-罗马数字</a></p>
</blockquote>
<p>罗马数字共有 7 个，即 I（1）、V（5）、X（10）、L（50）、C（100）、D（500）和 M（1000）。按照下述的规则可以表示任意正整数。需要注意的是罗马数字中没有“0”，与进位制无关。一般认为罗马数字只用来记数，而不作演算。</p>
<p>重复数次：一个罗马数字重复几次，就表示这个数的几倍。</p>
<p>右加左减：</p>
<ul>
<li>在较大的罗马数字的右边记上较小的罗马数字，表示大数字加小数字。</li>
<li>在较大的罗马数字的左边记上较小的罗马数字，表示大数字减小数字。</li>
<li>左减的数字有限制，仅限于 I、X、C。比如 45 不可以写成 VL，只能是 XLV</li>
<li>但是，左减时不可跨越一个位值。比如，99 不可以用 IC（100-1）表示，而是用 XCIX（[100-10]+[10-1]）表示。（等同于阿拉伯数字每位数字分别表示。）</li>
<li>左减数字必须为一位，比如 8 写成 VIII，而非 IIX。</li>
<li>右加数字不可连续超过三位，比如 14 写成 XIV，而非 XIIII。（见下方“数码限制”一项。）</li>
</ul>
<p>加线乘千：</p>
<ul>
<li>在罗马数字的上方加上一条横线或者加上下标的 Ⅿ，表示将这个数乘以 1000，即是原数的 1000 倍。</li>
<li>同理，如果上方有两条横线，即是原数的 1000000（1000^{{2}}）倍。</li>
</ul>
<p>数码限制：</p>
<ul>
<li>同一数码最多只能连续出现三次，如 40 不可表示为 XXXX，而要表示为 XL。</li>
<li>例外：由于 IV 是古罗马神话主神朱庇特（即 IVPITER，古罗马字母里没有 J 和 U）的首字，因此有时用 IIII 代替 IV。</li>
</ul>
<h3 id="自己的解法">自己的解法</h3>
<p>做一个数组对照的字典，遵守此条规定：在较大的罗马数字的左边记上较小的罗马数字，表示大数字减小数字。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">romanToInt</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">char</span><span class="o">[]</span><span class="w"> </span><span class="n">chr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="na">toCharArray</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Integer</span><span class="o">&gt;</span><span class="w"> </span><span class="n">map</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">HashMap</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">map</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;I&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">1</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">map</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;V&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">5</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">map</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;X&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">10</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">map</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;L&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">50</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">map</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;C&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">100</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">map</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;D&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">500</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">map</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;M&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">1000</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">int</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">chr</span><span class="p">.</span><span class="na">length</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="kt">int</span><span class="w"> </span><span class="n">nowInt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">String</span><span class="p">.</span><span class="na">valueOf</span><span class="p">(</span><span class="n">chr</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="kt">int</span><span class="w"> </span><span class="n">nextInt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">String</span><span class="p">.</span><span class="na">valueOf</span><span class="p">(</span><span class="n">chr</span><span class="o">[</span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">1</span><span class="o">]</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">nowInt</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">nextInt</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">result</span><span class="w"> </span><span class="o">-=</span><span class="w"> </span><span class="n">nowInt</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">result</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">nowInt</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">result</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">map</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">String</span><span class="p">.</span><span class="na">valueOf</span><span class="p">(</span><span class="n">chr</span><span class="o">[</span><span class="n">chr</span><span class="p">.</span><span class="na">length</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">1</span><span class="o">]</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="n">result</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Majority Element 169</title>
      <link>https://zyf.im/2016/08/24/leetcode-majority-element-169/</link>
      <pubDate>Wed, 24 Aug 2016 09:30:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/24/leetcode-majority-element-169/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/majority-element/&#34;&gt;169. Majority Element&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Given an array of size n, find the majority element. The majority element is the element that appears more than [ n/2 ] times.&lt;/p&gt;
&lt;p&gt;You may assume that the array is non-empty and the majority element always exist in the array.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h3 id=&#34;题目大意&#34;&gt;题目大意&lt;/h3&gt;
&lt;p&gt;给定一个数组，找这个数组中的主元素，主元素是元素出现次数大于⌊ n/2 ⌋的元素。
假设给定的数组非空，主元素都存在。&lt;/p&gt;
&lt;h3 id=&#34;自己的解法&#34;&gt;自己的解法&lt;/h3&gt;
&lt;p&gt;将数组进行排序，根据题意，那么中间的这个数就是 majority element&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;majorityElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Arrays&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;别人的解法&#34;&gt;别人的解法&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;http://blog.csdn.net/DERRANTCM/article/details/47902549&#34;&gt;【LeetCode-面试算法经典-Java 实现】【169-Majority Element（主元素）】&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/majority-element/">169. Majority Element</a></p>
<p>Given an array of size n, find the majority element. The majority element is the element that appears more than [ n/2 ] times.</p>
<p>You may assume that the array is non-empty and the majority element always exist in the array.</p>
<!-- more -->
<h3 id="题目大意">题目大意</h3>
<p>给定一个数组，找这个数组中的主元素，主元素是元素出现次数大于⌊ n/2 ⌋的元素。
假设给定的数组非空，主元素都存在。</p>
<h3 id="自己的解法">自己的解法</h3>
<p>将数组进行排序，根据题意，那么中间的这个数就是 majority element</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">majorityElement</span><span class="p">(</span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">nums</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Arrays</span><span class="p">.</span><span class="na">sort</span><span class="p">(</span><span class="n">nums</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">nums</span><span class="o">[</span><span class="n">nums</span><span class="p">.</span><span class="na">length</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">2</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="别人的解法">别人的解法</h3>
<p><a href="http://blog.csdn.net/DERRANTCM/article/details/47902549">【LeetCode-面试算法经典-Java 实现】【169-Majority Element（主元素）】</a></p>
<p>  用一个标记 cnt 记录某个元素出现的次数，如果后面的元素和它相同就加一，有一个元素和他不相同就减一，当 cnt 小于等于 0 时重新记录新的元素。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">majorityElement</span><span class="p">(</span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">nums</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">int</span><span class="w"> </span><span class="n">main</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">nums</span><span class="o">[</span><span class="n">0</span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="c1">// 用于记录主元素，假设第一个是主元素</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">int</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w"> </span><span class="c1">// 用于抵消数的个数</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">nums</span><span class="p">.</span><span class="na">length</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// 从第二个元素开始到最后一个元素</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// 如果两个数相同就不能抵消</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">count</span><span class="o">++</span><span class="p">;</span><span class="w"> </span><span class="c1">// 用于抵消的数据加1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">count</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// 如果不相同，并且有可以抵消的数</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="n">count</span><span class="o">--</span><span class="p">;</span><span class="w"> </span><span class="c1">// 进行数据抵消</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// 如果不相同，并且没有可以抵消的数</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="n">main</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="c1">// 记录最后不可以抵消的数</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 对于数组中可能没有主元素的情况，题中说明存在，此步可以省略。</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// count = 0;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// for (int a : nums) {</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// if (a == main) {</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// count++;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// }</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// }</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// if (count &gt;= nums.length / 2) {</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// return main;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// } else {</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// throw new RuntimeException(&#34;No majority element&#34;);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// }</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">main</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>LeetCode First Unique Character in a String 387</title>
      <link>https://zyf.im/2016/08/23/leetcode-first-unique-character-in-a-string-387/</link>
      <pubDate>Tue, 23 Aug 2016 14:30:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/23/leetcode-first-unique-character-in-a-string-387/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/first-unique-character-in-a-string/&#34;&gt;387. First Unique Character in a String&lt;/a&gt;
  Given a string, find the first non-repeating character in it and return it&amp;rsquo;s index. If it doesn&amp;rsquo;t exist, return -1.
  &lt;strong&gt;Examples:&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;s = &amp;#34;leetcode&amp;#34;
return 0.

s = &amp;#34;loveleetcode&amp;#34;,
return 2.
&lt;/code&gt;&lt;/pre&gt;&lt;!-- more --&gt;
&lt;p&gt;  &lt;strong&gt;Note:&lt;/strong&gt;
  You may assume the string contain only lowercase letters.&lt;/p&gt;
&lt;h3 id=&#34;自己的解法&#34;&gt;自己的解法&lt;/h3&gt;
&lt;p&gt;开始觉的遍历 char[] 然后判断在 String 的首位置和末位置，若一样就返回索引。呃，感觉有点偷鸡&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;firstUniqChar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;indexOf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;charAt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;charAt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查了下别人的，又是 &lt;code&gt;new int[26]&lt;/code&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/first-unique-character-in-a-string/">387. First Unique Character in a String</a>
  Given a string, find the first non-repeating character in it and return it&rsquo;s index. If it doesn&rsquo;t exist, return -1.
  <strong>Examples:</strong></p>
<pre tabindex="0"><code>s = &#34;leetcode&#34;
return 0.

s = &#34;loveleetcode&#34;,
return 2.
</code></pre><!-- more -->
<p>  <strong>Note:</strong>
  You may assume the string contain only lowercase letters.</p>
<h3 id="自己的解法">自己的解法</h3>
<p>开始觉的遍历 char[] 然后判断在 String 的首位置和末位置，若一样就返回索引。呃，感觉有点偷鸡</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">firstUniqChar</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="na">length</span><span class="p">();</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="na">indexOf</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="na">charAt</span><span class="p">(</span><span class="n">i</span><span class="p">))</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="na">lastIndexOf</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="na">charAt</span><span class="p">(</span><span class="n">i</span><span class="p">)))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="k">return</span><span class="w"> </span><span class="n">i</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="o">-</span><span class="n">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>查了下别人的，又是 <code>new int[26]</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">firstUniqChar</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">count</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="kt">int</span><span class="o">[</span><span class="n">26</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="na">length</span><span class="p">();</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">count</span><span class="o">[</span><span class="n">s</span><span class="p">.</span><span class="na">charAt</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="o">]++</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="na">length</span><span class="p">();</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">count</span><span class="o">[</span><span class="n">s</span><span class="p">.</span><span class="na">charAt</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="o">]</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="k">return</span><span class="w"> </span><span class="n">i</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="o">-</span><span class="n">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>但是在运行时间上，下面的方法更快</p>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Maximum Depth of Binary Tree 104</title>
      <link>https://zyf.im/2016/08/23/leetcode-maximum-depth-of-binary-tree-104/</link>
      <pubDate>Tue, 23 Aug 2016 11:30:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/23/leetcode-maximum-depth-of-binary-tree-104/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/maximum-depth-of-binary-tree/&#34;&gt;104. Maximum Depth of Binary Tree&lt;/a&gt;
  Given a binary tree, find its maximum depth.
  The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h3 id=&#34;自己的解法&#34;&gt;自己的解法&lt;/h3&gt;
&lt;p&gt;自己想到应该用递归、为空的返回 0，不为空的返回 1，递归累加；但是有两个点这么判断呢？其实很容易取两数字的 MAX&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;maxDepth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TreeNode&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;maxDepth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;left&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;maxDepth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;right&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;参考：
&lt;a href=&#34;https://jingjingshao.gitbooks.io/data-structure-and-algorithm-analysis/content/Tree/104_maximum_depth_of_binary_tree.html&#34;&gt;104 Maximum Depth of Binary Tree | Data Structure and algorithm analysis&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/maximum-depth-of-binary-tree/">104. Maximum Depth of Binary Tree</a>
  Given a binary tree, find its maximum depth.
  The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.</p>
<!-- more -->
<h3 id="自己的解法">自己的解法</h3>
<p>自己想到应该用递归、为空的返回 0，不为空的返回 1，递归累加；但是有两个点这么判断呢？其实很容易取两数字的 MAX</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">maxDepth</span><span class="p">(</span><span class="n">TreeNode</span><span class="w"> </span><span class="n">root</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">root</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">Math</span><span class="p">.</span><span class="na">max</span><span class="p">(</span><span class="n">maxDepth</span><span class="p">(</span><span class="n">root</span><span class="p">.</span><span class="na">left</span><span class="p">),</span><span class="w"> </span><span class="n">maxDepth</span><span class="p">(</span><span class="n">root</span><span class="p">.</span><span class="na">right</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>参考：
<a href="https://jingjingshao.gitbooks.io/data-structure-and-algorithm-analysis/content/Tree/104_maximum_depth_of_binary_tree.html">104 Maximum Depth of Binary Tree | Data Structure and algorithm analysis</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>【此生为完成】随笔</title>
      <link>https://zyf.im/2016/08/22/being-alive-is-a-gift-reading-notes/</link>
      <pubDate>Mon, 22 Aug 2016 23:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/22/being-alive-is-a-gift-reading-notes/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;我们要用多大的代价，才能认清活着的意义？
于娟，这个风华正茂的女子，拥有留洋经历和博士学位的复旦大学青年教师，在与晚期癌症抗争一年又四个月后，终于撒手人寰。她带走的家人的思念和不舍，给我们留下坚强的力量。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;《此生未完成》这本书是上周末聚会时 Zhen 提到的，上周京东买书就带上了。书里前半探讨作者在生死临界时对生活、工作、名利、家人、朋友的一些看法和思考。这对于刚刚毕业的我来说，确实有些是无法真切体会到的，毕竟经历少。但是在书的字里行间中，仍然可以看到一个（不知道用什么词）的女子。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;一&#34;&gt;（一）&lt;/h2&gt;
&lt;p&gt;疾病能让人有怎样的疼痛？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我还是没有哭，不是因为坚强，而是因为痛的想不起来哭，那个时候，只要能用尽全力顶着。如果稍微分神，我就会痛的晕厥。我不想家人看到我的痛苦。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;说来惭愧的一个对比，自己在喝多时有过这样的经历：自己抱着垃圾桶使劲顶着一股劲，要是有人来给自己说话，就会分神似的使劲的呕吐。可这蚀骨之痛要比这疼痛多少倍呢？&lt;/p&gt;
&lt;h2 id=&#34;二&#34;&gt;（二）&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;重要的是当他得知我生病消息后的第一反应，眼神表情乃至电话语气网络留言里端倪尽出，你会觉得世间很多人情世故是那么的让你淡然一笑。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在遇到大事的时候方显出真正的朋友和所谓的朋友，扬尘散土，洗沙留金。能遇到“大事”的机会不多，遇到的“大事”还能全身而退的机会就更少了。所以觉的自己应该及时去明辨所有的朋友，将真心给最真的，对于其他的就随他去吧。我不可能讨所有人喜欢，也用不着。&lt;/p&gt;
&lt;h2 id=&#34;三&#34;&gt;（三）&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;三十岁之前的努力更多是因为自己有着太多的欲望和执著，从没有“只要活着就好”的简单。名利权情，没有一样是不辛苦的，却没有一样可以带走。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这是一个作者很重要的一个观点。在现在的我看来，“只要活着就好”的活着，是一种精神上富有的活着，是充满智慧的活着。
就说现在的自己，什么都不曾拥有，或许不用拥有过的名、利，但是其他方面什么都没有，自己一切都是空的，看到好就会有强烈的“欲望”，如果再强烈一点就会有“执著”。在文字中我感受到了作者在精神方面的强大与富有，或许在这之后才能感受到在名、利之外更重要的东西。其实，自己真的很难想通，到底拥有什么自己就可以“苟”了，自己到底想要什么。了解自己真正想要什么真是一幸事。
作者提到的有时间，多多陪陪家人父母、有机会就尽孝，自己是很赞同的。&lt;/p&gt;
&lt;h2 id=&#34;四&#34;&gt;（四）&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;外公反映土豆每次经过，都趴在玻璃上看玻璃窗里的圣诞树、圣诞彩球和姜饼屋。土豆眼神里有点想，但却乖乖看着，并不讨。外公回想说：“那个眼神我想起了卖火柴的小女孩，有时候孩子听话，你却反而会心酸。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这文字碰触心灵最柔软的地方。&lt;/p&gt;
&lt;h2 id=&#34;五&#34;&gt;（五）&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;你的妈妈不是懦夫，所以你的人生里，遇到珍贵的人与关键的事，都要积极争取，可以失败，但是不能放弃。我想做一个让儿子骄傲的妈妈，只此一点，无论任何地步，我都不会选择自己走，哪怕，万劫不复的痛。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;六&#34;&gt;（六）&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;更多人不会明白，我们两个的谈笑深处埋藏着多少不能言表的无声叹息。上一次见面，我和梅两个事多么风华正茂，像展翅云霄的鹰隼，挥着翅膀相约下次的冲天。这次的相逢，是灰头土脸被命运按在尘土里依然微笑的土鸡之间的问候。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;七&#34;&gt;（七）&lt;/h2&gt;
&lt;p&gt;“为啥是我得癌症” 的非学术报告，是书的精华文章。从饮食习惯、睡眠习惯、突击作业、环境问题，分析了对身体不利的因素。突击作业其实就是工作习惯。&lt;/p&gt;
&lt;p&gt;在饮食上常吃不寻常的动物、暴饮暴食、嗜荤如命。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们要相信聪明的祖先，几千年的智慧沉淀，才最终锁定了我们现在的食材，并在远古对他们进行豢养。如果孔雀比鸡好吃，那么现在的就是孔雀，孔雀就是鸡。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在睡眠习惯上：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;二十三时至次日三时，是肝脏活动能力最强的时段，也是肝脏最佳的排毒时期，如果肝脏功能得不到休息，会引起肝脏血流相对不足，已受损的肝细胞难以修复并加剧恶化。所以“长期熬夜等于慢性自杀”的说法并不夸张。因此医生建议人们从二十三时左右开始上床睡觉，次日一至三时进入深睡眠状态，好好地养足肝血。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;突击作业：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一辆平时就跌跌撞撞一直不维修的破车，一踩油门就彻夜地疯跑疯开半个月。一年搞个五六次，就是钢筋铁打的汽车，开个二十几年也到报废了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;环境问题，空气污染、水污染和食品安全危机还有那可怕的甲醛家具。&lt;/p&gt;
&lt;h2 id=&#34;八&#34;&gt;（八）&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;不去想控制大局小局，不想去多管闲事淡事，我不在有对手，不再有敌人，我也不在关心谁比谁强，课题也好，任务也罢，暂且放着。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也许只有正在想明白的人，才懂的世间的一切，隔岸看花、风淡云轻。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p>我们要用多大的代价，才能认清活着的意义？
于娟，这个风华正茂的女子，拥有留洋经历和博士学位的复旦大学青年教师，在与晚期癌症抗争一年又四个月后，终于撒手人寰。她带走的家人的思念和不舍，给我们留下坚强的力量。</p>
</blockquote>
<p>《此生未完成》这本书是上周末聚会时 Zhen 提到的，上周京东买书就带上了。书里前半探讨作者在生死临界时对生活、工作、名利、家人、朋友的一些看法和思考。这对于刚刚毕业的我来说，确实有些是无法真切体会到的，毕竟经历少。但是在书的字里行间中，仍然可以看到一个（不知道用什么词）的女子。</p>
<!-- more -->
<h2 id="一">（一）</h2>
<p>疾病能让人有怎样的疼痛？</p>
<blockquote>
<p>我还是没有哭，不是因为坚强，而是因为痛的想不起来哭，那个时候，只要能用尽全力顶着。如果稍微分神，我就会痛的晕厥。我不想家人看到我的痛苦。</p>
</blockquote>
<p>说来惭愧的一个对比，自己在喝多时有过这样的经历：自己抱着垃圾桶使劲顶着一股劲，要是有人来给自己说话，就会分神似的使劲的呕吐。可这蚀骨之痛要比这疼痛多少倍呢？</p>
<h2 id="二">（二）</h2>
<blockquote>
<p>重要的是当他得知我生病消息后的第一反应，眼神表情乃至电话语气网络留言里端倪尽出，你会觉得世间很多人情世故是那么的让你淡然一笑。</p>
</blockquote>
<p>在遇到大事的时候方显出真正的朋友和所谓的朋友，扬尘散土，洗沙留金。能遇到“大事”的机会不多，遇到的“大事”还能全身而退的机会就更少了。所以觉的自己应该及时去明辨所有的朋友，将真心给最真的，对于其他的就随他去吧。我不可能讨所有人喜欢，也用不着。</p>
<h2 id="三">（三）</h2>
<blockquote>
<p>三十岁之前的努力更多是因为自己有着太多的欲望和执著，从没有“只要活着就好”的简单。名利权情，没有一样是不辛苦的，却没有一样可以带走。</p>
</blockquote>
<p>这是一个作者很重要的一个观点。在现在的我看来，“只要活着就好”的活着，是一种精神上富有的活着，是充满智慧的活着。
就说现在的自己，什么都不曾拥有，或许不用拥有过的名、利，但是其他方面什么都没有，自己一切都是空的，看到好就会有强烈的“欲望”，如果再强烈一点就会有“执著”。在文字中我感受到了作者在精神方面的强大与富有，或许在这之后才能感受到在名、利之外更重要的东西。其实，自己真的很难想通，到底拥有什么自己就可以“苟”了，自己到底想要什么。了解自己真正想要什么真是一幸事。
作者提到的有时间，多多陪陪家人父母、有机会就尽孝，自己是很赞同的。</p>
<h2 id="四">（四）</h2>
<blockquote>
<p>外公反映土豆每次经过，都趴在玻璃上看玻璃窗里的圣诞树、圣诞彩球和姜饼屋。土豆眼神里有点想，但却乖乖看着，并不讨。外公回想说：“那个眼神我想起了卖火柴的小女孩，有时候孩子听话，你却反而会心酸。”</p>
</blockquote>
<p>这文字碰触心灵最柔软的地方。</p>
<h2 id="五">（五）</h2>
<blockquote>
<p>你的妈妈不是懦夫，所以你的人生里，遇到珍贵的人与关键的事，都要积极争取，可以失败，但是不能放弃。我想做一个让儿子骄傲的妈妈，只此一点，无论任何地步，我都不会选择自己走，哪怕，万劫不复的痛。</p>
</blockquote>
<h2 id="六">（六）</h2>
<blockquote>
<p>更多人不会明白，我们两个的谈笑深处埋藏着多少不能言表的无声叹息。上一次见面，我和梅两个事多么风华正茂，像展翅云霄的鹰隼，挥着翅膀相约下次的冲天。这次的相逢，是灰头土脸被命运按在尘土里依然微笑的土鸡之间的问候。</p>
</blockquote>
<h2 id="七">（七）</h2>
<p>“为啥是我得癌症” 的非学术报告，是书的精华文章。从饮食习惯、睡眠习惯、突击作业、环境问题，分析了对身体不利的因素。突击作业其实就是工作习惯。</p>
<p>在饮食上常吃不寻常的动物、暴饮暴食、嗜荤如命。</p>
<blockquote>
<p>我们要相信聪明的祖先，几千年的智慧沉淀，才最终锁定了我们现在的食材，并在远古对他们进行豢养。如果孔雀比鸡好吃，那么现在的就是孔雀，孔雀就是鸡。</p>
</blockquote>
<p>在睡眠习惯上：</p>
<blockquote>
<p>二十三时至次日三时，是肝脏活动能力最强的时段，也是肝脏最佳的排毒时期，如果肝脏功能得不到休息，会引起肝脏血流相对不足，已受损的肝细胞难以修复并加剧恶化。所以“长期熬夜等于慢性自杀”的说法并不夸张。因此医生建议人们从二十三时左右开始上床睡觉，次日一至三时进入深睡眠状态，好好地养足肝血。</p>
</blockquote>
<p>突击作业：</p>
<blockquote>
<p>一辆平时就跌跌撞撞一直不维修的破车，一踩油门就彻夜地疯跑疯开半个月。一年搞个五六次，就是钢筋铁打的汽车，开个二十几年也到报废了。</p>
</blockquote>
<p>环境问题，空气污染、水污染和食品安全危机还有那可怕的甲醛家具。</p>
<h2 id="八">（八）</h2>
<blockquote>
<p>不去想控制大局小局，不想去多管闲事淡事，我不在有对手，不再有敌人，我也不在关心谁比谁强，课题也好，任务也罢，暂且放着。</p>
</blockquote>
<p>也许只有正在想明白的人，才懂的世间的一切，隔岸看花、风淡云轻。</p>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Intersection of Two Arrays 349</title>
      <link>https://zyf.im/2016/08/22/leetcode-intersection-of-two-arrays-349/</link>
      <pubDate>Mon, 22 Aug 2016 18:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/22/leetcode-intersection-of-two-arrays-349/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/intersection-of-two-arrays/&#34;&gt;349. Intersection of Two Arrays&lt;/a&gt;
  Given two arrays, write a function to compute their intersection.
  &lt;strong&gt;Example:&lt;/strong&gt;
  Given nums1 = &lt;code&gt;[1, 2, 2, 1]&lt;/code&gt;, nums2 = &lt;code&gt;[2, 2]&lt;/code&gt;, return &lt;code&gt;[2]&lt;/code&gt;.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;  &lt;strong&gt;Note:&lt;/strong&gt;
  Each element in the result must be unique.
  The result can be in any order.&lt;/p&gt;
&lt;h3 id=&#34;自己的解法&#34;&gt;自己的解法&lt;/h3&gt;
&lt;p&gt;使用 set 进行去重&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;intersection2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums1Set&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HashSet&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HashSet&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;integer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums1Set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;integer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;integer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nums1Set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;integer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;integer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;integer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;integer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;别人的思路&#34;&gt;别人的思路&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;www.programcreek.com/2015/05/leetcode-intersection-of-two-arrays-java/&#34;&gt;LeetCode – Intersection of Two Arrays (Java)&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/intersection-of-two-arrays/">349. Intersection of Two Arrays</a>
  Given two arrays, write a function to compute their intersection.
  <strong>Example:</strong>
  Given nums1 = <code>[1, 2, 2, 1]</code>, nums2 = <code>[2, 2]</code>, return <code>[2]</code>.</p>
<!-- more -->
<p>  <strong>Note:</strong>
  Each element in the result must be unique.
  The result can be in any order.</p>
<h3 id="自己的解法">自己的解法</h3>
<p>使用 set 进行去重</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="nf">intersection2</span><span class="p">(</span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">nums1</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">nums2</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Set</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span><span class="w"> </span><span class="n">nums1Set</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">HashSet</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Set</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span><span class="w"> </span><span class="n">set</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">HashSet</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">integer</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">nums1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">nums1Set</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">integer</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">integer</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">nums2</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">nums1Set</span><span class="p">.</span><span class="na">contains</span><span class="p">(</span><span class="n">integer</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="n">set</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">integer</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="kt">int</span><span class="o">[</span><span class="n">set</span><span class="p">.</span><span class="na">size</span><span class="p">()</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">integer</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">set</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">result</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">integer</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">i</span><span class="o">++</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">result</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="别人的思路">别人的思路</h3>
<p><a href="www.programcreek.com/2015/05/leetcode-intersection-of-two-arrays-java/">LeetCode – Intersection of Two Arrays (Java)</a></p>
<p>Binary Search</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="nf">intersection</span><span class="p">(</span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">nums1</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">nums2</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Arrays</span><span class="p">.</span><span class="na">sort</span><span class="p">(</span><span class="n">nums1</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Arrays</span><span class="p">.</span><span class="na">sort</span><span class="p">(</span><span class="n">nums2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span><span class="w"> </span><span class="n">list</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">for</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="o">=</span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">&lt;</span><span class="n">nums1</span><span class="p">.</span><span class="na">length</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">){</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="p">(</span><span class="n">i</span><span class="o">==</span><span class="n">0</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="o">&gt;</span><span class="n">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">nums1</span><span class="o">[</span><span class="n">i</span><span class="o">]!=</span><span class="n">nums1</span><span class="o">[</span><span class="n">i</span><span class="o">-</span><span class="n">1</span><span class="o">]</span><span class="p">)){</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">if</span><span class="p">(</span><span class="n">Arrays</span><span class="p">.</span><span class="na">binarySearch</span><span class="p">(</span><span class="n">nums2</span><span class="p">,</span><span class="w"> </span><span class="n">nums1</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">)</span><span class="o">&gt;-</span><span class="n">1</span><span class="p">){</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="n">list</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">nums1</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="kt">int</span><span class="o">[</span><span class="n">list</span><span class="p">.</span><span class="na">size</span><span class="p">()</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">int</span><span class="w"> </span><span class="n">k</span><span class="o">=</span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">for</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="p">:</span><span class="w"> </span><span class="n">list</span><span class="p">){</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">result</span><span class="o">[</span><span class="n">k</span><span class="o">++]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">i</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">result</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Time = O(nlog(n)).
Space = O(n).</p>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Same Tree 100</title>
      <link>https://zyf.im/2016/08/22/leetcode-same-tree-100/</link>
      <pubDate>Mon, 22 Aug 2016 16:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/22/leetcode-same-tree-100/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/same-tree/&#34;&gt;100. Same Tree&lt;/a&gt;
  Given two binary trees, write a function to check if they are equal or not.
  Two binary trees are considered equal if they are structurally identical and the nodes have the same value.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;关于树结构自己没怎么看过，查了查遍历通常是：递归、stack&lt;/p&gt;
&lt;h3 id=&#34;自己的解法&#34;&gt;自己的解法&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Definition for a binary tree node.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * public class TreeNode {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *     int val;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *     TreeNode left;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *     TreeNode right;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *     TreeNode(int x) { val = x; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Solution&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;isSameTree&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TreeNode&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TreeNode&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isSameTree&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;left&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;left&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isSameTree&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;right&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;right&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/same-tree/">100. Same Tree</a>
  Given two binary trees, write a function to check if they are equal or not.
  Two binary trees are considered equal if they are structurally identical and the nodes have the same value.</p>
<!-- more -->
<p>关于树结构自己没怎么看过，查了查遍历通常是：递归、stack</p>
<h3 id="自己的解法">自己的解法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Definition for a binary tree node.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * public class TreeNode {
</span></span></span><span class="line"><span class="cl"><span class="cm"> *     int val;
</span></span></span><span class="line"><span class="cl"><span class="cm"> *     TreeNode left;
</span></span></span><span class="line"><span class="cl"><span class="cm"> *     TreeNode right;
</span></span></span><span class="line"><span class="cl"><span class="cm"> *     TreeNode(int x) { val = x; }
</span></span></span><span class="line"><span class="cl"><span class="cm"> * }
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Solution</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">isSameTree</span><span class="p">(</span><span class="n">TreeNode</span><span class="w"> </span><span class="n">p</span><span class="p">,</span><span class="w"> </span><span class="n">TreeNode</span><span class="w"> </span><span class="n">q</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">q</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">q</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="na">val</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="na">val</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">return</span><span class="w"> </span><span class="n">isSameTree</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="na">left</span><span class="p">,</span><span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="na">left</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">isSameTree</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="na">right</span><span class="p">,</span><span class="w"> </span><span class="n">q</span><span class="p">.</span><span class="na">right</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Excel Sheet Column Title 168</title>
      <link>https://zyf.im/2016/08/22/leetcode-excel-sheet-column-title-168/</link>
      <pubDate>Mon, 22 Aug 2016 13:40:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/22/leetcode-excel-sheet-column-title-168/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/excel-sheet-column-title/&#34;&gt;168. Excel Sheet Column Title&lt;/a&gt;
  Given a positive integer, return its corresponding column title as appear in an Excel sheet.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;B&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;C&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Z&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;27&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AA&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AB&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;自己的思路&#34;&gt;自己的思路&lt;/h3&gt;
&lt;p&gt;十进制转“二十六进制”&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;convertToTitle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chrInt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chrInt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;sc&#34;&gt;&amp;#39;Z&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chrInt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;sc&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;别人的思路&#34;&gt;别人的思路&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;convertToTitle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;){&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Input is not valid!&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;StringBuilder&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sb&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;StringBuilder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;){&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;sc&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;reverse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;思路是一样的，但是代码更简洁，使用了&lt;code&gt;StringBuilder&lt;/code&gt; 和 &lt;code&gt;reverse()&lt;/code&gt;，提前减 1 也避免了特殊处理&amp;rsquo;Z&#39;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/excel-sheet-column-title/">168. Excel Sheet Column Title</a>
  Given a positive integer, return its corresponding column title as appear in an Excel sheet.</p>
<!-- more -->
<p>For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w">    </span><span class="n">1</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">A</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">2</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">B</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">3</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">C</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">26</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">Z</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">27</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">AA</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">28</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">AB</span><span class="w">
</span></span></span></code></pre></div><h3 id="自己的思路">自己的思路</h3>
<p>十进制转“二十六进制”</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">convertToTitle</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">int</span><span class="w"> </span><span class="n">chrInt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">26</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">char</span><span class="w"> </span><span class="n">chr</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">chrInt</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="n">chr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sc">&#39;Z&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="n">n</span><span class="w"> </span><span class="o">-=</span><span class="w"> </span><span class="n">26</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="n">chr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">char</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">chrInt</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="sc">&#39;A&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">str</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">chr</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">str</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">26</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">str</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="别人的思路">别人的思路</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">convertToTitle</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="n">0</span><span class="p">){</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalArgumentException</span><span class="p">(</span><span class="s">&#34;Input is not valid!&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">StringBuilder</span><span class="w"> </span><span class="n">sb</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">StringBuilder</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">while</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">0</span><span class="p">){</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">n</span><span class="o">--</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kt">char</span><span class="w"> </span><span class="n">ch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">char</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">26</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="sc">&#39;A&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">n</span><span class="w"> </span><span class="o">/=</span><span class="w"> </span><span class="n">26</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">sb</span><span class="p">.</span><span class="na">append</span><span class="p">(</span><span class="n">ch</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">sb</span><span class="p">.</span><span class="na">reverse</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">sb</span><span class="p">.</span><span class="na">toString</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>思路是一样的，但是代码更简洁，使用了<code>StringBuilder</code> 和 <code>reverse()</code>，提前减 1 也避免了特殊处理&rsquo;Z'</p>
<p>在算法问题中常常用到：</p>
<ul>
<li><code>1</code> 的 ascii 为 33，十六进制为 21H</li>
<li><code>A</code> 的 ascii 为 65，十六进制为 41H</li>
<li><code>a</code> 的 ascii 为 97，十六进制为 61H</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Excel Sheet Column Number 171</title>
      <link>https://zyf.im/2016/08/22/leetcode-excel-sheet-column-number-171/</link>
      <pubDate>Mon, 22 Aug 2016 13:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/22/leetcode-excel-sheet-column-number-171/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/excel-sheet-column-number/&#34;&gt;171. Excel Sheet Column Number&lt;/a&gt;
  Given a column title as appear in an Excel sheet, return its corresponding column number.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;B&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;C&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Z&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AA&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;27&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AB&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;自己的思路&#34;&gt;自己的思路&lt;/h3&gt;
&lt;p&gt;很像是“二十六进制”转十进制&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;titleToNumber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chars&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toCharArray&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chars&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;charInt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chars&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chars&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;pow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;charInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在算法问题中常常用到：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/excel-sheet-column-number/">171. Excel Sheet Column Number</a>
  Given a column title as appear in an Excel sheet, return its corresponding column number.</p>
<!-- more -->
<p>For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w">    </span><span class="n">A</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">B</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">C</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Z</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">26</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">AA</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">27</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">AB</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">28</span><span class="w">
</span></span></span></code></pre></div><h3 id="自己的思路">自己的思路</h3>
<p>很像是“二十六进制”转十进制</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">titleToNumber</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">char</span><span class="o">[]</span><span class="w"> </span><span class="n">chars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">s</span><span class="p">.</span><span class="na">toCharArray</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">chars</span><span class="p">.</span><span class="na">length</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">int</span><span class="w"> </span><span class="n">charInt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">chars</span><span class="o">[</span><span class="n">chars</span><span class="p">.</span><span class="na">length</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">i</span><span class="o">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">64</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">col</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">Math</span><span class="p">.</span><span class="na">pow</span><span class="p">(</span><span class="n">26</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">charInt</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">i</span><span class="o">++</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">col</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>在算法问题中常常用到：</p>
<ul>
<li><code>1</code> 的 ascii 为 33，十六进制为 21H</li>
<li><code>A</code> 的 ascii 为 65，十六进制为 41H</li>
<li><code>a</code> 的 ascii 为 97，十六进制为 61H</li>
</ul>
<p>上面有个写的不好的地方就是 64，好像的很莫名的一个数字，其实时在将 A 折算为 1</p>
]]></content:encoded>
    </item>
    <item>
      <title>【蛤蟆的油】随笔</title>
      <link>https://zyf.im/2016/08/17/something-like-an-autobiography-reading-notes/</link>
      <pubDate>Wed, 17 Aug 2016 23:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/17/something-like-an-autobiography-reading-notes/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;日本民间流传着这样一个故事：在深山里，有一种特别的蛤蟆，它和同类相比不仅外表更丑，而且还多长了几条腿。人们转到它后，将其放在镜前或者玻璃箱内，蛤蟆一看到自己丑陋不堪的真面目，不禁吓出一身油。这种油，也是民间用来治疗烧伤烫伤的珍贵药材。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第一次写人文类书籍的笔记。&lt;/p&gt;
&lt;p&gt;故事是从黑泽明一岁多光着身子洗澡开始回忆的，然后按时间，一个个小故事讲述自己。&lt;/p&gt;
&lt;p&gt;（一）&lt;/p&gt;
&lt;p&gt;二年级是他得到了“糖酥”这个绰号，因为有人揪他的头发，往他西装上抹鼻涕，让他哭了好几次。&lt;/p&gt;
&lt;p&gt;想到了自己大概三四年级的一件事：一次在公交车站等车被人嘲笑嘴巴大，是陌生的同龄人。自己当时被蒙掉了，也不知道说什么或者做点什么，傻傻的，后来车来了，上车回家了。我记得我是在车上委屈的哭了。十几年来这件事我一直都记得，总是把这事归为黑历史、很怂的事，也从来没和别人讲过。我当时为什么没有还击？是被大人教育的打架不是好孩子？还是自己的胆小？还是正中下怀？细思这些都有，是太小没有自己的判断力，也缺少一份对自己信心与肯定。&lt;/p&gt;
&lt;p&gt;要能再来我一定会把那小子打到哭。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;（二）&lt;/p&gt;
&lt;p&gt;小黑遇到了“崇尚自由、以鲜活的感性及创造精神从事教育的老师”，立川老师对智力发育缓慢、性格乖僻的他多方庇护，使他第一次有了自信。立川老师还为了使植草（小黑好友）尽快地开出灿烂的花，把植草移栽到了副班长这个盆里，而且放在向阳之处。&lt;/p&gt;
&lt;p&gt;遇到一个能指导你成长的同学、朋友、老师、哥哥姐姐真的是一件很幸运的事情。我觉的他们在有智慧的人群里，不是瞎折腾或者很世俗。&lt;/p&gt;
&lt;p&gt;（三）&lt;/p&gt;
&lt;p&gt;给客人吃的鱼，鱼头朝左，鱼腹朝着客人。给剖腹者上的鱼，大概是鱼头朝右，鱼背朝着本人。或许是因为，如果让剖腹者看到剖开的鱼腹，未免太残酷了。&lt;/p&gt;
&lt;p&gt;（四）&lt;/p&gt;
&lt;p&gt;小黑在学剑道时，道场的主人被汽车撞了，那是汽车本来是稀罕之物，却又让这稀罕之物撞伤，简直像挨马踢一样可笑。而对道场主人的尊敬烟消云散。自己在高野佐三郎的道场在学交叉坎时，被弹回来撞到墙上，眼前一阵发黑，两眼直冒金星，对自己剑道的自豪化为乌有。&lt;/p&gt;
&lt;p&gt;人世并不像想象的那么简单。人外有人，天外有天。自己不免是井底之蛙，总是管中窥豹。很多像这样的道理，我都听过，我也认同，可在没有遇到一件具体的事情的时候，总是看不到认不清自己的浅薄。多多经历，多多思考。&lt;/p&gt;
&lt;p&gt;（五）&lt;/p&gt;
&lt;p&gt;在体育课上，体育老师教跳高采取比赛的方法，撞掉竿就被淘汰下去。黑泽明刚一起跑，同学们就哄堂大笑。他们准是觉得我会头一个把横杆撞下来。出乎意料的是我轻松越过了横杆。横杆逐渐上移，然而，挑战的人中间总有我。看热闹的人们寂然无声了。不知道什么原因，居然出现了奇迹：只剩我一个人在挑战了。一个个无不呆呆地看着我。而且，只剩下我一个人之后，仍然几次跳过了横杆。也许是天使哀怜我体操课总得零分，给我的背插上了翅膀。
最后一句，写的真美。&lt;/p&gt;
&lt;p&gt;（六）&lt;/p&gt;
&lt;p&gt;人是可笑的，过分受惊时，头脑的一部分会脱离现实，想入非非，看起来显得十分沉着。当大地震时，抱着电线杆忍受着强烈的摇晃时，仍然想到了这些，而且非常佩服日式建筑的优越性。然而这绝不意味着我遇事沉着冷静。&lt;/p&gt;
&lt;p&gt;（七）&lt;/p&gt;
&lt;p&gt;没有经历过的人无法想象对人类来说何谓正真的黑暗，这黑暗又是多么的可怕。这恐怖夺走了人的正气。无论朝哪里望，什么都看不见，这是最使人感到孤立无援的地方，它使人内心深处产生了惊慌和不安，也使人处于名副其实的疑心生暗鬼状态。&lt;/p&gt;
&lt;p&gt;好像有点像我国的“吃盐防辐射”，都是巧妙地利用黑暗对人的威胁制造的阴谋？还是易受蛊惑的人心是社会发展的过程中要的经历？&lt;/p&gt;
&lt;p&gt;（八）&lt;/p&gt;
&lt;p&gt;结束这趟可怕的远足，当天晚上，我以为一定难以入睡，还会大做噩梦，但头刚一沾枕就到了第二天早晨。哥哥说：“面对可怕的事物闭眼不敢看，就会觉得它可怕；什么都不在乎，哪里还有什么可怕的呢？”。现在看来，那趟远足，对哥哥来说可能也是可怕的。正因为可怕，所以必须征服它。这次远足也是一次征服恐怖的远征。&lt;/p&gt;
&lt;p&gt;可是面对了自己可怕的东西后，还是怕的原因是什么呢？是还没有正面面对？&lt;/p&gt;
&lt;p&gt;（九）&lt;/p&gt;
&lt;p&gt;高山仰止&lt;/p&gt;
&lt;p&gt;对有气质、有修养或有崇高品德之人的崇敬、仰慕之情。&lt;/p&gt;
&lt;p&gt;（十）&lt;/p&gt;
&lt;p&gt;人能把卫星送进宇宙，在精神层面却不会向上看，而是像野狗一样，只注意脚下，徘徊不已。&lt;/p&gt;
&lt;p&gt;（十一）&lt;/p&gt;
&lt;p&gt;后娘用艾苦我身/我为后娘买大艾/为讨她欢心。继母为什么虐待前房孩子？如果说出于憎恨丈夫的前妻而虐待其子，这是没有道理的。认为这完全出于愚昧。愚昧是人的疯狂病症之一，以虐待没有反抗能力的孩子或者小动物为乐的人，纯粹是疯子。然而这类疯子并不认为这是犯罪，却认为是理所当然，所以难以对付。我能解开绑她的带子，然而无法把她从捆绑她的境遇中救出。对这个孩子来说，人们的同情是毫无意义的。那种温情反倒给她招来更多的麻烦。&lt;/p&gt;
&lt;p&gt;想到了打劫的故事，说：被劫持的人只要交的钱比上一人交的钱多一倍，就可以走。人争先恐后的交赎金，不再有人反抗。&lt;/p&gt;
&lt;p&gt;（十二）&lt;/p&gt;
&lt;p&gt;本来是红的，却不老实说它是红的，等到能坦率说出来的时候，已经到了晚年。很多人年轻时表现欲过强，这样反倒迷失了自己。我也毫不例外，拼命的考技巧作画，那画的俗气使我对自己心生憎恶，逐渐丧失了对自己才能有的信心，把绘画看作痛苦了。为了买油彩和画布还得干些不愿干的杂活儿赚钱才行。&lt;/p&gt;
&lt;p&gt;少搞些套路，玩花活，踏踏实实才是真。别瞎折腾，没有用。&lt;/p&gt;
&lt;p&gt;（十三）&lt;/p&gt;
&lt;p&gt;产生这种急于就业的焦虑和降格以求的心情，主要是以为哥哥突然去世，我要继他之后负起长子的责任。&lt;/p&gt;
&lt;p&gt;在年初有类似的经历，最后一个学期，都找工作去了，也要毕业要长大了，急着确定个工作。其实现在也着急着，想快有所作为。前几日，看到好友暗叶的朋友圈发：年轻人想法没多，都是还得慢慢来。我初看是不赞同。黑泽明的父亲也告诫他说：“不要着急，也没有着急的必要”，“要等下去，前面的道路自然会打开的”。这些话我确实还不太理解为什么，得慢慢的体会。&lt;/p&gt;
&lt;p&gt;（十四）&lt;/p&gt;
&lt;p&gt;凡事想用人的，必须先培养人。培养出人，激发出人的才能，这才能用。&lt;/p&gt;
&lt;p&gt;纷扰的现在，还有多少人或公司愿意培养人？应该说人培养应该是必须的。&lt;/p&gt;
&lt;p&gt;（十五）&lt;/p&gt;
&lt;p&gt;山本先生对于追随他的副导演，绝不干改变他们个性的事，而是一心一意的着力于发掘他们的个性；而且丝毫不让我们有从师学习的拘束心情，而是让我们充分的发展自己。
山本先生为了培养副导演，不惜牺牲自己的作品。
“那是卖香荷包的招牌。随随便便说话可不行！不知道的事情就该说不知道！”&lt;/p&gt;
&lt;p&gt;（十六）&lt;/p&gt;
&lt;p&gt;提出批评不难。但是，提出批评的人能够按照自己的批评意见亲自把剧本改好，却不是普通人能做到的事。&lt;/p&gt;
&lt;p&gt;太有同感了，常常有人在旁边指手画脚，却说不出什么切实可行的办法建议。&lt;/p&gt;
&lt;p&gt;（十七）&lt;/p&gt;
&lt;p&gt;人往往习惯于认为价值与辛苦成正比。这在电影界剪辑上是最要不得的。人们说电影是时间的艺术，所以，没有用的时间就应删去。&lt;/p&gt;
&lt;p&gt;这观点在互联网行业也行的通，做好的功能不合适了，说废也就废掉了。&lt;/p&gt;
&lt;p&gt;（十八）&lt;/p&gt;
&lt;p&gt;我常常把群众演员的名字忘掉，所以只好按他们的衣服颜色招呼。结果被山本先生训了几句：“黑泽明，那可不行。人都有个名嘛！”。名不见经传的演员听到山本先生如此亲切的招呼他，无不感到。难道能说山本先生有些滑头吗？我看应该说他善于用人。&lt;/p&gt;
&lt;p&gt;自己还不是什么重要人物，但是记住别人的名字应该是件重要的事情。&lt;/p&gt;
&lt;p&gt;（十九）&lt;/p&gt;
&lt;p&gt;关于演员的关键问题主要有以下几点：第一，人很难了解自己，不能客观的观察自己的说话方式和行为举止。第二，凡事有意识的动作，首先注意的不是动作本身，而是意识。第三，教给演员怎么做，同时必须告诉他为什么怎么做，而且让他充分理解、心悦诚服。&lt;/p&gt;
&lt;p&gt;做事情的时候应该反向考虑第三点，为什么要怎么做事情？目的是什么？&lt;/p&gt;
&lt;p&gt;（二十）&lt;/p&gt;
&lt;p&gt;电影评论家中冲有这样的人，看到电影中一些自己认为是缺点的地方就如获至宝，大发谬论，但是电影导演也乐于此道可就错了。&lt;/p&gt;
&lt;p&gt;现在这样的人越来越多，哗众取宠。&lt;/p&gt;
&lt;p&gt;（二十一）&lt;/p&gt;
&lt;p&gt;他们以为一部卖座影片的续集也必然会成功，于是不想再开拓新的领域，总想旧梦重温。重拍的影片绝不如前作，这虽然是已经证明的事实，但他们还要重蹈覆辙，这才是地地道道的愚蠢之举。&lt;/p&gt;
&lt;p&gt;学习新东西，找新路子。&lt;/p&gt;
&lt;p&gt;（二十二）&lt;/p&gt;
&lt;p&gt;我为了抵抗人的苦恼，戴上一幅强者的面具；而植草却为了沉溺与人的苦恼，戴上了一幅弱者的面具。事实不过如此。但这只是表面的不同，就本质来说。我们都是弱者。我不是特别的人。我既不是特别强的人，也不是得天独厚的有特殊才能的人。我不过是个不愿示弱于人，不愿输给别人，因而不懈努力的人。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p>日本民间流传着这样一个故事：在深山里，有一种特别的蛤蟆，它和同类相比不仅外表更丑，而且还多长了几条腿。人们转到它后，将其放在镜前或者玻璃箱内，蛤蟆一看到自己丑陋不堪的真面目，不禁吓出一身油。这种油，也是民间用来治疗烧伤烫伤的珍贵药材。</p>
</blockquote>
<p>第一次写人文类书籍的笔记。</p>
<p>故事是从黑泽明一岁多光着身子洗澡开始回忆的，然后按时间，一个个小故事讲述自己。</p>
<p>（一）</p>
<p>二年级是他得到了“糖酥”这个绰号，因为有人揪他的头发，往他西装上抹鼻涕，让他哭了好几次。</p>
<p>想到了自己大概三四年级的一件事：一次在公交车站等车被人嘲笑嘴巴大，是陌生的同龄人。自己当时被蒙掉了，也不知道说什么或者做点什么，傻傻的，后来车来了，上车回家了。我记得我是在车上委屈的哭了。十几年来这件事我一直都记得，总是把这事归为黑历史、很怂的事，也从来没和别人讲过。我当时为什么没有还击？是被大人教育的打架不是好孩子？还是自己的胆小？还是正中下怀？细思这些都有，是太小没有自己的判断力，也缺少一份对自己信心与肯定。</p>
<p>要能再来我一定会把那小子打到哭。</p>
<!-- more -->
<p>（二）</p>
<p>小黑遇到了“崇尚自由、以鲜活的感性及创造精神从事教育的老师”，立川老师对智力发育缓慢、性格乖僻的他多方庇护，使他第一次有了自信。立川老师还为了使植草（小黑好友）尽快地开出灿烂的花，把植草移栽到了副班长这个盆里，而且放在向阳之处。</p>
<p>遇到一个能指导你成长的同学、朋友、老师、哥哥姐姐真的是一件很幸运的事情。我觉的他们在有智慧的人群里，不是瞎折腾或者很世俗。</p>
<p>（三）</p>
<p>给客人吃的鱼，鱼头朝左，鱼腹朝着客人。给剖腹者上的鱼，大概是鱼头朝右，鱼背朝着本人。或许是因为，如果让剖腹者看到剖开的鱼腹，未免太残酷了。</p>
<p>（四）</p>
<p>小黑在学剑道时，道场的主人被汽车撞了，那是汽车本来是稀罕之物，却又让这稀罕之物撞伤，简直像挨马踢一样可笑。而对道场主人的尊敬烟消云散。自己在高野佐三郎的道场在学交叉坎时，被弹回来撞到墙上，眼前一阵发黑，两眼直冒金星，对自己剑道的自豪化为乌有。</p>
<p>人世并不像想象的那么简单。人外有人，天外有天。自己不免是井底之蛙，总是管中窥豹。很多像这样的道理，我都听过，我也认同，可在没有遇到一件具体的事情的时候，总是看不到认不清自己的浅薄。多多经历，多多思考。</p>
<p>（五）</p>
<p>在体育课上，体育老师教跳高采取比赛的方法，撞掉竿就被淘汰下去。黑泽明刚一起跑，同学们就哄堂大笑。他们准是觉得我会头一个把横杆撞下来。出乎意料的是我轻松越过了横杆。横杆逐渐上移，然而，挑战的人中间总有我。看热闹的人们寂然无声了。不知道什么原因，居然出现了奇迹：只剩我一个人在挑战了。一个个无不呆呆地看着我。而且，只剩下我一个人之后，仍然几次跳过了横杆。也许是天使哀怜我体操课总得零分，给我的背插上了翅膀。
最后一句，写的真美。</p>
<p>（六）</p>
<p>人是可笑的，过分受惊时，头脑的一部分会脱离现实，想入非非，看起来显得十分沉着。当大地震时，抱着电线杆忍受着强烈的摇晃时，仍然想到了这些，而且非常佩服日式建筑的优越性。然而这绝不意味着我遇事沉着冷静。</p>
<p>（七）</p>
<p>没有经历过的人无法想象对人类来说何谓正真的黑暗，这黑暗又是多么的可怕。这恐怖夺走了人的正气。无论朝哪里望，什么都看不见，这是最使人感到孤立无援的地方，它使人内心深处产生了惊慌和不安，也使人处于名副其实的疑心生暗鬼状态。</p>
<p>好像有点像我国的“吃盐防辐射”，都是巧妙地利用黑暗对人的威胁制造的阴谋？还是易受蛊惑的人心是社会发展的过程中要的经历？</p>
<p>（八）</p>
<p>结束这趟可怕的远足，当天晚上，我以为一定难以入睡，还会大做噩梦，但头刚一沾枕就到了第二天早晨。哥哥说：“面对可怕的事物闭眼不敢看，就会觉得它可怕；什么都不在乎，哪里还有什么可怕的呢？”。现在看来，那趟远足，对哥哥来说可能也是可怕的。正因为可怕，所以必须征服它。这次远足也是一次征服恐怖的远征。</p>
<p>可是面对了自己可怕的东西后，还是怕的原因是什么呢？是还没有正面面对？</p>
<p>（九）</p>
<p>高山仰止</p>
<p>对有气质、有修养或有崇高品德之人的崇敬、仰慕之情。</p>
<p>（十）</p>
<p>人能把卫星送进宇宙，在精神层面却不会向上看，而是像野狗一样，只注意脚下，徘徊不已。</p>
<p>（十一）</p>
<p>后娘用艾苦我身/我为后娘买大艾/为讨她欢心。继母为什么虐待前房孩子？如果说出于憎恨丈夫的前妻而虐待其子，这是没有道理的。认为这完全出于愚昧。愚昧是人的疯狂病症之一，以虐待没有反抗能力的孩子或者小动物为乐的人，纯粹是疯子。然而这类疯子并不认为这是犯罪，却认为是理所当然，所以难以对付。我能解开绑她的带子，然而无法把她从捆绑她的境遇中救出。对这个孩子来说，人们的同情是毫无意义的。那种温情反倒给她招来更多的麻烦。</p>
<p>想到了打劫的故事，说：被劫持的人只要交的钱比上一人交的钱多一倍，就可以走。人争先恐后的交赎金，不再有人反抗。</p>
<p>（十二）</p>
<p>本来是红的，却不老实说它是红的，等到能坦率说出来的时候，已经到了晚年。很多人年轻时表现欲过强，这样反倒迷失了自己。我也毫不例外，拼命的考技巧作画，那画的俗气使我对自己心生憎恶，逐渐丧失了对自己才能有的信心，把绘画看作痛苦了。为了买油彩和画布还得干些不愿干的杂活儿赚钱才行。</p>
<p>少搞些套路，玩花活，踏踏实实才是真。别瞎折腾，没有用。</p>
<p>（十三）</p>
<p>产生这种急于就业的焦虑和降格以求的心情，主要是以为哥哥突然去世，我要继他之后负起长子的责任。</p>
<p>在年初有类似的经历，最后一个学期，都找工作去了，也要毕业要长大了，急着确定个工作。其实现在也着急着，想快有所作为。前几日，看到好友暗叶的朋友圈发：年轻人想法没多，都是还得慢慢来。我初看是不赞同。黑泽明的父亲也告诫他说：“不要着急，也没有着急的必要”，“要等下去，前面的道路自然会打开的”。这些话我确实还不太理解为什么，得慢慢的体会。</p>
<p>（十四）</p>
<p>凡事想用人的，必须先培养人。培养出人，激发出人的才能，这才能用。</p>
<p>纷扰的现在，还有多少人或公司愿意培养人？应该说人培养应该是必须的。</p>
<p>（十五）</p>
<p>山本先生对于追随他的副导演，绝不干改变他们个性的事，而是一心一意的着力于发掘他们的个性；而且丝毫不让我们有从师学习的拘束心情，而是让我们充分的发展自己。
山本先生为了培养副导演，不惜牺牲自己的作品。
“那是卖香荷包的招牌。随随便便说话可不行！不知道的事情就该说不知道！”</p>
<p>（十六）</p>
<p>提出批评不难。但是，提出批评的人能够按照自己的批评意见亲自把剧本改好，却不是普通人能做到的事。</p>
<p>太有同感了，常常有人在旁边指手画脚，却说不出什么切实可行的办法建议。</p>
<p>（十七）</p>
<p>人往往习惯于认为价值与辛苦成正比。这在电影界剪辑上是最要不得的。人们说电影是时间的艺术，所以，没有用的时间就应删去。</p>
<p>这观点在互联网行业也行的通，做好的功能不合适了，说废也就废掉了。</p>
<p>（十八）</p>
<p>我常常把群众演员的名字忘掉，所以只好按他们的衣服颜色招呼。结果被山本先生训了几句：“黑泽明，那可不行。人都有个名嘛！”。名不见经传的演员听到山本先生如此亲切的招呼他，无不感到。难道能说山本先生有些滑头吗？我看应该说他善于用人。</p>
<p>自己还不是什么重要人物，但是记住别人的名字应该是件重要的事情。</p>
<p>（十九）</p>
<p>关于演员的关键问题主要有以下几点：第一，人很难了解自己，不能客观的观察自己的说话方式和行为举止。第二，凡事有意识的动作，首先注意的不是动作本身，而是意识。第三，教给演员怎么做，同时必须告诉他为什么怎么做，而且让他充分理解、心悦诚服。</p>
<p>做事情的时候应该反向考虑第三点，为什么要怎么做事情？目的是什么？</p>
<p>（二十）</p>
<p>电影评论家中冲有这样的人，看到电影中一些自己认为是缺点的地方就如获至宝，大发谬论，但是电影导演也乐于此道可就错了。</p>
<p>现在这样的人越来越多，哗众取宠。</p>
<p>（二十一）</p>
<p>他们以为一部卖座影片的续集也必然会成功，于是不想再开拓新的领域，总想旧梦重温。重拍的影片绝不如前作，这虽然是已经证明的事实，但他们还要重蹈覆辙，这才是地地道道的愚蠢之举。</p>
<p>学习新东西，找新路子。</p>
<p>（二十二）</p>
<p>我为了抵抗人的苦恼，戴上一幅强者的面具；而植草却为了沉溺与人的苦恼，戴上了一幅弱者的面具。事实不过如此。但这只是表面的不同，就本质来说。我们都是弱者。我不是特别的人。我既不是特别强的人，也不是得天独厚的有特殊才能的人。我不过是个不愿示弱于人，不愿输给别人，因而不懈努力的人。</p>
<p>在原来看到这样的句子是很不屑的，觉的成功的人，说什么都行。现在信了，真的人都本是普通人，只是有的人一直在进步成长就现在不再普通了。</p>
<p>（二十三）</p>
<p>有一天，我在电车里看到这种杂志的广告，简直目瞪口呆。那上面用大字标题写着：是谁夺走了 XX 的贞操？乍看起来，这似乎在为 XX 女士鸣不平，实际则是把 XX 当作玩物恣意戏耍。我认为，这不是言论自由，而是言论暴力。</p>
<p>没想到在那么早，就提到了“言论暴力”的话题，社交平台的评论区常常有的血雨腥风。</p>
<p>（二十四）</p>
<p>本民族人为什么对于本民族的存在毫无自信呢？为什么对异域的东西那么尊重，对于自己的东西就那么轻视呢？</p>
<p>黑泽明提到是：可悲的国民性。自己没太理解。</p>
<blockquote>
<p>人是很难如实的谈论自己的。人总是本能的美化自己。人不会老老实实的说自己是怎样一个人，常常是假托别人才能老老实实的谈论自己。
因为，在没有什么能比作品更好的说明作者了。</p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Move Zeroes 283</title>
      <link>https://zyf.im/2016/08/16/leetcode-move-zeroes-283/</link>
      <pubDate>Tue, 16 Aug 2016 19:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/16/leetcode-move-zeroes-283/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/move-zeroes/&#34;&gt;283. Move Zeroes&lt;/a&gt;
  Given an array nums, write a function to move all 0&amp;rsquo;s to the end of it while maintaining the relative order of the non-zero elements. For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You must do this in-place without making a copy of the array.&lt;/li&gt;
&lt;li&gt;Minimize the total number of operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;大体意思&#34;&gt;大体意思&lt;/h3&gt;
&lt;p&gt;将数组中 0 元素移动到数组的末尾，不改变其他元素的顺序；不能 copy 数组，最小化的操作&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/move-zeroes/">283. Move Zeroes</a>
  Given an array nums, write a function to move all 0&rsquo;s to the end of it while maintaining the relative order of the non-zero elements. For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].</p>
<!-- more -->
<p>Note:</p>
<ul>
<li>You must do this in-place without making a copy of the array.</li>
<li>Minimize the total number of operations.</li>
</ul>
<hr>
<h3 id="大体意思">大体意思</h3>
<p>将数组中 0 元素移动到数组的末尾，不改变其他元素的顺序；不能 copy 数组，最小化的操作</p>
<h3 id="自己的思路">自己的思路</h3>
<h4 id="交换冒泡">交换冒泡</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">moveZeroes</span><span class="p">(</span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">nums</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">hasChange</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">hasChange</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">nums</span><span class="p">.</span><span class="na">length</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">1</span><span class="o">]</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">1</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">1</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">hasChange</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">hasChange</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">Arrays</span><span class="p">.</span><span class="na">toString</span><span class="p">(</span><span class="n">nums</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h4 id="与-0-换位置参考了别人的想法">与 0 换位置，参考了别人的想法</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">moveZeroes2</span><span class="p">(</span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">nums</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">firstZoneIndex</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">-</span><span class="n">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">nums</span><span class="p">.</span><span class="na">length</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">firstZoneIndex</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="o">-</span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="c1">// 发现了过0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">nums</span><span class="o">[</span><span class="n">firstZoneIndex</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">firstZoneIndex</span><span class="o">++</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="c1">// 没有发现过0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">firstZoneIndex</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">i</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">Arrays</span><span class="p">.</span><span class="na">toString</span><span class="p">(</span><span class="n">nums</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="别人的算法">别人的算法</h3>
<ul>
<li><a href="http://blog.csdn.net/xudli/article/details/48574521">西施豆腐渣 - leetcode 283: Move Zeroes</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">moveZeroes</span><span class="p">(</span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">nums</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">nums</span><span class="p">.</span><span class="na">length</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">nums</span><span class="o">[</span><span class="n">j</span><span class="o">]</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">++]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">nums</span><span class="o">[</span><span class="n">j</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">nums</span><span class="o">[</span><span class="n">j</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="o">++</span><span class="n">i</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="o">++</span><span class="n">j</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>LeetCode Ransom Note 383</title>
      <link>https://zyf.im/2016/08/11/leetcode-ransom-note-383/</link>
      <pubDate>Thu, 11 Aug 2016 13:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/08/11/leetcode-ransom-note-383/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://leetcode.com/problems/ransom-note/&#34;&gt;383. Ransom Note&lt;/a&gt;
  Given an arbitrary ransom note string and another string containing letters from all the magazines, write a function that will return true if the ransom note can be constructed from the magazines ; otherwise, it will return false.
  Each letter in the magazine string can only be used once in your ransom note.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Note:
You may assume that both strings contain only lowercase letters.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://leetcode.com/problems/ransom-note/">383. Ransom Note</a>
  Given an arbitrary ransom note string and another string containing letters from all the magazines, write a function that will return true if the ransom note can be constructed from the magazines ; otherwise, it will return false.
  Each letter in the magazine string can only be used once in your ransom note.</p>
<!-- more -->
<p>Note:
You may assume that both strings contain only lowercase letters.</p>
<pre tabindex="0"><code>canConstruct(&#34;a&#34;, &#34;b&#34;) -&gt; false
canConstruct(&#34;aa&#34;, &#34;ab&#34;) -&gt; false
canConstruct(&#34;aa&#34;, &#34;aab&#34;) -&gt; true
</code></pre><hr>
<p>自己第一次做 LeetCode 上面的题，也没什么算法方面的学习，摸着石头过河，步步提高。</p>
<h3 id="大体意思">大体意思</h3>
<p>判断第二个字符串中的字母是否可以组成第一个字符串，每个字母只能用一次；两个字符串仅包含小写字母</p>
<h3 id="自己的思路">自己的思路</h3>
<p>将 ransom 转成 char 数组，遍历数组，然后在 magazines，进行对比，然后除去一个一样的字母</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">canConstruct</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">ransom</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">magazine</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">ransom</span><span class="p">.</span><span class="na">length</span><span class="p">()</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">magazine</span><span class="p">.</span><span class="na">length</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">char</span><span class="o">[]</span><span class="w"> </span><span class="n">ransomChar</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ransom</span><span class="p">.</span><span class="na">toCharArray</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">ransomChar</span><span class="p">.</span><span class="na">length</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">String</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">String</span><span class="p">.</span><span class="na">valueOf</span><span class="p">(</span><span class="n">ransomChar</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">magazine</span><span class="p">.</span><span class="na">indexOf</span><span class="p">(</span><span class="n">str</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="o">-</span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">magazine</span><span class="p">.</span><span class="na">indexOf</span><span class="p">(</span><span class="n">str</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">magazine</span><span class="p">.</span><span class="na">length</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">2</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="n">magazine</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">magazine</span><span class="p">.</span><span class="na">substring</span><span class="p">(</span><span class="n">0</span><span class="p">,</span><span class="w"> </span><span class="n">magazine</span><span class="p">.</span><span class="na">indexOf</span><span class="p">(</span><span class="n">str</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="n">magazine</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">magazine</span><span class="p">.</span><span class="na">substring</span><span class="p">(</span><span class="n">0</span><span class="p">,</span><span class="w"> </span><span class="n">magazine</span><span class="p">.</span><span class="na">indexOf</span><span class="p">(</span><span class="n">str</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">magazine</span><span class="p">.</span><span class="na">substring</span><span class="p">(</span><span class="n">magazine</span><span class="p">.</span><span class="na">indexOf</span><span class="p">(</span><span class="n">str</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">1</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="别人的算法">别人的算法</h3>
<ul>
<li><a href="https://www.hrwhisper.me/leetcode-ransom-note/">leetcode Ransom Note - 细语呢喃</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Solution</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">canConstruct</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">ransomNote</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">magazine</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">cnt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="kt">int</span><span class="o">[</span><span class="n">26</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">magazine</span><span class="p">.</span><span class="na">length</span><span class="p">();</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="n">cnt</span><span class="o">[</span><span class="n">magazine</span><span class="p">.</span><span class="na">charAt</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">97</span><span class="o">]++</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">ransomNote</span><span class="p">.</span><span class="na">length</span><span class="p">();</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">--</span><span class="n">cnt</span><span class="o">[</span><span class="n">ransomNote</span><span class="p">.</span><span class="na">charAt</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">97</span><span class="o">]</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">0</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>我的理解
26 个元素的数组，每个元素存放字母的个数，如果需要的多于存在的 return false</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>IMOOC 与 MySQL 的零距离接触</title>
      <link>https://zyf.im/2016/07/31/imooc-meet-mysql-notes/</link>
      <pubDate>Sun, 31 Jul 2016 10:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/07/31/imooc-meet-mysql-notes/</guid>
      <description>&lt;p&gt;涵盖全部 MySQL 数据库的基础，MySQL 数据库的基础知识、数据表的常用操作及各种约束的使用，以及综合的运用各种命令实现记录进行 CURD 等操作。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MySQL 安装与配置&lt;/li&gt;
&lt;li&gt;数据类型&lt;/li&gt;
&lt;li&gt;流程控制与运算符&lt;/li&gt;
&lt;li&gt;DDL、DCL、DQL、DML&lt;/li&gt;
&lt;li&gt;常用函数&lt;/li&gt;
&lt;li&gt;表类型（存储引擎）&lt;/li&gt;
&lt;li&gt;图形化工具&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;修改-mysql-提示符&#34;&gt;修改 MySQL 提示符&lt;/h2&gt;
&lt;p&gt;MySQL 客户端的默认提示符是 &lt;code&gt;mysql&amp;gt;&lt;/code&gt;，基本上没什么实际作用。其实可以修改这个提示符，让它显示一些有用的信息，例如当前所在的数据库等。修改方法有四种，其中前两种只对当前连接有效，后两种则对所有连接有效。&lt;/p&gt;
&lt;h3 id=&#34;连接客户端时通过参数指定&#34;&gt;连接客户端时通过参数指定&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mysql --prompt&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;(\u@\h) [\d]&amp;gt; &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;my.cnf&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-conf&#34; data-lang=&#34;conf&#34;&gt;[mysql]
prompt=mysql(\\u@\\h:\\d)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这样提示符就会变成 &lt;code&gt;(user@host) [database]&amp;gt;&lt;/code&gt;。其中常用的字符参数有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;\D&lt;/code&gt; 完整的日期&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\d&lt;/code&gt; 当前数据库&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\h&lt;/code&gt; 服务器名称&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\u&lt;/code&gt; 当前用户&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;连接上客户端后，通过 &lt;code&gt;prompt&lt;/code&gt; 命令修改&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;prompt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 MySQL 的配置文件中配置。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;prompt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过环境变量配置。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MYSQL_PS1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;(\u@\h) [\d]&amp;gt; &amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;参考：&lt;a href=&#34;http://renial.iteye.com/blog/773675&#34;&gt;http://renial.iteye.com/blog/773675&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;mysql-语法规范&#34;&gt;MySQL 语法规范&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;关键字和函数名称全部 &lt;strong&gt;大写&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;数据库名称，表名称，字段名称全部 &lt;strong&gt;小写&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;SQL 语句必须以 &lt;strong&gt;分号结尾&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;数据库操作&#34;&gt;数据库操作&lt;/h2&gt;
&lt;h3 id=&#34;数据库创建create&#34;&gt;数据库创建：CREATE&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;语法：CREATE {DATABASE SCHEMA} [IF NOT EXISTS] db_name [DEFAULT] CHARACTER SET [=] charset_name&lt;/li&gt;
&lt;li&gt;DATABASE 和 SCHEMA 是相同的，任选其一&lt;/li&gt;
&lt;li&gt;IF NOT EXISTS:如果创建的数据库存在，则不只报出 warning，不写会报错&lt;/li&gt;
&lt;li&gt;CHRARCTER SET utf8:为表设置编码方式，如果不设置则用 mysql 默认的编码方式&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;查看数据库列表show&#34;&gt;查看数据库列表：SHOW&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;SHOW { DATABASE SCHEMAS } [LIKE &amp;lsquo;pattern&amp;rsquo; WHERE expr]&lt;/li&gt;
&lt;li&gt;SHOW CREATE DATABASE xx：显示 xx 数据库信息&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;数据库的修改alter&#34;&gt;数据库的修改：ALTER&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;修改数据库编码方式：ALTER { DATABASE SCHEMAS } [db_name][default] CHARACTER SET [=] charset_name&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;删除数据库drop&#34;&gt;删除数据库：DROP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;删除数据库：DROP { DATABASE SCHEMAS } [IF EXISTS] db_name;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;--修改mysql操作符为当前日期
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;mysql&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uroot&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proot&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prompt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;--展示所有数据库
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;show&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;databases&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;--创建数据库
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;not&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;exists&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;character&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gbk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;--展示数据库t1的创建命令和编码形式
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;show&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;--修改数据库编码格式
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;alter&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;character&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utf8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;--删除数据库
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;drop&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;exists&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;--展示警告信息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;show&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;四数据类型&#34;&gt;四、数据类型&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&#34;160731-imooc-mysql-tutorials-notes-int&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/80199388-d5776280-8653-11ea-9182-29a23e53ba50.png&#34;&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>涵盖全部 MySQL 数据库的基础，MySQL 数据库的基础知识、数据表的常用操作及各种约束的使用，以及综合的运用各种命令实现记录进行 CURD 等操作。</p>
<ul>
<li>MySQL 安装与配置</li>
<li>数据类型</li>
<li>流程控制与运算符</li>
<li>DDL、DCL、DQL、DML</li>
<li>常用函数</li>
<li>表类型（存储引擎）</li>
<li>图形化工具</li>
</ul>
<!-- more -->
<h2 id="修改-mysql-提示符">修改 MySQL 提示符</h2>
<p>MySQL 客户端的默认提示符是 <code>mysql&gt;</code>，基本上没什么实际作用。其实可以修改这个提示符，让它显示一些有用的信息，例如当前所在的数据库等。修改方法有四种，其中前两种只对当前连接有效，后两种则对所有连接有效。</p>
<h3 id="连接客户端时通过参数指定">连接客户端时通过参数指定</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mysql --prompt<span class="o">=</span><span class="s2">&#34;(\u@\h) [\d]&gt; &#34;</span>
</span></span></code></pre></div><p><code>my.cnf</code></p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">[mysql]
prompt=mysql(\\u@\\h:\\d)&gt;
</code></pre><p>这样提示符就会变成 <code>(user@host) [database]&gt;</code>。其中常用的字符参数有：</p>
<ul>
<li><code>\D</code> 完整的日期</li>
<li><code>\d</code> 当前数据库</li>
<li><code>\h</code> 服务器名称</li>
<li><code>\u</code> 当前用户</li>
</ul>
<p>连接上客户端后，通过 <code>prompt</code> 命令修改</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">prompt</span><span class="w"> </span><span class="p">(</span><span class="err">\</span><span class="n">u</span><span class="o">@</span><span class="err">\</span><span class="n">h</span><span class="p">)</span><span class="w"> </span><span class="p">[</span><span class="err">\</span><span class="n">d</span><span class="p">]</span><span class="o">&gt;</span><span class="w">
</span></span></span></code></pre></div><p>在 MySQL 的配置文件中配置。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">prompt</span><span class="o">=</span><span class="p">(</span><span class="err">\\</span><span class="n">u</span><span class="o">@</span><span class="err">\\</span><span class="n">h</span><span class="p">)</span><span class="w"> </span><span class="p">[</span><span class="err">\\</span><span class="n">d</span><span class="p">]</span><span class="o">&gt;</span><span class="err">\\</span><span class="n">_</span><span class="w">
</span></span></span></code></pre></div><p>通过环境变量配置。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">export</span><span class="w"> </span><span class="n">MYSQL_PS1</span><span class="o">=</span><span class="s2">&#34;(\u@\h) [\d]&gt; &#34;</span><span class="w">
</span></span></span></code></pre></div><blockquote>
<p>参考：<a href="http://renial.iteye.com/blog/773675">http://renial.iteye.com/blog/773675</a></p>
</blockquote>
<h2 id="mysql-语法规范">MySQL 语法规范</h2>
<ul>
<li>关键字和函数名称全部 <strong>大写</strong></li>
<li>数据库名称，表名称，字段名称全部 <strong>小写</strong></li>
<li>SQL 语句必须以 <strong>分号结尾</strong></li>
</ul>
<h2 id="数据库操作">数据库操作</h2>
<h3 id="数据库创建create">数据库创建：CREATE</h3>
<ul>
<li>语法：CREATE {DATABASE SCHEMA} [IF NOT EXISTS] db_name [DEFAULT] CHARACTER SET [=] charset_name</li>
<li>DATABASE 和 SCHEMA 是相同的，任选其一</li>
<li>IF NOT EXISTS:如果创建的数据库存在，则不只报出 warning，不写会报错</li>
<li>CHRARCTER SET utf8:为表设置编码方式，如果不设置则用 mysql 默认的编码方式</li>
</ul>
<h3 id="查看数据库列表show">查看数据库列表：SHOW</h3>
<ul>
<li>SHOW { DATABASE SCHEMAS } [LIKE &lsquo;pattern&rsquo; WHERE expr]</li>
<li>SHOW CREATE DATABASE xx：显示 xx 数据库信息</li>
</ul>
<h3 id="数据库的修改alter">数据库的修改：ALTER</h3>
<ul>
<li>修改数据库编码方式：ALTER { DATABASE SCHEMAS } [db_name][default] CHARACTER SET [=] charset_name</li>
</ul>
<h3 id="删除数据库drop">删除数据库：DROP</h3>
<ul>
<li>删除数据库：DROP { DATABASE SCHEMAS } [IF EXISTS] db_name;</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">--修改mysql操作符为当前日期
</span></span></span><span class="line"><span class="cl"><span class="n">mysql</span><span class="w"> </span><span class="o">-</span><span class="n">uroot</span><span class="w"> </span><span class="o">-</span><span class="n">proot</span><span class="w"> </span><span class="n">prompt</span><span class="w"> </span><span class="err">\</span><span class="n">D</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">--展示所有数据库
</span></span></span><span class="line"><span class="cl"><span class="k">show</span><span class="w"> </span><span class="n">databases</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">--创建数据库
</span></span></span><span class="line"><span class="cl"><span class="k">create</span><span class="w"> </span><span class="k">database</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="nb">character</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="n">gbk</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">--展示数据库t1的创建命令和编码形式
</span></span></span><span class="line"><span class="cl"><span class="k">show</span><span class="w"> </span><span class="k">create</span><span class="w"> </span><span class="k">database</span><span class="w"> </span><span class="n">t1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">--修改数据库编码格式
</span></span></span><span class="line"><span class="cl"><span class="k">alter</span><span class="w"> </span><span class="k">database</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="nb">character</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">utf8</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">--删除数据库
</span></span></span><span class="line"><span class="cl"><span class="k">drop</span><span class="w"> </span><span class="k">database</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">t1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">--展示警告信息
</span></span></span><span class="line"><span class="cl"><span class="k">show</span><span class="w"> </span><span class="n">warnings</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h2 id="四数据类型">四、数据类型</h2>
<p><img alt="160731-imooc-mysql-tutorials-notes-int" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199388-d5776280-8653-11ea-9182-29a23e53ba50.png"></p>
<p><img alt="160731-imooc-mysql-tutorials-notes-float" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199384-d4decc00-8653-11ea-88ea-81e6e1f29f5a.png"></p>
<p><img alt="160731-imooc-mysql-tutorials-notes-char" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199382-d3150880-8653-11ea-9c7c-2385530649ad.png"></p>
<ul>
<li>char 型字符串有 0-255 之间的字节，通常被称作定长类型。所存字节不满 char 型所给它的字节，剩下的以空格补齐，仍会存储满你所给它的字节。</li>
<li>varchar 型的字符串是变长类型，存多少字节它就存储多少字节，不会在后面补上空格。字节长度在 0-65535 之间。</li>
<li>1 个字节等于 8 个 bit，L+ 几个字节后面的几个字节指的是他的最大存储范围。8 个 bit 等于就是 8 个 1，相当于 255，就是 2 的 8 次方。16 个 1 就相当于 2 的 16 次方。3 个字节四个字节依次类推。</li>
<li>ENUM 枚举值 就是给他几个选项，它从这几个选项中做选择，最多有 65535 个值。</li>
<li>SET 我们称之为集合，集合最多有 64 个成员，它在这些集合成员中做任意的排列组合。</li>
</ul>
<p><img alt="160731-imooc-mysql-tutorials-notes-time" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199391-d60ff900-8653-11ea-9836-4fa28ac1cb33.png"></p>
<ul>
<li>YEAR：2 位或者 4 位，1970 到 2069 年，允许 70 到 69</li>
<li>TIME：-8385959 到 8385959</li>
<li>DATE：1000.01.01 到 9999.12.31</li>
<li>DATETIME：1000.01.01 00:00:00 到 9999.12.31 23:59:59</li>
<li>TIMESTAMP：1970.01.01 00 点起 到 2037 年之间的一个值</li>
</ul>
<h2 id="五数据表操作">五、数据表操作</h2>
<ul>
<li>数据表（或表）是数据库最重要的组成部分之一，是其他对象的基础</li>
<li>表是一个二维表，行称为 <code>记录</code>，列称为 <code>字段</code></li>
</ul>
<h3 id="创建数据表据表">创建数据表据表</h3>
<p>1、打开数据库：<code>USE db_name;</code>
2、查看当前数据库：<code>SELECT DATABASE();</code>
3、创建数据表：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="p">[</span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="p">]</span><span class="w"> </span><span class="k">table_name</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">column_name</span><span class="w"> </span><span class="n">data_type</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">column_name</span><span class="w"> </span><span class="n">data_type</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">tb1</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">username</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">userage</span><span class="w"> </span><span class="n">TINYINT</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">salary</span><span class="w"> </span><span class="nb">FLOAT</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><h3 id="查看数据表">查看数据表</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">TABLES</span><span class="w"> </span><span class="p">[</span><span class="k">FROM</span><span class="w"> </span><span class="n">db_name</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="k">LIKE</span><span class="w"> </span><span class="s1">&#39;pattern&#39;</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">expr</span><span class="p">];</span><span class="w">
</span></span></span></code></pre></div><p>查看当前选择的数据库的所有表</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">TABLES</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>查看 MYSQL 数据库中的所有表，当前选择数据库位置不变</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">TABLES</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">MYSQL</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>查看当前选择的数据库</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">DATABASE</span><span class="p">();</span><span class="w">
</span></span></span></code></pre></div><p>查看数据表结构</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">COLUMNS</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="k">table_name</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h3 id="插入与查找">插入与查找</h3>
<p>插入记录</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="p">[</span><span class="k">INTO</span><span class="p">]</span><span class="w"> </span><span class="n">tb_name</span><span class="w"> </span><span class="p">[(</span><span class="n">col_name</span><span class="p">,</span><span class="n">col_name</span><span class="p">,...)]</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="n">va1</span><span class="p">,...)</span><span class="w">
</span></span></span></code></pre></div><p>查找记录</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">expr</span><span class="p">,..</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">tb_name</span><span class="w">
</span></span></span></code></pre></div><h3 id="空值与非空">空值与非空</h3>
<p>NULL，字段值可以为空
NOT NULL，字段值禁止为空</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">tb2</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">username</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">age</span><span class="w"> </span><span class="n">TINYINT</span><span class="w"> </span><span class="n">UNSIGNE</span><span class="w"> </span><span class="k">NULL</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><h3 id="自动编号">自动编号</h3>
<p>自动编号 <code>AUTO_INCREMENT</code>
自动编号，且必须与主键配合使用</p>
<p>自动编号 AUTO_INCREMENT 作用
1、自动编号：保证记录的唯一性
2、类型必须为整型（可以是 FLOAT(5,0)等，小数点后必须为 0），必须和主键 PRIMARY KEY 组合使用
3、默认情况下，起始值为 1，每次的增量为 1</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">tb3</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">id</span><span class="w"> </span><span class="nb">SMALLINT</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">username</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">-- 报错，自动增量字段必须设置成主键
</span></span></span></code></pre></div><h3 id="主键约束">主键约束</h3>
<p><code>PRIMARY KEY</code>，也可以写成<code>KEY</code>
1、每张数据表只能存在一个主键
2、主键保证记录的唯一性
3、主键自动为<code>NOT NULL</code>，即必须要为主键赋值。但如果记录中添加了<code>AUTO_INCREMENT</code>，那么不需要手动赋值
4、<code>auto_increment</code>必须和主键<code>primary key</code>一起使用，但主键<code>primary key</code>不一定要和<code>auto_increment</code>一块使用</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">tb3</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">id</span><span class="w"> </span><span class="nb">SMALLINT</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">username</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><h3 id="唯一约束">唯一约束</h3>
<p><code>PRIMARE KEY</code>
主键约束 一张表中只能有一个</p>
<p><code>UNIQUE KEY</code>
1、唯一约束
2、唯一约束可以保证记录的唯一性
3、唯一约束的字段可以为空值
4、每张数据表可以存在多个唯一约束</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">tb3</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">id</span><span class="w"> </span><span class="nb">SMALLINT</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">username</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">UNIQUE</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">age</span><span class="w"> </span><span class="n">tinyint</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>在创建索引时也是有区别的</li>
</ul>
<h3 id="默认约束">默认约束</h3>
<p><code>DEFAULT</code> 当插入记录时，如果没有明确为字段赋值，则自动赋予默认值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">tb6</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">id</span><span class="w"> </span><span class="nb">SMALLINT</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">username</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">UNIQUE</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">sex</span><span class="w"> </span><span class="n">ENUM</span><span class="p">(</span><span class="s1">&#39;1&#39;</span><span class="p">,</span><span class="s1">&#39;2&#39;</span><span class="p">,</span><span class="s1">&#39;3&#39;</span><span class="p">)</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="s1">&#39;3&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>其中对性别字段默认选择<code>3</code></p>
<h2 id="六约束以及修改数据表">六、约束以及修改数据表</h2>
<ul>
<li>FOREIGN KEY：保持数据一致性，完整性；实现一对一或一对多关系。</li>
<li>要求：父表和子表必须使用相同的存储引擎,而且禁止使用临时表；数据表的存储引擎只能为 InnoDB；外键列和参照列必须具有类似的数据类型。其中数字的长度或是否有符号位必须相同；而字符的长度则可以不同；外键列和参照列必须创建索引。如果外键列不存在索引的话，MySQL 将自动创建索引。</li>
<li>在 MY.ini 文件中编辑默认的存储引擎：default-storage-engine=INNODB；</li>
<li>显示创建表的语句：SHOW CREATE TABLE table_name；</li>
<li>查看表是否有索引：SHOW INDEXS FROM table_name；</li>
<li>以网格查看表是否有索引：SHOW INDEXS FROM table_name\G；</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">table_name1</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">id</span><span class="w"> </span><span class="nb">SMALLINT</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">name</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">table_name2</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">id</span><span class="w"> </span><span class="nb">SMALLINT</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">username</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">pid</span><span class="w"> </span><span class="nb">SMALLINT</span><span class="w"> </span><span class="n">UNSIGNED</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FOREIGN</span><span class="w"> </span><span class="k">KEY</span><span class="w"> </span><span class="p">(</span><span class="n">pid</span><span class="p">)</span><span class="w"> </span><span class="k">REFERENCES</span><span class="w"> </span><span class="n">table_name1</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="cm">/* 外键 pid 参照 table_name1中的 id 字段 */</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>IMOOC MySQL 开发技巧</title>
      <link>https://zyf.im/2016/07/25/imooc-mysql-development-skills-notes/</link>
      <pubDate>Mon, 25 Jul 2016 10:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/07/25/imooc-mysql-development-skills-notes/</guid>
      <description>&lt;p&gt;主要涉及：JOIN 、JOIN 更新、GROUP BY HAVING 数据查重/去重。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;inner&#34;&gt;INNER&lt;/h2&gt;
&lt;p&gt;INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN(MySQL 不支持)、CROSS JOIN&lt;/p&gt;
&lt;p&gt;这是在网上找到的非常好的一篇博文，图解 JOIN 语句：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.codinghorror.com/a-visual-explanation-of-sql-joins/&#34;&gt;CODING HORROR-A Visual Explanation of SQL Joins&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;下图可以很清楚的明白，JOIN 的数据选取范围：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;160725-imooc-mysql-development-skills-notes-001&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/80199172-9d701f80-8653-11ea-8d20-498f54e41708.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;160725-imooc-mysql-development-skills-notes-002&#34; loading=&#34;lazy&#34; src=&#34;https://user-images.githubusercontent.com/9289792/80199188-9fd27980-8653-11ea-9386-e76dca7b4d0c.jpeg&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;更新使用过滤条件中包括本身的表&#34;&gt;更新使用过滤条件中包括本身的表&lt;/h2&gt;
&lt;p&gt;更新 &lt;code&gt;t1&lt;/code&gt; &lt;code&gt;t2&lt;/code&gt; 表中 &lt;code&gt;col_a&lt;/code&gt; 重复的字段：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;hi&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INNER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ERROR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1093&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可转换为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;aa&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INNER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bb&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;aa&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;col_a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;hi&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;查询重复数据删除重复数据&#34;&gt;查询重复数据、删除重复数据&lt;/h2&gt;
&lt;p&gt;利用 &lt;code&gt;GROUP BY&lt;/code&gt; 和 &lt;code&gt;HAVING&lt;/code&gt; 查询重复数据：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>主要涉及：JOIN 、JOIN 更新、GROUP BY HAVING 数据查重/去重。</p>
<!-- more -->
<h2 id="inner">INNER</h2>
<p>INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN(MySQL 不支持)、CROSS JOIN</p>
<p>这是在网上找到的非常好的一篇博文，图解 JOIN 语句：</p>
<blockquote>
<p><a href="https://blog.codinghorror.com/a-visual-explanation-of-sql-joins/">CODING HORROR-A Visual Explanation of SQL Joins</a></p>
</blockquote>
<p>下图可以很清楚的明白，JOIN 的数据选取范围：</p>
<p><img alt="160725-imooc-mysql-development-skills-notes-001" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199172-9d701f80-8653-11ea-8d20-498f54e41708.png"></p>
<p><img alt="160725-imooc-mysql-development-skills-notes-002" loading="lazy" src="https://user-images.githubusercontent.com/9289792/80199188-9fd27980-8653-11ea-9386-e76dca7b4d0c.jpeg"></p>
<h2 id="更新使用过滤条件中包括本身的表">更新使用过滤条件中包括本身的表</h2>
<p>更新 <code>t1</code> <code>t2</code> 表中 <code>col_a</code> 重复的字段：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">t1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">col_a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;hi&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w"> </span><span class="n">t1</span><span class="p">.</span><span class="n">col_a</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">SELECT</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">col_a</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">FROM</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">INNER</span><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="k">on</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">a</span><span class="p">.</span><span class="n">col_a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">col_a</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">ERROR</span><span class="p">:</span><span class="mi">1093</span><span class="w">
</span></span></span></code></pre></div><p>可转换为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="n">aa</span><span class="w"> </span><span class="k">JOIN</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">SELECT</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">col_a</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">FROM</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">INNER</span><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="k">on</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">a</span><span class="p">.</span><span class="n">col_a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">col_a</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="n">bb</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">aa</span><span class="p">.</span><span class="n">col_a</span><span class="o">=</span><span class="w"> </span><span class="n">bb</span><span class="p">.</span><span class="n">col_a</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">SET</span><span class="w"> </span><span class="n">col_a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;hi&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h2 id="查询重复数据删除重复数据">查询重复数据、删除重复数据</h2>
<p>利用 <code>GROUP BY</code> 和 <code>HAVING</code> 查询重复数据：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">col_a</span><span class="p">,</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="n">t1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">col_a</span><span class="w"> </span><span class="k">HAVING</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>删除重复数据，对于相同数据保留 ID 最大的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">DELETE</span><span class="w"> </span><span class="n">a</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">SELECT</span><span class="w"> </span><span class="n">col_a</span><span class="p">,</span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">),</span><span class="k">MAX</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">id</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">FROM</span><span class="w"> </span><span class="n">t1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">col_a</span><span class="w"> </span><span class="k">HAVING</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="n">b</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">col_a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">col_a</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">WHERE</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">id</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">id</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>写一些人文的日志</title>
      <link>https://zyf.im/2016/07/11/write-some-humanities-diary/</link>
      <pubDate>Mon, 11 Jul 2016 09:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/07/11/write-some-humanities-diary/</guid>
      <description>&lt;p&gt;还是越来越觉的思想重要。每周给自己博客的计划是写三篇，稍稍改动下：其中一篇要是关于思想上的。&lt;/p&gt;
&lt;p&gt;周末的时候和北京的朋友们：龙、梦、思，小聚了下。问起毕业一个月感觉有啥变化，都说没啥。是，刚刚才一个月。可我心里却有些小慌张，总想搞个大新闻，让自己不那么平凡。是眼高手低吗？&lt;/p&gt;
&lt;p&gt;现在每天都会跑跑步，已经坚持了 6 天了也习惯了，挺赞的。也总是去了解新鲜的东西技术，并且尝试运用它们，才有点原来学长们不喜欢用用过的东西做事情的劲。关于英语的学习，现在找到一个法：阅读英文的技术教程或者技术文档。这些东西肯定和计算机有关，也写的比较通俗，感觉挺有收获的。&lt;/p&gt;
&lt;p&gt;最近状态还不错，就是晚上想看会书，可眼睛挺累的。职业生涯的第一个月，经济有些困难。想参与个开源项目。现在手头项目就自己一个人在编，有点孤独，也不知道自己设计的和写的哪里好哪里不好。还有就是在 GitHub 或者 StackOverflow 有人给自己点赞，感觉挺激动的。&lt;/p&gt;
&lt;p&gt;因为公司都是 Google 的服务，出门很方便，GitHub 也用的越来越多。时不时总能搜到、看到很多工程师写的博客，有教程、有分享，还有我最喜欢看的：他们的思想。我觉的他们现在都是比我棒的，我想知道他们是怎么想的、怎么解决问题、怎么看待问题、看待工作生活。&lt;/p&gt;
&lt;p&gt;最后毒奶一口自己：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;与那些名校的同学的差距不是毕业后的薪水，也不是学业水平的差距，而是一种思维方式与做事标准。
对自己的标准会不由自主的降低以适应这个环境，减少自身与环境的冲突，在一个低标准下，自觉“满意”的度过每一天。
 
为什么名企喜欢要名校的学生？其实名企要的不是多么高的 GPA 成绩，而是一种内在的精神状态。他们的辛苦不叫辛苦，也不为百万年薪。辛苦是他们获得自我实现的途径，自我是实现使他无穷快乐。这世界就是一拨人在昼夜不停的高速运转，另一拨人起床发现世界变了。
 
我们大部分人的工作和生活状态是怎样的呢？
上班稍微努力点就开始讲究公平，自己不得志就开始抱怨公司和领导，下班后看几页书就觉得自己特别上进，辛苦上几天就觉得自己要赶紧去享受一下生活了，加几天班就担心自己会过劳死。
遇到些鸡毛蒜皮的小事儿就郁郁寡欢，仿佛遇到了天大的人生难题磨磨唧唧解决不清。看见牛逼的人也会心生羡慕，但总也突破不了努力却总不得要领魔咒。
 
越能干，越努力；越有钱，越上进
而造成这一切的差异，最主要的来自人的精神内核。
 
你要用牛人的标准要求自己，不断的走到牛人当中去，拉近和牛人之间的距离。当你觉得自己能够成为他们中的一员的时候，你才能成为了真正的牛人。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;成功？我才刚上路呢。&lt;/p&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>还是越来越觉的思想重要。每周给自己博客的计划是写三篇，稍稍改动下：其中一篇要是关于思想上的。</p>
<p>周末的时候和北京的朋友们：龙、梦、思，小聚了下。问起毕业一个月感觉有啥变化，都说没啥。是，刚刚才一个月。可我心里却有些小慌张，总想搞个大新闻，让自己不那么平凡。是眼高手低吗？</p>
<p>现在每天都会跑跑步，已经坚持了 6 天了也习惯了，挺赞的。也总是去了解新鲜的东西技术，并且尝试运用它们，才有点原来学长们不喜欢用用过的东西做事情的劲。关于英语的学习，现在找到一个法：阅读英文的技术教程或者技术文档。这些东西肯定和计算机有关，也写的比较通俗，感觉挺有收获的。</p>
<p>最近状态还不错，就是晚上想看会书，可眼睛挺累的。职业生涯的第一个月，经济有些困难。想参与个开源项目。现在手头项目就自己一个人在编，有点孤独，也不知道自己设计的和写的哪里好哪里不好。还有就是在 GitHub 或者 StackOverflow 有人给自己点赞，感觉挺激动的。</p>
<p>因为公司都是 Google 的服务，出门很方便，GitHub 也用的越来越多。时不时总能搜到、看到很多工程师写的博客，有教程、有分享，还有我最喜欢看的：他们的思想。我觉的他们现在都是比我棒的，我想知道他们是怎么想的、怎么解决问题、怎么看待问题、看待工作生活。</p>
<p>最后毒奶一口自己：</p>
<blockquote>
<p>与那些名校的同学的差距不是毕业后的薪水，也不是学业水平的差距，而是一种思维方式与做事标准。
对自己的标准会不由自主的降低以适应这个环境，减少自身与环境的冲突，在一个低标准下，自觉“满意”的度过每一天。
 
为什么名企喜欢要名校的学生？其实名企要的不是多么高的 GPA 成绩，而是一种内在的精神状态。他们的辛苦不叫辛苦，也不为百万年薪。辛苦是他们获得自我实现的途径，自我是实现使他无穷快乐。这世界就是一拨人在昼夜不停的高速运转，另一拨人起床发现世界变了。
 
我们大部分人的工作和生活状态是怎样的呢？
上班稍微努力点就开始讲究公平，自己不得志就开始抱怨公司和领导，下班后看几页书就觉得自己特别上进，辛苦上几天就觉得自己要赶紧去享受一下生活了，加几天班就担心自己会过劳死。
遇到些鸡毛蒜皮的小事儿就郁郁寡欢，仿佛遇到了天大的人生难题磨磨唧唧解决不清。看见牛逼的人也会心生羡慕，但总也突破不了努力却总不得要领魔咒。
 
越能干，越努力；越有钱，越上进
而造成这一切的差异，最主要的来自人的精神内核。
 
你要用牛人的标准要求自己，不断的走到牛人当中去，拉近和牛人之间的距离。当你觉得自己能够成为他们中的一员的时候，你才能成为了真正的牛人。</p>
</blockquote>
<p>成功？我才刚上路呢。</p>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Ubuntu JDK Nginx WildFly MySQL 环境配置</title>
      <link>https://zyf.im/2016/07/07/ubuntu-jdk-nginx-wildfly-mysql-config/</link>
      <pubDate>Thu, 07 Jul 2016 10:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/07/07/ubuntu-jdk-nginx-wildfly-mysql-config/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 内容声明&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;本文写于 &lt;strong&gt;2016 年&lt;/strong&gt;，部分内容已过时，仅供历史参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ppa:webupd8team/java&lt;/code&gt; 已于 2019 年停止维护&lt;/li&gt;
&lt;li&gt;WildFly 10、MySQL 5.6 均已停止支持&lt;/li&gt;
&lt;li&gt;Ubuntu 14.04 已于 2019 年 EOL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;当前最佳实践（2024+）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JDK 安装&lt;/strong&gt;：使用 &lt;code&gt;apt install openjdk-21-jdk&lt;/code&gt; 或通过 &lt;a href=&#34;https://sdkman.io/&#34;&gt;SDKMAN&lt;/a&gt; 管理多版本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java 应用部署&lt;/strong&gt;：优先考虑 Spring Boot 内嵌容器 + systemd 服务，或直接容器化部署&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;环境编排&lt;/strong&gt;：使用 Docker Compose 统一管理 Nginx + 应用 + 数据库&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;：使用 MySQL 8.x 或考虑 PostgreSQL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反向代理&lt;/strong&gt;：Nginx 配置思路仍可参考，但建议结合 Let&amp;rsquo;s Encrypt 配置 HTTPS&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;新项目部署上线，主要参考 &lt;a href=&#34;http://blog.csdn.net/hanshileiai&#34;&gt;世雷博客&lt;/a&gt; 的内容，自己也总结了下。从 JDK 安装、Web 容器、数据库，都有涉及比较全面。&lt;/p&gt;
&lt;h2 id=&#34;jdk8&#34;&gt;JDK8&lt;/h2&gt;
&lt;h3 id=&#34;安装-jdk8&#34;&gt;安装 JDK8&lt;/h3&gt;
&lt;p&gt;1、添加软件源&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo add-apt-repository ppa:webupd8team/java
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;2、更新软件源&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;3、安装 jdk1.8&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>⚠️ 内容声明</strong></p>
<p>本文写于 <strong>2016 年</strong>，部分内容已过时，仅供历史参考：</p>
<ul>
<li><code>ppa:webupd8team/java</code> 已于 2019 年停止维护</li>
<li>WildFly 10、MySQL 5.6 均已停止支持</li>
<li>Ubuntu 14.04 已于 2019 年 EOL</li>
</ul>
<p><strong>当前最佳实践（2024+）：</strong></p>
<ul>
<li><strong>JDK 安装</strong>：使用 <code>apt install openjdk-21-jdk</code> 或通过 <a href="https://sdkman.io/">SDKMAN</a> 管理多版本</li>
<li><strong>Java 应用部署</strong>：优先考虑 Spring Boot 内嵌容器 + systemd 服务，或直接容器化部署</li>
<li><strong>环境编排</strong>：使用 Docker Compose 统一管理 Nginx + 应用 + 数据库</li>
<li><strong>MySQL</strong>：使用 MySQL 8.x 或考虑 PostgreSQL</li>
<li><strong>反向代理</strong>：Nginx 配置思路仍可参考，但建议结合 Let&rsquo;s Encrypt 配置 HTTPS</li>
</ul>
</blockquote>
<hr>
<p>新项目部署上线，主要参考 <a href="http://blog.csdn.net/hanshileiai">世雷博客</a> 的内容，自己也总结了下。从 JDK 安装、Web 容器、数据库，都有涉及比较全面。</p>
<h2 id="jdk8">JDK8</h2>
<h3 id="安装-jdk8">安装 JDK8</h3>
<p>1、添加软件源</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo add-apt-repository ppa:webupd8team/java
</span></span></code></pre></div><p>2、更新软件源</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get update
</span></span></code></pre></div><p>3、安装 jdk1.8</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get install oracle-java8-installer
</span></span></code></pre></div><h3 id="查看-java-安装路径">查看 Java 安装路径</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo update-alternatives --config java
</span></span><span class="line"><span class="cl">sudo update-alternatives --config javac
</span></span></code></pre></div><h3 id="查看-java-安装后的版本">查看 Java 安装后的版本</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">java -version
</span></span></code></pre></div><h3 id="扩展增加多版本-jdk-和切换方法">（扩展）增加多版本 JDK 和切换方法</h3>
<p>1、安装 JDK 6 和 JDK 7</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get install oracle-java6-installer
</span></span><span class="line"><span class="cl">sudo apt-get install oracle-java7-installer
</span></span></code></pre></div><p>2、查看所有 JDK 安装版本</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo update-java-alternatives -l
</span></span><span class="line"><span class="cl">java-6-oracle <span class="m">3</span> /usr/lib/jvm/java-6-oracle
</span></span><span class="line"><span class="cl">java-7-oracle <span class="m">4</span> /usr/lib/jvm/java-7-oracle
</span></span><span class="line"><span class="cl">java-8-oracle <span class="m">2</span> /usr/lib/jvm/java-8-oracle
</span></span></code></pre></div><p>3、通过 <code>-s</code> 参数可以方便的切换到其它的 JDK 版本</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo update-java-alternatives -s java-6-oracle
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo update-java-alternatives -s java-7-oracle
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo update-java-alternatives -s java-8-oracle
</span></span></code></pre></div><p>4、再次查看 JDK 版本</p>
<pre tabindex="0"><code>java -version
</code></pre><pre tabindex="0"><code>java version &#34;1.6.0_45&#34;
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)
</code></pre><!-- more -->
<h2 id="nginx">Nginx</h2>
<h3 id="nginx-安装">Nginx 安装</h3>
<p>1、更新软件源</p>
<pre tabindex="0"><code>sudo apt-get update
</code></pre><p>2、安装 Nginx</p>
<pre tabindex="0"><code>sudo apt-get install nginx
</code></pre><p>3、查看 Nginx 位置</p>
<pre tabindex="0"><code>whereis nginx
</code></pre><h3 id="nginx-禁止外网访问">Nginx 禁止外网访问</h3>
<p>防止 Google 收录</p>
<pre tabindex="0"><code>sudo vi /etc/nginx/sites-enabled/default
</code></pre><p>在 <code>server</code> 中添加</p>
<pre tabindex="0"><code># server add
# 公司代理IP
allow *.*.*.*;
deny all;
</code></pre><h3 id="nginx-域名调整">Nginx 域名调整</h3>
<p>域名 <code>www</code> 跳转 <code>non www</code>，<code>server</code> 中添加配置</p>
<pre tabindex="0"><code>server_name www.aaa.org aaa.org;
if ($host != &#39;aaa.org&#39;){
 rewrite ^/(.*)$ http://aaa.org/$1 permanent;
}
</code></pre><h3 id="nginx-gzip">Nginx GZip</h3>
<pre tabindex="0"><code>##
# Gzip Settings
##

gzip on;
gzip_disable &#34;msie6&#34;;

gzip_vary on;
gzip_min_length 1k;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/x-httpd-php image/jpeg image/gif image/png image/x-icon
</code></pre><h3 id="nginx-静态文件缓存">Nginx 静态文件缓存</h3>
<pre tabindex="0"><code>location ~*\.(gif|jpg|jpeg|png|bmp|swf|woff|icon)$ {
 proxy_cache my_zone;
 proxy_cache_bypass $http_cache_control;
 proxy_cache_valid 200 1d;
 add_header X-Proxy-Cache $upstream_cache_status;
 expires 15d;
}

location ~ .*\.(js|css|ttf)$ {
 proxy_cache my_zone;
 proxy_cache_bypass $http_cache_control;
 proxy_cache_valid 200 1d;
 add_header X-Proxy-Cache $upstream_cache_status;
 expires 15d;
}
</code></pre><p>静态文件放在配置的 <code>root</code> 下</p>
<pre tabindex="0"><code>root /opt/***;
</code></pre><p>还有个技巧，项目中大量资源文件，例如 PDF，在设计访问 url 时可以 <code>**/pdf/**</code>，这样在 Nginx 进行配置就可以将文件分离出项目；这些文件也放在 <code>root</code> 路径下。</p>
<pre tabindex="0"><code>location ^~ /html/ {
}
location ^~ /pdf/ {
}
</code></pre><h3 id="nginx-commands">Nginx Commands</h3>
<pre tabindex="0"><code>sudo service nginx stop
sudo service nginx start
sudo service nginx restart
</code></pre><h3 id="nginx-show-log">Nginx Show Log</h3>
<pre tabindex="0"><code>sudo tail -f /var/log/nginx/error.log
</code></pre><h2 id="wildfly-1000final">WildFly 10.0.0.Final</h2>
<h3 id="wildfly-安装">WildFly 安装</h3>
<p>1、下载 WildFly，并提取到 /opt 目录 WildFly 10.0.0.Final <a href="http://wildfly.org/downloads/">下载地址</a></p>
<pre tabindex="0"><code>cd /opt
sudo wget -c http://download.jboss.org/wildfly/10.0.0.Final/wildfly-10.0.0.Final.tar.gz
sudo tar -xzvf wildfly-10.0.0.Final.tar.gz
</code></pre><p>2、创建 WildFly 用户和组</p>
<pre tabindex="0"><code>sudo addgroup wildfly
sudo useradd -g wildfly wildfly
</code></pre><p>改变 wildfly 文件夹的所有权：</p>
<pre tabindex="0"><code>sudo chown -R wildfly:wildfly /opt/wildfly-10.0.0.Final
</code></pre><p>创建一个链接映射（好处：如果你改变 WildFly 版本,不需要更新其他配置）</p>
<pre tabindex="0"><code>sudo ln -s wildfly-10.0.0.Final /opt/wildfly
</code></pre><p>3、安装 init.d 脚本
设置并使用 init.d 脚本来启动和停止 WildFly。复制<code>/opt/wildfly/bin/init.d/wildfly-init-debian.sh</code>脚本到 <code>/etc/init.d/wildfly</code>，更改权限,并使其可执行</p>
<pre tabindex="0"><code>sudo cp /opt/wildfly/docs/contrib/scripts/init.d/wildfly-init-debian.sh /etc/init.d/wildfly
sudo chown root:root /etc/init.d/wildfly
sudo chmod ug+x /etc/init.d/wildfly
</code></pre><p>启动/停止 WildFly 命令</p>
<pre tabindex="0"><code>sudo /etc/init.d/wildfly start
sudo /etc/init.d/wildfly stop
</code></pre><p>4、WildFly 做为系统服务，开机启动</p>
<pre tabindex="0"><code>sudo update-rc.d wildfly defaults
</code></pre><h3 id="配置-wildfly-允许所有-ip-访问">配置 WildFly 允许所有 ip 访问</h3>
<p>1、打开配置文件 <code>standalone.xml</code></p>
<pre tabindex="0"><code>sudo vi /opt/wildfly/standalone/configuration/standalone.xml
</code></pre><p>2、替换此处</p>
<pre tabindex="0"><code>&lt;interface name=&#34;management&#34;&gt;
    &lt;inet-address value=&#34;${jboss.bind.address.management:127.0.0.1}&#34;/&gt;
&lt;/interface&gt;
&lt;interface name=&#34;public&#34;&gt;
    &lt;inet-address value=&#34;${jboss.bind.address:0.0.0.0}&#34;/&gt;
&lt;/interface&gt;
</code></pre><p>改为</p>
<pre tabindex="0"><code>&lt;interface name=&#34;management&#34;&gt;
    &lt;any-address/&gt;
&lt;/interface&gt;
&lt;interface name=&#34;public&#34;&gt;
    &lt;any-address/&gt;
&lt;/interface&gt;
</code></pre><p>3、保存后，重新启动 WildFily</p>
<pre tabindex="0"><code>sudo service wildfly restart
</code></pre><h3 id="删除默认欢迎内容可选">删除默认欢迎内容（可选）</h3>
<p>如果你部署了应用程序在上下文根目录里，欢迎你 将需要从 WildFly 配置删除默认内容。在 standalone.xml 文件里删除粗体突出显示的行</p>
<pre tabindex="0"><code>&lt;server name=&#34;default-server&#34;&gt;
    &lt;http-listener name=&#34;default&#34; socket-binding=&#34;http&#34;/&gt;
    &lt;host name=&#34;default-host&#34; alias=&#34;localhost&#34;&gt;
        **&lt;!-- &lt;location name=&#34;/&#34; handler=&#34;welcome-content&#34;/&gt; --&gt;**
        &lt;filter-ref name=&#34;server-header&#34;/&gt;
        &lt;filter-ref name=&#34;x-powered-by-header&#34;/&gt;
    &lt;/host&gt;
&lt;/server&gt;
&lt;handlers&gt;
    **&lt;!-- &lt;file name=&#34;welcome-content&#34; path=&#34;${jboss.home.dir}/welcome-content&#34;/&gt; --&gt;**
&lt;/handlers&gt;
</code></pre><h3 id="其它设置">其它设置</h3>
<p>改为可以修改 JSP 页面不用重启</p>
<pre tabindex="0"><code>&lt;servlet-container name=&#34;default&#34;&gt;
      &lt;jsp-config development=&#34;true&#34;/&gt;
&lt;/servlet-container&gt;
</code></pre><h3 id="注意事项">注意事项</h3>
<p>1、项目以站点根目录访问
你现在可以将应用程序部署到 WildFly 视图在 your_ip:8080
在你的项目目录 WEB-INF 下添加 jboss-web.xml，确保你的配置 context-root 设置为 /</p>
<pre tabindex="0"><code>&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;
&lt;jboss-web&gt;
    &lt;context-root&gt;/&lt;/context-root&gt;
&lt;/jboss-web&gt;
</code></pre><p>2、Linux 里设置端口 80 到 8080
<strong>注意：在之后的配置会使用 Nginx 反向代理，所用 WildFly 端口不用映射为 80，这里只是个方法的笔记</strong></p>
<p>注意，在 linux 里，由于内核的限制，普通用户不能使用 1024 一下的端口。所以在配置文件（standalone.xml）里改成 80，用普通用户是启动不了的。</p>
<p>此时，我们需要在 linux 下使用 root 用户运行一个命令，使访问 80 端口的应用转到 8080 上：</p>
<pre tabindex="0"><code>sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
</code></pre><p>以上端口转发为临时操作，重启 linux 服务器后失效。如果要重启服务器不丢失 FORWARD 转发”操作，可写入配置。</p>
<blockquote>
<p>在 Linux 的下面部署了 tomcat，为了安全我们使用非 root 用户进行启动，但是在域名绑定时无法直接访问 80 端口号。众所周知，在 unix 下，非 root 用户不能监听 1024 以上的端口号，这个 tomcat 服务器就没办法绑定在 80 端口下。所以这里需要使用 linux 的端口转发机制，把到 80 端口的服务请求都转到 8080 端口上。</p>
</blockquote>
<p>2.1、安装 iptables-persistent</p>
<pre tabindex="0"><code>sudo apt-get update
sudo apt-get install iptables-persistent
</code></pre><p>2.2、添加 80 端口跳转到 8080 规则</p>
<pre tabindex="0"><code>sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
</code></pre><p>2.3、保存跳转规则</p>
<pre tabindex="0"><code>sudo service iptables-persistent save
</code></pre><p>3、wildfly 不支持 struts2 的配置文件（.xml）里用通配符
<strong>这是原博文的内容，我没有使用 struts2；在 spring 中有用通配符，但是好像没什么影响</strong>
jboss wildfly 不支持 struts2 配置文件里用通配符 <code>*.xml</code>，如下：</p>
<pre tabindex="0"><code>    &lt;!-- &lt;include file=&#34;struts/*.xml&#34;&gt;&lt;/include&gt; --&gt;
    &lt;include file=&#34;struts/struts_post.xml&#34;&gt;&lt;/include&gt;
    &lt;include file=&#34;struts/struts_user.xml&#34;&gt;&lt;/include&gt;
</code></pre><p>4、增加部署扫描仪的超时设置
位置：</p>
<pre tabindex="0"><code>&lt;subsystem xmlns=&#34;urn:jboss:domain:deployment-scanner:2.0&#34;&gt;
    &lt;deployment-scanner path=&#34;deployments&#34; relative-to=&#34;jboss.server.base.dir&#34; scan-interval=&#34;5000&#34; /&gt;
&lt;/subsystem&gt;
</code></pre><p><code>&lt;deployment-scanner&gt;</code> 内增加属性 <code>deployment-timeout=&quot;1200&quot;</code> 如下：</p>
<pre tabindex="0"><code>&lt;subsystem xmlns=&#34;urn:jboss:domain:deployment-scanner:2.0&#34;&gt;
    &lt;deployment-scanner path=&#34;deployments&#34; relative-to=&#34;jboss.server.base.dir&#34; scan-interval=&#34;5000&#34; deployment-timeout=&#34;1200&#34; /&gt;
&lt;/subsystem&gt;
</code></pre><h3 id="nginx-反向代理-wildfly">Nginx 反向代理 WildFly</h3>
<p>1、在 <code>http</code> 中添加</p>
<pre tabindex="0"><code>upstream jboss {
 server 127.0.0.1:8080;
}
</code></pre><p>2、在 <code>server</code> 中修改</p>
<pre tabindex="0"><code>location /{
 proxy_pass http://jboss;
 proxy_redirect off;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header Host $http_host;
 index index.html index.htm index.jsp;
 # 动态网站要小心这个 缓存 选项
 add_header Cache-Control max-age=1728000;
}
</code></pre><h2 id="mysql">MySQL</h2>
<h3 id="mysql56-安装">MySQL5.6 安装</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get install mysql-server-5.6
</span></span></code></pre></div><h3 id="mysql-创建数据库">MySQL 创建数据库</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">CREATE DATABASE IF NOT EXISTS scrapy DEFAULT CHARSET utf8 COLLATE utf8_general_ci<span class="p">;</span>
</span></span></code></pre></div><h3 id="mysql-数据库导出导入">MySQL 数据库导出/导入</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mysqldump -u root -p123456 db_name <span class="p">|</span> gzip &gt; /home/backup/db_name.sql.gz
</span></span><span class="line"><span class="cl">mysqldump -u root -p123456 --all-databases <span class="p">|</span> gzip &gt; /home/backup/all.sql.gz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">mysqldump -h192.168.1.100 -uroot -p db_name &gt; db_name.sql
</span></span><span class="line"><span class="cl">mysqldump -uroot -p db_name_1 db_name_2 &gt; db_name_1_and_2.sql
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gunzip &lt; db_name.sql.gz <span class="p">|</span> mysql -u root -p123456 db_name
</span></span><span class="line"><span class="cl">gunzip &lt; all.sql.gz <span class="p">|</span> mysql -u root -p123456
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">mysql -uroot -p
</span></span><span class="line"><span class="cl">show databases<span class="p">;</span>
</span></span><span class="line"><span class="cl">use db_name<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/db_name.sql
</span></span></code></pre></div><h3 id="mysql-查看表结构">MySQL 查看表结构</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">desc</span><span class="w"> </span><span class="k">user</span><span class="w">
</span></span></span></code></pre></div><h2 id="project">Project</h2>
<h3 id="robotstxt-config-禁止所有爬虫爬取">robots.txt config 禁止所有爬虫爬取</h3>
<pre tabindex="0"><code>User-agent: *
Disallow: /
</code></pre><h2 id="references">References</h2>
<ul>
<li><a href="http://blog.csdn.net/hanshileiai/article/details/46968275">韩世雷-ubuntu 配置 java jdk1.8 环境，增加多版本 jdk 和切换方法</a></li>
<li><a href="http://blog.csdn.net/hanshileiai/article/details/46987859">韩世雷-ubuntu14.04 Terminal 配置 wildfly-10.0.0.Final 服务器</a></li>
<li><a href="http://blog.csdn.net/hanshileiai/article/details/47757217">韩世雷-Ubuntu14.04 配置 iptables 把 80 端口转到 8080</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>GitHub Webhook 自动部署 Hexo</title>
      <link>https://zyf.im/2016/07/01/github-webhook-example/</link>
      <pubDate>Fri, 01 Jul 2016 20:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/07/01/github-webhook-example/</guid>
      <description>&lt;p&gt;在 &lt;a href=&#34;https://zyf.im/2016/06/30/github-pages-forbidden-baiduspide-solution/&#34;&gt;GitHub Pages 不被百度收录解决方案&lt;/a&gt; 中，思路二是通过 Dnspod 的智能 DNS 服务。简而言之就是搭建一个 Server，做一个 Blog 的镜像站，专为百度收录使用。&lt;/p&gt;
&lt;p&gt;但是每次将新建的博客文章 &lt;code&gt;PUSH&lt;/code&gt; 到 GitHub 后，还要再登陆 Server &lt;code&gt;PULL&lt;/code&gt; 一下，简直是太蠢了。那有什么解决办法吗？答：GitHub Webhook。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;webhook&#34;&gt;Webhook&lt;/h2&gt;
&lt;p&gt;Webhook，也就是人们常说的钩子，是一个很有用的工具。你可以通过定制 Webhook 来监测你在 Github.com 上的各种事件，最常见的莫过于 push 事件。&lt;/p&gt;
&lt;p&gt;如果你设置了一个监测 push 事件的 Webhook，那么每当你的这个项目有了任何提交，这个 Webhook 都会被触发，这时 Github 就会发送一个 HTTP POST 请求到你配置好的地址。&lt;/p&gt;
&lt;p&gt;如此一来，你就可以通过这种方式去自动完成一些重复性工作；比如，你可以用 Webhook 来自动触发一些持续集成（CI）工具的运作，比如 Travis CI；又或者是通过 Webhook 去部署你的线上服务器。&lt;/p&gt;
&lt;p&gt;Github 开发者平台的文档中对 Webhook 的所能做的事是这样描述的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You’re only limited by your imagination.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;响应-webhook&#34;&gt;响应 Webhook&lt;/h2&gt;
&lt;p&gt;在参考文章里博主是使用 Node.js 编写的服务端响应代码，但考虑到自己对 Node.js 不熟悉，还要部署环境，所以改用 Python 语言编写响应代码。&lt;/p&gt;
&lt;p&gt;自己在 GitHub 搜索下 &lt;code&gt;github webhook&lt;/code&gt;，&lt;code&gt;language&lt;/code&gt; 选择 &lt;code&gt;Python&lt;/code&gt; 便找到了 Python 编写的：&lt;a href=&#34;https://github.com/razius/github-webhook-handler&#34;&gt;razius/github-webhook-handler&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>在 <a href="/2016/06/30/github-pages-forbidden-baiduspide-solution/">GitHub Pages 不被百度收录解决方案</a> 中，思路二是通过 Dnspod 的智能 DNS 服务。简而言之就是搭建一个 Server，做一个 Blog 的镜像站，专为百度收录使用。</p>
<p>但是每次将新建的博客文章 <code>PUSH</code> 到 GitHub 后，还要再登陆 Server <code>PULL</code> 一下，简直是太蠢了。那有什么解决办法吗？答：GitHub Webhook。</p>
<!-- more -->
<h2 id="webhook">Webhook</h2>
<p>Webhook，也就是人们常说的钩子，是一个很有用的工具。你可以通过定制 Webhook 来监测你在 Github.com 上的各种事件，最常见的莫过于 push 事件。</p>
<p>如果你设置了一个监测 push 事件的 Webhook，那么每当你的这个项目有了任何提交，这个 Webhook 都会被触发，这时 Github 就会发送一个 HTTP POST 请求到你配置好的地址。</p>
<p>如此一来，你就可以通过这种方式去自动完成一些重复性工作；比如，你可以用 Webhook 来自动触发一些持续集成（CI）工具的运作，比如 Travis CI；又或者是通过 Webhook 去部署你的线上服务器。</p>
<p>Github 开发者平台的文档中对 Webhook 的所能做的事是这样描述的：</p>
<blockquote>
<p>You’re only limited by your imagination.</p>
</blockquote>
<h2 id="响应-webhook">响应 Webhook</h2>
<p>在参考文章里博主是使用 Node.js 编写的服务端响应代码，但考虑到自己对 Node.js 不熟悉，还要部署环境，所以改用 Python 语言编写响应代码。</p>
<p>自己在 GitHub 搜索下 <code>github webhook</code>，<code>language</code> 选择 <code>Python</code> 便找到了 Python 编写的：<a href="https://github.com/razius/github-webhook-handler">razius/github-webhook-handler</a></p>
<p>自己的 VPS 是在 <a href="https://bandwagonhost.com/aff.php?aff=5403">Bandwagon Host</a> 上购买的，最合适的配置：</p>
<ul>
<li>Self-managed service</li>
<li>SSD: 10 GB</li>
<li>RAM: 512 MB</li>
<li>CPU: 1x Intel Xeon</li>
<li>BW: 1000 GB/mo</li>
<li>Link speed: 1 Gigabit</li>
<li>VPS technology: OpenVZ/KiwiVM</li>
<li>Linux OS: 32-bit and 64-bit Centos, Debian, Ubuntu, Fedora</li>
</ul>
<p>费用：</p>
<ul>
<li>$2.99 USD Monthly</li>
<li>$7.99 USD Quarterly</li>
<li>$12.99 USD Semi-Annually</li>
<li>$19.99 USD Annually</li>
</ul>
<p>Bandwagon Host 支持支付宝付款很是方便，优惠码：<code>IAMSMART52J3NC</code> 再打个 9.5 折左右。这配置有时会 <code>(out of stock)</code> 等等随缘会有的。</p>
<h3 id="安装-git">安装 Git</h3>
<pre tabindex="0"><code>apt-get update
apt-get install git
</code></pre><h3 id="clone-raziusgithub-webhook-handler">clone <a href="https://github.com/razius/github-webhook-handler">razius/github-webhook-handler</a></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/razius/github-webhook-handler.git
</span></span></code></pre></div><p>根据 github-webhook-handler Gettings started:</p>
<h4 id="installation-requirements">Installation Requirements</h4>
<p>Install dependencies found in requirements.txt</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pip install -r requirements.txt
</span></span></code></pre></div><h4 id="repository-configuration">Repository Configuration</h4>
<p>Edit repos.json to configure repositories, each repository must be registered under the form <code>GITHUB_USER/REPOSITORY_NAME</code>.</p>
<pre tabindex="0"><code>{
    &#34;razius/puppet&#34;: {
        &#34;path&#34;: &#34;/home/puppet&#34;,
        &#34;key&#34;: &#34;MyVerySecretKey&#34;,
        &#34;action&#34;: [[&#34;git&#34;, &#34;pull&#34;, &#34;origin&#34;, &#34;master&#34;]]
    },
    &#34;d3non/somerandomexample/branch:live&#34;: {
        &#34;path&#34;: &#34;/home/exampleapp&#34;,
        &#34;key&#34;: &#34;MyVerySecretKey&#34;,
        &#34;action&#34;: [[&#34;git&#34;, &#34;pull&#34;, &#34;origin&#34;, &#34;live&#34;],
            [&#34;echo&#34;, &#34;execute&#34;, &#34;some&#34;, &#34;commands&#34;, &#34;...&#34;]]
    }
}
</code></pre><p>过程如果出现：<code>pip: command not found</code> 可以执行：</p>
<pre tabindex="0"><code>sudo apt-get install python-pip
</code></pre><h4 id="set-environment-variable-for-the-reposjson-config">Set environment variable for the repos.json config</h4>
<pre tabindex="0"><code>export REPOS_JSON_PATH=/path/to/repos.json
</code></pre><h4 id="start-the-server">Start the server</h4>
<pre tabindex="0"><code>python index.py 80
</code></pre><h4 id="clone-and-pull-your-blog">clone and pull your Blog</h4>
<pre tabindex="0"><code>git clone git@github.com:imzyf/imzyf.github.io.git
git pull origin master
</code></pre><p>过程如果出现：<code>Permission denied (publickey).</code> 可以参考：</p>
<ul>
<li><a href="http://stackoverflow.com/questions/2643502/git-permission-denied-publickey">Stack Overflow-Git - Permission denied (publickey)</a></li>
</ul>
<pre tabindex="0"><code>cd ~/.ssh &amp;&amp; ssh-keygen
cat id_rsa.pub | xclip
</code></pre><p>然后添加到你的 GitHub 账户：Settings -&gt; SSH keys 中。</p>
<h3 id="config-imzyfgithubio-webhooks">config imzyf.github.io Webhooks</h3>
<p>首先进入你的 repo 主页，通过点击页面上的按钮 [settings] -&gt; [Webhooks &amp; service] 进入 Webhooks 配置主页面。也可以通过下面这个链接直接进入配置页面：</p>
<pre tabindex="0"><code>https://github.com/[ 用户名 ]/[ 仓库名称 ]/settings/hooks
</code></pre><p>此处只需要配置 Webhook 所发出的 POST 请求发往何处即可，于是我们就配置我们所需要的路径：
<a href="">http://104.236.xxx.xxx:9988/deploy/</a>。这个地址指向的就是那个能够响应 Webhook 所发出请求的服务器。</p>
<p>配置好 Webhook 后，Github 会发送一个 ping 来测试这个地址。如果成功了，那么这个 Webhook 前就会加上一个绿色的勾；如果你得到的是一个红色的叉，那就好好检查一下哪儿出问题了吧！</p>
<h3 id="config-nginx-server">config Nginx server</h3>
<h2 id="最后想说的">最后想说的</h2>
<p>为了一个百度收录大可不必这么麻烦，刚刚查到这些解决方案时心里也是烦的、虚的，但还是硬的头皮搞了。过程中已经不单单是为了百度收录自己的博客，而是变成学习了东西、思考问题。这些解决方案网上都有，也不是自己创造的，但是别人的东西自己不尝试，就还是别人的。</p>
<p>现在百度已经收录我的博客了，imzyf.github.io 到 Server 也是自动部署的。解决问题后的快乐和信心，才是我这次最大的收获。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://jerryzou.com/posts/webhook-practice/">jerryzou-Webhook 实践 —— 自动部署</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>GitHub Pages 不被百度收录解决方案</title>
      <link>https://zyf.im/2016/06/30/github-pages-forbidden-baiduspide-solution/</link>
      <pubDate>Thu, 30 Jun 2016 20:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/06/30/github-pages-forbidden-baiduspide-solution/</guid>
      <description>&lt;p&gt;2019-12-02 更新：现在我的 Blog 还是用的 GitHub Pages，反正没有备案的域名不会被百度收录。&lt;/p&gt;
&lt;p&gt;2017-04-22 更新：现在我的 Blog 使用的是 &lt;a href=&#34;https://www.ufovps.com/&#34;&gt;UFOVPS&lt;/a&gt; 直接部署的。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;在 &lt;a href=&#34;https://zyf.im/2016/06/24/hexo-github-blog/&#34;&gt;使用 Hexo 和 Github 搭建个人独立博客&lt;/a&gt; 几天后，发现百度并不对博客进行收录。&lt;/p&gt;
&lt;p&gt;在天朝使用百度搜索毕竟多数，使用百度站长工具-抓取诊断，在百度 Spider 抓取结果返回 HTTP 头：HTTP/1.1 403 Forbidden，原来是 GitHub 禁止了百度爬虫的爬去。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Google 后早已有许多热心网友给出了解决方案，自己在这里总结下。&lt;/p&gt;
&lt;h2 id=&#34;思路一利用-cdn-解决百度爬虫被-github-pages-拒绝的问题&#34;&gt;思路一：利用 CDN 解决百度爬虫被 Github Pages 拒绝的问题&lt;/h2&gt;
&lt;h3 id=&#34;解决思路&#34;&gt;解决思路&lt;/h3&gt;
&lt;p&gt;既然 Github 彻底和百度决裂了，那我们也只能自己动手来解决了。Github 可能是封了百度的 IP，也有可能是封了百度爬虫的 User-Agent。&lt;/p&gt;
&lt;p&gt;所以要解决这个问题，最好就不要让百度爬虫直接访问 Github 了，需要在中间套一层 &lt;a href=&#34;https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86&#34;&gt;反向代理&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;那么问题又来了，既然我可以搭一个反向代理服务器了，那我为什么不直接把博客放在这台服务器上？放 Github Pages 上不就是为了少一台服务器，少一点费用吗？&lt;/p&gt;
&lt;p&gt;那有没有免费的第三方反向代理服务呢？当然有，其实现在各种 &lt;strong&gt;CDN 服务&lt;/strong&gt; 不就是吗？而且还额外提供了各种网络环境下的加速功能。&lt;/p&gt;
&lt;p&gt;但是使用 CDN 也会有一个非常大的缺点：只能对 &lt;strong&gt;静态资源&lt;/strong&gt; 做，因为 CDN 和反向代理有一个很大的不同就是：它会做缓存，并向各个节点分发。&lt;/p&gt;
&lt;p&gt;所以 CDN 一般都是用来给静态资源做加速的。如果你对动态页面做加速，用户看到的页面在一段时间内就一直不会变了。但是我们不怕！因为 Github Pages 本来就是全静态的！&lt;/p&gt;
&lt;p&gt;国内提供 CDN 服务的有：加速乐、七牛云存储、又拍云等。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>2019-12-02 更新：现在我的 Blog 还是用的 GitHub Pages，反正没有备案的域名不会被百度收录。</p>
<p>2017-04-22 更新：现在我的 Blog 使用的是 <a href="https://www.ufovps.com/">UFOVPS</a> 直接部署的。</p>
<hr>
<p>在 <a href="/2016/06/24/hexo-github-blog/">使用 Hexo 和 Github 搭建个人独立博客</a> 几天后，发现百度并不对博客进行收录。</p>
<p>在天朝使用百度搜索毕竟多数，使用百度站长工具-抓取诊断，在百度 Spider 抓取结果返回 HTTP 头：HTTP/1.1 403 Forbidden，原来是 GitHub 禁止了百度爬虫的爬去。</p>
<!-- more -->
<p>Google 后早已有许多热心网友给出了解决方案，自己在这里总结下。</p>
<h2 id="思路一利用-cdn-解决百度爬虫被-github-pages-拒绝的问题">思路一：利用 CDN 解决百度爬虫被 Github Pages 拒绝的问题</h2>
<h3 id="解决思路">解决思路</h3>
<p>既然 Github 彻底和百度决裂了，那我们也只能自己动手来解决了。Github 可能是封了百度的 IP，也有可能是封了百度爬虫的 User-Agent。</p>
<p>所以要解决这个问题，最好就不要让百度爬虫直接访问 Github 了，需要在中间套一层 <a href="https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86">反向代理</a>。</p>
<p>那么问题又来了，既然我可以搭一个反向代理服务器了，那我为什么不直接把博客放在这台服务器上？放 Github Pages 上不就是为了少一台服务器，少一点费用吗？</p>
<p>那有没有免费的第三方反向代理服务呢？当然有，其实现在各种 <strong>CDN 服务</strong> 不就是吗？而且还额外提供了各种网络环境下的加速功能。</p>
<p>但是使用 CDN 也会有一个非常大的缺点：只能对 <strong>静态资源</strong> 做，因为 CDN 和反向代理有一个很大的不同就是：它会做缓存，并向各个节点分发。</p>
<p>所以 CDN 一般都是用来给静态资源做加速的。如果你对动态页面做加速，用户看到的页面在一段时间内就一直不会变了。但是我们不怕！因为 Github Pages 本来就是全静态的！</p>
<p>国内提供 CDN 服务的有：加速乐、七牛云存储、又拍云等。</p>
<p>最后选择了 <a href="https://www.upyun.com/index.html"><strong>又拍云</strong></a></p>
<p>CDN 回源配置</p>
<p>有设置缓存时间的功能，上面明确写了：CDN 缓存时间是指 UPYUN 回源取得文件后，文件在 UPYUN CDN 网络中的缓存的时间，超过缓存时间则重新回源获取文件。</p>
<p>是的，这才是真正满足我需求的功能！</p>
<p>这个解决方案我尝试了，但是在 CDN 域名配置时，有天朝特色要求域名必须备案，我的域名 <code>zyf.im</code> 是在 <a href="http://www.name.com">name.com</a> 上注册的，<code>.im</code> 的尾缀也无法在国内备案。所以这个方案不适合我。</p>
<h2 id="思路二通过-dnspod-的智能-dns-服务">思路二：通过 Dnspod 的智能 DNS 服务</h2>
<p>在这里博主首先分析了：使用 CDN 可能 <strong>无法</strong> 真正解决百度被拒绝的问题。理由是：<strong>CDN 节点众多，百度爬取的节点可能并没有页面的缓存</strong>。</p>
<p>通过 Dnspod 的智能 DNS 服务可以变相解决这个问题，大概的设置如下图所示，只要为你所有的主机记录重复添加 A 记录，把线路类型设置为百度，并将记录值指向你自己的云主机即可。</p>
<p>你说什么？等下！自己的云主机？没错，其实这种方式就是 <strong>专门为百度的爬虫增开了一个小窗口</strong>，使得它可以在你自己的服务器上爬取内容，而不是直接去爬取 Github Pages 的内容。你需要自己搭个服务器，并将你的静态网站架在上面。</p>
<p>我博客解决方案就是采用这种方法。看起来有些“笨”因为我又搭建了一个 Server 用于博客，相当于没有完全利用 GitHub Pages，实际上普通用户访问我的博客就是有利用 GitHub 的 CDN 服务的，更快一些。而且我这么做还利用了 GitHub Webhook，当我把新的页面提交到 GitHub 时，我的博客服务器会自动 <code>pull</code> 最新的项目，这么看也就不那么“傻”了。</p>
<p>请参看：<a href="/2016/06/30/github-webhook-example/">GitHub Webhook 自动部署 Hexo</a></p>
<p>经过这么一配置，我的博客现在已经被百度收录了。百度搜索：<code>site:zyf.im</code></p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://www.dozer.cc/2015/06/github-pages-and-cdn.html">DOZER-利用 CDN 解决百度爬虫被 Github Pages 拒绝的问题</a></li>
<li><a href="http://jerryzou.com/posts/feasibility-of-allowing-baiduSpider-for-Github-Pages/">jerryzou-解决 Github Pages 禁止百度爬虫的方法与可行性分析</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Eclipse Maven Spring SpringMVC Mybatis 整合</title>
      <link>https://zyf.im/2016/06/27/eclipse-maven-spring-springmvc-mybatis-example/</link>
      <pubDate>Mon, 27 Jun 2016 07:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/06/27/eclipse-maven-spring-springmvc-mybatis-example/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 内容声明&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;本文写于 &lt;strong&gt;2016 年&lt;/strong&gt;，部分内容已过时，仅供历史参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XML 配置方式的 Spring/SpringMVC 已不再推荐&lt;/li&gt;
&lt;li&gt;C3P0 连接池已被 HikariCP 取代&lt;/li&gt;
&lt;li&gt;JSP 视图技术已较少使用&lt;/li&gt;
&lt;li&gt;JUnit 4 已升级至 JUnit 5&lt;/li&gt;
&lt;li&gt;Eclipse 手动配置 Maven 项目的方式已过时&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;当前最佳实践（2024+）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;项目初始化&lt;/strong&gt;：使用 &lt;a href=&#34;https://start.spring.io/&#34;&gt;Spring Initializr&lt;/a&gt; 生成 Spring Boot 项目&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IDE&lt;/strong&gt;：IntelliJ IDEA 或 VS Code，内置 Maven/Gradle 支持&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;框架整合&lt;/strong&gt;：Spring Boot 自动配置，无需手动整合 Spring + SpringMVC + MyBatis&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置方式&lt;/strong&gt;：Java Config + 注解，或 &lt;code&gt;application.yml&lt;/code&gt; 配置&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据访问&lt;/strong&gt;：MyBatis-Plus 或 Spring Data JPA，简化 CRUD 操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;视图层&lt;/strong&gt;：前后端分离（REST API + Vue/React），或 Thymeleaf 模板&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连接池&lt;/strong&gt;：Spring Boot 默认使用 HikariCP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;测试&lt;/strong&gt;：JUnit 5 + &lt;code&gt;@SpringBootTest&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;新项目自己撘框架，想着用点新的。看慕课网 &lt;a href=&#34;http://www.imooc.com/index/search?words=%E7%A7%92%E6%9D%80&#34;&gt;Java 高并发秒杀 API&lt;/a&gt; 的系列课程时很受益。所以想着仿着来使用：Mavan-Spring-SpringMVC-Mybatis 的架构。框架整合的代码我已上传到我的 Github：&lt;a href=&#34;https://github.com/imzyf/maven-mybatis-spring-springmvc&#34;&gt;maven-mybatis-spring-springmvc&lt;/a&gt;。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>⚠️ 内容声明</strong></p>
<p>本文写于 <strong>2016 年</strong>，部分内容已过时，仅供历史参考：</p>
<ul>
<li>XML 配置方式的 Spring/SpringMVC 已不再推荐</li>
<li>C3P0 连接池已被 HikariCP 取代</li>
<li>JSP 视图技术已较少使用</li>
<li>JUnit 4 已升级至 JUnit 5</li>
<li>Eclipse 手动配置 Maven 项目的方式已过时</li>
</ul>
<p><strong>当前最佳实践（2024+）：</strong></p>
<ul>
<li><strong>项目初始化</strong>：使用 <a href="https://start.spring.io/">Spring Initializr</a> 生成 Spring Boot 项目</li>
<li><strong>IDE</strong>：IntelliJ IDEA 或 VS Code，内置 Maven/Gradle 支持</li>
<li><strong>框架整合</strong>：Spring Boot 自动配置，无需手动整合 Spring + SpringMVC + MyBatis</li>
<li><strong>配置方式</strong>：Java Config + 注解，或 <code>application.yml</code> 配置</li>
<li><strong>数据访问</strong>：MyBatis-Plus 或 Spring Data JPA，简化 CRUD 操作</li>
<li><strong>视图层</strong>：前后端分离（REST API + Vue/React），或 Thymeleaf 模板</li>
<li><strong>连接池</strong>：Spring Boot 默认使用 HikariCP</li>
<li><strong>测试</strong>：JUnit 5 + <code>@SpringBootTest</code></li>
</ul>
</blockquote>
<hr>
<p>新项目自己撘框架，想着用点新的。看慕课网 <a href="http://www.imooc.com/index/search?words=%E7%A7%92%E6%9D%80">Java 高并发秒杀 API</a> 的系列课程时很受益。所以想着仿着来使用：Mavan-Spring-SpringMVC-Mybatis 的架构。框架整合的代码我已上传到我的 Github：<a href="https://github.com/imzyf/maven-mybatis-spring-springmvc">maven-mybatis-spring-springmvc</a>。</p>
<p>本示例是在：Ubuntu15 上实现的；Windows 上安装 Maven 将不太相同。</p>
<h2 id="maven-install">Maven Install</h2>
<blockquote>
<p>2016-09-10 更新：较新版 Eclipse 都有集成 Maven，所以并不需要安装</p>
</blockquote>
<ol>
<li>Run command <code>sudo apt-get install maven</code>, to install the latest Apache Maven.</li>
<li>Run command <code>mvn -version to verify</code> your installation.</li>
<li>Where is Maven installed?
The command <code>apt-get</code> install the Maven in <code>/usr/share/maven</code>
The Maven configuration files are stored in <code>/etc/maven</code></li>
</ol>
<h2 id="eclipse-maven-plugin---m2e">Eclipse Maven Plugin - m2e</h2>
<blockquote>
<p>2016-09-10 更新：较新版 Eclipse 都有集成 Maven，所以并不需要安装</p>
</blockquote>
<ol>
<li>open Eclipse -&gt; Help -&gt; click &ldquo;Install New Software&rdquo; -&gt; click &ldquo;add&rdquo;</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">Name:m2e
</span></span><span class="line"><span class="cl">Location:http://download.eclipse.org/technology/m2e/releases
</span></span></code></pre></div><ol start="2">
<li>click &ldquo;ok&rdquo; -&gt; click &ldquo;Maven Integration for Eclipse&rdquo; -&gt; click &ldquo;Next&rdquo;</li>
<li>restrat Eclipse</li>
<li>config m2e -&gt; Window -&gt; Preferences -&gt; Maven -&gt; Installations -&gt; click &ldquo;Add…&rdquo; -&gt; select Maven</li>
</ol>
<!-- more -->
<h2 id="create-a-maven-project">Create a Maven Project</h2>
<ol>
<li>File -&gt; New -&gt; New Maven project</li>
<li>select &ldquo;Use default Workspace location&rdquo;</li>
<li>select &ldquo;maven-archetype-j2ee-simple&rdquo;</li>
<li>input info -&gt; Finish</li>
<li>选中项目右键菜单中选择 Properties -&gt; Project Facets -&gt; select &ldquo;Dynamic Web Module&rdquo; Version &ldquo;3.1&rdquo;</li>
</ol>
<p>Tips:</p>
<ul>
<li>如果在 <code>Project Facets</code> 选择版本时“can not change”，可以在项目目录下手动修改 <code>.settings/org.eclipse.wst.common.project.facet.core.xml</code> 文件配置</li>
<li>项目自动生成的 <code>web.xml</code> 版本较低，手动修改</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;web-app</span> <span class="na">xmlns=</span><span class="s">&#34;http://xmlns.jcp.org/xml/ns/javaee&#34;</span> <span class="na">xmlns:xsi=</span><span class="s">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="na">xsi:schemaLocation=</span><span class="s">&#34;http://xmlns.jcp.org/xml/ns/javaee
</span></span></span><span class="line"><span class="cl"><span class="s">                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="na">version=</span><span class="s">&#34;3.1&#34;</span> <span class="na">metadata-complete=</span><span class="s">&#34;true&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/web-app&gt;</span>
</span></span></code></pre></div><ul>
<li>项目结构</li>
</ul>
<pre tabindex="0"><code>├── src
    ├── main
    |   ├── java //java源代码
    |   ├── resources //配置资源文件
    |   └── webapp //web文件
    |
    └── test
     └── java //junit测试
</code></pre><h2 id="pomxml-config">pom.xml Config</h2>
<p><a href="https://github.com/imzyf/maven-mybatis-spring-springmvc/blob/master/pom.xml">Github-maven-mybatis-spring-springmvc pom.xml</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="c">&lt;!-- junit4 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;dependency&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;groupId&gt;</span>junit<span class="nt">&lt;/groupId&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;artifactId&gt;</span>junit<span class="nt">&lt;/artifactId&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;version&gt;</span>4.11<span class="nt">&lt;/version&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/dependency&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 日志 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 实现slf4j接口整合 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- JDBC MySQL Driver --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- DAO框架 mybatis --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- Servlet API --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 1 Spring 核心依赖 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 2 Spring DAO依赖 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 3 Spring web相关依赖 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 4 Spring Test相关依赖 --&gt;</span>
</span></span></code></pre></div><h2 id="logbackxml-config">logback.xml Config</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;configuration&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;appender</span> <span class="na">name=</span><span class="s">&#34;STDOUT&#34;</span> <span class="na">class=</span><span class="s">&#34;ch.qos.logback.core.ConsoleAppender&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder
</span></span></span><span class="line"><span class="cl"><span class="c">			by default --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;encoder&gt;</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&lt;pattern&gt;</span>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</span></span><span class="line"><span class="cl">   <span class="nt">&lt;/pattern&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/encoder&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;/appender&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;root</span> <span class="na">level=</span><span class="s">&#34;info&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;appender-ref</span> <span class="na">ref=</span><span class="s">&#34;STDOUT&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;/root&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/configuration&gt;</span>
</span></span></code></pre></div><h2 id="mybatis-config">Mybatis Config</h2>
<p><a href="https://github.com/imzyf/maven-mybatis-spring-springmvc/blob/master/src/main/resources/mybatis-config.xml">Github-maven-mybatis-spring-springmvc mybatis-config.xml</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;configuration&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;settings&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 使用jdbc的getGeneratedKays 获取数据库自增主键 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;setting</span> <span class="na">name=</span><span class="s">&#34;useGeneratedKeys&#34;</span> <span class="na">value=</span><span class="s">&#34;true&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 使用列别名替换列名 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;setting</span> <span class="na">name=</span><span class="s">&#34;useColumnLabel&#34;</span> <span class="na">value=</span><span class="s">&#34;true&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- 是否开启自动驼峰命名规则（camel case）映射，即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;setting</span> <span class="na">name=</span><span class="s">&#34;mapUnderscoreToCamelCase&#34;</span> <span class="na">value=</span><span class="s">&#34;true&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;/settings&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/configuration&gt;</span>
</span></span></code></pre></div><h2 id="spring-config">Spring Config</h2>
<p><a href="https://github.com/imzyf/maven-mybatis-spring-springmvc/tree/master/src/main/resources/spring">Github-maven-mybatis-spring-springmvc spring</a></p>
<h3 id="spring-dao-config">Spring-DAO Config</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="c">&lt;!-- 1 数据库配置文件位置 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;context:property-placeholder</span> <span class="na">location=</span><span class="s">&#34;classpath:jdbc.properties&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 2 数据库连接池 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- Employee DB data source. --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;bean</span> <span class="na">id=</span><span class="s">&#34;dataSource&#34;</span> <span class="na">class=</span><span class="s">&#34;com.mchange.v2.c3p0.ComboPooledDataSource&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;driverClass&#34;</span> <span class="na">value=</span><span class="s">&#34;${jdbc.driverClassName}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;jdbcUrl&#34;</span> <span class="na">value=</span><span class="s">&#34;${jdbc.dburl}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;user&#34;</span> <span class="na">value=</span><span class="s">&#34;${jdbc.username}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;password&#34;</span> <span class="na">value=</span><span class="s">&#34;${jdbc.password}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- c3p0连接池 私有属性 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;maxPoolSize&#34;</span> <span class="na">value=</span><span class="s">&#34;${jdbc.maxPoolSize}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;minPoolSize&#34;</span> <span class="na">value=</span><span class="s">&#34;${jdbc.minPoolSize}&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 关闭连接后不自动commit --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;autoCommitOnClose&#34;</span> <span class="na">value=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 获取连接超时时间 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;checkoutTimeout&#34;</span> <span class="na">value=</span><span class="s">&#34;1000&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 获取连接失败重试次数 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;acquireRetryAttempts&#34;</span> <span class="na">value=</span><span class="s">&#34;2&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/bean&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 设计原则：约定大于配置 --&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 3 配置 SqlSessionFactory 对象 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;bean</span> <span class="na">id=</span><span class="s">&#34;sqlSessionFactory&#34;</span> <span class="na">class=</span><span class="s">&#34;org.mybatis.spring.SqlSessionFactoryBean&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 注入数据库连接池 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;dataSource&#34;</span> <span class="na">ref=</span><span class="s">&#34;dataSource&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 配置mybitis 全局配置文件 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;configLocation&#34;</span> <span class="na">value=</span><span class="s">&#34;classpath:mybatis-config.xml&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 扫描entity包 使用别名 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;typeAliasesPackage&#34;</span> <span class="na">value=</span><span class="s">&#34;com.moma.dmv.entity&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 扫描sql配置文件 mapper 需要的xml --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;mapperLocations&#34;</span> <span class="na">value=</span><span class="s">&#34;classpath:mapper/*.xml&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/bean&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 4 配置扫描Dao接口包，动态实现Dao接口，注入到spring容器中 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;bean</span> <span class="na">class=</span><span class="s">&#34;org.mybatis.spring.mapper.MapperScannerConfigurer&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 注入sqlsessionFactory --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;sqlSessionFactoryBeanName&#34;</span> <span class="na">value=</span><span class="s">&#34;sqlSessionFactory&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 给出需要扫描Dao接口包 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;basePackage&#34;</span> <span class="na">value=</span><span class="s">&#34;com.moma.dmv.dao&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/bean&gt;</span>
</span></span></code></pre></div><h3 id="spring-service-config">Spring-Service Config</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="c">&lt;!-- 扫描service包下 所有使用注解的类型 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;context:component-scan</span> <span class="na">base-package=</span><span class="s">&#34;com.moma.dmv.service&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 配置事务管理器 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;bean</span> <span class="na">id=</span><span class="s">&#34;transactionManager&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="na">class=</span><span class="s">&#34;org.springframework.jdbc.datasource.DataSourceTransactionManager&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;dataSource&#34;</span> <span class="na">ref=</span><span class="s">&#34;dataSource&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/bean&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 配置基于注解的声明式事务 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;tx:annotation-driven</span> <span class="na">transaction-manager=</span><span class="s">&#34;transactionManager&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 使用注解控制事务方法的优点
</span></span></span><span class="line"><span class="cl"><span class="c">1：开发团队达成一致约定，明确标注事务方法的编程风格
</span></span></span><span class="line"><span class="cl"><span class="c">2：保证事务方法的执行时间尽可能短，不要穿插其他网络操作RPC/HTTP请求或者剥离到事务方法外部
</span></span></span><span class="line"><span class="cl"><span class="c">3：不是所有的方法都需要事务，比如只有一条修改操作，只读操作不需要事务控制
</span></span></span><span class="line"><span class="cl"><span class="c">--&gt;</span>
</span></span></code></pre></div><h3 id="spring-web-config">Spring-Web Config</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="c">&lt;!-- 1:开启springMVC 注解模式 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;mvc:annotation-driven</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 2 静态资源默认servlet配置 1 加入对静态资源的处理 js gif png 2 允许使用“/”做整体映射 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;mvc:default-servlet-handler</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 3:配置jsp 显示ViewResolver --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;bean</span>
</span></span><span class="line"><span class="cl"> <span class="na">class=</span><span class="s">&#34;org.springframework.web.servlet.view.InternalResourceViewResolver&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 决定视图类型，如果添加了jstl支持（即有jstl.jar），那么默认就是解析为jstl视图 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;viewClass&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">value=</span><span class="s">&#34;org.springframework.web.servlet.view.JstlView&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 视图前缀 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;prefix&#34;</span> <span class="na">value=</span><span class="s">&#34;/WEB-INF/jsp/&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 视图后缀 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;suffix&#34;</span> <span class="na">value=</span><span class="s">&#34;.jsp&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/bean&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;mvc:resources</span> <span class="na">location=</span><span class="s">&#34;/resources/&#34;</span> <span class="na">mapping=</span><span class="s">&#34;/resources/**&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- 4:扫描web相关的bean --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;context:component-scan</span> <span class="na">base-package=</span><span class="s">&#34;com.moma.dmv.web&#34;</span> <span class="nt">/&gt;</span>
</span></span></code></pre></div><h2 id="dao-mapper-example">DAO Mapper Example</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34; ?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE mapper PUBLIC &#34;-//mybatis.org//DTD Mapper 3.0//EN&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">&#34;http://mybatis.org/dtd/mybatis-3-mapper.dtd&#34;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;mapper</span> <span class="na">namespace=</span><span class="s">&#34;com.moma.dmv.dao.InfoDao&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="nt">&lt;select</span> <span class="na">id=</span><span class="s">&#34;queryById&#34;</span> <span class="na">resultType=</span><span class="s">&#34;Info&#34;</span> <span class="na">parameterType=</span><span class="s">&#34;long&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="cp">&lt;![CDATA[
</span></span></span><span class="line"><span class="cl"><span class="cp">  select id,`key`,`value` from info where id = #{id}
</span></span></span><span class="line"><span class="cl"><span class="cp">  ]]&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;/select&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;select</span> <span class="na">id=</span><span class="s">&#34;queryAll&#34;</span> <span class="na">resultType=</span><span class="s">&#34;Info&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="cp">&lt;![CDATA[
</span></span></span><span class="line"><span class="cl"><span class="cp">  select id,key,value
</span></span></span><span class="line"><span class="cl"><span class="cp">  from info
</span></span></span><span class="line"><span class="cl"><span class="cp">  limit #{offset},#{limit}
</span></span></span><span class="line"><span class="cl"><span class="cp">  ]]&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;/select&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/mapper&gt;</span>
</span></span></code></pre></div><h2 id="webxml-config">web.xml Config</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;servlet&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;servlet-name&gt;</span>dmv-dispatcher<span class="nt">&lt;/servlet-name&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;servlet-class&gt;</span>org.springframework.web.servlet.DispatcherServlet<span class="nt">&lt;/servlet-class&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 配置springMVC需要加载的配置文件 spring-dao.xml spring-service.xml spring-web.xml
</span></span></span><span class="line"><span class="cl"><span class="c">		mybatis -&gt; spring -&gt; springMVC --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;init-param&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;param-name&gt;</span>contextConfigLocation<span class="nt">&lt;/param-name&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;param-value&gt;</span>classpath:spring/spring-*.xml<span class="nt">&lt;/param-value&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;/init-param&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/servlet&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;servlet-mapping&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;servlet-name&gt;</span>dmv-dispatcher<span class="nt">&lt;/servlet-name&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="c">&lt;!-- 默认匹配所有的请求 --&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&lt;url-pattern&gt;</span>/<span class="nt">&lt;/url-pattern&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/servlet-mapping&gt;</span>
</span></span></code></pre></div><h2 id="junit-example">JUnit Example</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">javax.annotation.Resource</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.Test</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.runner.RunWith</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.slf4j.Logger</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.slf4j.LoggerFactory</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.test.context.ContextConfiguration</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.test.context.junit4.SpringJUnit4ClassRunner</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">com.moma.dmv.dao.InfoDao</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">com.moma.dmv.entity.Info</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">RunWith</span><span class="p">(</span><span class="n">SpringJUnit4ClassRunner</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">ContextConfiguration</span><span class="p">(</span><span class="n">locations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s">&#34;classpath:spring/spring-dao.xml&#34;</span><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">InfoDaoTest</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">InfoDao</span><span class="w"> </span><span class="n">infoDao</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">Logger</span><span class="w"> </span><span class="n">logger</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">LoggerFactory</span><span class="p">.</span><span class="na">getLogger</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">getClass</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">testQueryById</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="kt">long</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">Info</span><span class="w"> </span><span class="n">info</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">infoDao</span><span class="p">.</span><span class="na">queryById</span><span class="p">(</span><span class="n">id</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">logger</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="n">info</span><span class="p">.</span><span class="na">toString</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="http://www.mkyong.com/maven/how-to-install-maven-in-ubuntu/">MKyong-How to install Maven on Ubuntu</a></li>
<li><a href="http://blog.csdn.net/qjyong/article/details/9098213">Java 之道-使用 Eclipse 构建 Maven 项目 (step-by-step)</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>使用 GitHub 和 Hexo 搭建个人独立博客</title>
      <link>https://zyf.im/2016/06/24/hexo-github-blog/</link>
      <pubDate>Fri, 24 Jun 2016 18:30:00 +0000</pubDate>
      <guid>https://zyf.im/2016/06/24/hexo-github-blog/</guid>
      <description>&lt;p&gt;&lt;code&gt;Wordpress&lt;/code&gt; 这类博客系统功能强大，可对与我只想划拉的写点东西的人，感觉大材小用了。而且 &lt;code&gt;Wordpress&lt;/code&gt; 需要部署，网站的服务器也会带来问题，国内的服务器首先需要备案，费用不低，国外服务器访问速度受影响。&lt;/p&gt;
&lt;p&gt;近来接触到一种新的博客系统 Hexo，它的不同地方就是将：&lt;strong&gt;在上线编写博客和页面渲染的过程在线下完成&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在本地编写博文的 Markdown 文件，使用 Hexo 将博客网站的所有前台 HTML 等全部生成，让后将生成的文件上传的服务器就行了。&lt;/p&gt;
&lt;p&gt;那么原来 wp 中的评论等动态功能怎么办呢？放心第三方服务商早已为我们考虑了。例如：&lt;a href=&#34;https://disqus.com/&#34;&gt;disqus&lt;/a&gt;就是一家第三方社会化评论系统，主要为网站主提供评论托管服务。&lt;/p&gt;
&lt;p&gt;本文的操作的系统环境是 Ubuntu 15，Windows 下的搭建可触类旁通。&lt;/p&gt;
&lt;h2 id=&#34;了解-hexo&#34;&gt;了解 Hexo&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;A fast, simple &amp;amp; powerful blog framework&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://hexo.io/&#34;&gt;Hexo&lt;/a&gt; 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown（或其他渲染引擎）解析文章，在几秒内，即可利用靓丽的主题生成静态网页，&lt;a href=&#34;https://hexo.io/zh-cn/docs/setup.html&#34;&gt;Hexo setup 官方文档&lt;/a&gt;。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><code>Wordpress</code> 这类博客系统功能强大，可对与我只想划拉的写点东西的人，感觉大材小用了。而且 <code>Wordpress</code> 需要部署，网站的服务器也会带来问题，国内的服务器首先需要备案，费用不低，国外服务器访问速度受影响。</p>
<p>近来接触到一种新的博客系统 Hexo，它的不同地方就是将：<strong>在上线编写博客和页面渲染的过程在线下完成</strong>。</p>
<p>在本地编写博文的 Markdown 文件，使用 Hexo 将博客网站的所有前台 HTML 等全部生成，让后将生成的文件上传的服务器就行了。</p>
<p>那么原来 wp 中的评论等动态功能怎么办呢？放心第三方服务商早已为我们考虑了。例如：<a href="https://disqus.com/">disqus</a>就是一家第三方社会化评论系统，主要为网站主提供评论托管服务。</p>
<p>本文的操作的系统环境是 Ubuntu 15，Windows 下的搭建可触类旁通。</p>
<h2 id="了解-hexo">了解 Hexo</h2>
<blockquote>
<p>A fast, simple &amp; powerful blog framework</p>
</blockquote>
<p><a href="https://hexo.io/">Hexo</a> 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown（或其他渲染引擎）解析文章，在几秒内，即可利用靓丽的主题生成静态网页，<a href="https://hexo.io/zh-cn/docs/setup.html">Hexo setup 官方文档</a>。</p>
<h2 id="安装-git">安装 Git</h2>
<blockquote>
<p><a href="https://git-scm.com/download/linux">Download for Linux and Unix | git-scm</a></p>
</blockquote>
<h2 id="安装-nodejs">安装 Node.js</h2>
<blockquote>
<p><a href="/2017/07/06/install-node-js-in-ubuntu-and-faq/">Linux | Mac 安装 Node.js 与常见问题 | zyf.im</a></p>
</blockquote>
<h2 id="安装-hexo">安装 Hexo</h2>
<p>所有必备的应用程序安装完成后，即可使用 npm 安装 Hexo：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install -g hexo-cli
</span></span></code></pre></div><h2 id="建站">建站</h2>
<p>安装 Hexo 完成后，请执行下列命令，Hexo 将会在指定文件夹中新建所需要的文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hexo init &lt;folder&gt;
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> &lt;folder&gt;
</span></span><span class="line"><span class="cl">npm install
</span></span></code></pre></div><p>新建完成后，指定文件夹的目录如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">├── _config.yml
</span></span><span class="line"><span class="cl">├── package.json
</span></span><span class="line"><span class="cl">├── scaffolds
</span></span><span class="line"><span class="cl">├── source
</span></span><span class="line"><span class="cl">| ├── _drafts
</span></span><span class="line"><span class="cl">| └── _posts
</span></span><span class="line"><span class="cl">└── themes
</span></span></code></pre></div><h3 id="新建一篇文章">新建一篇文章</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hexo new <span class="o">[</span>layout<span class="o">]</span> &lt;title&gt;
</span></span></code></pre></div><p>如果没有设置 layout 的话，默认使用 <code>_config.yml</code> 中的 default_layout 参数代替。如果标题包含空格的话，请使用引号括起来。</p>
<h3 id="生成静态文件">生成静态文件</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hexo generate
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 可以简写为</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">hexo g
</span></span></code></pre></div><h3 id="启动服务器">启动服务器</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hexo server
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 可以简写为</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">hexo s
</span></span></code></pre></div><p>网站会在 <a href="http://localhost:4000">http://localhost:4000</a> 下启动。在服务器启动期间，Hexo 会监视文件变动并自动更新，您无须重启服务器。</p>
<h2 id="部署静态网页到-github">部署静态网页到 GitHub</h2>
<h3 id="注册设置-github">注册设置 GitHub</h3>
<ol>
<li>登录 GitHub，注册自定义用户名如：<code>imzyf</code></li>
<li>在主页右下角创建 New repository，name 必须和用户名一致如：<code>imzyf.github.io</code></li>
<li>等待 3 分钟左右，之后即可访问静态主页如：<code>https://imzyf.github.io</code></li>
</ol>
<h3 id="同步内容至-github">同步内容至 GitHub</h3>
<ol>
<li>在 Hexo 目录下 <code>git clone git@github.com:imzyf/imzyf.github.io.git</code></li>
<li>将 <code>public</code> 文件下的所有文件拷贝到 <code>imzyf.github.io</code> 下</li>
<li><code>git add .</code> 增加当前子目录下所有更改过的文件至 index</li>
<li><code>git commit -m 'xxx'</code> 提交到本地</li>
<li><code>git push origin master</code> 将当前分支 push 到远程 master 分支</li>
<li>最后访问主页<code>http://imzyf.github.io</code> 观察效果</li>
</ol>
<h2 id="绑定个人域名">绑定个人域名</h2>
<p>在 GitHub 项目页面，Settings -&gt; GitHub Pages，Source 选择 master branch，Custom domain 填写自己的域名，同时勾选 Enforce HTTPS 让你的网址支持 HTTPS。</p>
<p>在你的域名服务商那里，将填写的域名解析到：<code>&lt;username&gt;.github.io</code> 利于 <code>imzyf.github.io</code>。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://wsgzao.github.io/post/hexo-guide/">HellDog-使用 GitHub 和 Hexo 搭建免费静态 Blog</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>【Core Java】读书笔记</title>
      <link>https://zyf.im/2016/05/06/core-java-reading-notes/</link>
      <pubDate>Fri, 06 May 2016 11:00:00 +0000</pubDate>
      <guid>https://zyf.im/2016/05/06/core-java-reading-notes/</guid>
      <description>&lt;p&gt;本文总结 Core Java 书中的：第 3 章 Java 的基本程序设计结构、第 4 章 对象与类、第 5 章 继承。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;3-java-的基本程序设计结构&#34;&gt;3 Java 的基本程序设计结构&lt;/h2&gt;
&lt;h3 id=&#34;33-数据类型&#34;&gt;3.3 数据类型&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java 有 8 种基本类型（primitive type）：4 种整型、2 种浮点类型、char、boolean&lt;/li&gt;
&lt;li&gt;Java 7 起可用下划线增强数字可读性：&lt;code&gt;1_000_000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;浮点数不适用于金融计算（二进制无法精确表示 1/10），应使用 &lt;code&gt;BigDecimal&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;34-变量&#34;&gt;3.4 变量&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$&lt;/code&gt; 是合法字符，但仅用于编译器生成的名字，不要在代码中使用&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;36-字符串&#34;&gt;3.6 字符串&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;String 是不可变的（immutable）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要用 &lt;code&gt;==&lt;/code&gt; 比较字符串&lt;/strong&gt;：只有字符串常量是共享的，&lt;code&gt;+&lt;/code&gt; 或 &lt;code&gt;substring&lt;/code&gt; 产生的结果不共享&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;310-数组&#34;&gt;3.10 数组&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;数组赋值是引用拷贝，两个变量指向同一数组：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;luckyNumbers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;smallPrimes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;luckyNumbers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// smallPrimes[5] 也变成 12&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;值拷贝使用 &lt;code&gt;Arrays.copyOf&lt;/code&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;copiedNumbers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Arrays&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;copyOf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;luckyNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;luckyNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;彩票抽奖示例（从 1~n 中随机取 k 个不重复数字）：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>本文总结 Core Java 书中的：第 3 章 Java 的基本程序设计结构、第 4 章 对象与类、第 5 章 继承。</p>
<!-- more -->
<h2 id="3-java-的基本程序设计结构">3 Java 的基本程序设计结构</h2>
<h3 id="33-数据类型">3.3 数据类型</h3>
<ul>
<li>Java 有 8 种基本类型（primitive type）：4 种整型、2 种浮点类型、char、boolean</li>
<li>Java 7 起可用下划线增强数字可读性：<code>1_000_000</code></li>
<li>浮点数不适用于金融计算（二进制无法精确表示 1/10），应使用 <code>BigDecimal</code></li>
</ul>
<h3 id="34-变量">3.4 变量</h3>
<ul>
<li><code>$</code> 是合法字符，但仅用于编译器生成的名字，不要在代码中使用</li>
</ul>
<h3 id="36-字符串">3.6 字符串</h3>
<ul>
<li>String 是不可变的（immutable）</li>
<li><strong>不要用 <code>==</code> 比较字符串</strong>：只有字符串常量是共享的，<code>+</code> 或 <code>substring</code> 产生的结果不共享</li>
</ul>
<h3 id="310-数组">3.10 数组</h3>
<ul>
<li>数组赋值是引用拷贝，两个变量指向同一数组：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">luckyNumbers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">smallPrimes</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">luckyNumbers</span><span class="o">[</span><span class="n">5</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">12</span><span class="p">;</span><span class="w"> </span><span class="c1">// smallPrimes[5] 也变成 12</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>值拷贝使用 <code>Arrays.copyOf</code>：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">copiedNumbers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Arrays</span><span class="p">.</span><span class="na">copyOf</span><span class="p">(</span><span class="n">luckyNumbers</span><span class="p">,</span><span class="w"> </span><span class="n">luckyNumbers</span><span class="p">.</span><span class="na">length</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>彩票抽奖示例（从 1~n 中随机取 k 个不重复数字）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">harr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="kt">int</span><span class="o">[</span><span class="n">n</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">n</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">harr</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kt">int</span><span class="o">[]</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="kt">int</span><span class="o">[</span><span class="n">k</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">k</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">int</span><span class="w"> </span><span class="n">random</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Math</span><span class="p">.</span><span class="na">random</span><span class="p">()</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">result</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">harr</span><span class="o">[</span><span class="n">random</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">harr</span><span class="o">[</span><span class="n">random</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">harr</span><span class="o">[</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">1</span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="c1">// 用最后元素替换已取元素</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">n</span><span class="o">--</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Arrays</span><span class="p">.</span><span class="na">sort</span><span class="p">(</span><span class="n">result</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>关键点：每次取的是下标而非值，通过缩小有效范围避免重复抽取。</p>
<h2 id="4-对象与类">4 对象与类</h2>
<h3 id="41-面向对象程序设计概述">4.1 面向对象程序设计概述</h3>
<table>
  <thead>
      <tr>
          <th>关系</th>
          <th>含义</th>
          <th>示例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>依赖 (uses-a)</td>
          <td>一个类的方法操纵另一个类的对象</td>
          <td>Order 使用 Account</td>
      </tr>
      <tr>
          <td>聚合 (has-a)</td>
          <td>类 A 的对象包含类 B 的对象</td>
          <td>Order 包含 Item</td>
      </tr>
      <tr>
          <td>继承 (is-a)</td>
          <td>类 A 扩展类 B</td>
          <td>RushOrder 继承 Order</td>
      </tr>
  </tbody>
</table>
<p>设计原则：尽量减少类之间的依赖，降低耦合度。</p>
<h3 id="42-使用预定义类">4.2 使用预定义类</h3>
<ul>
<li>对象变量不是对象，只是对象的引用</li>
<li><code>Date deadline;</code> 只声明了变量，未引用任何对象</li>
<li><code>new</code> 操作符返回的是引用</li>
<li>可设置为 <code>null</code> 表示不引用任何对象</li>
</ul>
<h3 id="43-用户自定义类">4.3 用户自定义类</h3>
<p>构造器特点：</p>
<ul>
<li>与类同名、无返回值</li>
<li>可有多个（重载）</li>
<li>伴随 <code>new</code> 调用</li>
</ul>
<p>封装原则：</p>
<ul>
<li>实例域用 <code>private</code></li>
<li><strong>不要返回可变对象的引用</strong>，应返回 clone</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// 错误：破坏封装</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">Date</span><span class="w"> </span><span class="nf">getHireDay</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">hireDay</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 正确：返回副本</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">Date</span><span class="w"> </span><span class="nf">getHireDay</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="p">(</span><span class="n">Date</span><span class="p">)</span><span class="w"> </span><span class="n">hireDay</span><span class="p">.</span><span class="na">clone</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>关于 <code>final</code>：</p>
<ul>
<li>适用于基本类型或不可变类</li>
<li>对可变对象，<code>final</code> 只保证引用不变，对象内容仍可修改：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Date</span><span class="w"> </span><span class="n">hireDate</span><span class="p">;</span><span class="w"> </span><span class="c1">// 引用不可变，但 hireDate.setTime() 仍可调用</span><span class="w">
</span></span></span></code></pre></div><h3 id="44-静态域与静态方法">4.4 静态域与静态方法</h3>
<ul>
<li>静态域属于类，所有实例共享：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">Employee</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">nextId</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w"> </span><span class="c1">// 所有实例共享</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">id</span><span class="p">;</span><span class="w">                </span><span class="c1">// 每个实例独有</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>静态方法建议用类名调用：<code>Employee.getNextId()</code></li>
<li>每个类可有 <code>main</code> 方法用于单元测试</li>
</ul>
<h3 id="45-方法参数">4.5 方法参数</h3>
<p>Java <strong>始终是按值调用</strong>：</p>
<ul>
<li>不能修改基本类型参数</li>
<li>可以改变对象参数的状态</li>
<li>不能让对象参数引用新对象</li>
</ul>
<p>常见误解：Java 对象采用引用传递？实际上不是。反例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">swap</span><span class="p">(</span><span class="n">Employee</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">Employee</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Employee</span><span class="w"> </span><span class="n">temp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">x</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">temp</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Employee</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Employee</span><span class="p">(</span><span class="s">&#34;Alice&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">70000</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Employee</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Employee</span><span class="p">(</span><span class="s">&#34;Bob&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">60000</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">swap</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 结果：a 仍是 Alice，b 仍是 Bob</span><span class="w">
</span></span></span></code></pre></div><p>原因：调用 swap 时，a 将<strong>引用的副本</strong>传给 x，b 将引用的副本传给 y。方法内交换的只是 x、y 的指向，方法外的 a、b 不受影响。</p>
<h3 id="46-对象构造">4.6 对象构造</h3>
<ul>
<li>方法签名 = 方法名 + 参数类型（不含返回类型）</li>
<li>无构造器时系统提供默认构造器（域设为默认值）</li>
<li>提供了构造器后，默认构造器不再自动生成</li>
<li>建议显式初始化实例域：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">Employee</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="n">salary</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="410-类设计技巧">4.10 类设计技巧</h3>
<ol>
<li>保证数据私有</li>
<li>一定要初始化数据</li>
<li>不要过多使用基本类型，用类封装</li>
<li>不是所有域都需要 getter/setter</li>
<li>职责过多的类要分解</li>
<li>命名要体现职责</li>
</ol>
<h2 id="5-继承">5 继承</h2>
<h3 id="51-类超类和子类">5.1 类、超类和子类</h3>
<p><code>this</code> vs <code>super</code>：</p>
<ul>
<li><code>super</code> 不是对象引用，只是调用超类的关键字</li>
<li>两者调用构造器都必须是第一条语句，不能同时出现</li>
</ul>
<p>多态与动态绑定：</p>
<ul>
<li>多态：对象变量可指向多种实际类型</li>
<li>动态绑定：运行时自动选择调用哪个方法</li>
</ul>
<p>数组协变的陷阱：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Manager</span><span class="o">[]</span><span class="w"> </span><span class="n">managers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Manager</span><span class="o">[</span><span class="n">10</span><span class="o">]</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Employee</span><span class="o">[]</span><span class="w"> </span><span class="n">staff</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">managers</span><span class="p">;</span><span class="w"> </span><span class="c1">// 合法，但危险</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">staff</span><span class="o">[</span><span class="n">0</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Employee</span><span class="p">(...);</span><span class="w"> </span><span class="c1">// 编译通过，运行时出错</span><span class="w">
</span></span></span></code></pre></div><p>静态绑定 vs 动态绑定：</p>
<table>
  <thead>
      <tr>
          <th>特性</th>
          <th>静态绑定</th>
          <th>动态绑定</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>时机</td>
          <td>编译时</td>
          <td>运行时</td>
      </tr>
      <tr>
          <td>适用</td>
          <td>private/static/final 方法、构造器</td>
          <td>虚方法（可重写的方法）</td>
      </tr>
      <tr>
          <td>依据</td>
          <td>类信息</td>
          <td>对象信息</td>
      </tr>
      <tr>
          <td>对应</td>
          <td>重载 (Overload)</td>
          <td>重写 (Override)</td>
      </tr>
  </tbody>
</table>
<p>方法表：虚拟机为每个类预先创建方法表，避免每次调用都搜索。</p>
<p>访问修饰符：</p>
<table>
  <thead>
      <tr>
          <th>修饰符</th>
          <th>可见范围</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>private</td>
          <td>本类</td>
      </tr>
      <tr>
          <td>默认</td>
          <td>本包</td>
      </tr>
      <tr>
          <td>protected</td>
          <td>本包 + 所有子类</td>
      </tr>
      <tr>
          <td>public</td>
          <td>所有类</td>
      </tr>
  </tbody>
</table>
<h3 id="52-object所有类的超类">5.2 Object：所有类的超类</h3>
<p>equals 方法标准模板：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">equals</span><span class="p">(</span><span class="n">Object</span><span class="w"> </span><span class="n">otherObject</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 1. 同一引用</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">this</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">otherObject</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 2. null 检查</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">otherObject</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 3. 类型检查（子类语义不同用 getClass，相同用 instanceof）</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">getClass</span><span class="p">()</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">otherObject</span><span class="p">.</span><span class="na">getClass</span><span class="p">())</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 4. 类型转换</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Employee</span><span class="w"> </span><span class="n">other</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">Employee</span><span class="p">)</span><span class="w"> </span><span class="n">otherObject</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 5. 比较域（基本类型用 ==，对象用 equals）</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">Objects</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">other</span><span class="p">.</span><span class="na">name</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">salary</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">other</span><span class="p">.</span><span class="na">salary</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">Objects</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">hireDay</span><span class="p">,</span><span class="w"> </span><span class="n">other</span><span class="p">.</span><span class="na">hireDay</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="http://droidyue.com/blog/2014/12/28/static-biding-and-dynamic-binding-in-java/">Java 中的静态绑定和动态绑定</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>试着写东西</title>
      <link>https://zyf.im/2016/03/18/try-to-write-something/</link>
      <pubDate>Fri, 18 Mar 2016 17:41:00 +0000</pubDate>
      <guid>https://zyf.im/2016/03/18/try-to-write-something/</guid>
      <description>&lt;p&gt;现在是大四下学期，从学校出来实习。在面试一家公司时，面试官看到我的邮箱（168#yifans.com）是自己的域名后说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;问：&amp;ldquo;有自己的网站吗？&amp;rdquo;
&amp;ldquo;没有。&amp;rdquo;
问：&amp;ldquo;域名都买了不自己搭个网站？&amp;rdquo;
&amp;ldquo;……&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其实这也不是自己要搭博客的根本原因。&lt;/p&gt;
&lt;p&gt;在大学里，学了些、做了些东西，可思考的不多。现在越发觉得一个人的强大在于思想。看书、写些东西，我想是提高一个人思想的方法吧。文字写下来了也方便交流。&lt;/p&gt;
&lt;p&gt;出来了换了一个环境，遇到新的人、新的事，可以换个角度看人看事。原来我理解的技术博客，就是解决问题后的笔记本，有问题从中找，而且觉得很多网上的技术博客里的内容都是来回转来转去，很多解决方案都是过时的，没什么价值。可最近在浏览博客园、CSDN 时发现了很多精华——前人分享自己所理解的知识、技术中走的弯路，阅读后很受益。&lt;/p&gt;
&lt;p&gt;想想自己，很多事、很多情况下就是这样：没见多少就下结论，管中窥豹，too young。&lt;/p&gt;
&lt;p&gt;其中有这样的一个签名:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我不能保证写的每个地方都是对的，但是至少能保证不复制、不黏贴，保证每一句话、每一行代码都经过了认真的推敲、仔细的斟酌。每一篇文章的背后，希望都能看到自己对于技术、对于生活的态度。
我相信乔布斯说的，只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。面对压力，我可以挑灯夜战、不眠不休；面对困难，我愿意迎难而上、永不退缩。
其实我想说的是，我只是一个程序员，这就是我现在纯粹人生的全部。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;再认同不过了。&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://zhuanlan.zhihu.com/p/19743861&#34;&gt;为什么你要写博客？ - 知乎&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cnblogs.com/xrq730/p/8446246.html&#34;&gt;详解MySQL数据类型 | 五月的仓颉&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ndash; EOF &amp;ndash;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>现在是大四下学期，从学校出来实习。在面试一家公司时，面试官看到我的邮箱（168#yifans.com）是自己的域名后说：</p>
<blockquote>
<p>问：&ldquo;有自己的网站吗？&rdquo;
&ldquo;没有。&rdquo;
问：&ldquo;域名都买了不自己搭个网站？&rdquo;
&ldquo;……&rdquo;</p>
</blockquote>
<p>其实这也不是自己要搭博客的根本原因。</p>
<p>在大学里，学了些、做了些东西，可思考的不多。现在越发觉得一个人的强大在于思想。看书、写些东西，我想是提高一个人思想的方法吧。文字写下来了也方便交流。</p>
<p>出来了换了一个环境，遇到新的人、新的事，可以换个角度看人看事。原来我理解的技术博客，就是解决问题后的笔记本，有问题从中找，而且觉得很多网上的技术博客里的内容都是来回转来转去，很多解决方案都是过时的，没什么价值。可最近在浏览博客园、CSDN 时发现了很多精华——前人分享自己所理解的知识、技术中走的弯路，阅读后很受益。</p>
<p>想想自己，很多事、很多情况下就是这样：没见多少就下结论，管中窥豹，too young。</p>
<p>其中有这样的一个签名:</p>
<blockquote>
<p>我不能保证写的每个地方都是对的，但是至少能保证不复制、不黏贴，保证每一句话、每一行代码都经过了认真的推敲、仔细的斟酌。每一篇文章的背后，希望都能看到自己对于技术、对于生活的态度。
我相信乔布斯说的，只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。面对压力，我可以挑灯夜战、不眠不休；面对困难，我愿意迎难而上、永不退缩。
其实我想说的是，我只是一个程序员，这就是我现在纯粹人生的全部。</p>
</blockquote>
<p>再认同不过了。</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/19743861">为什么你要写博客？ - 知乎</a></li>
<li><a href="https://www.cnblogs.com/xrq730/p/8446246.html">详解MySQL数据类型 | 五月的仓颉</a></li>
</ul>
<p>&ndash; EOF &ndash;</p>
]]></content:encoded>
    </item>
    <item>
      <title>About Yifans_Z</title>
      <link>https://zyf.im/about/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://zyf.im/about/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;如若我从未成功，我想我会顺其自然，&lt;br&gt;
如若我只听从别人的建议过日，我的心会死去。&lt;br&gt;
我是太固执，管制精神的手段对我奈何不得，&lt;br&gt;
有的人必会成为明日之星，我想该轮到我了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我是 Yifan，一名在帝都的开发工程师，现在专注于财经领域。这里记录了我开发过程中的一些经验或是人生感悟，希望对你有帮助。&lt;/p&gt;
&lt;p&gt;下面是我个人微信二维码，欢迎添加。:)&lt;/p&gt;
&lt;img alt=&#34;yifanszhao_wechat_qrcode&#34; style=&#34;width:240px;&#34; src=&#34;https://user-images.githubusercontent.com/9289792/126154429-3143f154-9eb4-4218-9755-8dc087eea27b.jpg&#34; /&gt;
&lt;h2 id=&#34;timeline&#34;&gt;Timeline&lt;/h2&gt;
&lt;h3 id=&#34;2025&#34;&gt;2025&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：回顾 2025&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2024&#34;&gt;2024&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：回顾 2024&lt;/li&gt;
&lt;li&gt;10/08：加入 RightCapital&lt;/li&gt;
&lt;li&gt;05/11：福宝贴贴。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2023&#34;&gt;2023&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：回顾 2023&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2022&#34;&gt;2022&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：回顾 2022&lt;/li&gt;
&lt;li&gt;01/28：拖把光临。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2021&#34;&gt;2021&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：回顾 2021&lt;/li&gt;
&lt;li&gt;07/02：不动产权证书。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2020&#34;&gt;2020&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：回顾 2020&lt;/li&gt;
&lt;li&gt;05/11：终于复工了。&lt;/li&gt;
&lt;li&gt;01/02：&lt;a href=&#34;https://www.bilibili.com/video/BV1K7411o73B&#34;&gt;成都、丽江、大理&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2019&#34;&gt;2019&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：回顾 2019&lt;/li&gt;
&lt;li&gt;08/07：七月初七。&lt;/li&gt;
&lt;li&gt;06/13：加入腾讯音乐娱乐集团。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2018&#34;&gt;2018&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：蜈支洲 - 丽禾温德姆跨年。&lt;a href=&#34;https://zyf.im/2018/12/31/review-2018/&#34;&gt;回顾 2018&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;11/18：「你愿意嫁给我吗？」&lt;/li&gt;
&lt;li&gt;03/13：Swift Pandarow v1.4.2 Release。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2017&#34;&gt;2017&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：&lt;a href=&#34;https://zyf.im/2017/12/31/review-2017/&#34;&gt;回顾 2017&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;09/06：开始接触 iOS 开发。&lt;/li&gt;
&lt;li&gt;06/25：与你相遇，好幸运。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2016&#34;&gt;2016&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;12/31：&lt;a href=&#34;https://zyf.im/2016/12/31/review-2016/&#34;&gt;回顾 2016&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;08/31：&lt;a href=&#34;https://zyf.im/2016/08/31/20160601-20160831-report/&#34;&gt;刚刚毕业的两月个小结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;07/01：大学顺利毕业，入职实习公司，更专注于计算机技术。&lt;/li&gt;
&lt;li&gt;03/18：&lt;a href=&#34;https://zyf.im/2016/03/18/try-to-write-something/&#34;&gt;试着写东西&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;真实地活着。&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p>如若我从未成功，我想我会顺其自然，<br>
如若我只听从别人的建议过日，我的心会死去。<br>
我是太固执，管制精神的手段对我奈何不得，<br>
有的人必会成为明日之星，我想该轮到我了。</p>
</blockquote>
<p>我是 Yifan，一名在帝都的开发工程师，现在专注于财经领域。这里记录了我开发过程中的一些经验或是人生感悟，希望对你有帮助。</p>
<p>下面是我个人微信二维码，欢迎添加。:)</p>
<img alt="yifanszhao_wechat_qrcode" style="width:240px;" src="https://user-images.githubusercontent.com/9289792/126154429-3143f154-9eb4-4218-9755-8dc087eea27b.jpg" />
<h2 id="timeline">Timeline</h2>
<h3 id="2025">2025</h3>
<ul>
<li>12/31：回顾 2025</li>
</ul>
<h3 id="2024">2024</h3>
<ul>
<li>12/31：回顾 2024</li>
<li>10/08：加入 RightCapital</li>
<li>05/11：福宝贴贴。</li>
</ul>
<h3 id="2023">2023</h3>
<ul>
<li>12/31：回顾 2023</li>
</ul>
<h3 id="2022">2022</h3>
<ul>
<li>12/31：回顾 2022</li>
<li>01/28：拖把光临。</li>
</ul>
<h3 id="2021">2021</h3>
<ul>
<li>12/31：回顾 2021</li>
<li>07/02：不动产权证书。</li>
</ul>
<h3 id="2020">2020</h3>
<ul>
<li>12/31：回顾 2020</li>
<li>05/11：终于复工了。</li>
<li>01/02：<a href="https://www.bilibili.com/video/BV1K7411o73B">成都、丽江、大理</a></li>
</ul>
<h3 id="2019">2019</h3>
<ul>
<li>12/31：回顾 2019</li>
<li>08/07：七月初七。</li>
<li>06/13：加入腾讯音乐娱乐集团。</li>
</ul>
<h3 id="2018">2018</h3>
<ul>
<li>12/31：蜈支洲 - 丽禾温德姆跨年。<a href="/2018/12/31/review-2018/">回顾 2018</a></li>
<li>11/18：「你愿意嫁给我吗？」</li>
<li>03/13：Swift Pandarow v1.4.2 Release。</li>
</ul>
<h3 id="2017">2017</h3>
<ul>
<li>12/31：<a href="/2017/12/31/review-2017/">回顾 2017</a></li>
<li>09/06：开始接触 iOS 开发。</li>
<li>06/25：与你相遇，好幸运。</li>
</ul>
<h3 id="2016">2016</h3>
<ul>
<li>12/31：<a href="/2016/12/31/review-2016/">回顾 2016</a></li>
<li>08/31：<a href="/2016/08/31/20160601-20160831-report/">刚刚毕业的两月个小结</a></li>
<li>07/01：大学顺利毕业，入职实习公司，更专注于计算机技术。</li>
<li>03/18：<a href="/2016/03/18/try-to-write-something/">试着写东西</a></li>
</ul>
<blockquote>
<p>真实地活着。</p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>Links</title>
      <link>https://zyf.im/links/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://zyf.im/links/</guid>
      <description>&lt;h2 id=&#34;friends&#34;&gt;Friends&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.jiejiss.com/&#34;&gt;JieJiSS&amp;rsquo; Blog&lt;/a&gt; Student, Studying in Beijing, China.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.gplane.win/&#34;&gt;Pig Fang Blog&lt;/a&gt; Metro Fan / Minecraft&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;domains&#34;&gt;Domains&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://allhistory.net&#34;&gt;allhistory.net&lt;/a&gt; &lt;a href=&#34;https://www.allhistory.com/&#34;&gt;完美世界·全历史&lt;/a&gt; .net 域名（在阿里云市场低价出售）。&lt;/li&gt;
&lt;/ul&gt;</description>
      <content:encoded><![CDATA[<h2 id="friends">Friends</h2>
<ul>
<li><a href="https://blog.jiejiss.com/">JieJiSS&rsquo; Blog</a> Student, Studying in Beijing, China.</li>
<li><a href="https://blog.gplane.win/">Pig Fang Blog</a> Metro Fan / Minecraft</li>
</ul>
<h2 id="domains">Domains</h2>
<ul>
<li><a href="https://allhistory.net">allhistory.net</a> <a href="https://www.allhistory.com/">完美世界·全历史</a> .net 域名（在阿里云市场低价出售）。</li>
</ul>
]]></content:encoded>
    </item>
  </channel>
</rss>
