buildyuan

buildyuan 查看完整档案

成都编辑四川师范大学  |  电子信息工程 编辑北京琥玥天地科技有限公司  |  PHP工程师 编辑 www.forevam.com 编辑
编辑

talk is cheap,show me the code

个人动态

buildyuan 赞了文章 · 4月12日

GIT实操手册 —— Git Tag是时间的里程碑

目录

  • 为什么有必要使用Git Tag?
  • git tag的基本操作
  • git tag的分类

    • 轻量级标签
    • 创建带有说明的标签
  • git tag的作用
  • 那么问题来了?

为什么有必要使用Git Tag?

每一个提交都有一个commitID,但是每次记录commitID都很复杂,使用git tag可以在一些重要的版本和修改中加入一个标识,可以很快速的找到我们需要的版本。

git taggit commitID的作用,就和IP地址和域名的作用是一样的,一个 git tag 对应一个commitID,命名的时候不能重复。

git tag的基本操作

  • 查看所有标签和指定标签
# 所有标签
git tag
git tag -l
# 指定标签
git tag -l <tagName>
  • 在当前分支的最新HEAD上打新标签
git tag <name>
  • 忘记打标签时不要紧,可以给对应的某个commitID打标签
git tag <name> <commitID>

  • 删除本地标签
git tag -d <tagname>

git tag的分类

轻量级标签

git tag <tagName>

创建带有说明的标签

git tag -a <tagname> -m "message"
# 用-a指定标签名,说明要创建带说明的tag,-m指定说明文字

如果只是输入的git tag -a <tagname> 会跳出来输入框让主动进行输入说明文字。

这两个区别,就在于我们使用git show的时候会不会将说明显示出来。

git tag的作用

作用实践
1.可以快速进行分支切换
2.可以快速进行版本回滚
  1. 其他所有可以用commitID做的事情都是用tag代替完成| -

那么问题来了?

如果分支名称和tag名称一样的时候,会发生什么事情呢?

可以查查,应该是有冲突的。

查看原文

赞 1 收藏 0 评论 0

buildyuan 发布了文章 · 4月9日

Macbook m1 Big Sur 安装未上架IOS软件步骤

1 、在 macOS 的 App Store 下载 Apple Configurator 2,打开后 连接 iPhone/iPad
2 、点击 Apple Configurator 2 中的 iPhone,右键选择 添加 - app
3 、找到想要的 app,选择然后点 添加,就会自动开始下载
4 、下载完成后会提示你的设备上存在这个应用,这个时候不要点任何按钮
5 、访达 Command + Shift + G,打开这个路径:~/Library/Group Containers/K36BKF7T3D.group.com.apple.configurator/Library/Caches/Assets/TemporaryItems/MobileApps/
可以看到已经下载好的 ipa 安装包,把它拷贝出来,就安全了,不然会被自动删除

6 、双击 ipa 安装,打开时会报错没有权限
7 、打开终端,输入 sudo xattr -r -d com.apple.quarantine App 路径(例如 sudo xattr -r -d com.apple.quarantine /Applications/WeChat.app )

查看原文

赞 0 收藏 0 评论 0

buildyuan 回答了问题 · 4月9日

php 中括号里面一段代码什么意思

中括号/方括号 是数组的简写方法。
也就是说:

$a = array();
$b = [];
var_dump($a===$b);

输出 boolean true


数组内的俩方法是控制了缓冲区

关注 1 回答 1

buildyuan 赞了回答 · 4月9日

解决关于点餐实时共享菜单的实现逻辑

Redis存储订单定信息,配合websocket,实时广播就可以了。
有人下单,socket广播给 已经扫码的在线用户就可以了。技术难度并不大,

关注 3 回答 1

buildyuan 回答了问题 · 4月9日

PHP如何在二维数组获取上一级键值?

array_walk($path, function (&$value, $key) {
            $value['file_name'] = $key;
        });

关注 2 回答 1

buildyuan 回答了问题 · 4月9日

php curl_multi_exec问题

$active

一个是否仍在执行的标识的引用。

CURLM_CALL_MULTI_PERFORM

