mcfog

mcfog 查看完整档案

上海编辑  |  填写毕业院校  |  填写所在公司/组织 about.mcfog.wang 编辑
编辑

bWNmb2d3I2dtYWls

个人动态

mcfog 发布了文章 · 2019-06-30

虽然并没有贡献者出现,但我还是坚持把我的PHP框架堆到完成度很高的程度了

如果一个框架能用很少的业务代码实现特别多的功能,那么其实就是这个框架内置了大量的惯例,当这些惯例不符合项目预期的时候,如何添加代码精确地改变对应的惯例,而不搞砸其他部分,就会变的很难。经典的例子是各类CMF,drupal, wordpress,比起框架来已经更接近应用了。

如果一个框架能实现的功能很多,也很容易修改或定义其中的功能细节,那么这个框架往往需要写非常多的业务代码来填充起这些允许自定义的部分(因为如果这些部分有预设值,这个框架就落回到上一个类型)。一般而言的“重”框架往往是这种类型。

而一个框架如果要写的业务代码不多,也还是能够轻松的改变其中的功能,那么这个框架往往无法内置很多功能,需要开发者自己动手拼积木。

以上是我总结的框架的三角原理,业务代码(少),内置功能(多)和内部掌控(易)三者不可得兼。

作为开发者,我选择放弃内置功能的数量,因为我觉得这是唯一可以由外部弥补的,不属于一个框架本身素质。

啰嗦了这么多,这是一个以PSR为核心的,自带DI机制的微框架,其他一切皆可选配。如果你喜欢Slim,但又嫌弃他自身不模块化没法换轮子,DI不正宗搞service locator反模式,不妨了解一下。如果你还不了解Slim,还不知道为啥那么多老司机上手就是Slim,但又懒得四处安利Slim这么小的框架究竟好在哪里,那也可以和Slim一起看下。

http://litphp.github.io/blog/... (全英文预警)

查看原文

赞 10 收藏 4 评论 3

mcfog 评论了文章 · 2018-10-01

开源 PHP 项目找队友、顾问、指导、贡献者等等

介绍站点还没做,先直接甩代码链接了

https://github.com/litphp/litphp

Lit是什么?

Lit是我一直在撸的个人框架,按第一次上传代码来说历史 超过4年 了,从还能支持PHP5.2的第一版开始一直(龟速)迭代,同时为了跟上时代的发展又反复重写,到今天已经形成了一个以依赖注入Air,中间件管理Nimo为核心的PSR-7 + PSR-15组件群

为什么现在?

简单来说,PSR-15正式落地,我认为这是一个新的PHP框架出现的最好时机,伟大的Laravel和Symfony框架背负着http-foundation的包袱,Zend框架虽然推出了Expressive但并没有全力去推。

现在的状况?

第一个针对psr/http-server-middleware接口的版本 v0.8.0已经release,这个版本同时进行了最低程度的测试覆盖,虽然仍然是一个0文档没有社区的框架,但已经完全可用了(不如说其实lit的每个版本都是我一边写业余小玩意儿的同时一边迭代的)

关于v0.9,这个1.0之前最后的版本号(希望……我希望尽可能别0.10)的目标和进度可以 参考这里。三句话说完的话,完善文档和测试、优化API接口使之能无缝升级到第一个长期支持的v1.0,以及可能的话建立基础的社区。

实际项目例子?

https://github.com/team-a7s/d... (PHP代码在kadath目录)目标版本暂时是v0.8

你能帮上我的?

  • 试用。虽然目前 文档 非常少,但毕竟定位轻量级框架,只要关注PSR-15的同学,看 QuickStart 应该还是能很快上手的。 最麻烦的DI部分我会尽快补充文档……
  • 帮助完善文档
  • 帮助完善测试
  • 帮助建立周边站点和设施(主站、CI、Contributor Guideline等等),以及相应的美术支持
  • 重构/改进的建议或PR
  • 或者能帮我勾搭上能够合作的队友

我可以提供的?

必须的contributor credits、litphp组织成员、author列表等等开源社区应当提供的一切

