Categories
java microservice php

Sidecar

Spring Cloud Netflix Sidecar框架提供了Sidecar模式的现成解决方案。Spring Cloud Netflix Sidecar框架框架可以提供对其他非Spring Cloud技术栈的微服务的治理。比如,你可以使用Node或者Golang php 编写一个Web项目,这个服务同样可以以Sidecar模式,纳入到Spring Cloud管理中去。

Categories
php

php 静态变量与全局变量

静态变量 : 在函数内部以static 作为声明前缀

全局变量 : 在函数内部以global作为声明前缀

共同点

静态变量与全局变量当同一方法在一次访问后更新后 能一直维持该值的该值 第二次 也依然能读取上次的赋值

最大的访问域是一个请求 不同请求下的值不一样。(php 一次请求就是一个进程 进程内部变量不能共享)

不同点

静态变量 在方法内部声明 也只能在方法内部使用

全局变量 在任何地方声明 在任何地方调用

且可以通过GLOBALS[‘xxx’]调用

Categories
php

php 字符串模板

$var = "world";
$helloWorld = "hello {$var}";
echo $helloWorld;

双引号自带模板语法功能 花括号自带变量替换功能

Categories
php

php nginx 环境下载

nginx 配置

root /var/www/php;  // 新版本 root 要放在这里次啊能起作用
location   ~\.php$ {
                        root            /var/www/php;
                        index          index.php index.htmlindex.htm;
                        fastcgi_pass    unix:/run/php/php7.4-fpm.sock;
                        fastcgi_index   index.php;
                        include         fastcgi_params;
                }

服务器 安装 php-fpm

然后 nginx 通过unix:/run/php/php7.4-fpm.sock 转发请求到php-fpm上

tips : fastcgi_params 添加以下配置(若访问首页空白)

fastcgi_param SCRIPT_FILENAME     $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO           $fastcgi_script_name;
//从nginx变量 传到  cgi变量(即php变量)

fastcgi_param 参数详解 就是从nginx传过去的变量 可以通过_SERVER 获取

关键变量是

DOCUMENT_ROOT php 项目文件所在文件夹

SCRIPT_NAME php 项目文件

SCRIPT_FILENAME $document_root$fastcgi_script_name; //php 文件全路径

nginx变量 都是$开头小写下划线组合


fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

以上参数可以通过 php $_SERVER[‘SERVER_NAME’] 获取变量值

Categories
php

php trait

php 实现多继承的组件

<?php
trait ezcReflectionReturnInfo {
    function getReturnType() { /*1*/ }
    function getReturnDescription() { /*2*/ }
}

class ezcReflectionMethod extends ReflectionMethod {
    use ezcReflectionReturnInfo;
    /* ... */
}

class ezcReflectionFunction extends ReflectionFunction {
    use ezcReflectionReturnInfo;
    /* ... */
}
?>

trait 定义组件

内部function定义方法接口 可以有实现的代码

在具体某个类 内部 通过关键字 use 引入trait 组件

当有相同名称方法trait 还优先调用 可通过parent:: 调用本来继承下来的方法

Categories
php

laravel-admin 添加文件导入

安装excel 包

composer require maatwebsite/excel

新增一个action操作

php artisan admin:action WeinpinhuiUser/ImportAction --name="导入"
<?php

namespace App\Admin\Actions\WeinpinhuiUser;

use App\Imports\WeipinhuiUser\ImportWeipinhuiUser;
use Encore\Admin\Actions\Action;
use Encore\Admin\Facades\Admin;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;


class ImportAction extends Action
{
    protected $selector = '.import-action';

    public function handle(Request $request)
    {
        // $request ...
        try{
            // $request ...
            $file = $request-> file('file');
            Excel::import(new ImportWeipinhuiUser(),$file);

            return $this->response()->success('数据导入成功')->refresh();
        }catch (\Exception $e){
            return $this->response()->error($e -> getMessage());
        }

    }

    public function html()
    {
        return <<<HTML
        <a class="btn btn-sm btn-default import-action">导入</a>
HTML;
    }

    public function form()
    {
        $this
            ->file('file', '请选择文件')
            ->options(['showPreview' => false,
                'allowedFileExtensions'=>['xlsx','xls'],
                'showUpload'=>false
            ]);
    }