定义的常量,值为 -1, 意思是还未处理完成。

 This is not really an error. It means you should call {@see curl_multi_exec()} again without doing select() or similar in between.
 Before version 7.20.0 this could be returned by {@see curl_multi_exec()}, but in later versions this return code is never used.
 @link https://www.php.net/manual/en/function.curl-multi-exec.php
 @link https://curl.haxx.se/libcurl/c/libcurl-errors.html
define ('CURLM_CALL_MULTI_PERFORM', -1);
CURLM_OK

定义的常量,值为 0,意思是正常返回

 Things are fine.
 @link https://www.php.net/manual/en/function.curl-multi-exec.php
 @link https://curl.haxx.se/libcurl/c/libcurl-errors.html

define ('CURLM_OK', 0);

可以参考一下PHP官方的中文文档

curl_multi_exec ( resource $mh , int &$still_running ) : int
处理在栈中的每一个句柄。无论该句柄需要读取或写入数据都可调用此方法。

参数
multi_handle
由 curl_multi_init() 返回的 cURL 多个句柄。

still_running
一个用来判断操作是否仍在执行的标识的引用。

关注 2 回答 1

buildyuan 赞了文章 · 4月8日

给顶级开源项目 Spring Boot 贡献代码是一种什么样的体验?

先点赞再看,养成好习惯

背景

Spring Boot的默认日志框架一直是 Logback,支持的很好。而且针对Logback,Spring Boot还提供了一个扩展功能 - <springProfile>,这个标签可以在Logback的XML配置文件中使用,用于配合Spring的profile来区分环境,非常方便。

比如你可以像下面这样,只配置一个logback-spring.xml配置文件,然后用<springProfile>来区分环境,开发环境只输出到控制台,而其他环境输出到文件

<Root level="INFO">
  <!-- 开发环境使用Console Appender,生产环境使用File Appender -->
  <springProfile name="dev">
    <AppenderRef ref="Console"/>
  </springProfile>
  <SpringProfile name="!dev">
    <AppenderRef ref="File"/>
  </SpringProfile>
</Root>

这样做的好处是,我只需要一个logback.xml配置文件,就可以解决多环境的问题,而不是每个环境一个logback-spring.xml,实在太香了(这个Profile 的语法还可以有一些更灵活的语法(详细参考Spring Boot的官方文档))

但是有时候为了性能或其他原因,我们会选择log4j2作为Spring Boot的日志框架。Spirng Boot当然也是支持log4j2的。

切换到 log4j2 虽然很简单,但是Spring Boot并没有对 log4j2进行扩展!log4j2的xml配置方式,并不支持<SpringProfile>标签,不能愉快的配置多环境!搜索了一下,StackOverflow上也有人有相同的困惑,而且这个功能目前并没有任何人提供

于是,我萌生了一个大胆的想法 :自己开发一个Spring Boot - Log4j2 XML的扩展,让 log4j2 的XML也支持<SpringProfile>标签,然后贡献给Spring Boot,万一被采纳了岂不妙哉。

而且这可不是改个注释,改个标点符号,改个变量名之类的PR;这可是一个新 feature,一旦被采纳,Spring Boot的文档上就会有我的一份力了!
image.png

功能开发

说干就干,先分析Log4j2 XML解析的源码,看看好不好下手

Log4j2 XML解析源码分析

经过一阵分析,找到了 Log4j2 的 XML 文件解析代码在 org.apache.logging.log4j.core.config.xml.XmlConfiguration,仔细阅读+DEBUG这个类之后,发现这个XML解析类各种解析方法不是static就是private,设计之初就没有考虑过提供扩展,定制标签的功能。比如这个递归解析标签的方法,直接就是private的:

private void constructHierarchy(final Node node, final Element element) {
        processAttributes(node, element);
        final StringBuilder buffer = new StringBuilder();
        final NodeList list = element.getChildNodes();
        final List<Node> children = node.getChildren();
        for (int i = 0; i < list.getLength(); i++) {
            final org.w3c.dom.Node w3cNode = list.item(i);
            if (w3cNode instanceof Element) {
                final Element child = (Element) w3cNode;
                final String name = getType(child);
                final PluginType<?> type = pluginManager.getPluginType(name);
                final Node childNode = new Node(node, name, type);
                constructHierarchy(childNode, child);
                if (type == null) {
                    final String value = childNode.getValue();
                    if (!childNode.hasChildren() && value != null) {
                        node.getAttributes().put(name, value);
                    } else {
                        status.add(new Status(name, element, ErrorType.CLASS_NOT_FOUND));
                    }
                } else {
                    children.add(childNode);
                }
            } else if (w3cNode instanceof Text) {
                final Text data = (Text) w3cNode;
                buffer.append(data.getData());
            }
        }

        final String text = buffer.toString().trim();
        if (text.length() > 0 || (!node.hasChildren() && !node.isRoot())) {
            node.setValue(text);
        }
    }

连解析后的数据,也是private

private Element rootElement;

想通过继承的方式,只重写部分方法来实现根本不可能,除非重写整个类才能扩展自定义的标签……

风险 & 兼容性的思考

这下就尴尬了,重写整个类虽然也可以,但兼容性就得不到保证了。因为一旦Log4j2 的 XML配置有更新,我这套扩展就废了,不管是大更新还是小更新,但凡是这个类有变动我这个扩展就得跟着重写,实在不稳妥。

但我在查看了XmlConfiguration这个类的提交历史后发现,它最近一次更新的时间在2019年6月
image.png

而整个Log4j2框架 ,在2019年6月到2021年3月之间,发布了9次Release版本
image.png

整个项目更新了两年,快十个版本中,XmlConfiguration 只更新过一次,说明更新频率很低。而且对比变更记录发现,这个类近几次的更新内容也很少。

这么一想,我就算重写XmlConfiguration又怎么样,这么低的更新频率,这么少的更新内容,重写的风险也很低啊。而且我也不是全部重写,只是拷贝原有的代码,加上一点自定义标签的支持而已,改动量并不大。就算需要跟着Log4j2 更新的话,对比一下代码,重新调整一遍也不是难事。

就这样我说服了自己,开始拉代码……

fork/clone 代码,本地环境搭建

spring-boot 仓库地址:https://github.com/spring-projects/spring-boot

  1. Fork一份 Spring Boot的代码
  2. clone 这个fork的仓库
  3. 基于master,新建一个log4j2_enhancement分支用于开发

这里也可以直接通过IDEA clone,不过前提是你有个“可靠又稳定”的网络

由于Spring/Spring Boot已经将构建工具从Maven迁移到了Gradle,所以IDEA版本最好不要太老,太老的版本可能对Gradle支持的不够好。

如果你的网络足够“可靠和稳定”,那么只需要在IDEA中打开Spring Boot的源码,就可以自定构建好开发环境,直接运行测试了。否则可能会遇到Gradle和相关包下载失败,Maven仓库包下载失败等各种问题……

Spring Boot对Logback的支持扩展

既然Spring Boot对Logback(XML)进行了增强,那么先来看看它是怎么增强的,待会我支持Log4j2的话能省很多事。

经过一阵分析,找到了这个Logback的扩展点:

class SpringBootJoranConfigurator extends JoranConfigurator {

    private LoggingInitializationContext initializationContext;

    SpringBootJoranConfigurator(LoggingInitializationContext initializationContext) {
        this.initializationContext = initializationContext;
    }

    @Override
    public void addInstanceRules(RuleStore rs) {
        super.addInstanceRules(rs);
        Environment environment = this.initializationContext.getEnvironment();
        rs.addRule(new ElementSelector("configuration/springProperty"), new SpringPropertyAction(environment));
        rs.addRule(new ElementSelector("*/springProfile"), new SpringProfileAction(environment));
        rs.addRule(new ElementSelector("*/springProfile/*"), new NOPAction());
    }
}

就……这么简单?顺着这个类又分析了一遍JoranConfigurator和相关的类之后,发现这都是Logback的功劳。

