xdebug error message „Xdebug: [Step Debug] Could not connect to debugging client. Tried: host.docker.internal:9003“ in docker

I got this error message in docker log output:

Xdebug: [Step Debug] Could not connect to debugging client. Tried: host.docker.internal:9003

I could not connect xdebug in phpstorm. I checked port config, it is correct. But I am not sure, where the message „host.docker.internal“ come from. Until I found here:

https://github.com/docker/for-linux/issues/264#issuecomment-381437057

and

https://stackoverflow.com/questions/31324981/how-to-access-host-port-from-docker-container/43541732#43541732

So I add following config in my compose.yml for web server.

...
 extra_hosts:
      - "host.docker.internal:192.168.2.193"
...

After rebuild the image, you can check your hosts file in container. It has a new line in /etc/hosts, like

192.168.2.193 host.docker.internal

This is IP from my host, on that is runing phpstorm with listen to port 9003. And it works. Of course you must install xdebug and enbalbe it first. And check your php.ini the section:

[xdebug]
xdebug.mode=coverage
xdebug.client_host=localhost
xdebug.client_port=9003
xdebug.start_with_request=yes

Entity, Repository and Doctirne

三篇文章帮你理解Repository设计模式和运用.

我刚开始接触symfony的时候,觉得entity, repo类似zend framework里面的model和mapper的关系。 model是数据模型, mapper是操作数据模型。 显然这种理解是肤浅的。

Entity和Repo的概念是在DDD里面提出来的。详细的说明推荐阅读:殷浩详解DDD系列 第三讲 – Repository模式。 在这篇文章中,剖析Repo的本质比较透彻。他用的是Java世界的实现。在PHP世界,特别是Symfon框架内,与之相比又有一些比较特殊的地方。

Repo是一种规范,用来隔离Entity(实体,领域内的模型)和DO(数据模型,持久层的数据模型)。

1. Repo的使用“中性”的动词,而不该使用数据持久层的专用动词, 比如避免使用 select, insert, , update, delete这样的动词作为方法名。

2. Repo是服务于Entity的,同时自身不该直接操作DO(比如数据库)。 所以推荐动词findsaveremove 来作为方法名

在接触这些信息之前,我曾经在DH项目里自己实现过对DDD的理解。当时我理解的领域内的模型,就类似一个需要同时修改多个数据表的模型。

这篇文章中, 作者在symfony框架中如何使用Repo 发表了见解。 首先,他否定了symfony文档中的范例。在这个范例中,文档作者在controller里面通过继承获得的doctrie对象来获取entity manager, 然后用它创建Repo对象。作者说只有初学者才会在实际工作中使用这种方法,因为依赖被写死了。他建议使用service.

写一个repo service, 让controller只依赖这个service. 在这个service里,通过注入repo对象。然后repo对象也不要使用继承获得的findAll等方法,而只使用自家的方法。 作者称为“use composition instead of inheritance“. 所以需要写一个repo的interface,然后把自家的方法都在这个interface内定义好。这样写的好处是,脱耦。

结果调用关系就从

controller -> doctrine -> manager -> repo -> doctrine

变为了

controller-> service -> repo -> doctrine

这样的写的好处就是分层了。

在这篇文章内,不直接使用doctrine,而使用service,称为放弃 Service Locator Pattern ( 设计模式)而是使用 Dependcy injection (依赖注入模式)

不直接使用继承获得的方法,而是调用自己的方法叫做避免违反 Low of demeter 迪米特法则 (介绍)

分层好处的体现:

测试容易了。由于每一层只调用自己的下一层,所以mock起来特别方便。 要测试conterller只需要模拟一个service. 要测试service只需要模拟一个repo. 测试repo只需要模拟一个doctrine。避免了复杂的依赖关系

扩展容易了。比如如果需要在每次保存对象后触发一个事件,那么只要扩展service层就可以了。这样controller可以一直保持苗条。

Repository Pattern

Repository Pattern in Symfony

Doctrine Repositories should be collections without flush

Value Object in Symfony

看了这篇文章 ,接触到一个概念, value object. 所谓value object是用来保存value的一种对象。这种对象的几个主要特征是

关心值本身而不是基于id的

