分类目录归档:未分类

Iptables – 应用实例 – 修改出口数据包的默认IP

服务器有多IP,如果服务器发起请求,外网看到的来源IP总是服务器的第一个(或默认)IP。

有些工具,我们希望它出去的数据包的源IP不是默认IP,期望是指定IP。 这里涉及到了修改数据包的源IP,需要依靠Iptables来完成。

这里的问题是,某工具发起的数据包,怎么标记它? 最终要的是,某工具发起的数据包,把源地址修改为指定地址。Iptables中并没有办法识别某某工具发起的数据包,但是可以识别是某用户发起的数据包,所以可以指定某个用户运行某个工具,只要是这个用户发起的数据包,就标记一下,然后在路由后把源地址修改掉。

# 添加用户
# 接收6666端口进来的数据(这个步骤不是必须的)
iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 6666 -j ACCEPT
# 用户6666发出的数据,修改数据包,添加6666标记
iptables -t mangle -A OUTPUT -m owner --uid-owner 6666 -j MARK --set-mark 6666
# 对于标记了6666的数据,源地址修改为x.x.x.x
iptables -t nat -A POSTROUTING -m mark --mark 6666 -j SNAT --to-source x.x.x.x

看起来,后面两天语句可以写成一条:(把用户6666的数据源地址修改为x.x.x.x)

iptables -t nat -A POSTROUTING -m owner --uid-owner 6666 -j SNAT --to-source x.x.x.x

第三方账户登录 – Facebook

注册账户:

这里使用邮箱注册,所以需要登录邮箱激活:

打开开发者网站:
https://developers.facebook.com

如果没有登录,右上角出现”Log In”,点击登录,输入账户和密码,重新回到开发者页面。点击右上角的”Get Started”,开始注册成为开发者。

接下来验证账户,建立第一个APP:

填写“Site URL”,保存后,接下来是一系列的关于如何使用JS来控制登录的说明(如果不是通过JS来控制登录可忽略)。

然后完成:

然后开始往APP里面添加产品:

选择Facebook Login。然后选择类型:

然后回到左边上角导航处,选择”Settings -> Basic“:

保存相关信息后,把APP ID和APP Secret拷贝下来。

APP ID: xxxxx
App Secret: xxxxxxxxxxxxxx

最后还有重要的一个步骤,需要把APP开发模式关闭。因为默认是关闭的。否则将出现:

App Review,Do you want to make this app and all its live features available to the general public? Yes

在使用第三方账户登录时,授权提示:

Magento 2.x 安装

Magento 2.x的安装,官方提供了三个方式:
1 直接下载完整的压缩包
2 通过composer安装,并制定使用官方提供的仓库(需要配置)
3 克隆Github上的仓库,然后通过composer安装

第一和第三种方式,本质上没有多大差别。第二种方式中需要指定一个仓库,这个主要Magento本身的扩展市场生态,如果要使用到官方提供的扩展,第二种方式是唯一选择。

第三种安装方式:(https://github.com/magento/magento2)

#Git 克隆
yum install git
git clone https://github.com/magento/magento2.git
cd magento2
git checkout 2.2.3

composer install

#直接下载压缩包(先选中版本)
cd magento2

composer install

Nginx配置:

upstream fastcgi_backend {
   server  127.0.0.1:9001;
}
server {
   listen 80;
   server_name magento2.dev;
   set $MAGE_ROOT /var/www/magento/magento2;
   include /var/www/magento/magento2/nginx.conf.sample;
}

Bash Shell 变量

在Bash当中,当一个变量名称未被设置时,默认的内容是空的。

变量的设置规则:
1) 变量与变量内容以一个等号连接,等号两边不能接空格,注意,如果有空格则是逻辑比较
2) 变量名称只能是英文字母和数字,但开头不能是数字
3) 变量内容如含有空格,需要使用单引号或双引号括起来(如没有引号括起来,但是包含了转义,最终会转义输出),但双引号内特殊字符保持本性,单引号内的任何字符都是一般文本
4) 可以使用转义符将特殊字符变成一般字符
5) 如需要引用其它命令的结果,可以使用反单引号或$()语法,比如`uname –r`和$(uname -r)等同
6) 如要为变量增加内容,可以使用$变量名或${变量名}累加内容(不需要所谓的连接符)
7) 如变量需要在其它子进程中执行,需要以export来使变量变成环境变量:export PATH。
8) 通常大写字符为系统默认变量
9) 取消变量的方法是使用unset 变量名称

