php使用elasticsearch

1.引入包
composer require elasticsearch/elasticsearch

2.DEMO参考

<?php
require_once './vendor/autoload.php';

use Elasticsearch\ClientBuilder;

$hosts = [
    [
        'host' => '192.168.56.201',
        'port' => '9200',
        'scheme' => 'http',
        //'user' => 'username',
        //'password' => 'password'
    ],
];
try {
    $client = ClientBuilder::create()->setHosts($hosts)->build();
    //创建index并设置mapping
    /*$params = [
        'index' => 'demo',  //索引名(相当于关系型mysql的数据库)
        'body' => [
            'settings' => [
                'number_of_shards' => 1,  //分片数
                'number_of_replicas' => 1, //副本分骗术
            ],
            'mappings' => [
                'm_type' => [
                    '_all' => [
                         'enabled' => 'false'
                    ],
                    '_routing' => [
                        'required' => 'true'
                    ],
                    'properties' => [ //文档类型设置(相当于mysql的数据类型)
                        'name' => [
                            'type' => 'string',
                            'store' => 'true'
                        ],
                        'age' => [
                            'type' => 'integer'
                        ]
                    ]
                ]
            ]
        ]
    ];
    $response = $client->indices()->create($params);
    print_r($response);//Array ( [acknowledged] => 1 [shards_acknowledged] => 1 )
    */
//    索引操作
//    $params = [
//        'index' => 'demo',
//    ];
//    $response = $client->indices()->delete($params);    //删除索引
//    print_r($response);//Array ( [acknowledged] => 1 )
    //$response = $client->indices()->getSettings($params);//获取索引设置信息
    //print_r($response);//Array ( [demo] => Array ( [settings] => Array ( [index] => Array ( [creation_date] => 1502246895144 [number_of_shards] => 5 [number_of_replicas] => 5 [uuid] => tW3DB-9FRQC2W-bwXl0fbg [version] => Array ( [created] => 5040199 ) [provided_name] => demo ) ) ) )
    //$response = $client->indices()->exists($params);   //检测索引是否存在
    //print_r($response);//1
    //$response = $client->indices()->getMapping($params);   //获取索引的mapping信息
    //print_r($response);//Array ( [demo] => Array ( [mappings] => Array ( [m_type] => Array ( [_all] => Array ( [enabled] => ) [_routing] => Array ( [required] => 1 ) [properties] => Array ( [age] => Array ( [type] => integer ) [name] => Array ( [type] => text [store] => 1 ) ) ) ) ) )
    
    //添加文档
//    $params = [
//        'index' => 'demo',
//        'type' => 'm_type',
//        'id' => '2',
//        'body' => [
//            'name' => 'demo',
//            'age' => 150
//        ],
//        'routing' => '/demo/m_type'
//    ];
//    $response = $client->index($params);
//    print_r($response);
  
    //删除文档
//    $params = [
//        'index' => 'demo',
//        'type' => 'm_type',
//        'id' => '2',
//        'routing' =>   '/demo/m_type'
//    ];
//    $response = $client->delete($params);
//    print_r($response);//Array ( [found] => 1 [_index] => demo [_type] => m_type [_id] => 1 [_version] => 2 [result] => deleted [_shards] => Array ( [total] => 2 [successful] => 2 [failed] => 0 ) )
    
    //修改文档
//    $params = [
//        'index' => 'demo',
//        'type' => 'm_type',
//        'id' => '2',
//        'body' => [
//            'doc' => [
//                'name' => 'demo22',
//                'age' => 130
//            ]
//        ],
//        'routing' =>   '/demo/m_type'
//    ];
//    $response = $client->update($params);
//    print_r($response);
    //获取文档并指定字段
//    $params = [
//        'index' => 'demo',
//        'type' => 'm_type',
//        'id' => '2',
//        '_source' => [
//            'age'
//        ],
//        'routing' =>   '/demo/m_type'
//    ];
//    $response = $client->get($params);
//    print_r($response);
//    $response = $client->getSource($params);
//    print_r($response);
    
    //高级搜索
//    $params = [
//        'index' => 'demo',
//        'type'  => 'm_type',
//        'routing' => '/demo/m_type',
//        'body' => [
//            'query' => [
//                'match' => [
//                    'age' => 130,
//                ]
//            ]
//        ]
//    ];
//    $response = $client->search($params);
//    print_r($response);
    
} catch (Exception $e) {
    echo $e->getMessage();
}

备注:如果提示routing_missing_exception则参数当中需要传递routing

参考:https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html

PHPUnit使用笔记

1.引入composer
composer require  phpunit/phpunit

2.编写测试代码

<?php
require_once './vendor/autoload.php';
require_once './Demo.php';