Logback文档中提到,这个Joran 实际上是一个通用的配置系统,可以独立于日志系统使用。但我搜索了一下,除了Logback的文档以外,并没有找到这个Joran的出处在哪。

不过这并不重要,我就把他当做一个通用的配置解析器,被logback引用了而已。

这个解析器比较灵活,可以自定义标签/标签解析的行为,只需要重写addInstanceRules这个方法,添加自定义的标签名和行为类即可:

@Override
public void addInstanceRules(RuleStore rs) {
    super.addInstanceRules(rs);
    Environment environment = this.initializationContext.getEnvironment();
    rs.addRule(new ElementSelector("configuration/springProperty"), new SpringPropertyAction(environment));
    //就是这么简单……
    rs.addRule(new ElementSelector("*/springProfile"), new SpringProfileAction(environment));
    rs.addRule(new ElementSelector("*/springProfile/*"), new NOPAction());
}

然后在SpringProfileAction中,通过Spring的Environment对象,拿到当前激活的Profiles进行匹配就能搞定

如法炮制,添加Log4j2 的自定义扩展

虽然Log4j2的XML解析并不能像Logback那样灵活,直接插入扩展。但是基于我前面的风险&兼容性分析,重写XmlConfiguration也是可以实现自定义标签解析的:

先创建一个SpringBootXmlConfiguration

这个类的代码,是完全复制了org.apache.logging.log4j.core.config.xml.XmlConfiguration,然后增加俩Environment相关的参数:

private final LoggingInitializationContext initializationContext;

private final Environment environment;

接着在构造函数中增加initializationContext并注入:

public SpringBootXmlConfiguration(final LoggingInitializationContext initializationContext,
            final LoggerContext loggerContext, final ConfigurationSource configSource) {
        super(loggerContext, configSource);
        this.initializationContext = initializationContext;
        this.environment = initializationContext.getEnvironment();
        ...
}

最后只需要调整上面提到的递归解析方法,增加SpringProfile标签的支持即可:

private void constructHierarchy(final Node node, final Element element, boolean profileNode) {
    //SpringProfile节点不需要处理属性
    if (!profileNode) {
        processAttributes(node, element);
    }
    final StringBuilder buffer = new StringBuilder();
    final NodeList list = element.getChildNodes();
    final List<Node> children = node.getChildren();
    for (int i = 0; i < list.getLength(); i++) {
        final org.w3c.dom.Node w3cNode = list.item(i);
        if (w3cNode instanceof Element) {
            final Element child = (Element) w3cNode;

            final String name = getType(child);
            //如果是<SpringProfile>标签,就跳过plugin的查找和解析
            // Enhance log4j2.xml configuration
            if (SPRING_PROFILE_TAG_NAME.equalsIgnoreCase(name)) {
                //如果定义的Profile匹配当前激活的Profiles,就递归解析子节点,否则就跳过当前节点(和子节点)
                if (acceptsProfiles(child.getAttribute("name"))) {
                    constructHierarchy(node, child, true);
                }
                // Break <SpringProfile> node
                continue;
            }
            //查找节点对应插件,解析节点,添加到node,构建rootElement树
            //......
    }
}
//判断profile是否符合规则,从Spring Boot - Logback里复制的……
private boolean acceptsProfiles(String profile) {
    if (this.environment == null) {
        return false;
    }
    String[] profileNames = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(profile));
    if (profileNames.length == 0) {
        return false;
    }
    return this.environment.acceptsProfiles(Profiles.of(profileNames));
}

在配置SpringBootXmlConfiguration的入口

好了,大功告成,就这么简单,这么点代码就完成了Log4j2 XML的增强。现在只需要在装配Log4j2的时候,将默认的XmlConfiguration换成我的SpringBootXmlConfiguration即可:

//org.springframework.boot.logging.log4j2.Log4J2LoggingSystem
......
LoggerContext ctx = getLoggerContext();
URL url = ResourceUtils.getURL(location);
ConfigurationSource source = getConfigurationSource(url);
Configuration configuration;
if (url.toString().endsWith("xml") && initializationContext != null) {
    //XML文件并且initializationContext不为空时,就使用增强的SpringBootXmlConfiguration进行解析
    configuration = new SpringBootXmlConfiguration(initializationContext, ctx, source);
}
else {
    configuration = ConfigurationFactory.getInstance().getConfiguration(ctx, source);
}
......