环境变量与自定义变量
在一个Shell中启用一个子Shell,子Shell中会继承父Shell的环境变量,但是自定义变量不会被继承(除非自定义的变量声明为环境变量)。
查看当前Shell中的环境变量,可以使用env命令。
查看当前Shell中的所有变量(包括环境变量),可以使用set命令。
命令set可以查看已经存在的Shell变量,以及设置Shell变量的新变量值(可改环境变量值),但是不能定义新的Shell变量。
命令declare用来定义自定义变量,如果需要把自定义变量变成环境变量,需要使用export变量名;如果需要直接声明一个环境变量,可以使用declare +x格式。
如果需要直接设置一个环境变量,可以使用export var=value格式。

自定义变量是指用户自己定义的变量,定义时可以声明它为环境变量(+x),也可以之后重新以declare +x格式变换成环境变量,或以export方式将其变为环境变量。
自定义变量不会因为它变成了环境变量而脱离它是自定义变量的事实。
变量变成环境变量后,可以set一个新值,可以unset,但是无法去掉环境变量标签(即:总是环境变量,也总是自定义变量)。

#查看环境变量
env

#查看环境变量和自定义变量
set

#查看自定义变量(不管定义是不是环境变量)
#export var=value,也是自定义变量
declare

#修改变量值(var必须先存着)
set var=value

#定义一个环境变量
export var=value

#定义一个自定义变量
declare var=value

#把自定义变量转换为环境变量
export var

#定义一个环境变量(或把变量变成环境变量)
declare -x var

#取消变量
unset var

当export一个变量,实际应该调用的是declare +x,所以export默认的输出是自定义变量变成环境变量的列表:

export
declare -x USER="root"
declare -x XDG_RUNTIME_DIR="/run/user/0"
declare -x XDG_SESSION_ID="2237"
declare -x var="value"

总之,用户自定义的变量,永远是自定义的,这个自定义的变量可以变换为环境变量(但是还是自定义的)。env查看环境变量,declare查看自定义变量(包括自定义变成环境变量的变量),set查看所有变量(环境变量和自定义变量的并集),export可以查看有哪些自定义变量升级为环境变量。 可以使用declare定义变量(+x声明为环境变量),export可以把变量转换为环境变量(实际是declare +x的使用),set对存在的变量设置新的值。一般使用declare定义一个自定义变量(或定义时指定值),export定义一个环境变量(或定义时指定值,export是declare的一种用法),用set来改变变量值。

如果一个变量同时出现在env和declare的输出结果中,说明这个变量是从自定义变量升级为环境变量的。如果只出现在env结果中,说明不是由自定义变量升级而来。

如下图:

特别用法:

#若指令返回值不等于0,则立即退出Shell
set -e

Paypal API

Paypal提供了两套API,REST API和NVP/SOAP API。REST API没有完全覆盖NVP API的功能,NVP API历史久远,未来应该会被REST API替换。

REST API使用OAuth2标准,首先需要有一个开发者账户,然后在开发者账户中创建APP(产生client_id, client_secret),Paypal账户授权给这个APP。

开发者网站:http://developer.paypal.com,然后需要注册一个真实的Paypal账户(可以个人,也可以商用),拿这个账户作为开发者账户去登录,然后就可以创建APP,分两个环境:正式和测试。如何创建一个测试APP,那么会自动给你创建一个测试商家账户,还有一个买家账户。当然也可以自己建多个测试账户。

——————————————————————-
在REST API之前,只有NVP API。为了可以使用NVP API,需要到自己的Paypal中的API设置中取到相关API签名(API用户名和密码,以及签名),只要暴露了这三个信息,就相当于是开放了自己的账户。

每个Paypal可以作为一个第三方,其它的Paypal账户如果把某些权限赋给了它,它就可以扮演第一方的身份去获取信息。关于权限赋予的流程,官方文档有详细描述。而Paypal后台的第三方许可,实际对应这个操作,最终都是赋予第三方权限。

