PHP Interview Questions(8)

面试时间:2018 年 9 月 21 日,星期五,下午三点。


单例模式

final class Singleton
{
    /**
     * @var Singleton
     */
    private static $instance;
    /**
     * gets the instance via lazy initialization (created on first usage)
     */
    public static function getInstance(): Singleton
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }
        return static::$instance;
    }
    /**
     * is not allowed to call from outside to prevent from creating multiple instances,
     * to use the singleton, you have to obtain the instance from Singleton::getInstance() instead
     */
    private function __construct()
    {
    }
    /**
     * prevent the instance from being cloned (which would create a second instance of it)
     */
    private function __clone()
    {
    }
    /**
     * prevent from being unserialized (which would create a second instance of it)
     */
    private function __wakeup()
    {
    }
}

https://github.com/domnikl/DesignPatternsPHP/blob/master/Creational/Singleton/Singleton.php

https://phptherightway.com/pages/Design-Patterns.html

https://stackoverflow.com/questions/203336/creating-the-singleton-design-pattern-in-php5

MySQL 分区表

https://www.jianshu.com/p/89311703b320

363 total views, 2 views today

PHP Interview Questions(7)

面试时间:2018 年 9 月 14 日,星期五,下午六点。


MySQL 主从备份的作用是什么?

防止一台宕机。

PHP-FPM 的运行原理是什么?

master/worker架构。

MySQL 使用 delete 语句删除数据,空间没有释放怎么解决?

optmize table {name}

怎么设计 1000W 用户量的用户签到系统?

分库分表
中间件

315 total views, 2 views today

PHP Interview Questions(6)

面试时间:2018 年 9 月 13 日,星期五,下午 13 : 00。


存在两个时间,如何计算两个日期的天数差。

<?php
$dateStart = '2017-05-03 12:34:56';
$dateEnd = '2020-12-12 09:17:18';
// 计算两个时间天数差
return intval((strtotime($dateEnd) - strtotime($dateStart)) / (24 * 60 * 60));

PHP 中传值和引用的区别是什么?叙述下其应用场景和优劣。

http://php.net/manual/zh/language.references.php

请写一个正则表达式,用来验证手机号码的正确性。

\d{11}

请列举出常用的PHP魔术方法,并说明其应用场景。

http://php.net/manual/zh/language.oop5.magic.php

请写出 echo,print,printf,sprintf,print_r,var_dump 的差异。

http://php.net/manual/en/ref.strings.php

请无乱码的切割中文字符(实例字符:“布克大学,就要不凡”,截取“布克大学”)。

mb_substr("布克大学,就要不凡", 0, 4)
substr("布克大学,就要不凡", 0, 12)

PHP 中异常处理时(try catch finally),return 应该写在哪里?

都可以。

有如下三张表,请写出查询每个学生平均分数的 SQL 语句。

>>> 学生表/students:
id
name # 学生姓名

>>> 成绩表/scores:
id
student_id # 学生id@students.id
subject_id # 科目id@subjects.id
score # 分数

>>> 科目表/subjects:
id
name # 科目名称

结果要求输出 学生姓名 科目名称 平均分数

select students.name as student_name, subjects.name as subject_name, sum(scores.score)/count(students.id) as avg_score
from scores
join students on (students.id = scores.student_id)
join subjects on (subjects.id = scores.subject_id)
group by students.id

PHP 中创建多进程、多线程的方式有哪些,使用多线程、多进程有哪些优缺点。

多进程:
http://php.net/manual/zh/book.pcntl.php
多线程:
http://php.net/manual/en/book.pthreads.php

描述下 IoC 和 DI 的实现原理。

https://phptherightway.com/#dependency_injection

OAuth2 的工作原理是怎么样的?

https://laravel.com/docs/master/passport

(口述作答)根据你的经验,如何规划一个大流量、高并发的WEB站点。

http://network.51cto.com/art/201809/583103.htm

聊聊  MySQL 主从同步。

https://www.digitalocean.com/community/tutorials/how-to-set-up-master-slave-replication-in-mysql

聊聊 RESTful API 设计。

https://www.toptal.com/laravel/restful-laravel-api-tutorial

如果团队有一个消极的人怎么处理。如果团队成员玩手机被老板发现了怎么处理。能否带领一个 3 到 5 人的团队完成开发任务。

不处理。

380 total views, 1 views today

