<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/migration74.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'zh',
  ),
  'this' => 
  array (
    0 => 'migration74.incompatible.php',
    1 => '不向后兼容的变更',
    2 => '不向后兼容的变更',
  ),
  'up' => 
  array (
    0 => 'migration74.php',
    1 => '从 PHP 7.3.x 移植到 PHP 7.4.x',
  ),
  'prev' => 
  array (
    0 => 'migration74.constants.php',
    1 => '新的全局常量',
  ),
  'next' => 
  array (
    0 => 'migration74.deprecated.php',
    1 => 'PHP 7.4.x 废弃的功能',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'zh',
    'path' => 'appendices/migration74/incompatible.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="migration74.incompatible" class="sect1">
 <h2 class="title">不向后兼容的变更</h2>

 <div class="sect2" id="migration74.incompatible.core">
  <h3 class="title">PHP 核心中不向后兼容的变更</h3>

  <div class="sect3" id="migration74.incompatible.core.non-array-access">
   <h4 class="title">以数组形式访问非数组</h4>

   <p class="para">
    尝试以数组方式访问 <span class="type"><a href="language.types.null.php" class="type null">null</a></span>，<span class="type"><a href="language.types.boolean.php" class="type bool">bool</a></span>，
    <span class="type"><a href="language.types.integer.php" class="type int">int</a></span>，<span class="type"><a href="language.types.float.php" class="type float">float</a></span> 或 <span class="type"><a href="language.types.resource.php" class="type resource">resource</a></span>
    （例如 <code class="literal">$null[&quot;key&quot;]</code>）将会抛出 notice 通知。
   </p>
  </div>

  <div class="sect3" id="migration74.incompatible.core.get-declared-classes">
   <h4 class="title"><span class="function"><a href="function.get-declared-classes.php" class="function">get_declared_classes()</a></span> 函数</h4>

   <p class="para">
    <span class="function"><a href="function.get-declared-classes.php" class="function">get_declared_classes()</a></span>
    函数将不再返回匿名的类，假如它们没有被实例化的话。
   </p>
  </div>
  
  <div class="sect3" id="migration74.incompatible.core.fn">
   <h4 class="title"><code class="literal">fn</code> 关键词</h4>

   <p class="para">
    <code class="literal">fn</code> 成为了保留关键词。需要特别注意，它不能再做为函数名或类名使用，但是仍然可以做为方法名和常量名存在。
   </p>
  </div>

  <div class="sect3" id="migration74.incompatible.core.php-tag">
   <h4 class="title">文件尾部的 <code class="literal">&lt;?php</code> 标签</h4>

   <p class="para">
    文件尾部的 <code class="literal">&lt;?php</code>
    标签（不包含空行）将会被解释成 PHP 起始标记。之前，不是解释为短开始标记，后跟文字 <code class="literal">php</code>
    并导致语法错误（<code class="literal">short_open_tag=1</code>），就是解释为文字 <code class="literal">&lt;?php</code>
    字符串（<code class="literal">short_open_tag=0</code>）。
   </p>
  </div>

  
  <div class="sect3" id="migration74.incompatible.core.stream-wrappers">
   <h4 class="title">Stream 封装协议</h4>

   <p class="para">
    在 stream 上使用 include/require 时，将使用 <strong><code><a href="stream.constants.php#constant.stream-option-read-buffer">STREAM_OPTION_READ_BUFFER</a></code></strong> 选项调用
    <span class="methodname"><a href="streamwrapper.stream-set-option.php" class="methodname">streamWrapper::stream_set_option()</a></span>。编写的自定义 stream 封装协议可能需要实现
    <span class="methodname"><a href="streamwrapper.stream-set-option.php" class="methodname">streamWrapper::stream_set_option()</a></span> 方法以避免警告（始终返回 <strong><code><a href="reserved.constants.php#constant.false">false</a></code></strong> 就足够了）。
   </p>
  </div>

  <div class="sect3" id="migration74.incompatible.core.serialization">
   <h4 class="title">Serialization 序列化</h4>

   <p class="para">
    序列化类型 <code class="literal">o</code> 被移除。因为它不是由 PHP
    生成的，这可能会影响到之前项目中手动生成的序列化字符串。
   </p>
  </div>

  
  <div class="sect3" id="migration74.incompatible.core.password-algorithm-constants">
   <h4 class="title">密码算法常量</h4>

   <p class="para">
    密码哈希算法标识符现在是可空字符串，而不再是整数。
   </p>

   <ul class="itemizedlist">
    <li class="listitem">
     <span class="simpara">
      <strong><code><a href="password.constants.php#constant.password-default">PASSWORD_DEFAULT</a></code></strong> 之前是 int 1; 现在是“2y”（在 PHP 7.4.0、7.4.1、7.4.2 中是 <strong><code><a href="reserved.constants.php#constant.null">null</a></code></strong>）
     </span>
    </li>
    <li class="listitem">
     <span class="simpara">
      <strong><code><a href="password.constants.php#constant.password-bcrypt">PASSWORD_BCRYPT</a></code></strong> 之前是 int 1; 现在是 string &#039;2y&#039;
     </span>
    </li>
    <li class="listitem">
     <span class="simpara">
      <strong><code><a href="password.constants.php#constant.password-argon2i">PASSWORD_ARGON2I</a></code></strong> 之前是 int 2; 现在是 string &#039;argon2i&#039;
     </span>
    </li>
    <li class="listitem">
     <span class="simpara">
      <strong><code><a href="password.constants.php#constant.password-argon2id">PASSWORD_ARGON2ID</a></code></strong> 之前是 int 3; 现在是 string &#039;argon2id&#039;
     </span>
    </li>
   </ul>

   
   <p class="para">
    应用中如果正常使用了常量 PASSWORD_DEFAULT，PASSWORD_BCRYPT，PASSWORD_ARGON2I
    和 PASSWORD_ARGON2ID 将不会受到影响。
   </p>
  </div>

  
  <div class="sect3" id="migration74.incompatible.core.htmlentities">
   <h4 class="title"><span class="function"><a href="function.htmlentities.php" class="function">htmlentities()</a></span> 函数</h4>

   <p class="para">
    <span class="function"><a href="function.htmlentities.php" class="function">htmlentities()</a></span> 如果与仅支持基本实体替换（basic entity substitution）的编码一起使用，现在将发出
    notice（而不是严格标准的 warning），在这种情况下，相当于 <span class="function"><a href="function.htmlspecialchars.php" class="function">htmlspecialchars()</a></span>。
   </p>
  </div>

  <div class="sect3" id="migration74.incompatible.core.fread-fwrite">
   <h4 class="title"><span class="function"><a href="function.fread.php" class="function">fread()</a></span> and <span class="function"><a href="function.fwrite.php" class="function">fwrite()</a></span> 函数</h4>

   <p class="para">
    <span class="function"><a href="function.fread.php" class="function">fread()</a></span> 和 <span class="function"><a href="function.fwrite.php" class="function">fwrite()</a></span>
    在操作失败的时候会返回 <strong><code><a href="reserved.constants.php#constant.false">false</a></code></strong>。之前的版本中会返回空字符串或 0。EAGAIN/EWOULDBLOCK 不视为故障。
   </p>
   <p class="para">
    这些函数现在也会在失败时发出 NOTICE 通知，例如当试图写入一个只读文件资源时。
   </p>
  </div>

 </div>

 <div class="sect2" id="migration74.incompatible.bcmath">
  <h3 class="title">BCMath 任意精度数学</h3>

  <p class="para">
   如果传递了例如 <code class="literal">&quot;32foo&quot;</code> 这种格式不正确的数字，BCMath 函数现在将发出警告。和以前一样，参数将解释为 0。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.curl">
  <h3 class="title">CURL</h3>

  <p class="para">
   现在尝试序列化 <span class="classname"><a href="class.curlfile.php" class="classname">CURLFile</a></span> 类将会生成异常。之前仅会在反序列化时引发。
  </p>
  <p class="para">
   弃用 <strong><code><a href="curl.constants.php#constant.curlpipe-http1">CURLPIPE_HTTP1</a></code></strong>，并在 cURL 7.62.0 起不再支持。
  </p>
  <p class="para">
   弃用 <span class="function"><a href="function.curl-version.php" class="function">curl_version()</a></span> 的 <code class="literal">$version</code> 参数。如果传递了任何不等于默认
   <strong><code><a href="curl.constants.php#constant.curlversion-now">CURLVERSION_NOW</a></code></strong> 的值，则会引发警告并忽略参数。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.datetime">
  <h3 class="title">日期和时间</h3>

  <p class="para">
   在 <span class="classname"><a href="class.datetime.php" class="classname">DateTime</a></span> 或 <span class="classname"><a href="class.datetimeimmutable.php" class="classname">DateTimeImmutable</a></span> 实例上调用
   <span class="function"><a href="function.var-dump.php" class="function">var_dump()</a></span> 或类似方法将不会忘记对象上的可访问属性。
  </p>
  <p class="para">
   <span class="classname"><a href="class.dateinterval.php" class="classname">DateInterval</a></span> 对象的比较（使用
   <code class="literal">==</code>、<code class="literal">&lt;</code> 等）现在将生成警告并始终返回
   <strong><code><a href="reserved.constants.php#constant.false">false</a></code></strong>。之前所有的 <span class="classname"><a href="class.dateinterval.php" class="classname">DateInterval</a></span> 对象都认为相等，除非它们有属性。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.intl">
  <h3 class="title">Intl</h3>

  <p class="para">
   <span class="function"><a href="function.idn-to-ascii.php" class="function">idn_to_ascii()</a></span> 和 <span class="function"><a href="function.idn-to-utf8.php" class="function">idn_to_utf8()</a></span> 的默认参数的值现在为
   <strong><code><a href="intl.constants.php#constant.intl-idna-variant-uts46">INTL_IDNA_VARIANT_UTS46</a></code></strong>，而不是已弃用的 <strong><code><a href="intl.constants.php#constant.intl-idna-variant-2003">INTL_IDNA_VARIANT_2003</a></code></strong>。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.mysqli">
  <h3 class="title">MySQLi</h3>

  <p class="para">
   内嵌服务器功能已移除。自 PHP 7.0 起就已破坏了。
  </p>
  <p class="para">
   未记录的 <code class="literal">mysqli::$stat</code> 属性已移除，以支持 mysqli::stat()。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.openssl">
  <h3 class="title">OpenSSL</h3>

  <p class="para">
   <span class="function"><a href="function.openssl-random-pseudo-bytes.php" class="function">openssl_random_pseudo_bytes()</a></span> 函数现在将在错误情况下抛出异常，类似于 <span class="function"><a href="function.random-bytes.php" class="function">random_bytes()</a></span>。特别是，如果请求的字节数小于或等于零，则会引发
   <span class="classname"><a href="class.error.php" class="classname">Error</a></span>；如果无法收集到足够的随机性，则会引发 <span class="classname"><a href="class.exception.php" class="classname">Exception</a></span>。如果函数不抛出异常，则 <code class="literal">$crypto_strong</code>
   输出参数保证始终为 <strong><code><a href="reserved.constants.php#constant.true">true</a></code></strong>，因此不需要手动检查它。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.pcre">
  <h3 class="title">正则表达式（Perl 兼容）</h3>

  <p class="para">
   当使用 <strong><code><a href="pcre.constants.php#constant.preg-unmatched-as-null">PREG_UNMATCHED_AS_NULL</a></code></strong> 模式时，后跟不匹配的捕获组现在也将设置为 <strong><code><a href="reserved.constants.php#constant.null">null</a></code></strong>（如果启用了偏移捕获，则为 <code class="literal">[null, -1]</code>）。这意味着 <code class="literal">$matches</code> 的大小将始终相同。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.pdo">
  <h3 class="title">PHP 数据对象</h3>

  <p class="para">
   尝试序列化 <span class="classname"><a href="class.pdo.php" class="classname">PDO</a></span> 或 <span class="classname"><a href="class.pdostatement.php" class="classname">PDOStatement</a></span> 实例现在将生成
   <span class="classname"><a href="class.exception.php" class="classname">Exception</a></span> 而不是 <span class="classname"><a href="class.pdoexception.php" class="classname">PDOException</a></span>，这与其它不支持序列化的内部类一致。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.reflection">
  <h3 class="title">反射</h3>

  <p class="para">
   现在如果尝试对 Reflection 对象序列化将会生成异常。从未支持反射对象序列化并会引起反射对象错误。现在已明确禁止。
  </p>

  <p class="para">
   <span class="classname"><a href="class.reflectionclassconstant.php" class="classname">ReflectionClassConstant</a></span>、<span class="classname"><a href="class.reflectionmethod.php" class="classname">ReflectionMethod</a></span> 和
   <span class="classname"><a href="class.reflectionproperty.php" class="classname">ReflectionProperty</a></span> 类常量的值发生了变化。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.spl">
  <h3 class="title">PHP 标准库（SPL）</h3>

  <p class="para">
   现在对 <span class="classname"><a href="class.arrayobject.php" class="classname">ArrayObject</a></span> 实例调用 <span class="function"><a href="function.get-object-vars.php" class="function">get_object_vars()</a></span>
   将始终返回 <span class="classname"><a href="class.arrayobject.php" class="classname">ArrayObject</a></span> 本身（或子类）的属性。之前，除非指定了
   <strong><code><a href="class.arrayobject.php#arrayobject.constants.std-prop-list">ArrayObject::STD_PROP_LIST</a></code></strong> flag，否则将返回包装后的 array/object。
  </p>
  <p class="para">
   其它受到影响的操作有：
  </p>
  <ul class="itemizedlist">
   <li class="listitem">
    <span class="simpara">
     <span class="methodname"><strong>ReflectionObject::getProperties()</strong></span>
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     <span class="function"><a href="function.reset.php" class="function">reset()</a></span>、<span class="function"><a href="function.current.php" class="function">current()</a></span> 等等。
     改用 <span class="interfacename"><a href="class.iterator.php" class="interfacename">Iterator</a></span> 方法。
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     可能将对象属性作为列表处理的其它情况。比如 <span class="function"><a href="function.array-walk.php" class="function">array_walk()</a></span>。
    </span>
   </li>
  </ul>
  <p class="para">
   <code class="literal">(array)</code> 转换不受影响。它们将继续返回包装数组或 <span class="classname"><a href="class.arrayobject.php" class="classname">ArrayObject</a></span>
   属性，具体取决于是否使用 <strong><code><a href="class.arrayobject.php#arrayobject.constants.std-prop-list">ArrayObject::STD_PROP_LIST</a></code></strong> flag。
  </p>
  <p class="para">
   如果传递零，<span class="methodname"><a href="splpriorityqueue.setextractflags.php" class="methodname">SplPriorityQueue::setExtractFlags()</a></span>
   将引发异常。以前，这会在执行下个提取操作时生成可恢复的致命错误。
  </p>
  <p class="para">
   除了 <span class="interfacename"><a href="class.serializable.php" class="interfacename">Serializable</a></span> 接口之外，<span class="classname"><a href="class.arrayobject.php" class="classname">ArrayObject</a></span>、<span class="classname"><a href="class.arrayiterator.php" class="classname">ArrayIterator</a></span>、<span class="classname"><a href="class.spldoublylinkedlist.php" class="classname">SplDoublyLinkedList</a></span>
   和 <span class="classname"><a href="class.splobjectstorage.php" class="classname">SplObjectStorage</a></span> 现在还支持 <code class="literal">__serialize()</code> 和 <code class="literal">__unserialize()</code>
   机制。这意味着在旧版本上创建的序列化负荷仍然可以反序列化，但旧版本将无法理解 PHP 7.4 创建的新负荷（payloads）。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.tokenizer">
  <h3 class="title">Tokenizer</h3>

  <p class="para">
   <span class="function"><a href="function.token-get-all.php" class="function">token_get_all()</a></span> 现在将为非法字符发出 <strong><code><a href="tokens.php#constant.t-bad-character">T_BAD_CHARACTER</a></code></strong> token，而不是在 token stream 中留下漏洞。
  </p>
 </div>

 <div class="sect2" id="migration74.incompatible.cookie-decode">
  <h3 class="title">接收到的 Cookies</h3>

  <p class="para">
   从 PHP 7.4.11 开始，为了安全考虑，接受到的 Cookie 中的 <em>names</em> 参数不再被
   URL 编码。
  </p>
 </div>

</div><?php manual_footer($setup); ?>