这里需要输入的就是第三方Paypal账户的API的用户名(每个账户中的API签名部分),点击查找后会让你选择哪些权限赋予这个第三方:

结论:Paypal可以通过API签名开放账户,也可以把权限授予其它账户,由其它账户(第三方)代理访问。

<?php

namespace Paypal;

class Api
{
    protected $username = '';

    protected $password = '';

    protected $signature = '';

    protected $version = '95.0';

    protected $endPoint = 'https://api-3t.paypal.com/nvp';

    protected $subject = '';

    public function __construct($username, $password, $signature, $subject = '', $sandbox = false)
    {
        $this->username = $username;
        $this->password = $password;
        $this->signature = $signature;
        if (!empty($subject)) {
            $this->subject = trim($subject);
        }
        if ($sandbox) {
            $this->endPoint = 'https://api-3t.sandbox.paypal.com/nvp';
        }
    }

    // 作为第三方访问Paypal
    public function setSubject($email)
    {
        $this->subject = trim($email);
    }

    // 取回交易列表
    //$params = [
    //    'STARTDATE' => $startTime,
    //    'ENDDATE' => $endTime,
    //    'RECEIVER' => '',
    //    'TRANSACTIONCLASS' => 'All'
    //];
    public function getTransactions(array $params)
    {
        $return = ['success' => 0, 'message' => '', 'data' => []];
        if (empty($params['STARTDATE']) || empty($params['ENDDATE'])) {
            $return['message'] = '参数不合法';
            return $return;
        }

        $result = $this->post('TransactionSearch', $params);
        if (false === $result) {
            $return['message'] = 'CURL请求异常';
            return $return;
        }

        $tarr = explode('&', $result);
        $data = [];
        foreach ($tarr as $item) {
            $tmp = explode('=', rawurldecode($item));

            preg_match('/^L_([a-zA-Z\_]+)([0-9]+)/', $tmp[0], $m);
            if (isset($m[0]) && isset($m[1]) && isset($m[2])) {
                $data[$m[1]][$m[2]] = trim($tmp[1]);
            } else {
                $data[$tmp[0]] = trim($tmp[1]);
            }
        }

        // ACK 等于 Warming时,数据返回不齐全(Paypal每次查询最多返回100条)
        if (empty($data['ACK']) || ($data['ACK'] == 'Failure')) {
            $return['message'] = "API调用ACK返回Failure";
        } else {
            $return['success'] = 1;
            $return['data'] = $data;
        }

        return $return;
    }
 
    // 通用封装
    protected function post($api, array $params)
    {
        $global = [
            'METHOD' => $api,
            'VERSION' => $this->version,
            'USER' => $this->username,
            'PWD' => $this->password,
            'SIGNATURE' => $this->signature
        ];
        if (!empty($this->subject)) {
            $global['SUBJECT'] = $this->subject;
        }

        return $this->doRequest($this->endPoint, array_merge($global, $params));
    }

    // CURL请求
    protected function doRequest($url, $data)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_HEADER, '');
        curl_setopt($ch, CURLOPT_URL, trim($url));
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 90);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
        if (!empty($data)) {
            if (is_array($data)) {
                curl_setopt($ch, CURLOPT_POST, true);
                curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
            } elseif (is_string($data)) {
                curl_setopt($ch, CURLOPT_POST, true);
                curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
            }
        }
        if (\PHP_OS === 'WINNT') {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0");
        $result = curl_exec($ch);
        $error = curl_errno($ch);
        curl_close($ch);
        if ((int)$error == 0) {
            return $result;
        }
        return false;
    }
}

浏览器编程:WebDriver – ChromeDriver

浏览器编程有两个主要应用:
1 自动化测试
2 通过浏览器自动抓取内容(针对防抓的网站,模拟人工点击)

Selenium Server只是作为一个代理,它的作用是当要驱动远程浏览器(驱动一般只能监听本地端口),或需要驱动不同版本的浏览器时会有很大的作用。否则,应用程序直接面对具体的驱动即可(Selenium Server仅转发Json)。
注意:PhantomJS视乎是没有提供驱动,为了驱动这个无头浏览器,Selenium Server应该是把Json数据转换成了JS脚本让其执行(未证实)