use PHPUnit\Framework\TestCase;
use Mydemo\Demo;
class DemoTest extends TestCase
{
    public function testone()
    {
        //判断真假
        $this->assertTrue(Demo::add(1,2) == 3);
        //判断总数是否正确
        $this->assertEquals(5, count(Demo::fetchAll()));
        //判断结果是否包含
        $this->assertContains(1, Demo::fetchAll());
    }
}
更多内容参考:https://phpunit.de/manual/current/zh_cn/appendixes.assertions.html

3.编写类

<?php

namespace Mydemo;

class Demo
{
    public static function add($a, $b)
    {
        return $a + $b;
    }
    
    public static function fetchAll()
    {
        return [1,2,3,4,5];
    }
}

TDD、BDD和DDD

1.TDD,测试驱动开发
TDD指的是Test Drive Development,简单地说,TDD 就是在写代码前先写测试,并严格遵守(错误》正确》重构)的流程

2.BDD,行为驱动开发
BDD指的是Behavior Drive Development,实际上BDD可以看作是对TDD的一种补充,当然你也可以把它看作TDD的一个分支

3.DDD,领域驱动开发
DDD是指Domain Drive Design,也就是领域驱动开发,这是一种非常好的思想。在我们刚开始学习程序,甚至刚开始学习三层架构的时候,我们曾经面临过很多疑惑,比如如何来实现我们的数据层?后来我们开始学习MVC,MVP等架构,如何设计Model层又成了我们的新问题。我们见过太多这种情况,Model变成了单纯的数据容器,也就是我们经常说的贫血模式。DDD实际上也是建立在这个基础之上,因为它关注的是Service层的设计,着重于业务的实现,因此不可避免的以贫血模式为基础而存在。

monolog使用案例

1.首先要执行
composer require monolog/monolog
2.案例
<?php
require_once './vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\RedisHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\BrowserConsoleHandler;
use Monolog\Processor\WebProcessor;
use Monolog\Processor\IntrospectionProcessor;

use Monolog\Formatter\JsonFormatter;

$logfilename = "/data/logs/default/" .date('YmdH').".log";
$redis = new Redis();
$redis->connect('192.168.56.101',6379);
$key  = 'monolog';

$stream = new StreamHandler($logfilename,Logger::ERROR);
$stream->setFormatter(new JsonFormatter());

$redishandle = new RedisHandler($redis,$key,Logger::INFO);

$browserhandle = new BrowserConsoleHandler(Logger::INFO);

$channel1 = 'test';

$log = new Logger($channel1);
$log->pushHandler($stream);
$log->pushHandler($redishandle);
$log->pushHandler($browserhandle);

$log->pushProcessor(new WebProcessor());
$log->pushProcessor(new IntrospectionProcessor());


$log->info('info', ['a' => 'test']);
$log->error('error');

monolog使用解析

1.channel渠道、通道
可以区分同项目不同模块日志

2.handler处理器
一个channel设置多个handler,是按照堆栈的方式依次调用(后进先出,最后调用的先执行),方便将日志发送多个接收器(redis、elasticsearch、syslog等)
备注:可以自己根据自己需求实现handler

3.processor扩展信息
一个channel可以附带一些扩展信息,可以用来补充一些额外信息(内存、进程、服务器IP等信息)也可以用来记录上下文信息
备注:可以自己根据自己需求实现processor,上下文信息也可以使用扩展的数组传递

4.formatter格式化信息
每个handler可以设定特定的格式化信息(默认是LineFormatter)
备注:可以自己根据自己需求实现formatter

5.level日志等级
每个handler设定处理特定级别的日志

DEBUG(100)详细的DBUG信息
INFO(200) 提示信息,感兴趣的信息
NOTICE(250)通知信息,重大意义的信息
WARNING(300)警告信息,异常信息
ERROR(400)错误信息
CRITICAL(500)关键错误信息,内部组件错误
ALERT(550)依赖服务不可以用,例如数据库故障灯
EMERGENCY(600)系统不可用
备注:错误日志数字越大,表示错误越严重,如果一个handler设置的日志级别较低则可以处理较高级别的日志信息

MySQL最大连接数和当前并发数

1.查看当前连接数和并发
执行:show status like ‘Threads%’;
+——————-+——-+
| Variable_name     | Value |
+——————-+——-+
| Threads_cached    | 220   |
| Threads_connected | 16    |
| Threads_created   | 236   |
| Threads_running   | 3     |
+——————-+——-+

Threads_connected 跟show processlist结果相同,表示当前连接数,Threads_running是代表当前并发

2.查看最大连接数
执行:show variables like ‘%max_connections%’;
+—————–+——-+
| Variable_name   | Value |
+—————–+——-+
| max_connections | 1200  |
+—————–+——-+

Go函数知识

1.函数格式
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
//这里是处理逻辑代码
//返回多个值
return value1, value2
}
示例:
func max(a, b int) int {
if a > b {
return a
}
return b
}

2.多个返回值
func add(a, b int) (int,int) {
return a,a+b
}

3.参数个数不确定

func add(t ... int) (r int) {
for _, n := range t {
r+=n
fmt.Printf("And the number is: %d\n", n)
}
return
}