准备单元测试

功能已经完成了,现在要准备单元测试。这里还是可以参考Logback 相关的单元测试类,直接拷贝过来,修改成Log4j2的版本。

Spring Boot目前的版本使用的是Junit5,现在新建一个SpringBootXmlConfigurationTests类,然后模仿Logback的单元测试类写一堆测试方法和测试配置文件:

<!--profile-expression.xml-->
<springProfile name="production | test">
  <logger name="org.springframework.boot.logging.log4j2" level="TRACE" />
</springProfile>

<!--production-file.xml-->
<springProfile name="production">
  <logger name="org.springframework.boot.logging.log4j2" level="TRACE" />
</springProfile>

<!--multi-profile-names.xml-->
<springProfile name="production, test">
  <logger name="org.springframework.boot.logging.log4j2" level="TRACE" />
</springProfile>

<!--nested.xml-->
<springProfile name="outer">
  <springProfile name="inner">
    <logger name="org.springframework.boot.logging.log4j2" level="TRACE" />
  </springProfile>
</springProfile>

...
void profileActive();
void multipleNamesFirstProfileActive();
void multipleNamesSecondProfileActive();
void profileNotActive();
void profileExpressionMatchFirst();
void profileExpressionMatchSecond();
void profileExpressionNoMatch();
void profileNestedActiveActive();
void profileNestedActiveNotActive();
......

折腾了一会,终于把单元测试编写完成,并全部测试通过。接下来可以准备提PR了

提交PR

首先,在fork后的项目中,进行Pull request
image.png

然后,选择要pr的分支,创建pr即可
image.png
然后需要详细填写你这个PR的描述
image.png

我详细的描述了我提交的功能,以及我上面分析的兼容性和风险问题:

Enhance the configuration of log4j2 (xml), support Profile-specific Configuration (<SpringProfile>), consistent with logback extension.
Spring Boot currently only enhances the Logback (XML) configuration to support the tag. This feature is very useful, but is not supported by Log4j2.
I copied the code in Log4j2 XML to parse the XML configuration and created a new SpringBootXmlConfiguration to support the tag, which is as simple and easy to use as Logback Extension.
Compatibility issues with rewriting the Log4j2 parsing code:

  1. I just copied the XmlConfiguration code directly from Log4j2, adding very little code and making no big changes like formatting. If there is an update to Log4j2, it is easy to rewrite the parsing class and update it accordingly.
  2. The XmlConfiguration class in Log4j2 was last updated in June 2019, with no updates between [2.12.0,2.14.1] and the default dependent version of Log4j2 in Springboot (master) is 2.14.1

To sum up, there is no risk in this kind of enhancement

被冷漠无情的CI检查卡住

在提交PR后,我以为事情到这里就告一段落了……

结果Spring Boot的Github Action有一个CI检查,漫长的等待之后,告诉我构建失败……
image.png

这里details可以进入详情查看具体构建日志
image.png

checkFormat/checkStyle 失败……

卧草大意了,忘了有checkStyle了,这种开源项目对代码风格要求一定很严格,我的代码是从Log4j2拷过来的,两个项目代码风格标准肯定不一样!

调整代码风格

我又回过头去翻Spring Boot的贡献指南,发现他们提到了一个spring-javaformat插件,用于检查/格式化代码,Eclipse/Idea插件都有,还有gradle/maven插件。

我天真的以为,这个IDEA插件可以很方便的把我的代码格式化成Spring 的规范,装上之后,Reformat Code发现并没有什么卵用,仍然过不了checkstyle………有知道怎么用的同学,可以在评论区分享下

然后我就开始在本地执行它的checkstyle task,不断的调整代码风格……

这个checkstyle/checkformat的执行,是通过Gradle执行的,所以也可以在IDEA 的Gradle面板上执行:
image.png