目前主流浏览器(Chrome Firefox)都提供了WebDriver的实现,比如Chrome对应的是ChromeDriver,Firefox对应的FirefoxDriver。注:WebDriver只是一个规范标准(https://w3c.github.io/webdriver/webdriver-spec.html),而实现的方式可以不同,而https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol这里描述的就是一种实现方式,任何实现了这个规范的驱动,都具有相同的API。

WebDriver is an open source tool for automated testing of webapps across many browsers. It provides capabilities for navigating to web pages, user input, JavaScript execution, and more. ChromeDriver is a standalone server which implements WebDriver’s wire protocol for Chromium. ChromeDriver is available for Chrome on Android and Chrome on Desktop (Mac, Linux, Windows and ChromeOS).
ChromeDriver是一个实现了WebDriver无线协议的独立服务器,所以需要下载这个服务器(驱动,启动后在本地监听9515端口,所有的操作发送到9515端口,这个驱动负责解析数据并操作浏览器,所以它是一个中间件)。

下载地址:https://sites.google.com/a/chromium.org/chromedriver/downloads(有三个平台,需要注意的是不同的版本对应的Chrome版本是不同的)。

关于使用,ChromeDriver提供了一个文档:
https://sites.google.com/a/chromium.org/chromedriver/getting-started(关键点:Chrome需要安装在默认位置(否则需要指定),下载正确的ChromeDriver版本)

ChromeDriver作为一个独立的服务,可以手动启动并监控,也可以在使用SDK中提供的方法启动。

#####
@RunWith(BlockJUnit4ClassRunner.class)
public class ChromeTest extends TestCase {

  private static ChromeDriverService service;
  private WebDriver driver;

  @BeforeClass
  public static void createAndStartService() {
    service = new ChromeDriverService.Builder()
        .usingDriverExecutable(new File("path/to/my/chromedriver"))
        .usingAnyFreePort()
        .build();
    service.start();
  }

  @AfterClass
  public static void createAndStopService() {
    service.stop();
  }

  @Before
  public void createDriver() {
    driver = new RemoteWebDriver(service.getUrl(),
        DesiredCapabilities.chrome());
  }

  @After
  public void quitDriver() {
    driver.quit();
  }

  @Test
  public void testGoogleSearch() {
    driver.get("http://www.google.com");
    // rest of the test...
  }
}

####独立启动
$ ./chromedriver
Started ChromeDriver
port=9515
version=14.0.836.0

WebDriver driver = new RemoteWebDriver("http://127.0.0.1:9515", DesiredCapabilities.chrome());
driver.get("http://www.google.com");

####PHP
$service = new \Facebook\WebDriver\Chrome\ChromeDriverService(‘path/to/my/chromedriver’, 9515);
$service->start();
$service->stop();

$driver = RemoteWebDriver::create( $service->getURL(),[
                ChromeOptions::CAPABILITY => $options,
                WebDriverCapabilityType::PROXY => [
                    'proxyType'=> 'manual',
                    'httpProxy' => 'SOCKS5://127.0.0.1:1086',
                    'sslProxy' => 'SOCKS5://127.0.0.1:1086',
                    'socksProxy' => 'SOCKS5://127.0.0.1:1086'
                ]]);

ChromeDriver实际释放的是一套RESTfull API,可以参考:https://chromium.googlesource.com/chromium/src/+/master/docs/chromedriver_status.md,所以只要按照规范发送数据即可,也可以使用SDK,比如PHP,Facebook提供了一套实现:

composer require facebook/webdriver

应用实例:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Laravel\Dusk\Chrome\ChromeProcess;
use Laravel\Dusk\Browser;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\WebDriverCapabilityType;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;

class Test extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'test';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //$process = (new ChromeProcess())->toProcess();
        //$process->start();

        try {
            // 取回已经打开的SessionID
            $reuseSessionId = '51492cf202343defea198867e32a81e3';
            try {
                $driver = $this->driverBy($reuseSessionId);
            } catch (\Exception $e) {
                $driver = $this->driver();
            }

            $driver->execute(DriverCommand::CLICK);
        } catch (\Exception $e) {
            echo 'Browser can not start up: ' . $e->getMessage();
            return;
        }

        // 取回所有SESSION
        print_r($driver->getAllSessions('http://localhost:9515'));

        $sessionID = $driver->getSessionID();
        echo "\n";
        echo $sessionID;
        echo "\n";

        $browser = new Browser($driver);
        $browser->visit('https://www.baidu.com/')->type("#kw", 'ip')->press("#su");
        $browser->visit('https://www.amazon.com');

        $driver->close();
        $driver->get('http://blog.ifeeline.com');
    }

    protected function driverBy($sessionId)
    {
        $driver = RemoteWebDriver::createBySessionID($sessionId, 'http://localhost:9515');
        $driver->execute(DriverCommand::CLICK);

        return $driver;
    }

    protected function driver()
    {
        $options = (new ChromeOptions)->addArguments([
            //'--disable-gpu',
            //'--headless'
        ]);

        return RemoteWebDriver::create(
            'http://localhost:9515',
            [
                ChromeOptions::CAPABILITY => $options,
                WebDriverCapabilityType::PROXY => [
                    'proxyType'=> 'manual',
                    'httpProxy' => 'SOCKS5://127.0.0.1:1086',
                    'sslProxy' => 'SOCKS5://127.0.0.1:1086',
                    'socksProxy' => 'SOCKS5://127.0.0.1:1086'
                ]
            ]
        );

        /*
        return RemoteWebDriver::create(
            'http://localhost:9515',
            DesiredCapabilities::chrome()->setCapability(
                ChromeOptions::CAPABILITY, $options
            )->setCapability(
                WebDriverCapabilityType::PROXY,
                [
                    'proxyType'=> 'manual',
                    'httpProxy' => 'SOCKS5://127.0.0.1:1086',
                    'sslProxy' => 'SOCKS5://127.0.0.1:1086',
                    'socksProxy' => 'SOCKS5://127.0.0.1:1086'
                ]
            )
        );
        */
    }
}

