从 0 开始编译安装 swoole 示例

官方文档:https://wiki.swoole.com/wiki/page/6.html

Swoole扩展是按照PHP标准扩展构建的。使用phpize来生成编译检测脚本,./configure来做编译配置检测,make进行编译,make install进行安装。
上面所说都是编译 php 扩展的通用过程。

# 下载 release 版本 #
# 注意:在国内下载 github 的资源,有时候不稳定,可以用迅雷等下载工具在本机下载好,再通过 SFTP 传到服务器 #
cd /usr/local/src
wget https://github.com/swoole/swoole-src/archive/master.tar.gz

# 解压、进入目录 #
tar xzvf swoole-src-master.tar.gz
cd swoole-src-master

# 生成编译检测脚本、配置检查、编译安装 #
# phpize、php-config 请使用你自己的路径 #
/www/server/php/72/bin/phpize && \
./configure --with-php-config=/www/server/php/72/bin/php-config \
--enable-coroutine --enable-openssl  \
--enable-http2  \
--enable-async-redis \
--enable-sockets \
--enable-mysqlnd && \
make clean && make && sudo make install

精简默认安装

/www/server/php/72/bin/phpize && \
./configure --with-php-config=/www/server/php/72/bin/php-config && \
make clean && make && sudo make install

如果上述步骤顺利的话,就会有 Build complete 输出:

Build complete.
Don't forget to run 'make test'.

Installing shared extensions:     /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/
Installing header files:          /www/server/php/72/include/php/

cd /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/ 就能找到 swoole.so 文件了

修改 php.ini 文件,将 swoole.so 加载进去

[swoole]
extension = /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/swoole.so

通过 php -m | grep swoole 来查看是否成功加载了swoole

相关错误

编译安装swoole报错 Enable http2 support, require nghttp2 library.

swoole安装异步redis –enable-async-redis出错 hiredis/hiredis.h: No such file or directory 解决方法

swoole安装异步redis –enable-async-redis出错 hiredis/hiredis.h: No such file or directory 解决方法

在安装 swoole 异步redis,会带上这个选项

--enable-async-redis

编译过程中会有下列报错产生:

hiredis/hiredis.h: No such file or directory

这个原因是没有安装 hiredis 导致。

解决办法 安装 hiredis:

cd /usr/local/src

# 安装之前看一下github,如果有新版本,就安装新的 #
wget https://github.com/redis/hiredis/archive/v0.14.0.tar.gz
mv v0.14.0.tar.gz hiredis-0.14.0.tar.gz
tar xzvf hiredis-0.14.0.tar.gz
cd hiredis-0.14.0
make && make install

# 在底部添加这行环境变量 #
vim /etc/profile
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

source /etc/profile

OK,到这里就安装完成了,重新编译 swoole 即可。

编译安装swoole报错 "Enable http2 support, require nghttp2 library."

安装swoole时, 因为现在搭建环境的时候不会涉及到http2.0, 所以不会安装对应的依赖包, 会报出类似以下错误:

In file included from /root/build/swoole-src-master/swoole.c:16:0:
/root/build/swoole-src-master/php_swoole.h:143:2: error: #error "Enable http2 support, require nghttp2 library."
 #error "Enable http2 support, require nghttp2 library."

解决方法 安装 nghttp2:

# 如果 github 上有新版本的就下载安装新版的 #
wget https://github.com/nghttp2/nghttp2/releases/download/v1.34.0/nghttp2-1.34.0.tar.gz
tar zxvf nghttp2-1.34.0.tar.gz
cd nghttp2-1.34.0
./configure
make && make install

安装好 nghttp2,再次编译安装 swoole 即可。

译文管理平台

译文管理平台

Author Sure Yu
E-mail yusureyes@163.com

项目介绍

  这是一个用于管理多语言资源的译文管理系统,由于用户分布于全球各地,公司的 App 需要显示多语言,Android 和 iOS 有大量的译文需要管理,手工维护极其麻烦,于是这个系统诞生了,方便 translator 在平台翻译,翻译完成之后,开发者将一键导出代码,直接放置在项目中。

  • 注意 translator 需要自己找人翻译,本系统只是维护译文资源,并不会自动翻译。

    公司 App 下载方式:软件商店搜索 Yeelight

