phpstorm 插件开发实例

0 简介

PhpStorm 是 JetBrains 公司开发的一款商业的 PHP 集成开发工具,是我用过的最好的php ide,比zend studio,netbeans 强大太多。phpstorm的强大,很大程度是依赖安装各种插件的。 phpstorm的底层是IntelliJ IDEA,一款java开发ide,JetBrains旗下的PyCharm、WebStorm 都是如此。可以理解为通过对IntelliJ IDEA各种高级制定、开发各种插件组合形成不同语言特色的ide。

0.1目标

java 零基础开始写一个phpstorm的小插件, 实现按快捷键快速输入 Yii::t('category','lng') ,这样用Yii框架写多语言的网站就轻松多了。
效果图:

0.2文档

http://www.jetbrains.org/intellij/sdk/docs/welcome.html

有很详细的介绍,都是英文

2 环境准备

2.1 安装jdk

http://www.oracle.com/technetwork/java/javase/downloads/index.html

2.2 安装IntelliJ IDEA Community Edition 社区版就够了

http://www.jetbrains.com/idea/download/#section=windows

2.3 安装phpstorm

http://www.jetbrains.com/phpstorm/download/#section=windows

3 代码

3.1 新建项目

3.2 添加java jdk,IntelliJ IDEA Community Edition lib

2017-08-29-14-06-45

3.3 添加 phpstorm 相关库php-openapi.jar、php.jar

在 【phpstorm安装目录】\plugins\php\lib 下

2017-08-29-14-09-31

3.4 设置引用库的依赖关系 comppile => provided

不然打包的时候phpstorm会把php-openapi.jar、php.jar 也一起加上
2017-08-29-14-10-43

打开META-INF/plugin.xml
添加

  <depends>com.jetbrains.php</depends>
  <depends>com.intellij.modules.platform</depends>

文档说明:http://www.jetbrains.org/intellij/sdk/docs/phpstorm/setting_up_environment.html

3.5 新建package

src 目录,右键 new -> package => com.xu

3.6 新建action

2017-08-29-14-23-02
2017-08-29-14-46-18
生成 com/xu/YiiAction.java,META-INF/plugin.xml 会自动增加配置

  <action id="com.xu.YiiAction" class="com.xu.YiiAction" text="Yii::t complete">
          <add-to-group group-id="EditorPopupMenu" anchor="first"/>
          <keyboard-shortcut keymap="$default" first-keystroke="alt 3"/>
      </action>

在 com/xu/YiiAction.java 的actionPerformed() 入口方法写个hello 弹窗

    public void actionPerformed(AnActionEvent e) {
        Messages.showMessageDialog(e.getProject(), "Hello", "Information", Messages.getInformationIcon());
    }

以上操作就定义了一个action,在ide编辑器右键菜单就会出现 Yii::t complete了

3.7 打包测试一下

2017-08-29-14-29-00

将生成的插件Yiitest.jar 安装到phpstorm
最快捷的方法是直接将Yiitest.jar 复制到phpstorm的插件安装目录,再重启phpstorm(C:\Users\sherman.PhpStorm2017.2\config\plugins)

效果:
2017-08-29-14-51-08
2017-08-29-14-51-21

3.8 具体功能逻辑的实现

3.8.1 大概步骤

    1. 获取选中文本的值 lng
    1. 获取当前打开文件的路径,分析路径,取得 Yii::t() 中的第一个参数 cate
    1. 清空选中的文本,Yii::('cate','lng') 替换。

3.8.2 具体代码

com/xu/YiiAction.java

package com.xu;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;

import static com.intellij.openapi.actionSystem.CommonDataKeys.VIRTUAL_FILE;

public class YiiAction extends AnAction {

    @Override
    public void actionPerformed(AnActionEvent e) {
        final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
        final Project project = e.getRequiredData(CommonDataKeys.PROJECT);
        final PsiElement psiElement = e.getData(CommonDataKeys.PSI_ELEMENT);
        final PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);


        final Document document = editor.getDocument();
        final SelectionModel selectionModel = editor.getSelectionModel();