在控制浏览器上,可以应用了–disable-gpu和–headless,这样就是一个无头浏览器了(不显示具体的过程)。另外,在创建的浏览器时,可以指定代理。另外,如果一个SESSION没有正确退出,那么它还是活动的,但是它却无法重用。在知道SESSIONID的情况下,SESSION可以重用。 一般来说,进行一个任务就开启一个浏览器,完毕后正常退出记录。

Laravel中的Dusk程序包,封装的更加狠一些,连ChromeDriver二进制程序包都拉取回来,自动启动监听,对个Facebook SDK进行二次封装,使API更加友好。

如果模拟人工进行大量操作,就会频繁启动关闭浏览器,实际上,浏览器启动后对应一个SESSION,接下来只要重用这个SESSION即可,基本思路:如果当前有可重用的SESSION,就重用,没有就新建;在任务执行完后,判断SESSION是否超过了最大值,超过则关闭,否则,标记该SESSION可重用(配合定时重启脚本,防止意外)。

Mac 定义自己的系统

苹果Mac系列产品:
MacBook Air 轻薄本,主流11和13英寸
MacBook Pro 笔记本,主流13,15英寸
Mac mini 一个盒子(可看做是一个主机,普通主机或服务器)
iMac 一体机
Mac Pro 台式机

OS X扫盲
OS X是一个基于Unix Darwin内核构建的系统。

每个用户对应/Users目录下的一个以用户名称命名目录(一般是如此)。所有与用户相关的内容都在这个目录内。

Finder是一个资源管理器。
Dock是一个工具栏,是一个资源访问的快捷方式。

OS X中,磁盘映像文件后缀名是dmg,双击dmg文件可以直接打开,然后在Finder左边的设备总可以找到挂接好的磁盘映像。

OS X中,安装一个软件实际就是打开dmg文件并启用的应用拖入/Application,卸载就是从其中删除。

OS X 下的大部分软件具备状态保持的功能(记住上次打开的状态)

快捷键
Mac的键盘跟Windows键盘有一些不一样。有些键没有,比如Home和End键和PageUP和PageDown和Backspace,另外ALT键也叫Option键,Windows中的Wind键对应Command键。