Spring Boot的代码风格非常严谨,比如注释必须加句号啊,文件尾部必须空行结尾啊,导包顺序要求啊,每行代码长度要求啊等等等等……非常多

在执行checkstyle/checkformat插件后,插件会提示你哪个文件,哪一行有什么问题,跟着修改就行

经过我一个多小时的调整,终于通过了代码检查……眼镜都花了

再次提交代码

代码风格/格式调整完成后,我又一次的提交了代码,还是原来的分支。这里提交的话,那个PR里的CI检查会自动触发。

大概过了二十多分钟,终于构建完成,并且通过
image.png

来自官方人员的回复

过了三四天,我收到了官方人员的回复,随之而来的是我提交的PR被关闭了……
image.png

官方的回复态度还是很友好的,大概意思是,无论我提交的代码稳定性如何,但这种暴力重写的方式还是不太好,他们希望由Log4j2来提供一个扩展,然后Spring Boot通过扩展来实现对Log4j2的增强。

并且附上了一个issue,主题就是Spring Boot 对Log4j2支持的问题,并且追加了我这次的PR:
https://github.com/spring-projects/spring-boot/issues/22149

image.png

总结

虽然Spring Boot没有接受我贡献的代码,但并不是因为我的代码写的屎 😂,而是这种方式侵入性太强,有风险,并不够友好,通过扩展的方式去实现会更好。

这也体现了程序的扩展性是多么重要,在设计程序或者框架的时候,一定要多考虑扩展性,遵循开闭原则。

这次拒绝了我的贡献也不要紧,至少Spring Boot官方了解到有这个需求,并且有现成的实现代码,日后有机会的话,我还是会继续贡献其他的代码。

附录

这次提交的代码,和相关的PR地址都在这了,有兴趣的同学可以参考一下。

原创不易,未经授权禁止转载。如果我的文章对您有帮助,请点赞/收藏/关注鼓励支持一下吧❤❤❤❤❤❤
查看原文

赞 16 收藏 3 评论 3

buildyuan 赞了文章 · 4月7日

一文掌握PHP Xdebug 本地与远程调试

原文链接:一文掌握PHP Xdebug 本地与远程调试

很久没写PHP了、同样很很久没有用XDebug了,近期有个简单的场景要用到,简单记录一下关键步骤。

本地调试

本地环境:

$ php -v
PHP 7.3.4 (cli) (built: Apr  8 2019 10:21:33) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.4, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.4, Copyright (c) 1999-2018, by Zend Technologies
    with Xdebug v2.7.1, Copyright (c) 2002-2019, by Derick Rethans

环境搭建

// 安装xdebug
$ pecl install xdebug
// 配置xdebug
$ vim /usr/local/etc/php/7.3/conf.d/ext-xdebug.ini
[xdebug]
 zend_extension="xdebug.so"
 xdebug.remote_enable=On
 xdebug.remote_port=9001
 xdebug.idekey=PHPSTORM
 xdebug.remote_connect_back = 1
// 重启php服务
$ brew services restart php@7.3

IDE 配置

我是在~/Downloads/xdebug/ 目录下放了一个简单的PHP文件test.php来测试,在这个目录下用php -S 127.0.0.1:7790起了个http服务,可参考进行下述配置。

IDEA的几个配置项
IDEA-PHP-Servers
IDEA-PHP-XDebug
IDEA-PHP-XDebug-DBGp-Proxy
开启监听
IDEA-Listening

效果演示

设置之后, 在IDE里打上断点, 在浏览器打开这个链接即可开启测试

http://127.0.0.1:7790/test.php?XDEBUG_SESSION_START=PHPSTORM

效果如下:
Debug

浏览器没有必要安装 Xdebug Helper 插件。


远程调试

与上一篇联动, 本篇是远程调试, 主要记录一下过程

本地环境:

# php -v
PHP 7.2.16 (cli) (built: Mar 10 2019 21:22:49) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Xdebug v2.8.0-dev, Copyright (c) 2002-2019, by Derick Rethans

环境搭建

// 如果没有安装过需要先行安装
# yum install -y php-devel gcc