软件架构

PHP 7.1+
Mysql 5.6+
框架: Laravel 5.3
后台系统: iDashboard

安装教程

  1. git clone 项目至本地目录
  2. composer install
  3. cp .env.example .env 修改配置信息
  4. php artisan key:generate 生成 APP_KEY
  5. database/sql 找到 SQL 文件导入数据库
  6. 配置 Apache / Nginx 站点,浏览访问

如果 storage 不可写,请赋权限:
chmod -R 777 storage

演示地址

http://translate.demo.yusure.cn
管理员账号密码: admin 123456
Translator账号密码: translator 123456

使用说明

如何录入源语言(中文)
  1. 创建应用(可以后期创建):可以将多个 Project 分配到一个应用下,因为项目迭代会出现多个 Project,为方便管理,增加应用管理。
  2. 创建项目:点击 Project List,勾选需要翻译的语言,右上角添加项目
  3. 回到 Project List,点击 “录入” 按钮,一个小键盘的图标,录入 key(程序用的) 和 源语言(中文)
如何配置待翻译语言:

修改配置文件config/languages.php

return [
    /* 英语 */
    'en'    => 'English',
    /* 韩语 */
    'ko'    => 'Korean',
    /* 法语 */
    'fr'    => 'French',
];
原文录入完成之后,如何邀请 translator 帮忙翻译:
  1. 首先帮 translator 创建好账号,并发送给他。
  2. 点击查看 Project,在语言管理页面,点击红色的小手图标邀请按钮,将其账号勾选提交。

  3. 在邀请的图标后面是锁定功能,锁定之后,translator 不能修改译文,在 translator 完成翻译之后,该语言的译文自动锁定,如果需要修改,管理员可以随时解锁。
  4. 最后面是给 translator 发送邮件提醒,邮箱是帮 translator 创建账号时添加的,发信配置在 .env 文件。
如何配置对照语言:

例如翻译英文需要参考中文,翻译法语需要英文作为参考,那么就需要修改这个配置文件
config/translator.php

如何导出译文:

当译文都 ready 的时候,需要导出译文,导出译文有两种方式:第一种基于语言去导出,第二种针对整个应用(可以合并多个 Project)可以导出压缩包。
目前可以导出三种格式 Android xml、iOS strings、RN js。

translator 视角
  1. 支持对译文资源进行评论
  2. 支持标记有问题的译文资源,方便后续定位。(注意必须要处理掉所有标记才能完成翻译)

项目截图

本项目在公司内部运行半年有余,经过很多细节优化,为 Android、iOS 工程师提供了便利,现在将其开源出来,为开源事业添砖加瓦!
本项目为开源项目,允许把它用于任何地方,不受任何约束,欢迎 star、 fork 项目。

记一次 Laravel 项目迁移之后 Model 报错问题

  之前迁移过一个 Laravel 5.3 的网站,发布完代码,composer update 之后,能正常访问,随便点了点就再没去管它,后来在后台点击反馈模块就报错,当时在 laravel.log 看到 sql 语句是表名后面没有 s,那肯定报错啊,于是徒手在那个 Model 里面指定上 $table,解决了之后,也就没去深究,后来感觉心里越来越不安,虽然不是我写的,但没去深究,就感觉有罪恶感,于是决定重现这个问题来深入研究一下。

问题现象

数据库有数据表 feedbacks, 对应的 Model 为 Feedback.php 内部没有指定 $table.
在我本地是没有问题的,可以正确指向到 feedbacks 表,于是我从服务器上把代码打了个包,download 到本地重放,果然在本地也报错,可以断言是代码的问题。

代码是这个样子

Feedback.php
<?php 

namespace App\Http\Models;

use Illuminate\Database\Eloquent\Model;

class Feedback extends Model {

    protected $fillable = [];

    protected $dates = [];

    public static $rules = [

    ];

}

报错是这个样子