PHP Interview Questions(5)

面试时间:2018 年 9 月 12 日,星期三,下午两点。


PHP 怎么实现多继承?

trait

Git 怎么撤销上次提交?

git commit --amend

ls -l 命令的前十位代表什么?

drwxrwxrwx

解释什么是 XSS,CSRF,SQL 注入以及如何防范

http://www.cnblogs.com/leecong/p/5898370.html

高并发下如何锁库存,防止超卖?/ 支付回调如何防止重复写入。

乐观锁

Nginx location 匹配规则。

http://nginx.org/en/docs/http/ngx_http_core_module.html#location

Nginx 为什么比 Apache 快?

Nginx 采用 epoll 模型,异步非阻塞。
Apache 采用 select 模型,同步阻塞。

TCP 和 UDP 的区别都有哪些?

https://www.diffen.com/difference/TCP_vs_UDP

322 total views, no views today

PHP Interview Questions (4)

面试时间:2018 年 9 月 10 日,周一,上午十点。


1. 求一个矩阵中最大的二维矩阵(元素和最大).如:
1 2 0 3 4
2 3 4 5 1
1 1 5 3 0
中最大的是:
4 5
5 3
要求: (1)写出代码; (2)分析时间复杂度

$matrix = [
[1, 2, 0, 3, 4],
[2, 3, 4, 5, 1],
[1, 1, 5, 3, 0]
];
$row = 3;
$col = 5;
$maxMatrix = [];
$maxSum = 0;

for ($i = 0; $i < $row - 1; $i++) {
    for ($j = 0; $j < $col - 1; $j++) {
        $sum = $matrix[$i][$j] + $matrix[$i][$j + 1] + $matrix[$i + 1][$j] + $matrix[$i + 1][$j + 1];
        if ($sum > $maxSum) {
            $maxSum = $sum;
            $maxMatrix = [
                [$matrix[$i][$j], $matrix[$i][$j + 1]],
                [$matrix[$i + 1][$j], $matrix[$i + 1][$j + 1]]
            ];
        }
    }
}

O(row*col)

2. 输入 n 个整数,输出其中最大的 k 个。例如输入 1,2,3,4,5,6,7,8 这 8 个数字,则其中最大的 3 个数字为 8,7,6。

$n = 8;
$k = 3;
$arr = [1, 2, 3, 4, 5, 6, 7, 8];

O(N*log2N)
rsort($arr);
for ($i = 0; $i < $k; $i++) {
    echo $arr[$i], "\n";
}

O(N*K)
$maxKeys = [];
for ($i = 0; $i < $k; $i++) {
    $max = 0;
    foreach ($arr as $key => $value) {
        if (!in_array($key, $maxKeys) && $value > $max) {
            $max = $value;
            $maxKey = $key;
        }
    }
    echo $max, "\n";
    $maxKeys[] = $maxKey;
}

3. 输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。

<?php
$str = 'abc';
$arr = str_split($str);
$len = strlen($str);

function exchange(&$a, &$b) {
    $temp = $a;
    $a = $b;
    $b = $temp;
}

function permutation($arr, $i, $len) {
    if ($i == $len) {
        return;
    }
    if ($i == $len - 1) {
        echo join('', $arr), "\n";
    }
    for ($j = $i; $j < $len; $j++) {
        exchange($arr[$i], $arr[$j]);
        permutation($arr, $i + 1, $len);
    }
}

permutation($arr, 0, $len);

348 total views, no views today

PHP Interview Questions (3)

面试时间:2018 年 9 月 7 日,周五,下午两点。


PHP

1. 数组 $arr = [1,  2,  3,…],如何在数组里追加一个元素x,请给出至少两种代码

array_push($arr, x);

$arr[count($arr)] = x;

2. 下面的代码,输出是 true 还是 false?

is_null(0)
is_null([])
empty(false)
empty(0)
false == []
null == []
false false true true true true

3. 现有一个字符串,$str = “欢迎来到享换机,You are welcome!”,请给出代码,截取前 7 个文字

echo mb_substr($str, 0, 7);

echo substr($str, 0, 21);

4. 请写一个正则表达式验证电子邮件的格式是否正确

^[A-Za-z0-9_\-\.]+@[A-Za-z0-9_\-\.]+\.[A-Za-z]{2,4}$

5. 给出下面代码的结果