// 安装xdebug
# cd /usr/local/src
# git clone https://github.com/xdebug/xdebug.git
# cd xdebug
# ./rebuild.sh
// xdebug配置文件
# vim /etc/php.d/xdebug.ini

zend_extension=xdebug.so
xdebug.remote_enable=On
xdebug.remote_port=11955
xdebug.idekey=PHPSTORM
// 重启PHP服务
# systemctl restart php-fpm
# systemctl status php-fpm

然后重点来了,一定不要忘记开放上面配置的xdebug.remote_port端口号,开放此端口号、开放此端口号、开放此端口号。

开放端口每个系统都不太一样,在CentOS7中可以使用firewall-cmd来操作

# firewall-cmd --zone=public --add-port=11955/tcp --permanent
# firewall-cmd --reload

IDE 配置

IDE的配置与本地调试不太一样,总的来说有两种方式,一种是通过在IDE配置Remote Debug, 需要配置登录远端服务器;一种是通过ssh来做远程端口转发到本地来实现类似本地调试的方式。
在这里,我选用了后一种方式来操作。

远程端口转发

// HOST 为远程服务器在~/.ssh/config的配置host名, 可以替换为你的 比如 root@1.1.1.1
ssh -NT -R 11955:127.0.0.1:9001 HOST

这样就实现了远程xdebug端口11955到本地9001的映射。

IDE配置

与本地调试的配置不一样的地方就是host和port,其他地方大同小异。

IDEA-PHP-Servers的配置:
host 为远程web服务的域名, port 为远程web提供服务的端口号。
同时需要注意的是:需要勾选 path mappings, 并正确配置要debug的工程目录在远程和本地两个环境的映射关系。
IDEA-PHP-Servers

IDEA-PHP-XDebug-DBGp-Proxy的配置:
更改hostport为远程web服务配置,与上一步相同

效果演示

设置之后, 在IDE里打上断点, 在浏览器打开这个链接即可开启测试

http://example.com/test.php?X...

效果如下:

同样的,浏览器没有必要安装 Xdebug Helper 插件。

查看原文

赞 50 收藏 39 评论 2

buildyuan 发布了文章 · 4月6日

Macbook m1 Big Sur 安装Valet 运行yii2

准备工作

假定你已经在本机安装完成了 brew / composer / php

检查环境变量

$echo $PATH | grep composer

#如果没有,添加环境变量
$export PATH=~/.composer/vendor/bin:$PATH
#检查一下
$echo $PATH | grep composer
/Users/yourname/.composer/vendor/bin:

全局安装Valet

$composer global require laravel/valet
Changed current directory to /Users/yuanjian/.composer
Using version ^2.14 for laravel/valet
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 22 installs, 0 updates, 0 removals
  - Installing symfony/polyfill-php80 (v1.22.1): Loading from cache
  - Installing symfony/polyfill-mbstring (v1.22.1): Loading from cache
  - Installing symfony/polyfill-php72 (v1.22.1): Loading from cache
  - Installing symfony/var-dumper (v4.4.21): Downloading (100%)         
  - Installing tightenco/collect (v8.0.4): Downloading (100%)         
  - Installing nategood/httpful (0.2.20): Downloading (100%)         
  - Installing symfony/process (v4.4.20): Loading from cache
  - Installing psr/container (1.0.0): Loading from cache
  - Installing php-di/invoker (2.0.0): Downloading (100%)         
  - Installing symfony/service-contracts (v1.1.9): Downloading (100%)         
  - Installing symfony/polyfill-php73 (v1.22.1): Loading from cache
  - Installing symfony/console (v4.4.21): Loading from cache
  - Installing mnapoli/silly (1.7.2): Downloading (100%)         
  - Installing psr/simple-cache (1.0.1): Loading from cache
  - Installing symfony/translation-contracts (v1.1.10): Downloading (100%)         
  - Installing symfony/translation (v4.4.21): Downloading (100%)         
  - Installing nesbot/carbon (2.46.0): Downloading (100%)         
  - Installing doctrine/inflector (1.3.1): Downloading (100%)         
  - Installing illuminate/contracts (v5.8.36): Downloading (100%)         
  - Installing illuminate/support (v5.8.36): Downloading (100%)         
  - Installing illuminate/container (v5.8.36): Downloading (100%)         
  - Installing laravel/valet (v2.14.1): Downloading (100%)         