具体的快捷键可以参考:https://support.apple.com/zh-cn/HT201236

最常用的复制粘贴等组合键,在Mac中用Command对应Control键:Control + C对应Command + C。

为了高效工作,以下键必须记一记:

Command + Tab		向前循环切换应用程序
Shift + Command + Tab	向后循环切换应用程序
Command + Delete	把选中的资源移到废纸篓
Shift + Command + Delete清空废纸篓(清空回收站,提示)
Command + ~		同一个应用多窗口切换(开了多窗口是有用)
Command + C/V		复制粘贴
Command + Option + V	移动(先复制,后粘贴,类似剪贴)
Command + N		新建应用程序窗口
Command + Q		退出应用程序(一个应用可以开多窗口,窗口的叉表示关闭窗口,而应用未关闭)
Command + +/-		放大缩小字体
Control + Space		输入法切换
Command + Space		调出Spotlight


FN + 左键 		HOME
FN + 右键     		END
FN + 上键 		PageUP
FN + 下键 		PageDown
FN + DELETE 		后删除(CTR + D)

比较遗憾的是,最小化所有窗口没有找到对应的快捷键(Option + Command + H再结合Command + M)。

鼠标与触摸板
系统偏好设置 – 鼠标

其中滚动方式:自然,默认是勾上的,这个和Window下的默认设置刚好相反,所以如果不习惯需要去掉勾选。

系统偏好设置 – 触控板

1 光标与点按
一般都勾上,单个手指轻按模拟鼠标左键点击,两个手指轻安模拟鼠标右击,三个手指轻击进行文本查询(比如翻译)
2 滚动与缩放
两个手指触摸移动模拟鼠标滚动,不勾上时,方向和Window一样。
3 更多手势
三个手指向上滑动(Mission Control),四个手指抓(LaunchPad),四个手指快速展开(显示桌面)

另外,如果需要使用触控板来实现拖动窗口,就复杂一些:
系统偏好设置 – 辅助功能 – 鼠标与触控板 – 触控板选项

启动三指拖动。

Dock
Dock就是一个停靠条。默认放在最下方。可以通过简单拖放的方式,把应用放到Dock中,以一目了然地知道启动了那些应用以及快速管理常用的应用。

注意:连按窗口标题栏以 这里默认是最小化,可以宣威缩放,这样就可以实现最大化。

AppStore
这个就是安装App的入口,里面有非常多的软件,一般建议注册个apple id,然后通过AppStore来进行安装应用,AppStore是无法包含所有应用。可以直接下载第三方应用镜像包来进行安装。一般是dmg镜像,双击自动进行挂载斌打开镜像包,然后把应用直接拖入Application文件夹就完成安装(当然也可以来一个安装步骤导向)。


工具:

1 搜索
Spotlight 是OS X 自带的搜索工具(Command + Space)。

2.Launcher(应用程序 – Launcher,有对应手势:抓动作)
Launcher(启动器)的主要作用之一就是快速定位并启动应用程序。

开发工具:
SecureCRT(收费)
Chrome + SwitchyOmega
FireFox
SourceTree
PhpStorm(收费)
Navicat(收费)
TeamView
VMware Fusion(收费)
VirtualBox
FileZilla

Microsoft Office (收费)
钉钉
QQ

Avast Security(家庭免费版)
ShadowsocksX-NG
PDF Reader

Mac 文件共享与远程虚拟机映射

在Mac中设置共享文件夹:

系统偏好设置 – 共享 – 文件共享:选择要共享的文件夹(不能自定义名字),选择用户和用户权限,然后点击“选项”:

如果使用SMB来共享,需要按照”Windows文件共享”说明来设置(大体就是设置一个跟系统不一样的密码,这个密码专门让Window来存储)。

在远程机器上,可以mount这个共享:

#CentOS 
yum install cifs-utils
#Ubuntu
sudo apt-get install cifs-utils

然后安装如下脚本格式进行Mount:

#mount共享文件夹
mount -t cifs //192.168.1.121/www /var/www -o username=administrator,password='xxx',uid=1000,gid=1000

如果远程机器是Windows,那么直接使用映射即可。

其它参考:http://blog.ifeeline.com/2512.html

Laravel PhpStorm 使用