一些(好吧,不多)其他种种原因未公开的以前的个人项目的源码访问

联系我 => mcfogw在gmail


更新了主页 http://litphp.github.io

查看原文

mcfog 评论了文章 · 2018-10-01

开源 PHP 项目找队友、顾问、指导、贡献者等等

介绍站点还没做,先直接甩代码链接了

https://github.com/litphp/litphp

Lit是什么?

Lit是我一直在撸的个人框架,按第一次上传代码来说历史 超过4年 了,从还能支持PHP5.2的第一版开始一直(龟速)迭代,同时为了跟上时代的发展又反复重写,到今天已经形成了一个以依赖注入Air,中间件管理Nimo为核心的PSR-7 + PSR-15组件群

为什么现在?

简单来说,PSR-15正式落地,我认为这是一个新的PHP框架出现的最好时机,伟大的Laravel和Symfony框架背负着http-foundation的包袱,Zend框架虽然推出了Expressive但并没有全力去推。

现在的状况?

第一个针对psr/http-server-middleware接口的版本 v0.8.0已经release,这个版本同时进行了最低程度的测试覆盖,虽然仍然是一个0文档没有社区的框架,但已经完全可用了(不如说其实lit的每个版本都是我一边写业余小玩意儿的同时一边迭代的)

关于v0.9,这个1.0之前最后的版本号(希望……我希望尽可能别0.10)的目标和进度可以 参考这里。三句话说完的话,完善文档和测试、优化API接口使之能无缝升级到第一个长期支持的v1.0,以及可能的话建立基础的社区。

实际项目例子?

https://github.com/team-a7s/d... (PHP代码在kadath目录)目标版本暂时是v0.8

你能帮上我的?

  • 试用。虽然目前 文档 非常少,但毕竟定位轻量级框架,只要关注PSR-15的同学,看 QuickStart 应该还是能很快上手的。 最麻烦的DI部分我会尽快补充文档……
  • 帮助完善文档
  • 帮助完善测试
  • 帮助建立周边站点和设施(主站、CI、Contributor Guideline等等),以及相应的美术支持
  • 重构/改进的建议或PR
  • 或者能帮我勾搭上能够合作的队友

我可以提供的?

必须的contributor credits、litphp组织成员、author列表等等开源社区应当提供的一切

一些(好吧,不多)其他种种原因未公开的以前的个人项目的源码访问

联系我 => mcfogw在gmail


更新了主页 http://litphp.github.io

查看原文

mcfog 发布了文章 · 2018-09-30

开源 PHP 项目找队友、顾问、指导、贡献者等等

介绍站点还没做,先直接甩代码链接了

https://github.com/litphp/litphp

Lit是什么?

Lit是我一直在撸的个人框架,按第一次上传代码来说历史 超过4年 了,从还能支持PHP5.2的第一版开始一直(龟速)迭代,同时为了跟上时代的发展又反复重写,到今天已经形成了一个以依赖注入Air,中间件管理Nimo为核心的PSR-7 + PSR-15组件群

为什么现在?

简单来说,PSR-15正式落地,我认为这是一个新的PHP框架出现的最好时机,伟大的Laravel和Symfony框架背负着http-foundation的包袱,Zend框架虽然推出了Expressive但并没有全力去推。

现在的状况?

第一个针对psr/http-server-middleware接口的版本 v0.8.0已经release,这个版本同时进行了最低程度的测试覆盖,虽然仍然是一个0文档没有社区的框架,但已经完全可用了(不如说其实lit的每个版本都是我一边写业余小玩意儿的同时一边迭代的)

关于v0.9,这个1.0之前最后的版本号(希望……我希望尽可能别0.10)的目标和进度可以 参考这里。三句话说完的话,完善文档和测试、优化API接口使之能无缝升级到第一个长期支持的v1.0,以及可能的话建立基础的社区。

实际项目例子?

https://github.com/team-a7s/d... (PHP代码在kadath目录)目标版本暂时是v0.8