$a = 1;
$b = $a;
$c = &$a;
$d = $c;
$e = &$c;
$e = 2;
echo $a, "\n", $b, "\n", $c, "\n", $d, "\n", $e, "\n";
2
1
2
1
2

6. 请简述Session原理,以及使用Session时的注意事项

https://www.jianshu.com/p/2b7c10291aad

7. 已有数组$arr = [1, 2, 3…] 以及变量 $a,使用 array_map 函数,生成新的数组,其每个元素在 $arr 每个元素的基础上加 $a。请给出代码

return array_map(function($v) use ($a) {
    return $v + $a;
}, $arr);

8. 请给出代码,输出当月最后一天的当前时间,格式:年-月-日 时:分:秒。eg:

当前时间:2018-05-10 22:05:35

输出时间:2018-05-31 22:05:35

echo date('Y-m-d H:i:s', strtotime("last day of this month"));

9. PHP 怎么避免 XSS 攻击?怎么避免 SQL 注入?

https://www.cnblogs.com/ITtangtang/p/3982297.html

10. 请给出下面代码在php7下的结果。如有错误,请指出

declare(strict_types=0);
static $c = 1;
$d = 3;
$res1 = test(1.5, 2);
echo $res1, "\n";
$res2 = test(1.5, 2);
echo $res1, "\n";

function test (int $a, int $b) : int {
    static $d = 4;
    $res = $a + $b;
    if (isset($c)) {
        $res += $c;
    }
    if (isset($d)) {
        $res += $d;
        $d += 1.5;
    }
    return $res;
}
7
7

数据结构 & 算法

1. n 只猴子,围成 1 圈坐,依次按 1、2、3、…、m循环报数,报到 m 的淘汰。请写出代码,求最后剩下的猴子。

function monkey($n, $m) {
    $out = [];
    $pointer = 0;

    for ($i = 1; $i <= $n - 1; $i++) {
        echo "round ", $i, ": ";
        $count = 0;
        while ($count < $m) {
            $pointer = $pointer % $n + 1;

            if (!in_array($pointer, $out)) {
                $count++;
                if ($count == $m) {
                    echo $pointer, "(out)\n";
                } else {
                    echo $pointer, ", ";
                }
            }
        }
        $out[] = $pointer;
    }

    for ($i = 1; $i <= $n; $i++) {
        if (!in_array($i, $out)) {
            return $i;
        }
    }
}

2. 请写出代码,构造单向链表。

class ListNode {
    public $val;
    public $next;

    public function __construct($val) {
        $this->val = $val;
        $this->next = null;
    }
}

$array = [1, 2, 3, 4, 5];

foreach ($array as $key => $val) {
    if ($key == 0) {
        $head = $node = new ListNode($val);
    } else {
        $node->next = new ListNode($val);
        $node = $node->next;
    }
}

return $list;

3. 有两个数组 [1, 4, 7, 8, 9], [2, 3, 7, 8, 10],请写出代码,将两个数组合并成一个升序的数组。

$arr1 = [1, 4, 7, 8, 9];
$arr2 = [2, 3, 7, 8, 10];
$arr = array_merge($arr1, $arr2);
sort($arr);

MySQL

1. 取表 A 里面 status 字段为 1 的 1000 到 1010 行数据,请写出 sql

select * from A where status = 1 limit 10, 1000

2. 请简述 left join, inner join, right join 的区别

https://stackoverflow.com/questions/448023/what-is-the-difference-between-left-right-outer-and-inner-joins

3. 表 A 有一个联合索引 idx(a, b, c)。下面的情况,是否可以使用到索引

select * from A where b = 2;
select * from A where a = 2;
select * from A where a > 2;
select * from A where c = 1;
select * from A where a = 1 and c = 2;
select * from A where a in (1, 2, 3) and c = 2;
No Yes Yes No Yes Yes

逻辑

1. 1 块钱可以买 2 瓶汽水,2 个瓶盖可以换 1 瓶汽水。问十块钱最多可以喝到多少瓶汽水?

40

2. 1000 个瓶子中有一瓶毒药,一只老鼠吃到毒药后一周才会死亡。如果要检测出有毒药的一瓶,问至少需要多少只老鼠?

一周时间:10 只老鼠
两周时间: 7 只老鼠
三周时间: 5 只老鼠

356 total views, no views today

PHP Interview Questions(2)