4.值传递和指针传递
值传递
func add(a int) int {
a=a+1
return a
}
func main() {
a:=1
b:=add(a)
fmt.Println(a)
fmt.Println(b)
}

指针传递
func add(a *int) int {
*a=*a+1
return *a
}
func main() {
a:=1
b:=add(&a)
fmt.Println(a)
fmt.Println(b)
}

5.defer
如果有很多调用defer,那么defer是采用后进先出模式可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回

6.函数作为类型
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
备注:定义为typeName类型的函数当做值来传递

7.panic和recover

func badCall() {
panic("bad end")
}

func test() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panicing %s\r\n", e)
}
}()
badCall()
fmt.Printf("After bad call\r\n") // <-- wordt niet bereikt
}

func main() {
fmt.Printf("Calling test\r\n")
test()
fmt.Printf("Test completed\r\n")
}

8.main和init
点操作:你可以省略前缀的包名,也就是前面你调用的fmt.Println("hello world")可以省略的写成Println("hello world")
别名操作:别名操作的话调用包函数时前缀变成了我们的前缀
_空操作:别名操作的话调用包函数时前缀变成了我们的前缀


Go的基础知识1

1.关键字
break    default      func    interface    select
case     defer        go      map          struct
chan     else         goto    package      switch
const    fallthrough  if      range        type
continue for          import  return       var

2.入口函数
每一个可独立运行的Go程序,必定包含一个package main,在这个main包中必定包含一个入口函数main,而这个函数既没有参数,也没有返回值。

3.定义变量
var name type //定义一个type类型的name变量
var name1,name2,name3 type //定义3个type类型的变量
var name type = value //定义type类型变量并初始化为value
var name1,name2,name3 type = v1,v2,v3 //定义三个type类型变量并分别初始化
var name1,name2,name3 = v1,v2,v3 //定义三个type类型变量并分别初始化
name1,name2,name3 := v1,v2,v3 //定义三个type类型变量并分别初始化,只能在函数内部用
_ 是特殊变量,任何值都会被舍弃

备注:变量如果声明未使用则会编译报错

4.常量
const name = value 定义一个常量,常量的值是数值、bool、
const name type = value 指定类型

5.数值类型
整数类型有无符号和带符号两种。Go同时支持int和uint,这两种类型的长度相同,但具体长度取决于不同编译器的实现。Go里面也有直接定义好位数的类型:rune, int8, int16, int32, int64和byte, uint8, uint16, uint32, uint64。其中rune是int32的别称,byte是uint8的别称。浮点数的类型有float32和float64两种(没有float类型),默认是float64。
Go还支持复数。它的默认类型是complex128(64位实数+64位虚数)。如果需要小一些的,也有complex64(32位实数+32位虚数)

6.字符串
字符串使用UTF-8字符集使用双引号或者反引号,类型是string
字符串可以使用+来连接
用反引号可以声明多行字符串

7.错误类型
内置一个error类型用来处理错误信息,还有一个errors的包可以处理错误

8.分组声明
import(
“fmt”
“os”
)
const(
i = 100
pi = 3.1415
prefix = “Go_”
)

9.iota枚举
const( 
x = iota
y = iota
z
)
那么y=1,z=2
备注:它默认开始值是0,const中每增加一行加1

const (
h, i, j = iota, iota, iota //h=0,i=0,j=0 iota在同一行值相同
)

10.设计原则
大写字母的变量是可以到处的
大写字母开头的函数等同public函数

11.数组array
var arr [n]type 声明一个type类型长度是n的arr数组
b := [10]int{1, 2, 3} //数组前三个元素用1 2 3初始化
c := […]int{4, 5, 6} //go自动计算数组长度
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}} //声明二维数组

12.动态数组slice
var fslice []int  //声明数组,但不带长度
// 声明一个含有10个元素元素类型为byte的数组
var ar = [10]byte {‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’}

// 声明两个含有byte的slice
var a, b []byte

// a指向数组的第3个元素开始,并到第五个元素结束,
a = ar[2:5]
//现在a含有的元素: ar[2]、ar[3]和ar[4]

// b是数组ar的另一个slice
b = ar[3:5]
// b的元素是:ar[3]和ar[4]

slice是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值

13.map类型
map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取
map的长度是不固定的,也就是和slice一样,也是一种引用类型
内置的len函数同样适用于map,返回map拥有的key的数量
map的值可以很方便的修改,通过numbers[“one”]=11可以很容易的把key为one的字典值改为11
map和其他基本型别不同,它不是thread-safe,在多个go-routine存取时,必须使用mutex lock机制

composer开发的一些坑

1.问题描述:
 Your configuration does not allow connections to xxxxxx See https://getcomposer.org/doc/06-config.md#secure-http for details.
解决办法:
 "config":{
        "secure-http":false
    },
2.问题描述:
Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your min                                      imum-stability setting
   see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more det                                      ails.
问题解决:
可能包不存在或者版本不存在