你能帮上我的?

  • 试用。虽然目前 文档 非常少,但毕竟定位轻量级框架,只要关注PSR-15的同学,看 QuickStart 应该还是能很快上手的。 最麻烦的DI部分我会尽快补充文档……
  • 帮助完善文档
  • 帮助完善测试
  • 帮助建立周边站点和设施(主站、CI、Contributor Guideline等等),以及相应的美术支持
  • 重构/改进的建议或PR
  • 或者能帮我勾搭上能够合作的队友

我可以提供的?

必须的contributor credits、litphp组织成员、author列表等等开源社区应当提供的一切

一些(好吧,不多)其他种种原因未公开的以前的个人项目的源码访问

联系我 => mcfogw在gmail


更新了主页 http://litphp.github.io

查看原文

赞 15 收藏 4 评论 6

mcfog 赞了文章 · 2018-08-17

程序员职场算法,一条真正的发展捷径

学员面试失败了,来找我咨询。
“老师,我觉得整个过程,没什么问题啊。”
我问他,除了技术,你和HR还聊了什么呀?
他说,HR问我为什么选择他们公司。
我就实话实说呗,为了公司能提供给我一个成长的平台;
积累项目经验,学习最新的技术……
我说,问题就在于:
你刚刚所说,可曾有那么一点点,跟人家公司有关系的?
图片描述
以我这些年的经验,这不是个例。
身在职场,就要按照职场的规则;
 
职场的规则

所谓“职场规则”,说白了就是四个字:公平交易。
俗话说,一手交钱,一手交货。其他内容,概不关心。
 
试想,你去停车,工作人员说:
我在这停车场都干了十年了,你多交点儿停车费呗?
或者,你去买西瓜,瓜农说:
最近家里要装修,缺钱,西瓜300块一斤,好不好?
作何感想?为什么觉得不可理喻?
这交易,不公平。
图片描述 

企业需要的,是我们能提供的价值;
只要按时完成任务,提供足够多的价值;
你加不加班,每晚熬到几点,上班穿大裤衩还是西装革履:
这些跟交易本身没有太大关系,所以通通不重要。
 
至于你能不能获得成长,在工作中的心路历程;
以及敲代码时的喜怒哀乐……
更是和公司没有关系。
职场呀,成年人啊,公平交易啊。
 
所谓的发展机会、环境福利、薪资奖金;
这是企业提供给个人的。
交易要公平,就需要我们拿“货”来换。
对于劳动力的买家,企业只认价值。
越早认识到这一点,努力的价值就越大。
 

从心态上顺应规则

想要利用规则,就要先顺应它。
很多学员身体已经走出校门,心智还是个大学生。
好学生,未必是好员工,角色转变,至关重要。
 
学生时代,好学,被看成是一种美德。
可到了职场,好学就变成了个人的事情;
要是因为好学而耽误了工作,后果会很严重。
 
图片描述
有学员曾经很不理解:学了技术,工作效率高了,不是公司受益吗?
可是当你能力提升后,薪资同样会提升。
站在企业的角度,成本也提高了。
学到的技术,是你的本事,也是你的“私事”;
公司或者平台的作用,只是把你的本事兑换成财富,仅此而已。
 
好学,自然没错,况且在IT这个行业。
但方式方法,一定不要越过“公平交易”这个底线。
在国外,这叫“契约精神”。
企业花钱想满足的需求,是出活;而不是让干活的人成长。
在出活的过程中成长,没问题,但过度关注成长,容易主次混淆。
 
上面的话,有些同学可能认为我是站在公司或者老板的角度;
可认真想想,其实企业的需求,同样是市场需求。
我们个人想在市场经济发达的社会,分上一杯羹。
不关心身边的实际项目需求、市场需求,而是按照自己的想象和推测。
钻研“以后可能用得上的炫酷技术”,舍本逐末了。
图片描述

具体怎么做,才能适应职场规则?

一句话:做好当下的事,在实战中积累核心竞争力。
到了35岁,还在担心被时代抛弃的程序员;
一般是这山望着那山高,总在挑一条“最好的路”。
关注了“人工智能”,了解过“三大框架”。
而现实中,还在做着“切图”之类的基础工作。
时间就在不断地关注和焦虑中浪费掉:
 