面试时间:2018 年 9 月 4 日,星期二,下午两点。


PHP

1. 列举出几种你知道的 PHP 遍历或者迭代数组的方法

for, foreach, array_walk, array_map

安全防护

2. 解释什么是 XSS,CSRF,SQL 注入以及如何防范

http://www.cnblogs.com/leecong/p/5898370.html

网络

3. 在浏览器中输入网址到页面显示,期间发生了哪些过程?

http://www.cnblogs.com/kongxy/p/4615226.html

数据库

4. 一张采用 Innodb 的 User 表,其中 id 为主键,name 为普通索引,试从 索引的数据结构 角度分析,以下两条语句(均返回一条记录)在检索过程中有哪些区别?

Sql 1: SELETE id, name, address from User where name = "smith";
Sql 1: SELETE id, name, address from User where id = 1;
http://blog.codinglabs.org/articles/theory-of-mysql-index.html

5. 现有一统计网站独立访客需求,流量百万以上,如以 IP 为标识,可以查看当天实时或指定某天的 IP 数(需要去重),采用 MySQL 来实现,那么:

5.1 你会如何设计表和索引?(文字、sql 均可,方案尽可能高效)

IP 地址转换为整形存储,按日期分表,以 (IP, 日期) 作为联合索引。

5.2 数据如何入库,当天实时和某天数据该如何查询?(写出 sql 语句)

redis 队列缓存 -> MySQL 批量入库

select count(ip) from visit_log_{yyyymm} where date = {yyyy-mm-dd};

面试部分

你平时都是怎么设计数据库的?

按照业务逻辑设计(其实想考察的是数据库三大范式)。

说说 PHP 的垃圾回收机制。

http://php.net/manual/zh/features.gc.php

437 total views, 2 views today

PHP Interview Questions(1)

面试时间:2018 年 8 月 31 日,星期五,下午两点。


PHP 基础

1. 写个匹配日期格式为 “2018-01-01 08:00:00 AM” 的正则表达式

preg_match("/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\sAM|PM$/", "2018-01-01 08:00:00 AM");

2. 类似 _constrcut() 的常见的魔术方法还有哪些(3 – 5 个即可)

http://php.net/manual/zh/language.oop5.magic.php

3. 简述几个 http 状态码 40X 、50X 的区别

40X 客户端请求错误
50X 服务器错误

4. array + array 与 array_merge() 的区别

$arr1 = [1, 2, 3, 'color' => 'red'];
$arr2 = [4, 5, 6, 7, 8, 'color' => 'green'];

$arr1 + $arr2 = [1, 2, 3, 'color' => 'red', 7, 8];

array_merge($arr1, $arr2) = [1, 2, 3, 'color' => 'green', 4, 5, 6, 7, 8];

5. 简述 include 和 require 的区别

https://stackoverflow.com/questions/2418473/difference-between-require-include-require-once-and-include-once

The require() function is identical to include(), except that it handles errors differently. If an error occurs, the include() function generates a warning, but the script will continue execution. The require() generates a fatal error, and the script will stop.

存储

1. MySQL 存储引擎 MyISAM 与 InnoDB 区别(不少于 2 点)

                                                 MyISAM   InnoDB
----------------------------------------------------------------
Required full-text search                        Yes      5.6.4
----------------------------------------------------------------
Require transactions                                      Yes
----------------------------------------------------------------
Frequent select queries                          Yes      
----------------------------------------------------------------
Frequent insert, update, delete                           Yes
----------------------------------------------------------------
Row locking (multi processing on single table)            Yes
----------------------------------------------------------------
Relational base design                                    Yes

https://stackoverflow.com/questions/20148/myisam-versus-innodb

2. MySQL ABC 联合索引,以下哪些可以走到索引

A: AB   B: AC   C: BC   D: CA   E: CA

AB

3. 简单介绍 memcache 和 redis 的区别(不少于 2 点)

redis 支持持久化,更多的数据类型。

https://stackoverflow.com/questions/10558465/memcached-vs-redis

代码

1. 写个函数对数组 [{“name”:”sam”, “age”:22}, {“name”:”jim”, “age”:20}, {“name”:”paul”, “age”:26}, …] 按年龄倒叙排序

$array = [
    ["name" => "sam", "age" => 22],
    ["name" => "jim", "age" => 20],
    ["name" => "paul", "age" => 26]
];