symfony/service-contracts suggests installing symfony/service-implementation
symfony/console suggests installing symfony/event-dispatcher
symfony/console suggests installing symfony/lock
symfony/console suggests installing psr/log (For using the console logger)
symfony/translation suggests installing symfony/config
symfony/translation suggests installing symfony/yaml
illuminate/support suggests installing illuminate/filesystem (Required to use the composer class (5.8.*).)
illuminate/support suggests installing moontoast/math (Required to use ordered UUIDs (^1.1).)
illuminate/support suggests installing ramsey/uuid (Required to use Str::uuid() (^3.7).)
illuminate/support suggests installing vlucas/phpdotenv (Required to use the env helper (^3.3).)
Writing lock file
Generating autoload files
11 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

Valet install

这将配置并安装 Valet 和 DnsMasq。此外,Valet 依赖的守护程序将配置为在系统启动时启动:

$valet install
Stopping nginx...
Installing nginx...
[nginx] is not installed, installing it now via Brew... 🍻
Installing nginx configuration...
Installing nginx directory...
Updating PHP configuration...
Restarting php@7.1...
Installing dnsmasq...
[dnsmasq] is not installed, installing it now via Brew... 🍻
Updating Dnsmasq configuration...
Restarting dnsmasq...
Valet is configured to serve for TLD [.test]
Restarting nginx...

Valet installed successfully!

结果测试

测试DnsMasq

$ping cc.test
PING cc.test (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.066 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.180 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.161 ms

park

切换到你本地的站点目录,例如我的目录是 ~/Sites

$cd ~/Sites
$valet park

支持Yii2项目

下载驱动

由于 Valet 本身并不支持 Yii2,因此我们需要自己写一个自定义驱动Laravel Valet 自定义站点驱动程序(learnku.com)
当然,你也可以选择github上现成的的chinaphp/yii2-valet-driver: Yii2 Valet Driver (github.com)

$cd ~/.config/valet/Drivers/
wget https://raw.githubusercontent.com/chinaphp/yii2-valet-driver/master/Yii2ValetDriver.php 
如果没有wget,可以使用brew install wget安装

链接Yii2站点目录

valet link yii2-name
http://yii2-name.test
查看原文

赞 0 收藏 0 评论 0

buildyuan 发布了文章 · 4月6日

通过CURL获取本机/指定IP 地址相关信息

curl获取本机代理IP地址

$curl ip.sb
101.*.*.112
$curl ip.gs
101.*.*.112

curl获取IP地址+地区

$curl ipinfo.io

{
  "ip": "101.*.*.112",
  "city": "Kowloon",
  "region": "Kowloon City",
  "country": "HK",
  "loc": "22.3167,114.1833",
  "org": "AS132203 Tencent Building, Kejizhongyi Avenue",
  "timezone": "Asia/Hong_Kong",
  "readme": "https://ipinfo.io/missingauth"
}%     

curl获取本机IP地址

$curl cip.cc
IP    : 125.*.*.87
地址    : 中国  天津
运营商    : 联通

数据二    : 天津市 | 联通

数据三    : 

URL    : http://www.cip.cc/125.*.*.87

curl查询指定IP信息

$curl cip.cc/114.114.114.114


IP    : 114.114.114.114
地址    : 114DNS.COM  114DNS.COM

数据二    : 江苏省南京市 | 南京信风网络科技有限公司GreatbitDNS服务器

数据三    : 中国江苏南京

URL    : http://www.cip.cc/114.114.114.114

$curl cip.cc/127.0.0.1

IP    : 127.0.0.1
地址    : 本机地址  本机地址

数据二    : 本机地址

数据三    : 内网IP | 内网IP

URL    : http://www.cip.cc/127.0.0.1
查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 7 次点赞
  • 获得 6 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 6 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

注册于 2017-10-26
个人主页被 1.4k 人浏览