很多学员总想着:等我学了足够多的知识;
成长起来之后,就能解决实际工作中的问题。
那么学什么好呢?从此陷入选择困难症……
还不如在工作中遇到了问题,去解决它,这才能获得真正的成长。
图片描述
 
很少有人,会想着把最新的技术,用来解决当前问题;
比如:我怎么用Vue,把现在的产品优化一下呢?
 
职场的财富逻辑,是你能解决多少实际问题,就能收获多大价值。
不去解决手边现成的问题,却囤了一堆课程,学了一堆知识点,还没有实践机会。
这不就是屠龙之术么:
练了半天,龙没来,屠龙刀倒是生锈了;
最后只能当废铁卖掉,还耽误半天功夫。

大部分高端技术,是被问题激发出来的。
换句话说,任何人,都是在解决业务逻辑中成长起来的。
淘宝网的框架,是一个阿里的大牛写的。
人家可不是学了很多东西,然后一气呵成。
而是在搭完框架之后,不断发现问题,又不断完善;
才有了这样一个优秀作品,才收获成长。
图片描述 

之前面试过一个程序员,讲起来自己研究过的“高端技术”,滔滔不绝。
我问道:你所研究的知识,对你当时的工作,有什么促进作用吗?
或者说,解决了什么实际项目中的问题呢?优化了什么进程?
他沉默了一小会儿,巧妙地转移了话题。
 
根据我的经验,HR可能不关心你研究了多少高端技术;
更不关心你敲代码的喜怒哀乐,而是上来就问:
能干什么活?操作过什么项目?
或者是:来了,能立马干活吗?
图片描述 

大家也看到了,实战给了我们成长的方向;
同样保证了我们努力的价值。
 
回到之前的逻辑,当你能够解决实际项目中的任何问题;
在实战中积累了大量的经验,能够独当一面的时候。
按照“公平交易”的原则;
薪资是不可能原地踏步的。
职场规则,针对的可不是个人,而是整个人才市场。
这才是符合职场的算法,如果说程序员的职业发展;
真的有捷径的话,我相信,这条路就是,并且是最简单的一条。
·END·

查看原文

赞 12 收藏 3 评论 4

mcfog 赞了文章 · 2018-01-25

小而美的backbone

本文已同步在我的博客

在这个reactvue如日中天、jquery逐渐被大家抛弃的年代,我还是想要来说一说backbone

16年6月初,在没有任何前端框架使用经验、js水平也较一般的情况下,被告知需要在几个工作日内搭建完成一个后台管理系统,没有页面设计稿、没有组件库,一切都是从零开始。当时面临两个选择,backbone和react。虽然我很希望能够拿react来练手,但是考虑到学习成本和项目时间问题,leader还是建议我使用backbone来完成,就这样,一直用到差不多现在。虽然到项目后期业务场景越来越复杂,backbone的这套技术栈体现出越来越多的问题,但是对于小型项目来说,我还是认为backbone是个不错的选择,而且学习成本低,上手极快~

backbone是个非常轻量的mvc库,本文将基于backbone的源码谈一谈其实现的核心部分,以及其中一些巧妙的思想和代码实现技巧。

事件机制(Events)

事件部分的核心逻辑其实比较简单,简化一下可以用如下的伪代码来表示:

var events = {
    evt1: handlers1,
    evt2: handlers2,
    ...
}

//注册事件
function on(name, callback) {
    const handlers = events[name] || (events[name] = []);
    handles.push(callback);
}

//触发事件
function trigger(name) {
    if (!events[name]) {
        return;
    }
    const handlers = events[name];
    for (let i = 0, len = handlers.length; i < len; i++) {
        handlers[i]();
    }
}

//解除绑定
function off(name) {
    delete events[name];
}

当然了,以上写法有很多细节的地方没有加入进来,比如上下文绑定对多种传参方式的支持触发事件时对事件处理器传参的处理等等。