usort($array, function($a, $b) {
    return $b['age'] - $a['age'];
});

2. 假设有这样一个二维数组

[
["id"=>1, "pid"=>0, "name"=>"a"],
["id"=>2, "pid"=>1, "name"=>"b"],
["id"=>3, "pid"=>1, "name"=>"c"],
["id"=>4, "pid"=>2, "name"=>"d"],
...
]

表示这样的一棵树

a
|  \
b   c
|
d

请写个函数将二维数组转换成这样的 json 输出

{
"id": 1, "name": "a",
"children": [
    {
        "id": 2, "name": "b",
        "children": [
            {"id": 4, "name": "d"}
        ]
    },
    {"id": 3, "name": "c"}
]
}
$array = [
    ["id"=>1, "pid"=>0, "name"=>"a"],
    ["id"=>2, "pid"=>1, "name"=>"b"],
    ["id"=>3, "pid"=>1, "name"=>"c"],
    ["id"=>4, "pid"=>2, "name"=>"d"]
];

class TreeNode
{
    public $id;
    public $name;
}

class Tree {
    public static function toJson($array) {
        $nodes = [];
        foreach ($array as $k => $v) {
            $node = new TreeNode();
            $node->id = $v['id'];
            $node->name = $v['name'];
            $nodes[$v['id']] = $node;
            if ($v['pid'] === 0) {
                $head = $nodes[$v['id']];
            } else {
                $nodes[$v['pid']]->children[] = $node;
            }
        }
        return json_encode($head);
    }
}

echo Tree::toJson($array);

面试部分

Linux

  • 查看进程
  • 查看端口占用
  • 统计字符串出现次数

Nginx

MySQL

Redis

PHP

JQuery

算法

347 total views, no views today

What’s new in PHP 7.2

注意:需要完整版本的 PHP 7.2 新特性介绍请查看官方文档或者翻译版本。本文只会介绍我自己感兴趣的部分。


Moving MCrypt to PECL

MCrypt 扩展从 PHP 7.1 版本开始被标记为废弃,在 PHP 7.2 版本正式从 PHP 核心移到 PECL。相应的,Sodium 扩展从 PECL 移到了 PHP 核心。(由于 libmcrypt 从 2007 年开始就再也没有更新过,所以强烈建议不要再使用 MCrypt 扩展,推荐使用 OpenSSL 或者 Sodium 扩展。)

下面是分别用 MCrypt,OpenSSL,Sodium 三种扩展实现的加密和解密方法,具体可以查看:https://github.com/tanghengzhi/php-crypto。

PHP Extension Algorithm
Mcrypt AES256-ECB
OpenSSL AES256-CBC
Sodium AES256-GCM

trait Key {
    public static $key = "secret\0\0\0\0\0\0\0\0\0\0";
}

class Mcrypt {
    use Key;
    public static function encrypt($data) {
        $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);
        $encryptText = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, self::$key, $data, MCRYPT_MODE_ECB, $iv);
        return base64_encode($encryptText);
    }

    public static function decrypt($data) {
        $encryptText = base64_decode($data);
        $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);
        $decryptText = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, self::$key, $encryptText, MCRYPT_MODE_ECB, $iv);
        return $decryptText;
    }
}

class OpenSSL {
    use Key;

    public static function encrypt($data) {
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('AES-256-CBC'));
        $encryptText = openssl_encrypt($data, 'AES-256-CBC', self::$key, OPENSSL_RAW_DATA, $iv);
        return base64_encode($iv . "::" . $encryptText);
    }

    public static function decrypt($data) {
        list($iv, $encryptText) = explode('::', base64_decode($data));
        $decryptText = openssl_decrypt($encryptText, 'AES-256-CBC', self::$key, OPENSSL_RAW_DATA, $iv);
        return $decryptText;
    }
}

class Sodium {
    use Key;

    public static function encrypt($data) {
        $nonce = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES);
        $encryptText = sodium_crypto_aead_aes256gcm_encrypt($data, '', $nonce, str_pad(self::$key, SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES));
        return base64_encode($nonce . "::" . $encryptText);
    }

    public static function decrypt($data) {
        list($nonce, $encryptText) = explode('::', base64_decode($data));
        $decryptText = sodium_crypto_aead_aes256gcm_decrypt($encryptText, '', $nonce, str_pad(self::$key, SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES));
        return $decryptText;
    }
}