        int start = selectionModel.getSelectionStart();
        int end = selectionModel.getSelectionEnd();
        String replaceStr = selectionModel.getSelectedText();

        final VirtualFile file = e.getData(VIRTUAL_FILE);
        if (file == null) {
            Messages.showMessageDialog("No active file", "Error",Messages.getErrorIcon());
            return;
        }
        if(psiElement!= null){
            Messages.showMessageDialog("Please select the text", "Error",Messages.getErrorIcon());
            return;
        }
        final String findName = Common.getYiiTName(file.getCanonicalPath());

        assert psiFile != null;
        if(psiFile.findElementAt(editor.getCaretModel().getOffset()).getLanguage().getDisplayName().equals("PHP")){
            replaceStr = "Yii::t('"+findName+"','"+replaceStr+"')";
            start--;
            end++;
        }else{
            replaceStr = "<?=Yii::t('"+findName+"','"+replaceStr+"')?>";
        }

        final String str = replaceStr;
        final int LastStart = start;
        final int LastEnd= end;
        //替换选中的文本
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                document.replaceString(LastStart,LastEnd,str);
            }
        };
        WriteCommandAction.runWriteCommandAction(project,runnable);
        selectionModel.removeSelection();
    }

    /**
     *  判断文本被选中,才触发插件显示
     * @param e  AnActionEvent
     */
    @Override
    public void update(AnActionEvent e) {
        final Project project = e.getData(CommonDataKeys.PROJECT);
        final Editor editor = e.getData(CommonDataKeys.EDITOR);
        e.getPresentation().setVisible(project != null && editor != null && editor.getSelectionModel().hasSelection());
    }
}

com/xu/Common.java

package com.xu;

import org.jetbrains.annotations.NotNull;

/**
 * zhenjun.xu
 * 2016/12/30
 */
class Common {

    /**
     *  根据文件路径,生成 Yii::t 所需的分类名称
     * @param filePath 路径
     * @return 分类名称
     */
    static String getYiiTName(String filePath){
        String[] pathArray = filePath.split("/");
        String module = "";
        String controller = "";
        String name = "site";
        int modulePosition = 0;
        /*
         遍历字符串数组,查找module,controller
         */
        for (int i=0;i<pathArray.length;i++){
            if(pathArray[i].contentEquals("modules")){
                module = pathArray[i+1];
                modulePosition = i;
            }
            if(pathArray[i].contentEquals("controllers")){
                controller = pathArray[pathArray.length-1].replace("Controller.php","");
            }
            if(pathArray[i].contentEquals("views")){
                controller = toUpperCaseFirstOne(pathArray[i+1]);
            }
        }
        if(module.length() >0  && controller.length()>0){
            name = module+controller;
        }
        if(module.length()==0 && controller.length()>0){
            name = toLowerCaseFirstOne(controller);
        }
        if(module.length()>0 && controller.length()==0){
            name = pathArray[modulePosition+3];
        }
        if(module.length()==0 && controller.length()==0){
            name = reverse(pathArray)[1];
        }
        return name;
    }