我们知道,对于MVC来说,M(模型)的变化会反映在V(视图)上,实际上是视图监听了模型的变化,再根据模型去更新自身的状态,这当中最重要的一个功能就是监听(listen)。该功能也是由Events部分实现的,包括:listenTostopListening

listenToon类似,都是监听一个事件,只不过listenTo是监听其他对象的对应事件,而on是监听自身的对应事件。stopListening同理。比如:

a.on('testevent', function(){
    alert('1');
});
a.trigger('testevent');

如果其他对象希望监听atestevent事件呢?则可以通过listenTo来实现:

b.listenTo(a, 'testevent', function() {
    alert('catch a\'s testevent');
})

其中第一个参数为要监听的对象,第二个参数为事件名称

当调用on方法的时候,会为对象自身创建一个_event属性;而调用listenTo方法时,会为监听对象创建_event属性,同时为了记录监听者,被监听对象还会创建一个_listeners属性:

a.on('testevent', handlers1);

a会变成:

{
    _events: {
        testevent: [handlers1]
    },
    on: function() {
        //...
    }
    ...
}

当有其他对象监听a时,如:

    b.listenTo(a, 'testevent', handlers2);

a会变成:

{
    _events: {
        testevent: [handlers1, handlers2]
    },
    _listeners: b,
    on: function() {
        //...
    }
    ...
}

在事件机制的实现部分,除了核心逻辑之外,在对一些方法的使用上,也很考究。为了绑定函数执行的上下文,我们经常会使用applycall这些方法,而源码中多次提到apply的执行效率要低一些,因此,有这样的实现:

// A difficult-to-believe, but optimized internal dispatch function for
// triggering events. Tries to keep the usual cases speedy (most internal
// Backbone events have 3 arguments).
var triggerEvents = function(events, args) {
    var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
    switch (args.length) {
      case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
      case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
      case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
      case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
      default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
    }
};

有关为什么callapply的效率更高的解释可以参考这篇文章

模型(Model)

model用于维护数据,其中最关键的是对数据的更新部分,即set

// Trigger all relevant attribute changes.
if (!silent) {
    if (changes.length) this._pending = options;
    for (var i = 0; i < changes.length; i++) {
        this.trigger('change:' + changes[i], this, current[changes[i]], options);
    }
}

// You might be wondering why there's a `while` loop here. Changes can
// be recursively nested within `"change"` events.
if (changing) return this;
if (!silent) {
    while (this._pending) {
        options = this._pending;
        this._pending = false;
        this.trigger('change', this, options);
    }
}
this._pending = false;
this._changing = false;
return this;

每次set数据的时候,根据数据变化的部分,使用trigger方法触发相应的事件;在set数据时,如果不希望触发change事件,可以设置silenttrue

这部分比较容易让人产生疑惑的是while循环部分,这个while循环有什么用呢?举个例子:

new Model.on("change", function() {
    console.log('model change');
}).set({
    a: 1
});

以上代码是最简单的情况,监听change事件,当model变化时,打印出model change
在源码中,当第一次进入while后,紧接着this._pending被置为false,而事件触发回调函数也不会更改this._pending的值,因此再次判断时条件不成立,while内的代码段只会执行一次。

但是实际情况往往不是这么简单,如代码注释中所说,有可能会有嵌套的情况,比如:

new Model.on("change", function() {
    this.set({
        b: 1
    })
}).set({
    a: 1
});

在这种情况下,第一次trigger触发change的回调函数中,又再次对model进行了更新操作,

this.set({
    b: 1
})

每次set时,会更新this._pendingtrue,这样当set b后,就会再次进入while内,触发change事件。而如果没有使用while循环的话,对b属性更新的操作就无法触发change事件,导致其监听者到无法根据最新的数据更新自身状态。

视图(View)

View部分的实现比较简单,其中最主要的是events部分,通常在一个View中,都会绑定一些dom事件,比如:

{
    'click .preview-btn': 'preview',
    'click .save-btn': 'save'
}

主要有两点需要说明:

  • backbone中是采用的事件委托的方式绑定事件,因此,一些不冒泡的事件,比如scroll,是无法通过这样的方式绑定的
  • 回调函数会保持正确的this指向。backbone内部进行了处理