    public function handleActionPromise()
    {
        $resolve = <<<SCRIPT
var actionResolverss = function (data) {
            $('.modal-footer').show()
            $('.tips').remove()
            var response = data[0];
            var target   = data[1];

            if (typeof response !== 'object') {
                return $.admin.swal({type: 'error', title: 'Oops!'});
            }

            var then = function (then) {
                if (then.action == 'refresh') {
                    $.admin.reload();
                }

                if (then.action == 'download') {
                    window.open(then.value, '_blank');
                }

                if (then.action == 'redirect') {
                    $.admin.redirect(then.value);
                }
            };

            if (typeof response.html === 'string') {
                target.html(response.html);
            }

            if (typeof response.swal === 'object') {
                $.admin.swal(response.swal);
            }

            if (typeof response.toastr === 'object') {
                $.admin.toastr[response.toastr.type](response.toastr.content, '', response.toastr.options);
            }

            if (response.then) {
              then(response.then);
            }
        };

        var actionCatcherss = function (request) {
            $('.modal-footer').show()
            $('.tips').remove()

            if (request && typeof request.responseJSON === 'object') {
                $.admin.toastr.error(request.responseJSON.message, '', {positionClass:"toast-bottom-center", timeOut: 10000}).css("width","500px")
            }
        };
SCRIPT;

        Admin::script($resolve);

        return <<<SCRIPT
         $('.modal-footer').hide()
         let html = `<div class='tips' style='color: blue;font-size: 15px;'>正在处理中<img src="data:image/gif;base64,R0lGODlhEAAQAPQAAP///1VVVfr6+np6eqysrFhYWG5ubuPj48TExGNjY6Ojo5iYmOzs7Lq6utjY2ISEhI6OjgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAkKAAAALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkECQoAAAAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkECQoAAAAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAkKAAAALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAkKAAAALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA=="><\/div>`
         $('.modal-header').append(html)
process.then(actionResolverss).catch(actionCatcherss);
SCRIPT;
    }
}

在列表工具按钮添加导入action

 $grid->tools(function (Grid\Tools $tools) {
            $tools->append(new ImportAction());
        });

界面渲染已完成

现在处理数据保存

添加数据保存对象

php artisan make:import WeipinHuiUser/ImportWeipinhuiUser --model=App\Models\WeipinhuiUser
<?php

namespace App\Imports\Member;

use App\Models\MemberModel;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithStartRow;

class ImportWeinpinhuiUser implements ToModel,WithStartRow
{
    /**
    * @param array $row
    *
    * @return \Illuminate\Database\Eloquent\Model|null
    */
    public function model(array $row)
    {
        // 0代表的是第一列 以此类推
        // $row 是每一行的数据
        return new WeipinhuiUser([
             'employee_no' => $row[0],
            'name' => $row[1]
        ]);
    }


    /**
     * 从第几行开始处理数据 就是不处理标题
     * @return int
     */
    public function startRow(): int
    {
        return 2;
    }
}

注意:对应的model添加对应属性为可批量处理

class WeipinhuiUser extends Model
{
    use HasFactory;

    protected $fillable = ['employee_no', 'name'];
}
Categories
php

php 7.4

安装php7.4

add-apt-repository ppa:ondrej/php
apt-get update

Categories
php

laravel 部署

以phph命令运行

php arisan serve 部署命令
php artisan serve --port 8090 指定端口
php artisan servce --host 127.0.0.1 指定主机
第二种以容器方式运行

把整个项目复制到nginx 或者 apache web 网站静态资源目录
静态资源入口指向 public/index.php
Categories
php