    /**
     * 首字母大写
     * @param name 字符串
     * @return 首字母大写
     */
    private static String toUpperCaseFirstOne(String name) {
        char[] cs=name.toCharArray();
        cs[0]-=32;
        return String.valueOf(cs);
    }
    /**
     * 首字母转小写
     * @param s  字符串
     * @return 首字母大写
     */
    private static String toLowerCaseFirstOne(String s){
        if(Character.isLowerCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }

    /**
     *
     * 返回一个单元顺序相反的数组
     * 当 arr 为 null 时返回 new String[0] 。
     *
     * @param arr 数组
     * @return 数组
     */
    private static String[] reverse(String[] arr) {
        String[] rarr = new String[0];
        if (arr != null) {
            rarr = new String[arr.length];
            int j = 0;
            for (int i = arr.length - 1; i >= 0; i--) {
                rarr[j++] = arr[i];
            }
        }
        return rarr;
    }

}


以上源码已上传到github: https://github.com/xuzhenjun130/Yii-t

phpstorm 可以搜索下载

2017-08-29-15-04-18

leanote 折腾笔记

写博客、做笔记,使用markdown 是挺爽的,然而图片插入却不是很方便。目前发现能使用ctrl+v 完成截图插入的笔记软件有为知笔记、leanote,很可惜,这两个软件都开始收费了,leanote更是霸道,不续费,以前的笔记都没法查看。好在leanote是开源的,改改代码,使leanote生成的私有图片协议链接,变成本地链接;去掉vip续费提醒也不失为一款出色的markdown编辑器。

默认图片是保存到本地的,导出markdown文件,将会上传图片到七牛云,替换markdown中的图片链接,这样就方便发布到博客、分享给别人看了。使用效果如下:

修改过程记录一下,给用的上的人参考。

1.修改图片保存目录

public/js/app/service.js
public/js/app/service_login.js

var basePath = app.getPath('appData') + '/leanote';

改为

const path = require('path');
var basePath = path.dirname(path.dirname(app.getAppPath())) + '/data';
basePath = basePath.replace(/\\/g,'/');

修改后,笔记数据就默认保存到leanote软件目录下的data目录了。

2. 修改md 插入图片路径

2.1 截图粘贴图片

node_modules/file.js

pasteImage2 方法

me._addImage(Common.objectId(), filePath, function(newImg) {
                callback && callback(Evt.getImageLocalUrl(newImg.FileId));
            });

改为

me._addImage(Common.objectId(), filePath, function(newImg) {
                callback && callback(Evt.getImageLocalUrl(newImg.Path));
            });

node_modules/evt.js

    getImageLocalUrl: function(fileId) {
        return 'leanote://file/getImage?fileId=' + fileId;
    }

改为

    getImageLocalUrl: function(fileId) {
        return fileId;
    }

2.2 插入图片

public/js/common.js

insertLocalImage 方法

var url = EvtService.getImageLocalUrl(newImage.FileId);

改为

var url = EvtService.getImageLocalUrl(newImage.Path);

3.去掉过期需要升级vip

public/js/app/page.js

UserService.init 方法