delegateEvents: function(events) {
    events || (events = _.result(this, 'events'));
    if (!events) return this;
    this.undelegateEvents();
    for (var key in events) {
        var method = events[key];
        if (!_.isFunction(method)) method = this[method];
        if (!method) continue;
        var match = key.match(delegateEventSplitter);
        this.delegate(match[1], match[2], _.bind(method, this));
    }
    return this;
}

结语

以上部分介绍了backbone中最核心部分的实现机制。可以看到其实现非常的简单,但是对于小型项目来说,确实可以帮我们做一些对数据的维护和管理工作,提高开发效率。但是随着业务逐渐复杂,会越来越发现,backbone所能做的实现有限,而对于数据维护部分也非常不方便,尤其是需要是对多个模块间的通信和数据维护问题。后续我会结合在复杂业务中的使用谈一谈backbone的缺点,以及更优的框架能带来的便利。

说句题外话,虽然去年由于时间原因选择了backbone,这一年基本没有在复杂业务场景中使用react技术栈,都是自己做个小demo练手。但是也正是因为有了使用backbone去写复杂业务的经历,在数据维护上和模块间通信上非常麻烦,以及backbone渲染dom时直接全部更新的会导致的页面渲染性能问题,才更让我感觉react + redux的美好。知其然,还需知其所以然啊~ ~
不然我觉得我可能会一直疑惑为什么要用一套这么复杂的技术栈,异步请求这块写起来还那么麻烦。这么看,坏事也算是好事了吧~~

查看原文

赞 6 收藏 12 评论 3

mcfog 赞了回答 · 2017-12-31

解决CSRF 生成 token 的方式这样可以攻击吗?

在中间网站我请求两次,第一次通过 GET 方式请求这个表单页面从而获取 token,第二次带上这个 token 发起 POST 请求,这样不就成功伪装了吗?我这个想法应该有问题,但好像又可以,错在哪?

中间网站是什么?

如果是指中间人攻击,那么,你应该关注的是 HTTPS。CSRF 不处理中间人攻击。

如果是指第三方网站,那么,除非你的网站通过 Access-Control-Allow-Origin 头允许,否则第三方网站无法读取请求返回的内容(跟其它一些跨域请求的处理一样,能请求,但是未经允许不得访问),也就拿不到 token。


PS: 这么基础的问题,那么多回答,竟然只有一个稍微靠谱点的…………

关注 10 回答 7

mcfog 关注了问题 · 2017-12-19

解决PHP可变长参数(...)和生成器问题

问题来源于 https://segmentfault.com/q/10... 这里。看了@elarity 的回答,他使用了200000的元素插入到redis集合。于是乎我使用了1百万个元素数组来插入,在我这里是内存溢出的,所以我使用了生成器的方式

function xrange() {
        for ($i=0; $i<1000000; $i++) {
                yield $i;
        }
}
$r = xrange();

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$key = 'jimu';
$redis->del($key);
$begin = microtime(true);
$redis->sadd($key, ...$r);

$end = microtime(true);
echo ($end - $begin) . "\n";

输出结果:

[vagrant@localhost ~]$ php redis.php 
1.2786898612976
[vagrant@localhost ~]$

然后redis-cli中确实有了一百万个元素。那么当我把代码中的一百万修改为一千万的时候又报内存溢出

[vagrant@localhost ~]$ php redis.php 
PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in /home/vagrant/redis.php on line 6

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in /home/vagrant/redis.php on line 6

根据我理解的生成器知识不应该出现内存溢出的情况。因为自始至终生成器xrange只占用一个变量($i)内存?
所以我猜测是不是$redis->sadd($key, ...$r);这一步的时候...$r依然会解析成大数组。 现在不知道如何证实。


补充:
我在sadd之前使用var_dump(...$r);exit;发现输出的都是

int(999775)
int(999776)
int(999777)
int(999778)
int(999779)
int(999780)
int(999781)

这样可以证明生成器确实是一个一个产出值的。那么为什么将...$r传入到sadd()的时候还报内存不足呢?不明白这个...的原理,还请大佬们指点。