[2018-03-28 19:59:40] production.ERROR: PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'xxx.feedback' doesn't exist in /www/....../vendor/laravel/framework/src/Illuminate/Database/Connection.php:333

开始排查代码

Step 1. 打印表名

在调用 Feedback 模型之前打印表名出来看看,结果是 feedback 没有 s,报错是肯定的!

$feedbackObj = new Feedback();
$table = $feedbackObj->getTable();
dump( $table );

Step 2. 进入 Model.php 排查

文件路径:/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php, 跳转到 getTable 方法 从源码很容易看出,如果我在模型里面指定了 $table,会走 if 这块代码直接返回自己设置的表名,如果我没有设置 table,肯定走的下面的自动获取表名逻辑,既然锁定了问题出在自动获取表名这里,就在 return 之前依次打印结果观察。

源码如下:

    /**
     * Get the table associated with the model.
     *
     * @return string
     */
    public function getTable()
    {
        if (isset($this->table)) {
            return $this->table;
        }

        return str_replace('\\', '', Str::snake(Str::plural(class_basename($this))));
    }

打印代码及打印结果如下:

dump( class_basename($this) );    // Feedback
dump( Str::plural( class_basename($this) ) );    // Feedback
dump( Str::snake( Str::plural( class_basename($this) ) ) );    // feedback

从打印结果来看 Str::plural( class_basename($this) ) 这一行已经出现问题了

Step 3. 继续进入 Str.php 排查

文件路径:/vendor/laravel/framework/src/Illuminate/Support/Str.php, 跳转到 plural 方法

代码很简单,获取英文单词的复数形式,用 Pluralizer 类去调用 plural 静态方法

    /**
     * Get the plural form of an English word.
     *
     * @param  string  $value
     * @param  int     $count
     * @return string
     */
    public static function plural($value, $count = 2)
    {
        return Pluralizer::plural($value, $count);
    }

Step 4. 继续进入 Pluralizer.php 排查

文件路径:/vendor/laravel/framework/src/Illuminate/Support/Pluralizer.php, 跳转到 plural 方法

if 这段代码不会走,因为 $count 默认是2,feedback 这个单词没有在 $uncountable 这个数组里面出现,两个条件没有一个成立的。
继续打印 $plural,打印结果 Feedback,这里就有问题了。

源码如下:

    /**
     * Get the plural form of an English word.
     *
     * @param  string  $value
     * @param  int     $count
     * @return string
     */
    public static function plural($value, $count = 2)
    {
        if ((int) $count === 1 || static::uncountable($value)) {
            return $value;
        }

        $plural = Inflector::pluralize($value);

        return static::matchCase($plural, $value);
    }

Step 5. 继续进入 Inflector.php 排查

文件路径:/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php, 跳转到 pluralize 方法。

    /**
     * Returns a word in plural form.
     *
     * @param string $word The word in singular form.
     *
     * @return string The word in plural form.
     */
    public static function pluralize(string $word) : string
    {
        if (isset(self::$cache['pluralize'][$word])) {
            return self::$cache['pluralize'][$word];
        }

        if (!isset(self::$plural['merged']['irregular'])) {
            self::$plural['merged']['irregular'] = self::$plural['irregular'];
        }

        if (!isset(self::$plural['merged']['uninflected'])) {
            self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
        }

        if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) {
            self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')';
            self::$plural['cacheIrregular']   = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')';
        }

        if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
            self::$cache['pluralize'][$word] = $regs[1] . $word[0] . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);

            return self::$cache['pluralize'][$word];
        }

        if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) {
            self::$cache['pluralize'][$word] = $word;

            return $word;
        }

        foreach (self::$plural['rules'] as $rule => $replacement) {
            if (preg_match($rule, $word)) {
                self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);

                return self::$cache['pluralize'][$word];
            }
        }
    }

这个 function 里面 if 判断很多,通过打印锁定在这一行

if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs))

self::$plural['cacheUninflected'] 打印结果里面发现了 feedback 这个单词,原因就是在这里了,但这个单词是怎么来的呢?