laravel-admin form

  protected function form()
    {

        $form = new Form(new InfusionTicket());

        $form->select('clinic_id', "诊所信息")->options(Clinic::all()->pluck('name', 'id'))->required();
        $form->date('date', __('Date'))->default(date('Y-m-d'))->required();
        $form->select('inject', __('Inject'))->options(['静脉输液'=>'静脉输液',"肌肉(皮下)注射"=>"肌肉(皮下)注射","抽血"=>"抽血"])->required();
        $form->number('ticket', __('Ticket'))->required();
        $form->select('time_index', __('时间'))->options(InfusionTicketController::$timeArray)->required();
        $form->hidden('time');

        $form->saving(function ($form){

            $timeIndex = $form->time_index;
            Log::debug('数据');
            Log::debug($timeIndex);
            $time = InfusionTicketController::$timeArray[$timeIndex];
            Log::debug($time);
            $form->time= $time;
        });
        //保存后回调
        $form->saved(function ($form){
            Log::debug("执行创建");
            $model = $form->model();
            $clinicId = $model->clinic_id;
            $inject = $model->inject=='静脉输液'?'jmsy':'default';
            $date = $model->date;
            $dateFormat = Carbon::parse($date)->format('Y-m-d');
            $timeIndex = $model->time_index;
            $ticket = $model->ticket;
            Redis::select(2);
            $key = 'infusion-tickets:'.$clinicId.':'.$inject.':'.$dateFormat.':'.$timeIndex;
            Log::info($key);
            Redis::set($key,$ticket);

        });

        return $form;
  

在form表单中设置time为不可见(一定要设置time 不然无法保存time信息)

上述代码在保存时 添加time信息

在保存后 把对应信息 存储在redis中

 protected function detail($id)
    {
        $show = new Show(InfusionTicket::findOrFail($id));
        $show->panel()->tools(function ( \Encore\Admin\Show\Tools $tools) {
            $tools->disableEdit();
        });

        $show->clinic('诊所信息', function ($clinic)  {
            $clinic->setResource('/admin/clinics/');
            $clinic->field('name',__('Name'));
        });
        $show->field('date', __('Date'));
        $show->field('inject', __('Inject'));
        $show->field('ticket', __('Ticket'));
        $show->field('time', __('Time'));

        return $show;
    }

详情页面 屏蔽“编辑”按钮

 public static function boot()
    {
        parent::boot(); // TODO: Change the autogenerated stub
        static:: deleted(function (InfusionTicket $model) {
            Log::debug("模型删除");
            $clinicId = $model->clinic_id;
            $inject = $model->inject=='静脉输液'?'jmsy':'default';
            $date = $model->date;
            $dateFormat = Carbon::parse($date)->format('Y-m-d');
            $timeIndex = $model->time_index;
            Redis::select(2);
            $key = 'infusion-tickets:'.$clinicId.':'.$inject.':'.$dateFormat.':'.$timeIndex;
            Log::info($key);
            Redis::del($key);
        });
    }

在数据模型中 增加删除回调 删除redis值

Categories
php

laravel http test 集成测试

http 测试 (在feature是集成测试 在unit是单元测试 想要跑起laravel的 要在feature里做测试)

例子

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function test_a_basic_request()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

mock 请求头

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function test_interacting_with_headers()
    {
        $response = $this->withHeaders([
            'X-Header' => 'Value',
        ])->post('/user', ['name' => 'Sally']);

        $response->assertStatus(201);
    }
}

cookie

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_interacting_with_cookies()
    {
        $response = $this->withCookie('color', 'blue')->get('/');

        $response = $this->withCookies([
            'color' => 'blue',
            'name' => 'Taylor',
        ])->get('/');
    }
}

session

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_interacting_with_the_session()
    {
        $response = $this->withSession(['banned' => false])->get('/');
    }
}

身份mocking

<?php

namespace Tests\Feature;

use App\Models\User;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_an_action_that_requires_authentication()
    {
        $user = User::factory()->create();

        $response = $this->actingAs($user)
                         ->withSession(['banned' => false])
                         ->get('/');
    }
}
$this->actingAs($user, 'api') //关键是这句

打印响应

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $response = $this->get('/');

        $response->dumpHeaders();

        $response->dumpSession();

        $response->dump();
    }
}

json api 测试

?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function test_making_an_api_request()
    {
        $response = $this->postJson('/api/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJson([
                'created' => true,
            ]);
    }
}
$this->assertTrue($response['created']);

判断json结果完全一样

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function test_asserting_an_exact_json_match()
    {
        $response = $this->json('POST', '/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertExactJson([
                'created' => true,
            ]);
    }
}

判断json嵌套对象

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function test_asserting_a_json_paths_value()
    {
        $response = $this->json('POST', '/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJsonPath('team.owner.name', 'Darian');
    }
}

文件上传mocking

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_avatars_can_be_uploaded()
    {
        Storage::fake('avatars');

        $file = UploadedFile::fake()->image('avatar.jpg');

        $response = $this->post('/avatar', [
            'avatar' => $file,
        ]);

        Storage::disk('avatars')->assertExists($file->hashName());
    }
}
//Storage::disk('avatars')->assertMissing('missing.jpg');
 //文件不存在

所有的response assert判断

assertCookie assertCookieExpired assertCookieNotExpired assertCookieMissing assertCreated assertDontSee assertDontSeeText assertExactJson assertForbidden assertHeader assertHeaderMissing assertJson assertJsonCount assertJsonFragment assertJsonMissing assertJsonMissingExact assertJsonMissingValidationErrors assertJsonPath assertJsonStructure assertJsonValidationErrors assertLocation assertNoContent assertNotFound assertOk assertPlainCookie assertRedirect assertSee assertSeeInOrder assertSeeText assertSeeTextInOrder assertSessionHas assertSessionHasInput assertSessionHasAll assertSessionHasErrors assertSessionHasErrorsIn assertSessionHasNoErrors assertSessionDoesntHaveErrors assertSessionMissing assertStatus assertSuccessful assertUnauthorized assertViewHas assertViewHasAll assertViewIs assertViewMissing