可以直接到官网下载:http://www.jetbrains.com/phpstorm/download/,默认提供30天的试用。建议购买正版,目前看到一年的标价是USD 159。也可以先下载官方免费的Toolbox,网址:http://www.jetbrains.com/toolbox/app,这个工具提供了非常便利的软件安装,启动,升级更新等操作,如果还有其它的系列的软件需要使用,比如WebStorm,这个Toolbox也可以很好的管理。

首先建议更换皮肤(File – Setting – Appearance):

这是一个黑色皮肤,非常护眼,建议使用。

第二就是知道可以安装插件,位置:File – Setting – Plugin,有大量第三方查看可用。

PhpStorm常用快捷键:

    全局搜索(ctrl + shift + F)
    显示类中的方法 (ctrl + 7)
    函数追踪 (ctrl +鼠标点击)
    单行注释/取消(ctrl + /)
    输入行号跳到某一行(ctrl + l)
    列出打开的文件(ctrl + e)
    删除当前行(ctrl + x)
    复制当前行(ctrl + d)
    跳到变量申明处(ctrl + b)

    格式化代码(command + alt + l)
    关闭当前窗口 (ctrl + w)
    项目刷新 (ctrl + alt + y)
    多行注释(ctrl + alt + /)
    查找//@todo标签(ctrl + 6)
    列出左侧文件(ctrl + 1)
    切换大小写(ctrl + shift + u)
    复制(ctrl + c)
    粘贴(ctrl + v)
    撤销(ctrl + z)

一般设置:
1 显示行号
File – Settings – Editor – General – Appearance – Show Line Number

2 换皮肤,设置字体大小
File – Settings – Editor – Colors & Fonts

针对Laravel项目,Laravel的IDE Helper工具是必须安装的,它是一个PHP包,参考:http://blog.ifeeline.com/2101.html

另外,针对PhpStrom的Laravel Plugin可以安装一下,这个插件主要针对Routes/Controllers/Views/Configuration/Services/Translations的代码补全。另外一个插件:Laravel Live Templates for PhpStorm,也可以安装一下,地址:https://github.com/koomai/phpstorm-laravel-live-templates#requests–input

PhantomJS 模拟登录一

"use strict";

var webpage = require('webpage');
var page = webpage.create();

/// 登录页
page.open('https://signin.sandbox.ebay.com/ws/eBayISAPI.dll', function (status) {
	if (status === "success") {
		var cookies = page.cookies;
		for(var i in cookies) {
			console.log(cookies[i].name + '=' + cookies[i].value);
		}
		//console.log(page.content);
		page.close();

		/// 登录
                // 需要提取其它的表单字段
		var postData = 'userid=testuser_xxx&pass=xxx';

		var login = webpage.create();
		login.open('https://signin.sandbox.ebay.com/ws/eBayISAPI.dll?co_partnerId=2&siteid=0&UsingSSL=1', 'POST', postData, function (status) {
		  if (status === "success") {

			var cookies = login.cookies;
			var saveData = '';
			for(var i in cookies) {
				console.log(cookies[i].name + '=+' + cookies[i].value);
				saveData += cookies[i].name+"="+cookies[i].value+"&";
			}

			//console.log(login.content);
			login.close();

			///	成功登录后发送数据
			var save = webpage.create();
			save.open('http://120.24.42.192:7070/i.php', 'POST', saveData, function (status) {
			  if (status === "success") {

				console.log(save.content);
				save.close();

				///
			  }
			  phantom.exit();
			});
		  } else {
			 console.log("Login Failed...");
			 phantom.exit();
		  }
		  
		});
	} else {
		phantom.exit();
	}
});

这里的phantom可以看做是浏览器进程,调用exit()方法表示直接关闭浏览器。page相当于浏览器中的Tab(开一个tab,跟建一个page概念相同),Tab或叫page打开以后,需要给一个URL,然后把这些个内容加载回来,然后在浏览器中就可以点击了,而在PhantomJs中,需要通过接口去模拟点击,要操作这个page,可以玩的花样很多,比如往这个page中注入一段JS,包含一个JS文件,修改DOM的内容,提交表单等等。page的evaluate方法一定程度上,可以看做是