不可变 (immutable). 意思就是说,只有getter方法, 没有setter等等可从外边改变该对象的方法。该对象的值在创建对象的时候通过构造函数就被确定了,且不能再被修改了。

使用场景

在symfony的form里,用户通过表单提交来创建一个新的value object. 当用户通过表单编辑对象的时候,本能的会想到调用setter方法来修改对象。但是这会失败,因为value object没有setter方法。 这时候需要实现额外的interface,来实现这些个功能。最后在Type (关于symfony的form type是另外一个抽象概念,看文档)里面,把这个方法设置为formBuilder的DataMapper方法。

Wow, Doctrine reverse engineering is so cool

just with two simple Symfony commands are all model php files is genereated. It is so cool.

php bin/console doctrine:mapping:import „App\Entity“ annotation –path=src/Entity
for getter/setter
php bin/console make:entity –regenerate App

Source: https://symfony.com/doc/current/doctrine/reverse_engineering.html

看到几句有启发的话

在infoq的网站是那个看到Uber公司的一个开发者的一篇文章,听有意义的。摘录如下:

原文链接:

写在最后

我认为,计算机科学当中的一切东西都存在一种权衡,不存在所谓的通用的高级语言。无论你做什么,都要明白你为什么要这么做,不要让它演变成各派固执己见的政治斗争。

设立好故障点。如果你意识到自己犯了一个错误,你要弄清楚如何做出权衡,并给自己一条出路。你陷在错误决策中的时间越长,成本就越高。不要做一个对解决问题没有贡献的坏脾气的人,不要做一个给别人制造更大问题的狂热者。与我共事过的那些优秀的工程师们都很善于避免落入这两个陷阱。

https://threadreaderapp.com/thread/1336890442768547845.html

初玩 nginx的反向代理

得到任务要完善公司的ngix服务。加入自定义的错误页面。然后就在自己的机器的docker里装上了2个ngix的容器。一个扮演proxy,一个扮演backend app server.

第一步 为2台服务器准备2个内部的ip。然后写个简单的 docker-composer.yaml让2个ngix都能自己跑起来。里面有个小插曲,就是当nginx配置错误,或导致docker启动错误,就自己关闭了。在最后加上一个tty:true可以让进程不挂掉。

本地的host文件加入

192.168.88.18 xxxxxxx.vm
192.168.88.19 app.xxxxxxx.vm

proxy的docker-composer 这样写

version: "3.5"
services:
  # nginx server
  app:
    container_name: xxxxxxx-master
    restart: "no"    
    image: nginx:1.17
    volumes:
        - /home/hchen/private/xxxxxxx/nginx:/var/www/html:delegated
    ports: 
      - "8080:80" 
    expose:
      - "80"  
    networks:
      dh_net:
        ipv4_address: 192.168.88.18
    extra_hosts:
      - "echobot.vm:192.168.88.18"
    tty: true

networks:
  dh_net:    
    name: dh_network
    ipam:
      driver: default
      config:
        - subnet: 192.168.88.0/24
  

然后app的类似,这样写

version: "3.5"
services:
  # nginx server
  app:
    container_name: xxxxxx-app
    restart: "no"    
    image: nginx:1.17
    volumes:
        - /home/hchen/private/xxxxxx/apps:/var/www/html:delegated
    ports: 
      - "8081:80"    
    networks:
      dh_net:
        ipv4_address: 192.168.88.19
    extra_hosts:
      - "xxxxxxx.vm:192.168.88.18"
      - "app.xxxxxxx.vm:192.168.88.19"

networks:
  dh_net:    
    name: dh_network
    ipam:
      driver: default
      config:
        - subnet: 192.168.88.0/24
  

写完以后,跑起来,分别可以打开这2个站点。

第二步,把proxy改为反向代理。修改default.conf

upstream app {
   server 192.168.88.19;
}

server {
    listen       80;
    server_name  xxxxxxx.vm;
     
    location / {
       proxy_pass   http://app;
    }
     
    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    } 
}

主要就是i加上upstream 名称 ,去掉本地 /的解析,改为proxy_pass http://名称。然后reload.

这样一台简单的反向代理就搭建好了。