mac m1 下搭建 php 开发环境
发表于|更新于|码不能停
|总字数:506|阅读时长:2分钟|浏览量:
一番挣扎之后,还是下手了 m1,真香。

homebrew 安装
1 | /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" |
网慢的话,host 加映射。
1 | 199.232.68.133 raw.githubusercontent.com |
php 安装
看到好多人说,要把自带的 php 先卸载掉, 但是我折腾一圈发现,卸着太麻烦,不卸也没啥问题。
1 | brew install php@7.4 |
然后根据输出的信息添加环境变量:
1 | echo 'export PATH="/opt/homebrew/opt/php@7.4/bin:$PATH"' >> ~/.zshrc |
如果错过了这些信息,执行以下命令可以再次查看:
1 | brew info php@7.4 |
php-exc 安装
redis
安装扩展需要用到 pecl, 先查看有没有正确安装:
1 | pecl help version |
正常情况下,输出信息应该和 php 版本一致,即 php7.4。
1 | pecl install redis |
mongodb
1 | ln -s /opt/homebrew/Cellar/pcre2/10.36/include/pcre2.h /opt/homebrew/Cellar/php@7.4/7.4.16/include/php/ext/pcre/pcre2.h |
xdebug
xdebug3 配置有改动,具体如下:
1 | xdebug.mode = debug |
nginx 安装
1 | brew install nginx |
安装完成后,需要解析 php。
默认文件不动,添加一个新的配置文件:
vi /opt/homebrew/etc/nginx/services/laravel:
1 | server { |
然后重启或刷新 nginx:
1 | brew services reload nginx || brew services restart nginx |
重启以后,记得改下 hosts,把域名加到映射。
1 | vi /etc/hosts |
redis 安装
1 | brew install redis |
composer 安装
1 | php -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');" |
文章作者: m-finder
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 M-finder!
相关推荐

2019-03-15
使用 laravel mix 编译资源
学习下如何在 laravel 框架中,用 laravel mix 编译前端资源。 使用本次操作的环境依然是 laradock,如果没用特殊说明,以后应该默认 laradock。 workspace 容器中,已经提前装好了 node 环境,而在项目根目录中,package.json 和 webpack.mix.js 也已经为我们预设好了,所以laravel 项目建好后,直接在根目录安装即可: 1npm install 在 webpack.mix.js 中,已经加载了两个默认的文件: 12mix.js('resources/js/app.js', 'public/js') .sass('resources/sass/app.scss', 'public/css'); 我们只需要把自己的资源文件,按照同样的格式写入进去,然后开始运行,就可以生成编译后的资源了。 虽然示例中只写了 sass 一种样式文件,但是其实可以支持常见的以及不常见的很多中格式,只需要调用对应的接口即可。而且还可以把多个资源文件合并成一个。 举个🌰: 123456mix.less('resources/assets/less/app.less', 'public/stylesheets/styles.css');mix.styles([ 'public/css/vendor/normalize.css', 'public/css/vendor/videojs.css'], 'public/css/all.css'); 运行12npm run devnpm run watch 在上一篇中,我们修改视图后使之生效的命令,其实就是通知 mix 开始工作的。 单独使用在 laravel 框架之外也是可以使用 mix 的,具体教程请参考 [ learnku ] 其实不难,挺简单的。😎

2018-06-12
Laravel使用ftp传输文件时报错ftp_put() No data connection的解决
代码: 123456789101112131415161718192021222324252627282930313233<?php$file = "test.dat";$ftp_server="ftp.server.com";$ftp_user = "myname";$ftp_pass = "mypass";$destination_file = "test.dat";$cid=ftp_connect($ftp_server);if(!$cid) { exit("Could not connect to server: $ftp_server\n");}$login_result = ftp_login($cid, $ftp_user, $ftp_pass);if (!$login_result) { echo "FTP connection has failed!"; echo "Attempted to connect to $ftp_server for user $ftp_user"; exit;} else {echo "Connected to $ftp_server, for user $ftp_user";}$upload = ftp_put($cid, $destination_file, $file, FTP_BINARY);if (!$upload) { echo "Failed upload for $source_file to $ftp_server as $destination_file<br>"; echo "FTP upload has failed!";} else { echo "Uploaded $source_file to $ftp_server as $destination_file";}ftp_close($cid);?> 原因是没有定义ftp的主被动模式,true是被动模式: 1ftp_pasv($cid, true);

2018-03-11
CentOS 7.2 64位安装LNMP php7+Mysql 5.7搭建教程
前前后后搭建过无数次环境,大大小小的坑也差不多都踩了,今天趁着腾讯搞活动,120块买了台16个月的服务器,想着晚上把环境搭建下吧,结果把之前的文档掏出来发现是没更新的,最新版在公司…… 算了,老子再搞一遍,放在网上随便看。 更新组件123456789yum -y updateyum -y groupinstall 'Development Tools'yum install gcc-c++ patch readline readline-devel zlib zlib-devel bzip2 autoconf automake libtool bison iconv-devel libyaml-devel libffi-devel openssl-devel make yum -y install readline readline-devel ncurses-devel gdbm-devel glibc-devel tcl-devel openssl-devel curl-devel expat-devel db4-devel byacc sqlite-devel libyaml libyaml-devel libffi libffi-devel libxml2 libxml2-devel libxslt libxslt-devel libicu libicu-devel system-config-firewall-tui sudo wget crontabs logwatch logrotate perl-Time-HiRes git cmake libcom_err-devel.i686 libcom_err-devel.x86_64yum install libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel libcurl libcurl-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel gmp gmp-devel libmcrypt libmcrypt-devel readline readline-devel libxslt libxslt-devel libicu-devel openldap-devel 安装Pcre: 123456wget https://excellmedia.dl.sourceforge.net/project/pcre/pcre2/10.31/pcre2-10.31.tar.gztar -xf pcre2-10.31cd pcre2-10.31./configure make make install 安装Libmcrypt: 123456wget https://nchc.dl.sourceforge.net/project/mcrypt/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gztar -xf libmcrypt-2.5.8.tar.gzcd libmcrypt-2.5.8./configuremake make install 安装php嗯……最新版7.2开怼 123wget http://cn2.php.net/get/php-7.2.3.tar.gz/from/this/mirrortar -xf mirrorcd php-7.2.3 该编译了,好长… 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566./configure \--prefix=/usr/local/php \--with-config-file-path=/usr/local/php/etc \--enable-fpm \--with-fpm-user=nginx \--with-fpm-group=nginx \--enable-inline-optimization \--disable-debug \--disable-rpath \--enable-shared \--enable-soap \--with-libxml-dir \--with-xmlrpc \--with-openssl \--with-mcrypt \--with-mhash \--with-pcre-regex \--with-sqlite3 \--with-zlib \--enable-bcmath \--with-iconv \--with-bz2 \--enable-calendar \--with-curl \--with-cdb \--enable-dom \--enable-exif \--enable-fileinfo \--enable-filter \--with-pcre-dir \--enable-ftp \--with-gd \--with-openssl-dir \--with-jpeg-dir \--with-png-dir \--with-zlib-dir \--with-freetype-dir \--enable-gd-native-ttf \--enable-gd-jis-conv \--with-gettext \--with-gmp \--with-mhash \--enable-json \--enable-mbstring \--enable-mbregex \--enable-mbregex-backtrack \--with-libmbfl \--with-onig \--enable-pdo \--with-mysqli=mysqlnd \--with-pdo-mysql=mysqlnd \--with-zlib-dir \--with-pdo-sqlite \--with-readline \--enable-session \--enable-shmop \--enable-simplexml \--enable-sockets \--enable-sysvmsg \--enable-sysvsem \--enable-sysvshm \--enable-wddx \--with-libxml-dir \--with-xsl \--enable-zip \--enable-mysqlnd-compression-support \ 结束后make,make install,时间较长,耐心等待。 好了以后,复制一份ini文件备份,然后把php-fpm添加成service 123456cp php.ini-development /usr/local/php/etc/php.inicp sapi/fpm/init.d.php-fpm /etc/init.d/php-fpmcp /usr/local/php/etc/php-fpm.d/www.conf.default www.confcp /usr/local/php/etc/www.conf.default www.confchmod +x /etc/init.d/php-fpmservice php-fpm start 添加php进环境变量: 123vim /etc/profile PATH=$PATH:/usr/local/php/bin export PATH 使修改生效: 1source /etc/profile 查看路径和php版本: 12echo $PATHphp -v 安装nginx安装nginx yum源 1yum localinstall http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm 然后安装nginx: 1yum install nginx 然后,启动下试试? 1service nginx start 访问http://你的ip/ 如果成功安装会出来nginx默认的欢迎界面 没成功就检查下端口是否可以访问 ,虚拟机可以直接干掉防火墙 安装mysql下载文件: 1wget http://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpm 开始安装: 12rpm -ivh mysql57-community-release-el7-8.noarch.rpmyum install mysql-community-server 这个比较快,装好开始运行: 1systemctl start mysqld 加入开机自启: 1systemctl enable mysqld 查看默认密码: 12grep 'temporary password' /var/log/mysqld.log 登陆修改密码: 1234567891011121314151617181920212223242526272829303132[root@VM_34_176_centos ~]# mysql -u root -pEnter password: Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 2Server version: 5.7.21Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('Xx..XXXX');#密码不能太简单Query OK, 0 rows affected, 1 warning (0.00 sec)mysql> show databases;+--------------------+| Database |+--------------------+| information_schema || mysql || performance_schema || sys |+--------------------+4 rows in set (0.00 sec)mysql> exit;Bye 安装swoole12345pecl install swoole#添加到php.inicd /etcvi php.iniextension=swoole.so 注:安装好PHP后复制ini文件是因为编译时指定了 1--with-config-file-path=/usr/local/php/etc \ 如果不复制的话也一样能够运行php,但是就无法装扩展了,踩坑千百遍终有一疏,装完swoole发现无法加载,最后查到时这里的问题。 打完收工,一个不小心又搞到凌晨一点,感觉头上凉凉哒~

2017-12-22
代码备忘录
杂七杂八大乱炖。 计算两个日期的差值12345$datetime1 = new DateTime('2009-10-11'); //new DateTime = date_create , 在某些框架中用 new DateTime 需要加 '\' ;$datetime2 = new DateTime('2009-10-13'); $interval = $datetime1->diff($datetime2); echo $interval->format('%R%a days'); 新建文件夹1234$dir = iconv("UTF-8", "GBK", "C:/www/report/"); //linux下可不转码if (!file_exists($dir)){ mkdir ($dir,0755,true);} Ascii 码转换12345678910111213141516function stringFromColumnIndex($pColumnIndex = 0) { static $_indexCache = array(); if (!isset($_indexCache[$pColumnIndex])) { // Determine column string if ($pColumnIndex < 26) { $_indexCache[$pColumnIndex] = chr(65 + $pColumnIndex); } elseif ($pColumnIndex < 702) { $_indexCache[$pColumnIndex] = chr(64 + ($pColumnIndex / 26)) . chr(65 + $pColumnIndex % 26); } else { $_indexCache[$pColumnIndex] = chr(64 + (($pColumnIndex - 26) / 676)) . chr(65 + ((($pColumnIndex - 26) % 676) / 26)) . chr(65 + $pColumnIndex % 26); } } return $_indexCache[$pColumnIndex];}echo stringFromColumnIndex(37); 计算时间过去了多久 天\时\分\秒123456789101112131415161718192021function secsToStr($secs) { $r = ''; if ($secs >= 86400) { $days = floor($secs / 86400); $secs = $secs % 86400; $r = $days . ' 天'; } if ($secs >= 3600) { $hours = floor($secs / 3600); $secs = $secs % 3600; $r .= $hours . ' 小时'; } if ($secs >= 60) { $minutes = floor($secs / 60); $secs = $secs % 60; $r .= $minutes . ' 分钟'; } $r .= $secs . ' 秒'; return $r;}echo secsToStr(545517.1111111111); win10 配置 pthreads 多线程扩展pthreads 只支持 ts 版本的 php , 即线程安全版 phpstudy 自定义版本时总在报错 , 所以换了 xmapp pthreads 下载地址 : 没错,点我 要对应版本号和位数 位数以 php 的为准 下载解压后 , 把 php_pthreads.dll 和 pthreadVC2.dll 复制到 php\ext 下边 php.ini 最后边新增 : extension=php_pthreads.dll 然后再复制一个 pthreadVC2.dll 到系统目录 系统为 32 位的就复制进 C:\Windows\System32 64 位的就复制进 C:\Windows\SysWOW64 重启 xmapp , 新建一个 test.php 12345678910111213141516class AsyncOperation extends \Thread { public function __construct($arg){ $this->arg = $arg; } public function run(){ if($this->arg){ printf("Hello %s\n", $this->arg); } }}$thread = new AsyncOperation("World");if($thread->start()){ $thread->join();} 通过 url 访问 test 文件 , 出现 Hello World 就成功了 或者查看 phpinfo 里边有没有 pthreads 扩展 Js调用系统桌面通知:12345678if (window.Notification && Notification.permission !== "denied") { Notification.requestPermission(function (status) { var n = new Notification("sir, you got a message", { icon: '/img/logo.png', body: 'you will have a meeting 5 minutes later.' }); });} js声音提醒123audioElementHovertree = document.createElement('audio'); audioElementHovertree.setAttribute('src', 'http://w.qq.com/audio/classic.mp3'); audioElementHovertree.setAttribute('autoplay', 'autoplay'); 数据分配:12345678910111213141516$max_worker_num = 30;$data_num = 74;if ($data_num == 0) { return;}for ($i = 0; $i < $data_num; $i++) { $data[$i] = $i;}$min_worker_num = min($data_num, $max_worker_num);$data_arr = array_chunk($data, ceil($data_num / $min_worker_num), true);$worker_num = ceil($data_num / ceil($data_num / $min_worker_num));

2017-12-22
laravel 使用笔记
先从最简单的开始: 安装laravel 的安装需要借助 composer ,百度一下,安装,然后去 GitHub 下载 laravel 切换到项目文件夹 ,在不选中任何文件的前提下按住 shift + 鼠标右键,打开 Powershell 或者 cmd 输入:composer install 将 .env.example 另存为 .env 修改数据库配置信息和邮件系统配置信息 然后在命令行输入:php artisan key:generate 生成密钥 然后配置一个本地域名指向 public 文件夹,然后,就好了。[哈哈] 邮件邮件系统配置示例: 12345678MAIL_DRIVER=smtpMAIL_HOST=smtp.mxhichina.comMAIL_PORT=25//465MAIL_USERNAME=m@m-finderMAIL_PASSWORD=邮箱密码MAIL_FROM_NAME=M-finderMAIL_FROM_ADDRESS=m@m-finderMAIL_ENCRYPTION=null//ssl #如果用465端口的话,需要参数 MAIL_ENCRYPTION=ssl 邮件有 3 种模式(可能更多,暂时只接触到3种):一种用 Mail::send 方法一种用 Mail::raw另外一种则是官方文档中的,新建一个类,然后发送邮件时实例化这个类。3 种方法实现的功能一样。Mail::send 1234Mail::send('admin.email', ['orderPrice' => 'laravel'], function ($message) { $message->to('m@m-finder'); $message->subject('我是自定义标题');}); 这个方法第一个参数为视图文件,视图文件的用法等同于普通视图,第二个参数为视图中所用到的数据Mail::raw 1234567$content = '这是一封来自Laravel的测试邮件.';$toMail = 'm@m-finder'; Mail::raw($content, function ($message) use ($toMail) { $message->subject('[ 测试 ] 测试邮件SendMail - ' . date('Y-m-d H:i:s')); $message->to($toMail);}); 基本等同于Mail::send 第三种方法 12345678910php artisan make:controller MailControllerphp artisan make:mail OrderShipped在 OrderShipped 增加内容return $this->view('admin.email')->with([ 'orderName' => 'test', 'orderPrice' => 1500, ]); 然后在要发送邮件的方法中调用: 1Mail::to('m@m-finder')->send(new OrderShipped()); 开放路由,访问下就可以了。 如果需要自定义邮件标题,可以试一下以下方法(未测试): 在你的类中定义一个subject变量: 1public $subject = '这里是邮件自定义标题'; 或者在你的view后跟一个subject方法: 1view('emails.activate-user')->subject('这里定义邮件标题'); 多视图共享数据在 app\Providers 文件夹下 boot() 方法中写入要共享的数据即可 , 例如 : 123456789101112public function boot() { $links = Link::orderBy('id', 'desc')->get(); $web_info = SysConfig::first(); $menus = Menu::select('id', 'name', 'type', 'seo_title', 'seo_describe', 'link') ->where('pid', '=', 0) ->where('is_show', '=', '2') ->get(); view()->share('links', $links); view()->share('web_info', $web_info); view()->share('menus', $menus);} 这样写完以后,你会发现你的 migrate 挂了,哈哈 ,解决办法是使用闭包,即 composer 方法: 12345678910111213public function boot() { Schema::defaultStringLength(191); //解决数据库版本过低无法执行 migrate view()->composer(['layouts.home', 'layouts.userhome','layouts.admin'], function($view) { $links = Link::orderBy('id', 'desc')->get(); $web_info = SysConfig::first(); $menus = Menu::select('id', 'name', 'type', 'seo_title', 'seo_describe', 'link') ->where('pid', '=', 0) ->where('is_show', '=', '2') ->get(); $view->with(['links' => $links, 'web_info' => $web_info, 'menus' => $menus]); }); } 文件上传config 文件夹下有一个 filesystems.php,里边是默认的上传地址,可以根据自己的需要做修改或者添加 上传的控制器代码: 12345678910111213141516if ($file->isValid()) { if ($file->getClientSize() > 2097152) { return $this->json_response(1, "请上传小于 2 mb 的图片", 0); } $ext = $file->getClientOriginalExtension(); $realPath = $file->getRealPath(); $type = $file->getClientMimeType(); $filename = date('Y-m-d-H-i-s') . '-' . uniqid() . '.' . $ext; $bool = Storage::disk($path)->put($filename, file_get_contents($realPath)); $url = Storage::disk($path)->url($filename); if ($filename) { return ['code' => 0, 'msg' => '', 'src' => $url, 'data'=>['src'=>$url,'title'=>$filename]]; //{"code": 0 ,"msg": "" ,"data": {"src": "图片路径","title": "图片名称"} layui 图片上传接口 }} 访问上传到本地的文件资源,需要先创建一个软连接:php artisan storage:link 一个页面中,如果有 ajax 调取数据的,可以把 ajax 使用的路由和页面的路由名称定义为同一个,不同的是页面是 get ,ajax 是 post 自定义404页面在 app\Exceptions文件夹下的Hander中有个render方法,改造一下: 123456789101112public function render($request, Exception $exception){ if ($exception instanceof ModelNotFoundException) { $exception = new NotFoundHttpException($exception->getMessage(), $exception); } if ( ! config('app.debug')) { return response()->view('errors.500', [], 500); } return parent::render($request, $exception);} 然后在views文件夹新建error文件夹和对应错误代码的blade文件。

2024-02-28
Laravel Octane 和 Swoole 协程的使用分析
之前在工作中使用 Laravel Octane 的 concurrently 处理并发时,发现在队列和定时任务中不会触发并发效果。经过分析,作了如下猜测:队列定时任务都属于一个独立的进程,与 Octane 服务无关,而 Octane conturrently 恰恰需要在 Octane 环境下才能运行。 后来通过代码进行环境检测和查看 php 的进程,证明猜想成立。 1234info('check env', [ 'served by octane' => isset($_SERVER['LARAVEL_OCTANE']) && ((int)$_SERVER['LARAVEL_OCTANE'] === 1), 'on swoole server' => (extension_loaded('swoole') || extension_loaded('openswoole')) && app()->bound(Server::class)]); 为了能够在任意代码中实现并发,我们研究参考了 Hyperf 框架关于协程的代码,然后抽取了如下两个类: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071<?phpnamespace App\Services;use App\Exceptions\ParallelExecutionException;use Laravel\Octane\Facades\Octane;use Throwable;use Swoole\Coroutine as Co;class Parallel{ protected array $callbacks = []; protected array $results = []; /** * @var Throwable[] */ protected array $throwables = []; public function add(callable $callable, $key = null): void { if (is_null($key)) { $this->callbacks[] = $callable; } else { $this->callbacks[$key] = $callable; } } public function wait(bool $throw = true): array { if (isset($_SERVER['LARAVEL_OCTANE']) && ((int)$_SERVER['LARAVEL_OCTANE'] === 1)) { return Octane::concurrently($this->callbacks, 300000); } app('log')->useLoggingLoopDetection(false); Co\run(function () { foreach ($this->callbacks as $key => $callback) { Co::create(function () use ($callback, $key) { try { $this->results[$key] = $callback(); } catch (Throwable $throwable) { $this->throwables[$key] = $throwable; unset($this->results[$key]); } }); } }); if ($throw && ($throwableCount = count($this->throwables)) > 0) { $message = 'Detecting ' . $throwableCount . ' throwable occurred during parallel execution:' . PHP_EOL . $this->formatThrowAbles($this->throwables); $executionException = new ParallelExecutionException($message); $executionException->setResults($this->results); $executionException->setThrowAbles($this->throwables); unset($this->results, $this->throwables); throw $executionException; } app('log')->useLoggingLoopDetection(true); return $this->results; } private function formatThrowAbles(array $throwables): string { $output = ''; foreach ($throwables as $key => $value) { $output .= sprintf('(%s) %s: %s' . PHP_EOL . '%s' . PHP_EOL, $key, get_class($value), $value->getMessage(), $value->getTraceAsString()); } return $output; }} 1234567891011121314151617181920212223242526272829303132<?phpnamespace App\Exceptions;use RuntimeException;class ParallelExecutionException extends RuntimeException{ protected array $results = []; protected array $throwables = []; public function getResults(): array { return $this->results; } public function setResults(array $results): void { $this->results = $results; } public function getThrowAbles(): array { return $this->throwables; } public function setThrowAbles(array $throwables): array { return $this->throwables = $throwables; }} 其中,第一个类的作用是检测系统是否运行在 Octane 环境下,是则调用Octane concurrently,否则就执行 Swoole 协程代码,使用起来也比较简单: 1234567$parallel = new Parallel();$parallel->add(fn() => $this->analysisStructure(), 'structure');$parallel->add(fn() => $this->analysisHabit(), 'habit');[ 'structure' => $structure, 'habit' => $habit,] = $parallel->wait(); 之所以没有完全使用 Swoole 协程,是因为相比之下,Octane 代码更加优雅,我们在期待着某天更新后,Octane concurrently 也能直接在队列中运行使用。 第二个类的作用比较简单,就是对协程中异常的一个定义。 另外在分析过程中,我们也发现了一个比较有意思的事情: 如图所示,当我在路由中运行检测代码时,Octane 和 Swoole server 都为 true;在控制器中运行检测代码时,又只有 Octane 为true;为什么会有这样的区分?我个人猜测是 Octane 在将框架代码读进内存时,特意跳过了控制器中的代码,以避免数据更新不及时等问题。 有知道具体原因的小伙伴,欢迎留言探讨。