 fullSync(function(err, info) {
                    err = false; //加上这行
                    if (err) {

4.导出md, 图片上传到七牛云

4.1 修改配置文件

public/config.js

var Config = {
    "plugins": [
        "theme",
        "import_leanote",
        "import_evernote",
        "import_html",
        "export_md", //新增
        "export_pdf",
        "export_html",
        "export_leanote",
        "export_evernote",
        "langs",
        "accounts"
    ],
    "langs": [
        {
            "filename": "en-us",
            "name": "English"
        },
        {
            "filename": "de-de",
            "name": "Deutsch"
        },
        {
            "filename": "zh-cn",
            "name": "简体中文"
        },
        {
            "filename": "zh-hk",
            "name": "繁体中文"
        },
        {
            "filename": "ja-jp",
            "name": "日本語"
        }
    ],
    "lang": "",
    "theme": "",
    "view": "snippet",
    //新增七牛配置
    "qiniu": {
        "ak": "xx",
        "sk": "xxx",
        "bucket": "mynote",
        "domain": "http://note.img.xuphp.com", //图片域名
        "uploadUrl": "http://up-z2.qiniu.com" //上传域名
    }
};

4.2 添加第三方七牛云sdk

npm install qn

4.3 新增导出markdown插件

public/plugins/export_md/plugin.js

/**
 * 导出markdown插件
 * 上传图片到七牛云
 *
 */
define(function () {
    var async; //  = require('async');
    var resanitize; // = require('resanitize');
    //===========
    // start

    var exportmarkdown = {
        langs: {
            'en-us': {
                'export': 'Export markdown',
                'Exporting': 'Exporting',
                'Exporting: ': 'Exporting: ',
                'exportSuccess': 'markdown saved successful!',
                'exportFailure': 'markdown saved failure!',
                'notExists': 'Please sync your note to ther server firslty.'
            },
            'de-de': {
                'export': 'Als markdown exportieren',
                'Exporting': 'Exportiere',
                'Exporting: ': 'Exportiere: ',
                'exportSuccess': 'markdown erfolgreich gespeichert!',
                'exportFailure': 'markdown speichern fehlgeschlagen!',
                'notExists': 'Bitte Notizen zuerst mit dem Server synchronisieren.'
            },
            'zh-cn': {
                'export': '导出markdown',
                'Exporting': '正在导出',
                'Exporting: ': '正在导出: ',
                'exportSuccess': 'markdown导出成功!',
                'exportFailure': 'markdown导出失败!'
            },
            'zh-hk': {
                'export': '導出markdown',
                'Exporting': '正在導出',
                'Exporting: ': '正在導出: ',
                'exportSuccess': 'markdown導出成功!',
                'exportFailure': 'markdown導出失敗!'
            }
        },

        _inited: false,
        init: function () {
            var me = this;
            if (me._inited) {
                return;
            }

            async = require('async');
            resanitize = require('resanitize');

            me._inited = true;
        },

        replaceAll: function (src, pattern, to) {
            if (!src) {
                return src;
            }
            while (true) {
                var oldSrc = src;
                src = src.replace(pattern, to);
                if (oldSrc === src) {
                    return src;
                }
            }
        },

        fixFilename: function (filename) {
            var reg = new RegExp("/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\", 'g');
            filename = filename.replace(reg, "-");
            // 防止出现两个连续的-
            while (filename.indexOf('--') != -1) {
                filename = this.replaceAll(filename, '--', '-');
            }
            if (filename.length > 1) {
                // 最后一个-
                filename = filename.replace(/\-$/, '');
            }
            return filename;
        },


        render: function (note, callback) {
            var me = this;
            me.fixFiles(note, function (content, files) {
                // 非markdown才需要这样, 补全html标签
                if (!note.IsMarkdown) {
                    content = $('<div>' + content + '</div>').html();
                }

                callback(content);
            });
        },

        findAllImages: function (note) {
            var content = note.Content;
            var allMatchs = [];

            // markdown下
            // [](http://localhost://fileId=32);
            if (note.IsMarkdown) {
                var reg = new RegExp('!\\[([^\\]]*?)\\]\\((.+)\\)', 'g');
                var matches = reg.exec(content);
                while (matches) {
                    var all = matches[0];
                    var title = matches[1]; // img与src之间
                    var fileId = matches[2];
                    allMatchs.push({
                        fileId: fileId,
                        title: title,
                        all: all
                    });
                    // 下一个
                    matches = reg.exec(content);
                }
            }
            return allMatchs;
        },
        /**
         * 七牛云
         * @param file 本地路径
         * @param fileName
         */
        upload: function (file,fileName) {
            var path = require("path");

            var qn = require('qn');
            var client = qn.create({
                accessKey: Config.qiniu.ak,
                secretKey: Config.qiniu.sk,
                bucket: Config.qiniu.bucket,
                //origin: 'http://{bucket}.u.qiniudn.com',
                // timeout: 3600000, // default rpc timeout: one hour, optional
                uploadURL: Config.qiniu.uploadUrl
            });

            // upload a file with custom key
            client.uploadFile(file, {key: fileName}, function (err, result) {
                console.log(result);
                Notify.show({title: 'Info', body: getMsg(result.url+"上传成功!")});
                // {
                //   hash: 'FhGbwBlFASLrZp2d16Am2bP5A9Ut',
                //   key: 'qn/lib/client.js',
                //   url: 'http://qtestbucket.qiniudn.com/qn/lib/client.js'
                //   "x:ctime": "1378150371",
                //   "x:filename": "client.js",
                //   "x:mtime": "1378150359",
                //   "x:size": "21944",
                // }
            });
        },

        fixFiles: function (note, callback) {
            var me = this;

            var content = note.Content;

            var allImages = me.findAllImages(note) || [];

            var allMatchs = allImages;

            if (allMatchs.length == 0) {
                callback(content, []);
                return;
            }


            var files = {}; // fileId => {}

            function replaceContent() {

                for (var i = 0; i < allMatchs.length; ++i) {
                    var eachMatch = allMatchs[i];
                    var link;
                    var href;
                    var fileName = eachMatch.fileId.split("/").slice(-3).join("/");

                    href = Config.qiniu.domain + '/' + fileName;
                    me.upload(eachMatch.fileId,fileName);
                    //上传图片到七牛云
                    link = '![' + eachMatch.title + '](' + href + ')';
                    content = content.replace(eachMatch.all, link);
                }
            }

            // 得到图片资源
            var fileIdFixed = {};
            async.eachSeries(allImages, function (eachMatch, cb) {
                var fileId = eachMatch.fileId;
                if (fileIdFixed[fileId]) {
                    cb();
                    return;
                }

                Api.fileService.getImageInfo(fileId, function (err, doc) {
                    fileIdFixed[fileId] = true;
                    if (doc) {
                        var base64AndMd5 = Api.fileService.getFileBase64AndMd5(doc.Path);
                        if (base64AndMd5) {
                            files[doc.FileId] = {
                                base64: base64AndMd5.base64,
                                md5: base64AndMd5.md5,
                                type: doc.Type,
                                title: doc.Title
                            }
                        }
                        cb();
                    }
                    else {
                        cb();
                    }
                });

            }, function () {
                replaceContent();
                callback(content, files);
            });
        },

        //--------------

        // 得到可用的文件名, 避免冲突
        getExportedFilePath: function (pathInfo, n, cb) {
            var me = this;
            if (n > 1) {
                pathInfo.nameNotExt = pathInfo.nameNotExtRaw + '-' + n;
            }
            var absPath = pathInfo.getFullPath();

            // Api.nodeFs.existsSync(absPath) 总是返回false, 不知道什么原因
            // 在控制台上是可以的
            Api.nodeFs.exists(absPath, function (exists) {
                if (!exists) {
                    cb(absPath);
                }
                else {
                    me.getExportedFilePath(pathInfo, n + 1, cb);
                }
            });
        },

        getTargetPath: function (callback) {
            // showSaveDialog 不支持property选择文件夹
            Api.gui.dialog.showOpenDialog(Api.gui.getCurrentWindow(),
                {
                    defaultPath: Api.gui.app.getPath('userDesktop') + '/',
                    properties: ['openDirectory']
                },
                function (targetPath) {
                    callback(targetPath);
                }
            );
        },

        loadingIsClosed: false,

        exportmarkdownForNotebook: function (notebookId) {
            var me = this;
            if (!notebookId) {
                return;
            }
            me.getTargetPath(function (targetPath) {
                if (!targetPath) {
                    return;
                }

                me.loadingIsClosed = false;
                Api.loading.show(Api.getMsg('导出markdown'),
                    {
                        hasProgress: true,
                        isLarge: true,
                        onClose: function () {
                            me.loadingIsClosed = true;
                            setTimeout(function () {
                                me.hideLoading();
                            });
                        }
                    });
                Api.loading.setProgress(1);

                Api.noteService.getNotes(notebookId, function (notes) {
                    if (!notes) {
                        me.hideLoading();
                        return;
                    }

                    var total = notes.length;
                    var i = 0;
                    async.eachSeries(notes, function (note, cb) {
                        if (me.loadingIsClosed) {
                            cb();
                            me.hideLoading();
                            return;
                        }
                        i++;
                        Api.loading.setProgress(100 * i / total);
                        me._exportmarkdown(note, targetPath, function () {
                            cb();
                        }, i, total);
                    }, function () {
                        me.hideLoading();
                        Notify.show({title: 'Info', body: getMsg('导出 markdown成功')});
                    });
                });
            });
        },

        hideLoading: function () {
            setTimeout(function () {
                Api.loading.hide();
            }, 1000);
        },

        exportmarkdown: function (noteIds) {
            var me = this;
            if (!noteIds || noteIds.length == 0) {
                return;
            }
            me.getTargetPath(function (targetPath) {
                if (!targetPath) {
                    return;
                }

                me.loadingIsClosed = false;
                Api.loading.show(Api.getMsg('导出markdown'),
                    {
                        hasProgress: true,
                        isLarge: true,
                        onClose: function () {
                            me.loadingIsClosed = true;
                            setTimeout(function () {
                                me.hideLoading();
                            });
                        }
                    });
                Api.loading.setProgress(1);

                var i = 0;
                var total = noteIds.length;

                async.eachSeries(noteIds, function (noteId, cb) {
                    if (me.loadingIsClosed) {
                        cb();
                        return;
                    }

                    i++;
                    Api.loading.setProgress(100 * i / total);
                    Api.noteService.getNote(noteId, function (note) {
                        me._exportmarkdown(note, targetPath, function () {
                            cb();
                        }, i, total);
                    });

                }, function () {
                    me.hideLoading();
                    Notify.show({title: 'Info', body: getMsg('导出 markdownSuccess')});
                });
            });
        },

        _exportmarkdown: function (note, path, callback, i, total) {
            var me = this;
            if (!note) {
                return;
            }

            if (me.loadingIsClosed) {
                callback();
                return;
            }

            setTimeout(function () {
                Api.loading.setMsg(Api.getMsg('导出markdown: ') + (note.Title || getMsg('Untitled')));
                Api.loading.setProgressRate(i + '/' + total);
            }, 100);

            var name = note.Title ? note.Title + '.md' : getMsg('Untitled') + '.md';
            name = me.fixFilename(name);

            var targetPath = path + Api.commonService.getPathSep() + name;

            // 将路径和名字区分开
            var pathInfo = Api.commonService.splitFile(targetPath);
            pathInfo.nameNotExt = me.fixFilename(pathInfo.nameNotExt); // 重新修正一次
            var nameNotExt = pathInfo.nameNotExt;
            pathInfo.nameNotExtRaw = pathInfo.nameNotExt;

            // 得到可用文件的绝对路径
            me.getExportedFilePath(pathInfo, 1, function (absmarkdownFilePath) {
                me.render(note, function (content) {
                    Api.commonService.writeFile(absmarkdownFilePath, content);
                    callback();
                });
            });
        },

        // 打开前要执行的
        onOpen: function () {
            var me = this;
            var gui = Api.gui;

            var menu = {
                label: Api.getMsg('导出 markdown'),
                enabled: function (noteIds) {
                    return true;
                },
                click: (function () {
                    return function (noteIds) {
                        me.init();
                        me.exportmarkdown(noteIds);
                    }
                })()
            };
            Api.addExportMenu(menu);

            Api.addExportMenuForNotebook({
                label: Api.getMsg('导出 markdown'),
                enabled: function (notebookId) {
                    return true;
                },
                click: (function () {
                    return function (notebookId) {
                        me.init();
                        me.exportmarkdownForNotebook(notebookId);
                    }
                })()
            });
        },
        // 打开后
        onOpenAfter: function () {
        },
        // 关闭时需要运行的
        onClose: function () {
        }
    };

    return exportmarkdown;
});

大功告成,以后有空再增加一键发布到wordpress的功能。

centos6.8 yum 安装 lamp

安装 EPEL源

https://fedoraproject.org/wiki/EPEL

找到相应的centos版本

If you are running an EL6 version, please visit here to get the newest ‘epel-release’ package for EL6: The newest version of ‘epel-release’ for EL6

  1. rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm

安装 remi 源

http://rpms.famillecollet.com/

找到相应的centos版本

Enterprise Linux 6 – repository auto-configuration package : remi-release-6.rpm

  1. rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm

安装mysql

  1. yum --enablerepo=remi install mysql mysql-server

可能因为网络问题,连接不上remi源,先用vpn:https://www.flyvpn.com/cn/

配置mysql

  1. #配置 mysqld 开机自启动
  2. chkconfig mysqld on
  3. #启动 mysql
  4. service mysqld start
  5. #更改 root 密码
  6. mysql -uroot
  7. #进入 mysql,然后
  8. update mysql.user SET password=password('123456') WHERE user='root';
  9. # 让外网可以访问
  10. update mysql.user set host = '%' where user ='root';
  11. flush privileges;
  12. quit;

安装php

  1. yum --enablerepo=remi-php56 install php php-mysql php-common php-gd php-mbstring php-mcrypt php-devel php-xml php-soap php-fpm php-cli php-pdo

php可用源版本选择

http://rpms.famillecollet.com/enterprise/6/

设置php-fpm 开机启动

  1. chkconfig --add php-fpm
  2. chkconfig php-fpm on

安装nginx

http://nginx.org/en/linux_packages.html

创建nginx的yum 安装源
/etc/yum.repos.d/nginx.repo

  1. [nginx]
  2. name=nginx repo
  3. baseurl=http://nginx.org/packages/centos/6/$basearch/
  4. gpgcheck=0
  5. enabled=1
  1. #安装
  2. yum install --enablerepo=nginx nginx
  3. #启动
  4. service nginx start
  5. #设置为开机启动:
  6. chkconfig --add nginx
  7. chkconfig nginx on

nginx配置:/etc/nginx/conf.d/default.conf

  1. server {
  2. listen 80;
  3. server_name foo.com;
  4. root /www;
  5. index index.html index.htm index.php;
  6. location / {
  7. try_files $uri $uri/ /index.php;
  8. }
  9. location ~ \.php$ {
  10. try_files $uri =404;
  11. fastcgi_index index.php;
  12. fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  13. include fastcgi_params;
  14. fastcgi_pass 127.0.0.1:9000;
  15. }
  16. }

phalcon 笔记(不定期更新)

1. model 初始化配置

public function initialize()
	{
		$this->useDynamicUpdate(true); // 动态更新字段,非全部更新
	}

2.数据库配置,记录查询sql

$di->setShared('db', function () use ($config,$di) {
    $dbConfig = $config->database->toArray();
    $adapter = $dbConfig['adapter'];
    unset($dbConfig['adapter']);

    $class = 'Phalcon\Db\Adapter\Pdo\\' . $adapter;
    /** @var Phalcon\Db\Adapter\Pdo\Mysql $connection */
    $connection =  new $class($dbConfig);
    /** @var Phalcon\Events\Manager $eventsManager */
    $eventsManager = $di->get("eventsManager");
    $logger = new \Phalcon\Logger\Adapter\File(__DIR__."/../logs/debugs.log");
    $eventsManager->attach('db', function($event, $connection) use ($logger) {
        if ($event->getType() == 'beforeQuery') {
            $logger->log($connection->getSQLStatement(), \Phalcon\Logger::INFO);
        }
        if ($event->getType() == 'beforeSave') {
            $logger->log($connection->getSQLStatement(), \Phalcon\Logger::INFO);
        }
    });
    $connection->setEventsManager($eventsManager);

    return $connection;
});

2.禁用或启用特性
https://docs.phalconphp.com/en/latest/reference/models.html#disabling-enabling-features
phalcon的orm有坑,如果数据库某个字段为null,那么update,save的时候会报错,因为phalcon默认检测字段是否Not null。
应该在model将此特性关闭。将notNullValidations设置为false

 

   public function initialize()
    {
        $this->setup(['notNullValidations'=>FALSE]);
    }

phalcon数据库配置添加表前缀

Phalcon默认居然不能设置数据表前缀,常用的框架都支持。
修改方法一:
新建一个基础模型,然后所有的模型在该类上继承即可

<?php
class BaseModel extends \Phalcon\Mvc\Model {
    public function getSource()
    {
        return 'gw_'.strtolower(get_class($this));
    }
}

但是此种方法,用phalcon devtools命令行模式生成model文件,文件名称是有表前缀的

修改方法二:
2.1 先在app/config/config.php 配置文件加上数据库前缀配置tablePrefix

    'database' => array(
        'adapter'     => 'Mysql',
        'host'        => 'localhost',
        'username'    => 'root',
        'password'    => '',
        'dbname'      => 'test',
        'charset'     => 'utf8',
        'port' => '3306',
        'tablePrefix' => 'gw_'
    ),

2.2 修改phalcon devtools
代码phalcon\devtools\scripts\Phalcon\Builder\Model.php
在220行$table = $this->options->get(‘name’);之后加上代码

$table = $this->options->get('name');
if(isset($config->database->tablePrefix)){
    $table = $config->database->tablePrefix.$table;
}

在480行
$methodRawCode[] = $this->snippet->getModelSource($this->options->get(‘name’));
修改代码为:
$methodRawCode[] = $this->snippet->getModelSource($table);

此时使用工具命令

phalcon model user

生成model :
app/models/User.php
内容如下,不会提示table不存在了:

<?php

use Phalcon\Mvc\Model\Validator\Email as Email;

class User extends \Phalcon\Mvc\Model
{

    /**
     *
     * @var integer
     */
    public $id;

    /**
     *
     * @var string
     */
    public $username;

    /**
     *
     * @var string
     */
    public $password;

    /**
     *
     * @var integer
     */
    public $status;

    /**
     *
     * @var string
     */
    public $real_name;

    /**
     *
     * @var string
     */
    public $mobile;

    /**
     *
     * @var string
     */
    public $email;

    /**
     *
     * @var integer
     */
    public $sex;

    /**
     *
     * @var integer
     */
    public $logins;

    /**
     *
     * @var integer
     */
    public $create_time;

    /**
     * Validations and business logic
     *
     * @return boolean
     */
    public function validation()
    {
        $this->validate(
            new Email(
                array(
                    'field'    => 'email',
                    'required' => true,
                )
            )
        );

        if ($this->validationHasFailed() == true) {
            return false;
        }

        return true;
    }

    /**
     * Initialize method for model.
     */
    public function initialize()
    {
        $this->setSource("gw_user");
    }

    /**
     * Returns table name mapped in the model.
     *
     * @return string
     */
    public function getSource()
    {
        return 'gw_user';
    }

    /**
     * Allows to query a set of records that match the specified conditions
     *
     * @param mixed $parameters
     * @return User[]
     */
    public static function find($parameters = null)
    {
        return parent::find($parameters);
    }

    /**
     * Allows to query the first record that match the specified conditions
     *
     * @param mixed $parameters
     * @return User
     */
    public static function findFirst($parameters = null)
    {
        return parent::findFirst($parameters);
    }

}

 

php7.04 编译安装

安装参考:http://www.tuicool.com/articles/bi2iYj3

编译参数:

./configure
--prefix=/usr/local/php7
--exec-prefix=/usr/local/php7
--bindir=/usr/local/php7/bin
--sbindir=/usr/local/php7/sbin
--includedir=/usr/local/php7/include
--libdir=/usr/local/php7/lib/php
--mandir=/usr/local/php7/php/man
--with-config-file-path=/usr/local/php7/etc
--with-mysql-sock=/var/run/mysql/mysql.sock
--with-mcrypt=/usr/include
--with-mhash
--with-openssl
--with-mysqli=shared,mysqlnd
--with-pdo-mysql=shared,mysqlnd
--with-gd
--with-iconv
--with-zlib
--enable-zip
--enable-inline-optimization
--disable-debug
--disable-rpath
--enable-shared
--enable-xml
--enable-bcmath
--enable-shmop
--enable-sysvsem
--enable-mbregex
--enable-mbstring
--enable-ftp
--enable-gd-native-ttf
--enable-pcntl
--enable-sockets
--with-xmlrpc
--enable-soap
--without-pear
--with-gettext
--enable-session
--with-curl
--with-jpeg-dir
--with-freetype-dir
--enable-opcache
--enable-fpm
--with-fpm-user=nginx
--with-fpm-group=nginx
--without-gdbm 
--disable-fileinfo

 

要把 编译参数弄成一行再执行

编译如果报错,使用 ./configure –help 查看哪些参数是能用的,每个版本可能都不一样

使用 apache 还需要添加参数 –with-apxs2=/usr/sbin/apxs