imageantialias() is now also available if compiled with a system libgd.

曾经因为 Ubuntu 系统自带的 php70-gd 不支持 imageantialias() 折腾了一天,现在这个问题终于解决了。

Exciting!!!


参考:

http://php.net/manual/en/migration72.php

https://laravel-china.org/topics/9814/introduction-of-new-functions-of-php-72

705 total views, 3 views today

如何导出中文报表

前提:

使用 utf-8 编码。

支持 macOS 上的 Numbers 和 Windows 上的 Excel 打开。(LibreOffice Calc 没有测试,Linux爱好者可以帮忙测试。)


1. 导出 Excel

这里我们使用 PHPOffice(https://github.com/PHPOffice)。PHPOffice 有两个导出 Excel 的项目,PHPExcel 和 PhpSpreadsheet,PHPExcel 支持 PHP 5.2 及以上的版本,PhpSpreadsheet 支持 PHP5.6 及以上的版本,具体的区别可以查看 Github 页面上的对比。


PHPExcel vs PhpSpreadsheet ?

PhpSpreadsheet is the next version of PHPExcel. It breaks compatibility to dramatically improve the code base quality (namespaces, PSR compliance, use of latest PHP language features, etc.).

Because all efforts have shifted to PhpSpreadsheet, PHPExcel will no longer be maintained. All contributions for PHPExcel, patches and new features, should target PhpSpreadsheet develop branch.

However PhpSpreadsheet is still unstable and not yet released. So if you need stability stick to PHPExcel until this project is released. If you prefer to live on the edge you can try to install this project manually via composer, but there is no guarantee and it will likely break again before an official release.

简单来说,PhpSpreadsheet 是用来代替 PHPExcel 的。不过由于 PhpSpreadsheet 现在还没有发布稳定版,所以我们这里还是使用 PHPExcel。

推荐使用 composer 安装。

composer require phpoffice/phpexcel

Examples 目录下有详细的示例代码,可以参考。


// Create new PHPExcel object
$objPHPExcel = new PHPExcel();

// Set document properties
$objPHPExcel->getProperties()->setCreator("Victor Tang")
    ->setLastModifiedBy("Victor Tang")
    ->setTitle("PHPExcel Test Document")
    ->setSubject("PHPExcel Test Document");

// Add some data
$objPHPExcel->setActiveSheetIndex(0)
    ->setCellValue('A1', 'Hello')
    ->setCellValue('B2', 'world!')
    ->setCellValue('C1', '你好')
    ->setCellValue('D2', '世界!');

// Redirect output to a client’s web browser (Excel2007)
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="PHPExcel Test Document.xlsx"');
header('Cache-Control: max-age=0');

$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->save('php://output');

2. 导出 CSV

直接导出 UTF-8 格式的 CSV 文件在 macOS 和 Linux 系统是能完美支持的,但是在 Windows 下打开可能就会乱码,所以这里我们需要导出 UTF-8 with BOM 格式的 CSV 文件。


// UTF-8 with BOM
$csv = chr(0xEF) . chr(0xBB) . chr(0xBF);

$csv .= "Hello,World\n";
$csv .= "你好,世界\n";

测试了 macOS 上的 Numbers 和 Excel 2016,Excel 格式和 UTF-8 with BOM 的 CSV 格式都没有乱码。一台 Windows 10 + Excel 2007 的机器上 UTF-8 with BOM 的 CSV 格式乱码了,一台 Windows 7 + Excel 2007 的机器上 UTF-8 with BOM 的 CSV 格式没有乱码。

样本太少,也不好下什么结论,虽然有一台机子打开乱码了,但是我还是认为 Excel 格式和 UTF-8 with BOM 的 CSV 格式应该是可以兼容所有操作系统的。另外在搜索资料的过程中看到好多叫嚣 “UTF-8 without BOM 才是正统” 的文章,还是不禁有些感概。比起讨论开源还是闭源,理论标准还是事实标准,我们更需要的是能够解决实际问题的方法。


参考:

https://github.com/PHPOffice/PhpSpreadsheet

https://github.com/PHPOffice/PHPExcel

https://stackoverflow.com/questions/5601904/encoding-a-string-as-utf-8-with-bom-in-php

https://en.wikipedia.org/wiki/Byte_order_mark

488 total views, 2 views today