关注 5 回答 2

mcfog 回答了问题 · 2017-12-14

解决PHP可变长参数(...)和生成器问题

好久没看到想答的问题了,来一波

a. 这个问题和redis毫无关系

b. 上代码

<?php
//splat.php
function gen() {
  global $argv;
  $max = $argv[1];
  while($max--) {
    yield(str_repeat('x', 10000));
  }
}

function noop() {

}

function getargs() {
  $arg = func_get_args();
}

function splat(...$arg) {

}

function printmemory($msg) {
  printf("%s: %d/%d\n", $msg, memory_get_usage(), memory_get_peak_usage());
}

printmemory(__LINE__);
$gen = gen();
printmemory(__LINE__);
foreach(gen() as $r) {
  crc32($r);
}
printmemory(__LINE__);
$argv[2](...$gen);
printmemory(__LINE__);
~/Desktop $ php splat.php 10000 getargs
27: 357896/394272
29: 358504/394272
33: 370816/394272
35: 382912/123779064
~/Desktop $ php splat.php 10000 noop
27: 357896/394272
29: 358504/394272
33: 370816/394272
35: 382912/123250912
~/Desktop $ php splat.php 10000 splat
27: 357896/394272
29: 358504/394272
33: 370816/394272
35: 382912/123779064
~/Desktop $ php splat.php 1000 splat
27: 357896/394272
29: 358504/394272
33: 370816/394272
35: 382912/12695544
~/Desktop $ php splat.php 100 splat
27: 357896/394272
29: 358504/394272
33: 370816/394272
35: 382912/1607672

c. 解释

27-29-33之间,几乎没有内存占用,这是所谓的"生成器节省内存”的现象,也就是各种相关文章里都会解释的,在30行迭代生成器的时候,每次循环都会进到生成器内部去yield一次,产生一个大字符串,下次循环的时候循环变量又重新被赋值,之前的字符串自然会被GC回收,所以无论循环多大多少次,占用的内存是稳定的(包括上面的$gen=gen()也是几乎不占内存的)

33-35,无论被调用的函数如何,甚至noop函数,都一样会占用大量内存,占用内存的量明显和次数成正比,也就是说生成器的内容被合并到一起而占用了一整块内存。这其实很容易解释,几乎的所有语言“调用函数”的过程都是类似的

  1. 首先计算所有参数,形成参数列表
  2. 生成call frame(其中包含调用被调双方、文件行号、参数列表等等信息),压入call stack中
  3. 控制权移交给函数内部

(当然省略了超级多的细节,比如实参形参的映射/copy啊,内存管理啊等等什么的,和本题无关)

...$args这个操作符其实影响的就是第一个阶段,计算参数的时候,看到...操作符,就需要展开其中的参数来形成参数列表,那么用生成器的场合,这个阶段内存就从原有生成器的少量占用变成了完整的占用了,所以即使是空的noop函数也会占用几乎一样多的内存,你的理解是正确的

回到原题的那个redis问题的话,因为重复调用redis方法一定会占用大量的额外网络开销,而一次性批量插入又铁定逃不开内存占用(其实你想redis扩展要发送这个批量的指令给redis,那么这块内存肯定是要的),比较好的方式就是分组了,每1000个或者10000个合并成一次$redis调用,mysql也好其他场景也是类似的

关注 5 回答 2

mcfog 赞了回答 · 2017-09-18

解决手机号码一般遮住后4位还是中间4位

MDN号码的结构如下:CC + MAC + H0H1H2H3 + ABCD其中:
【CC】:国家码,中国使用86。
【MAC】:移动接入码。
【H0H1H2H3】:HLR识别码,由运营商统一分配。
【ABCD】:移动用户号,由各HLR自行分配。

隐藏中间4位,能在保护用户号码的同时,识别不同的用户

关注 11 回答 11

认证与成就

  • 获得 1430 次点赞
  • 获得 39 枚徽章 获得 0 枚金徽章, 获得 11 枚银徽章, 获得 28 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2014-04-03
个人主页被 10.1k 人浏览