"(?:.*[nrlm]ese|.*deer|.*fish|.*measles|.*ois|.*pox|.*sheep|people|cookie|police|.*?media|Amoyese|audio|bison|Borghese|bream|breeches|britches|buffalo|cantus|carp|chassis|clippers|cod|coitus|compensation|Congoese|contretemps|coreopsis|corps|data|debris|deer|diabetes|djinn|education|eland|elk|emoji|equipment|evidence|Faroese|feedback|fish|flounder|Foochowese|Furniture|furniture|gallows|Genevese|Genoese|Gilbertese|gold|headquarters|herpes|hijinks|Hottentotese|information|innings|jackanapes|jedi|Kiplingese|knowledge|Kongoese|love|Lucchese|Luggage|mackerel|Maltese|metadata|mews|moose|mumps|Nankingese|news|nexus|Niasese|nutrition|offspring|Pekingese|Piedmontese|pincers|Pistoiese|plankton|pliers|pokemon|police|Portuguese|proceedings|rabies|rain|rhinoceros|rice|salmon|Sarawakese|scissors|sea[- ]bass|series|Shavese|shears|sheep|siemens|species|staff|swine|traffic|trousers|trout|tuna|us|Vermontese|Wenchowese|wheat|whiting|wildebeest|Yengeese)"

顺着往上找发现在 第三个 if 判断的时候执行了这一行代码,self::$uninflected 这个是关键,马上查找这个变量。

if (!isset(self::$plural['merged']['uninflected'])) {
    self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
}

在 :223 行找到了这个变量的所有值,这个变量的意思是复数是单词原形,不受影响,What?feedback 复数不加 s?顺手查了一下,百度词典,金山词霸很明确的说复数加s,有道词典没有说明,只显示 feedbacks 是名词回馈的意思,通过查了一些资料还是推荐 feedback 为复数形式。
参考资料链接:
http://www.learnenglishwithwill.com/feedback-vs-feedbacks-plural-form/

    /**
     * Words that should not be inflected.
     *
     * @var array
     */
    private static $uninflected = array(
        '.*?media', 'Amoyese', 'audio', 'bison', 'Borghese', 'bream', 'breeches',
        'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'compensation', 'Congoese',
        'contretemps', 'coreopsis', 'corps', 'data', 'debris', 'deer', 'diabetes', 'djinn', 'education', 'eland',
        'elk', 'emoji', 'equipment', 'evidence', 'Faroese', 'feedback', 'fish', 'flounder', 'Foochowese',
        'Furniture', 'furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'gold', 
        'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'jedi',
        'Kiplingese', 'knowledge', 'Kongoese', 'love', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', 'metadata',
        'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', 'nutrition', 'offspring',
        'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'plankton', 'pliers', 'pokemon', 'police', 'Portuguese',
        'proceedings', 'rabies', 'rain', 'rhinoceros', 'rice', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass',
        'series', 'Shavese', 'shears', 'sheep', 'siemens', 'species', 'staff', 'swine', 'traffic',
        'trousers', 'trout', 'tuna', 'us', 'Vermontese', 'Wenchowese', 'wheat', 'whiting', 'wildebeest', 'Yengeese'
    );

代码找到这里,这个问题就已经明白了,是因为 update 了 doctrine/inflector 这个包导致的。

Step 6. 继续深究

于是重开一个目录,pull 一下这几个版本发现 1.3.0 开始发生了变化,加入了 feedback 没有复数形式。

composer require doctrine/inflector 1.2.0
composer require doctrine/inflector 1.3.0

继续开新目录 Clone 源代码分析:

git clone https://github.com/doctrine/inflector.git 

查看 git log 看到了这个注释信息 Added more uninflected words

探究到这里,我想这个问题真的明白了。

按照惯例得总结一下结尾:

  1. Model 里面尽量指定一个 $table,有可能把握不准单词复数的形式。
  2. composer update 之后要通过 composer.lock 检查有版本变化的包。
  3. 英文真的很重要。
  4. 源码面前,了无秘密。
  5. 祝阅读到最后的人技术再上一个 level。