Linux部署protobuf 的 php扩展

下载Protobuf的linux环境的包,地址https://github.com/google/protobuf/releases,比如protoc-3.6.0-linux-x86_64.zip Protobuf的PHP拓展 https://github.com/google/protobuf/tree/master/php 安装
composer install
运行
protoc --php_out=out_dir test.proto
文档:Protocol Buffers 指南

ceontos安装php7.2 提示cURL version 7.10.5 or later is required to compile php with cURL support

centos 安装 php7.2及以上版本的时候还是有几点需要注意的。 不要使用 php.net Download 提供的下载地址,而是使用 git上的下载地址,原因php.net下的里面存在一些bug,一些文件不全,导致无法安装mysqlnd扩展等。类似这种错误:
ext/curl/multi.c:501: undefined reference to curl_pushheader_bynum
先安装一些依赖包
sudo yum install -y libxml2-devel
sudo yum -y install libcurl-devel
sudo yum install openssl openssl-devel
sudo yum install bzip2 bzip2-devel
sudo yum install freetype-devel
sudo yum install libxslt-devel
在运行
./configure --with-pdo-mysql=mysqlnd --with-mysqli=mysqlnd --enable-fpm --with-curl --with-fd --enable-mbstring
如果 centos7 php7动态编译出现mysqlnd: configure: error: Cannot find OpenSSL's 错误,显然已经安装了openssl, openssl-devel,为什么还是提示这个错误,搜索了一下evp.h,这个文件也存在。GOOGLE 了一下,在stackoverflow,找到了答案,原来是 phpize 生成的configure脚本有问题。 解决方法:
export PHP_OPENSSL_DIR=yes
./configure -with-openssl-dir=/usr/include/openssl --with-pdo-mysql=mysqlnd --with-mysqli=mysqlnd --enable-fpm --with-curl --with-fd --enable-mbstring
以上参数可根据实际情况先不带,后面需要在增加相关扩展。 Ubuntu先编译提示 evp.h的请参考 解决Ubuntu下php 7.0.6 生成 openssl.so报错,configure: error: Cannot find OpenSSLs evp.h。  

解决Python shared libraries libpythonxx.so.1.0 cannot open shared object file

在centos上源码安装python3.6,整个安装过程没有错误,但运行python时提示 python error while loading shared libraries: libpython3.6m.so.1.0: cannot open shared object file: No such file or directory。 1.需要在编译的时候启用 –enable-shared, 如果不起用可能还有其他问题:Can’t connect to HTTPS URL because the SSL module is not available。 2. 安装完成后需要将 libpython3.6m.so.1.0 放入到 lib库中,有人说是 /usr/lib 但因为操作系统是64位的需要放的位置是 /usr/lib64 例如:
sudo ln -s /usr/local/python3.6.5/lib/libpython3.6m.so.1.0 /usr/lib64/libpython3.6m.so.1.0
问题解决

微信支付HTTPS根证书更换问题解决方案

微信支付根证书升级,实际更换中遇到的问题在这里整理下来 资料1:微信支付HTTPS服务器证书验证指引 此处是指导商户更新证书的指导页面,此处需要几点注意的是  证书安装和验证 验证证书使用 纱窗地址https://apitest.mch.weixin.qq.com/sandboxnew/pay/getsignkey 模拟数据可以参考发放普通红包 的 xml信息 验证命令:终端输入
echo ''|curl -X POST -H 'Content-type:text/xml' -d @- https://apitest.mch.weixin.qq.com/sandboxnew/pay/getsignkey
返回成功信息如下
其他参数请参考资料1文档 如果返回值为FAIL 则看安装证书部分 安装证书我们使用C/C++的这样可以直接在命令行中运行 查看跟证书命令
openssl version -a
可以看到类似记录 OPENSSLDIR: "/etc/pki/tls" 在运行
openssl s_client -connect api.mch.weixin.qq.com:443  -verify_return_error  -CApath $OPENSSLDIR #将OPENSSLDIR更换为上面获得的路径
看是否含有如下信息
depth=3 C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
verify return:1
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = GeoTrust RSA CA 2018
verify return:1
depth=0 C = CN, L = Shenzhen, O = Tencent Technology (Shenzhen) Company Limited, OU = R&D, CN = payapp.weixin.qq.com
如果不上上面 的 Baltimore 和 DigiCert  可能还需要为服务器绑定下host
113.96.240.139 api.mch.weixin.qq.com #电信使用
#157.255.180.139 api.mch.weixin.qq.com #联通使用
#121.51.30.139 api.mch.weixin.qq.com #其他使用
请根据实际情况选用 之后重新查看上面命令运行是否正常 更换跟证书之后 尝试正式环境支付出现SSL的60错误或者提示 Peer certificate cannot be authenticated with known CA certificates
CURLE_SSL_CACERT (60)
Peer certificate cannot be authenticated with known CA certificates.
错误代码地址:libcurl error codes 这个错误是 curl中 curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); curl_setopt($ch,CURLOPT_CAINFO,‘/cacert/DigiCert_Global_Root_CA.pem’); 可以设置 CURLOPT_SSL_VERIFYPEER 为FALSE 或者 CURLOPT_SSL_VERIFYPEER 设置为TRUE,CURLOPT_CAINFO 修改为 Baltimore_CyberTrust_Root.pem 这个证书 由于目前微信证书并未实际切换到对应服务器上 则还是需要保证 host记录是存在的 113.96.240.139 api.mch.weixin.qq.com #电信使用 官方提醒:注意:验收完成后,请及时恢复服务器上的host配置,微信支付服务器证书更新完成后,此处使用的IP会被关停。 目前我只能说等到5月底在将host记录删除掉吧 不知道有没有其他好的办法不用写host  

使用PHPOffice/PHPPresentation生成超大PPT文件

估计一般大家也用不到这个方式,必定需要生成几个G甚至几十G的PPT用途也不大,但生产环境有时候真的需要。下面就简单介绍分批生成单个超大PPT的方式。 原理:Office家族 Excel,PowerPoint都是压缩文件,将文件名后缀修改为.zip就会发现原来他们都是压缩包,针对里面的关联文件做修改便可以实现追加或者删除操作,是不是被惊到了呢?!这可是我研究了好多方式才发现的哦。 因为代码太据针对性,不是通用的,这里只列举一些通用的东西。 先说保存之前,所需数据都是差不多的, 首先需要一个封装好的类 PowerPoint.php 这里面封装通用的方法 调用时候代码说明(以CI(CodeIgniter)为例)
require_once APPPATH  . 'third_party' . DIRECTORY_SEPARATOR . 'PHPOffice' . DIRECTORY_SEPARATOR . 'PowerPoint.php';
//创建类
$powerPoint = new PowerPoint();
//第一次生成的时候加上文档信息把,追加PPT的时候就不需要了
$powerPoint->getDocumentProperties()->setCreater('CX')->setCompany('Focusmedia')->setTitle('PPT属性里面的标题')->setDescription('PPT属性里面的描述')->setCategory('PPT')->setLastModifiedBy('System');
//无论首次还是追加都需要设置母版
$powerPoint->createMaster();
//第一次的时候可能需要单独的一个头部信息
$powerPoint->createHeader($dataHeader);
//循环调用此方法,每调用一次增加一个PPT页面
$powerPoint->createCotent($data);
//判断所有PPT页面都加完了调用此方法
$powerPoint->createFooter();
//保存或者追加用此方法,里面判断不存在文件时用自带的save方法,否则追加,追加方法看后面
$powerPoint->appendSave($file);
下面举例调用方式,比如你上万条记录需要生成PPT,我们要分批追加,这样才不会导致内存溢出而无法正常使用。 可以先将数据整理成二维数组,每组为1个页面,看实际情况
$list_page = array();
foreach ($list as $key => $item) {
    $list_page[$item['page_number']][] = $item;
}
$per_group = 100;//每生成100页PPT保存1次
$group_page = ceil( count($list_page) / $per_group);//总数据分的页数
//引入类
require_once APPPATH  . 'third_party' . DIRECTORY_SEPARATOR . 'PHPOffice' . DIRECTORY_SEPARATOR . 'PowerPoint.php';
$tmp_file_path = '要保存的文件名.pptx'
for( $i = 1; $i <= $group_page; $i++) {
    $powerPoint = new PowerPoint();//每批都需要重新创建此类
    $powerPoint->createMaster();//每批都需要设置母版
    //这里每批要处理的循环
    for($key = ($i-1) * $per_group + 1; $key <= $i * $per_group AND $key <= count($list_page); $key++ ) {
        if($key == 1) { //第一条记录为头部
            $powerPoint->getDocumentProperties()->setCreater('System Report monitor')->setCompany('Focusmedia')
                ->setTitle($report['contract_no'] . '_' . $report['city_name'].'_'.$report['publish_date'])
                ->setDescription($report['contract_no'] . '_' . $report['city_name'].'_'.$report['publish_date'] . '_' . ($report['export_war'] == 2 ? $report['design_no_str'] : $report['export_war']))
                ->setCategory($report['contract_no'])->setLastModifiedBy('System');
            //创建第一页,因为第一页可能和后面的内容不同;
            $powerPoint->createHeader($list_page[$key][0]);
        } else {
            $powerPoint->createContent($list_page[$key]);
        }
        if($key == count($list_page)) {
            //创建完所有数据是不是应该还有个结束页,谢谢大家观赏
            $powerPoint->createFooter($type);
        }
        if($key == $i * $per_group || $key == count($list_page)) {
            //******此次循环结束或数据结束,保存PPT
            $powerPoint->appendSave($tmp_file_path);
            $powerPoint = null;
        }
    }
}
//循环执行完,就得到想要的文件了, 以下重点介绍PowerPoint.php的内容,尤其是appendSave()方法,是整个操作的核心
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'PHPPresentation' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'PhpPresentation' . DIRECTORY_SEPARATOR . 'Autoloader.php';
\PhpOffice\PhpPresentation\Autoloader::register();
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Common' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Common' . DIRECTORY_SEPARATOR . 'Autoloader.php';
\PhpOffice\Common\Autoloader::register();
use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\Style\Alignment;
use PhpOffice\PhpPresentation\Style\Color;
use PhpOffice\PhpPresentation\IOFactory;
use PhpOffice\PhpPresentation\DocumentLayout;
use PhpOffice\Common\Drawing;
use PhpOffice\PhpPresentation\Style\Border;
use PhpOffice\PhpPresentation\Style\Font;
use PhpOffice\PhpPresentation\Style\Fill;
use PhpOffice\PhpPresentation\Slide\Background\Image;
use PhpOffice\PhpPresentation\Slide\Note;
use PhpOffice\PhpPresentation\HashTable;
use PhpOffice\PhpPresentation\Writer\AbstractWriter;
use PhpOffice\PhpPresentation\Shape\Chart;
use PhpOffice\PhpPresentation\Shape\Drawing\AbstractDrawingAdapter;
use PhpOffice\PhpPresentation\Shape\Drawing\Base64;
use PhpOffice\PhpPresentation\Shape\Group;


class PowerPoint extends PhpOffice\PhpPresentation\Writer\PowerPoint2007\PptSlides
{
    //概要
    protected $creater = 'My name';
    protected $company = 'My factory';
    protected $title = 'My title';
    protected $description = 'My description';
    protected $category = 'My category';
    protected $last_modify_by = 'modify name';
    protected $created_time;
    protected $modified_time;
    protected $subject = 'My subject';
    protected $keywords = 'my, key, word';
    protected $objPHPPresentation;
    protected $properties;
    private $type = 'focusmedia';
    private $log;
    private $objZip;
    protected $oSlideLayout = null;
    protected $oHashTable;

    public function __construct($type)
    {
        $this->type = $type;
        $this->log = new MY_Log();
        $this->oHashTable =  new HashTable();
    }

    private function init()
    {
        if( empty($this->objPHPPresentation)) {
            $this->objPHPPresentation = new PhpPresentation();
            $this->objPHPPresentation->removeSlideByIndex(0);
        }
    }

    public function getDocumentProperties()
    {
        $this->init();
        $this->properties = $this->objPHPPresentation->getDocumentProperties();
        return $this;
    }
    public function setCreater($creater)
    {
        $this->creater = $creater;
        $this->properties->setCreator($this->creater);
        return $this;
    }
    public function setCompany($company)
    {
        $this->company = $company;
        $this->properties->setCompany($this->company);
        return $this;
    }
    public function setTitle($title)
    {
        $this->title = $title;
        $this->properties->setTitle($this->title);
        return $this;
    }
    public function setDescription($description)
    {
        $this->description = $description;
        $this->properties->setDescription($this->description);
        return $this;
    }
    public function setCategory($category)
    {
        $this->category = $category;
        $this->properties->setCategory($this->category);
        return $this;
    }
    public function setLastModifiedBy($last_modify_by)
    {
        $this->last_modify_by = $last_modify_by;
        $this->properties->setLastModifiedBy($this->last_modify_by);
        return $this;
    }
    public function setCreated($created_time)
    {
        $this->created_time = $created_time;
        $this->properties->setCreated(empty($this->created_time) ? time() : $this->created_time);
        return $this;
    }
    public function setModified($modified_time)
    {
        $this->modified_time = $modified_time;
        $this->properties->setModified(empty($this->modified_time) ? time() : $this->modified_time);
        return $this;
    }
    public function setSubject($subject)
    {
        $this->subject = $subject;
        $this->properties->setSubject($this->subject);
        return $this;
    }
    public function setKeywords($keywords)
    {
        $this->keywords = $keywords;
        $this->properties->setKeywords($this->keywords);
        return $this;
    }
/**
 * 创建幻灯片母版
 */
public function createMaster()
{
    //初始化,每次创建母版或幻灯片都新处理一下
    $this->init();
    //幻灯片大小,这里使用的是毫米,后面位置我使用的是厘米,这样可以更准确的创建幻灯片
    $this->objPHPPresentation->getLayout()->setCX( 296.82, DocumentLayout::UNIT_MILLIMETER)->setCY( 209.86,  DocumentLayout::UNIT_MILLIMETER);
    //var_dump($this->oMasterSlide);
    $oMasterSlide_array = $this->objPHPPresentation->getAllMasterSlides();
    $oMasterSlide = $oMasterSlide_array[0];
    //LOGO 位置信息都是里面哦
    $logoWidth =  4.39;
    $logoHeight = 0.73;
    $logoOffsetX = 0.84;
    $logoOffsetY =  1.29;
    //充本地文件中添加图片,母版里面的图片一般都是固定的, 使用自定义的 shapeImgSource
    $this->shapeImgSource($oMasterSlide, dirname(__FILE__) . '/resource/logo.png', $logoWidth, $logoHeight, $logoOffsetX, $logoOffsetY, array('name' =>  $this->type . ' logo',    'description'   =>  $this->type . ' logo'));
    return true;
}

/**
 * 创建幻灯片第一张 首页
 * @param array $data
 * @return bool
 */
public function createHeader($data = array())
{
    if( empty($data)) return false;
    $this->init();
    $headerSlide = $this->objPHPPresentation->createSlide();
    $headerSlide->getSlideLayout()->layoutNr = 1;

    //方案标题
    $titleWidth = 16.99;
    $titleHeight = ($this->type == 'focusmedia') ? 1.64 : 1.8;
    $titleOffsetX = 2.05;
    $titleOffsetY = ($this->type == 'focusmedia') ? 9.25 : 8.97;
    $titleFont = array('size'   =>  32, 'bold'  =>  true, 'family' =>  '华文细黑', 'color' =>  'ff4d4d4d');
    $titleShadow = null;
    $this->shapeRichText($headerSlide,$data['design_name'], $titleWidth, $titleHeight, $titleOffsetX, $titleOffsetY, 100, $titleFont, $titleShadow);

    //创建图片框和阴影
    $mediaFrameWidth = 7.4;
    $mediaFrameHeight = 9.39;
    $mediaFrameOffsetX = 20.05;
    $mediaFrameOffsetY = 5.7;
    $mediaFrameShadow = array('direction'  =>  45, 'distance'  =>  11.3, 'alpha'  =>  50, 'color'    =>  'ff808080', 'blur_radius'   =>  0);
    $this->shapeMediaFrame($headerSlide, $mediaFrameWidth, $mediaFrameHeight, $mediaFrameOffsetX, $mediaFrameOffsetY, $mediaFrameShadow, 20);

    //创建图片
    $this->shapeImgUrl($headerSlide, $data['img_oss_path'], $data['img_path'], $mediaFrameWidth, $mediaFrameHeight, $mediaFrameOffsetX, $mediaFrameOffsetY);

    return true;
}

/**
 * 通用底部
 */
public function createFooter()
{
    $this->init();
    //新增一个slide
    $footerSlide = $this->objPHPPresentation->createSlide();
    $footerSlide->getSlideLayout()->layoutNr = 1;
    //Title
    $footerTitle = '如对本报告有任何问题或疑问,请于一周内向以下分众机构查询!';
    $footerTitleWidth = 24;//24.54;
    $footerTitleHeight = 1.44;
    $footerTitleOffsetX = 5.5;
    $footerTitleOffsetY = 5.1;
    $footerTitleFont = array('size'   =>  18, 'bold'  =>  false, 'family' =>  '华文细黑', 'color' =>  ($this->type == 'focusmedia') ? 'ff4d4d4d' : 'ff000000');
    $footerTitleShadow = array('direction'  =>  45, 'distance'  =>  4, 'alpha'  =>  100, 'color'    =>  'ffc0c0c0', 'blur_radius'   =>  4);
    $this->shapeRichText($footerSlide, $footerTitle, $footerTitleWidth, $footerTitleHeight, $footerTitleOffsetX, $footerTitleOffsetY, 100, $footerTitleFont, $footerTitleShadow);

    return true;
}
//创建中间页面
public function createContent($data = array())
{
    $this->init(); 
    //新增一个slide 
    $oSlide = $this->objPHPPresentation->createSlide(); 
    $oSlide->getSlideLayout()->layoutNr = 1;
    //TODO 添加正文内容
}

//保存文件,这个只是作为首次生成不追加时候使用PHPPresentation的Save
public function save( $fileName = '')
{
    $oWriterPPTX = IOFactory::createWriter($this->objPHPPresentation, 'PowerPoint2007');
    if(empty($fileName)) {//如果文件名为空,则随机生成文件名
        $fileName = time().'.pptx';
    }
    $fileName = trim($fileName, '/');
    $oWriterPPTX->save(FCPATH . $fileName);
    return $fileName;
}
/**
 * 直接打开原pptx文件(不存在则调用save方法),只循环修改其中涉及到文件,其他文件不要动 (此方法较快捷,需要对PHPPresentation比较了解)
 * @param string $fileName
 */
public function appendSave($fileName = '')
{
    $this->init();
    $fileName = trim(trim($fileName, DIRECTORY_SEPARATOR), '/');
    //如果指定的文件名不存在,直接调用 save 存储整个文档
    if( ! file_exists( FCPATH . $fileName)) {
        return array('err'  =>  0,  'msg'   =>  $this->save($fileName));
    }
    //此方法内有追加
    $result = $this->slideRender(FCPATH . $fileName);
    return empty($result) ? array('err'  =>  1,  'msg'   =>  'failed') : array('err'  =>  0,  'msg'   =>  $fileName);
}

private function readZip( $fileName = '')
{
    if( empty($fileName)) return false;
    $docString = $this->objZip->getFromName($fileName);
    return $docString;
}

private function readZipDocNoteCount( $fileName = '', $elements = '')
{
    $count = 0;
    if( empty($this->objZip) OR empty($fileName)) return $count;
    $docString = $this->readZip($fileName);
    $dom = new \DOMDocument();
    $dom->loadXML($docString);
    $xpath = new DOMXPath($dom);
    $elements = trim($elements, '/');
    if( strpos($elements, '/') !== false) {
        //去掉跟节点
        $arrParents = explode('/', $elements);
        unset($arrParents[0]);
        $elements = implode('/', $arrParents);
    }
    $count = $dom->getElementsByTagName($elements)->length;
    if($count > 0) return $count;
    $count = $xpath->query($elements)->length;

    return $count;
}

/**
 * @param $fileName
 * @param string $parentElement
 * @param string $element
 * @param array $attrs  二维数组
 */
private function addZipXmlAttr($fileName, $parentElement = '', $element = '', $attrs = array())
{
    $contentString = $this->readZip($fileName);
    if( empty($contentString)) {
        $this->log->write_log_file('power_point', 'empty content string '. $fileName, ' parent '.$parentElement . ' element '. $element);
    }
    $dom = new \DOMDocument();
    $dom->loadXML($contentString);
    $xpath = new DOMXPath($dom);

    foreach ($attrs as $k => $item) {
        $ele = $dom->createElement($element);
        foreach ($item as $key => $value) {
            $ele->setAttribute($key, $value);
        }
        $parentElement = trim($parentElement, '/');
        if( strpos($parentElement, '/') !== false) {
            //去掉跟节点
            $arrParents = explode('/', $parentElement);
            unset($arrParents[0]);
            $queryStr = implode('/', $arrParents);
            if( ! empty($xpath->query($queryStr)->item(0))) {
                $xpath->query($queryStr)->item(0)->appendChild($ele);
            }
        } else {
            $dom->firstChild->appendChild($ele);
        }
    }
    //echo $dom->saveXML();
    //exit;
    $this->objZip->addFromString($fileName, $dom->saveXML());
}

private function slideRender($file = '')
{
    if(empty($file)) return false;
    //打开压缩文件
    $this->objZip = new ZipArchive();
    $this->objZip->open($file);
    //presentation.xml.rels 记录条数
    $presentationCount = $this->readZipDocNoteCount( 'ppt/_rels/presentation.xml.rels', '/Relationships/Relationship');
    //读取已存在的slide个数
    $hasSlideCount = $this->readZipDocNoteCount('ppt/presentation.xml', '/p:presentation/p:sldIdLst/p:sldId');
    //循环保存新slide文件和媒体
    $newSlideCount = 0;
    foreach ($this->objPHPPresentation->getAllSlides() as $idx => $oSlide) {
        $newSlideCount++;
        $oSlide->getSlideLayout()->layoutNr = 1;
        $this->objZip->addFromString('ppt/slides/_rels/slide' . ($hasSlideCount + $idx + 1) . '.xml.rels', $this->writeSlideRelationships($oSlide));
        $this->objZip->addFromString('ppt/slides/slide' . ($hasSlideCount + $idx + 1) . '.xml', $this->writeSlide($oSlide));

        // Add note slide
        if ($oSlide->getNote() instanceof Note) {
            if ($oSlide->getNote()->getShapeCollection()->count() > 0) {
                $this->objZip->addFromString('ppt/notesSlides/notesSlide' . ($hasSlideCount + $idx + 1) . '.xml', $this->writeNote($oSlide->getNote()));
            }
        }

        // Add background image slide
        $oBkgImage = $oSlide->getBackground();
        if ($oBkgImage instanceof Image) {
            $this->objZip->addFromString('ppt/media/'.$oBkgImage->getIndexedFilename($hasSlideCount + $idx), file_get_contents($oBkgImage->getPath()));
        }

        $presentationAttr[$idx] = array(
            'id'    =>  256 + $hasSlideCount + $idx,
            'r:id'  =>  'rId'.($presentationCount + $idx + 1),
        );
        //添加 presentation.xml[.rels], [Content_Types].xml文件关系
        $presentationRelsAttr[$idx] = array(
            'Id'        =>  'rId'.($presentationCount + $idx + 1),
            'Target'    =>  'slides/slide'.($hasSlideCount + $idx + 1).'.xml',
            'Type'      =>  'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide',
        );
        $contentTypesAttr[$idx] = array(
            'PartName'      =>  '/ppt/slides/slide'.($hasSlideCount + $idx + 1).'.xml',
            'ContentType'   =>  'application/vnd.openxmlformats-officedocument.presentationml.slide+xml',
        );
    }

    //保存媒体文件    
    // Create drawing dictionary
    $this->getDrawingHashTable()->addFromSource($this->allDrawings());
    for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
        $shape = $this->getDrawingHashTable()->getByIndex($i);
        if (!$shape instanceof AbstractDrawingAdapter) {
            continue;
        }
        $this->objZip->addFromString('ppt/media/' . $shape->getIndexedFilename(), $shape->getContents());
    }

    if( ! empty($presentationAttr)) {
        $this->addZipXmlAttr( 'ppt/presentation.xml', 'p:presentation/p:sldIdLst', 'p:sldId', $presentationAttr);
    }
    if( ! empty($presentationRelsAttr)) {
        $this->addZipXmlAttr('ppt/_rels/presentation.xml.rels', 'Relationships', 'Relationship', $presentationRelsAttr);
    }
    if( ! empty($contentTypesAttr)) {
        $this->addZipXmlAttr('[Content_Types].xml', 'Types', 'Override', $contentTypesAttr);
    }

    //修改幻灯片个数
    $contentFile = 'docProps/app.xml';
    $contentString = $this->readZip($contentFile);
    $contentString = str_replace(''.$hasSlideCount.'', ''.($hasSlideCount + $newSlideCount).'', $contentString);
    $this->objZip->addFromString($contentFile, $contentString);
    //关闭压缩文件
    $this->objZip->close();
    $this->objZip = null;
    return true;
}

//获取元素属性
public function getObjPHPPresentation()
{
    $this->init();
    return $this->objPHPPresentation;
}
public function getPresentationProperties()
{
    $this->init();
    return $this->objPHPPresentation->getPresentationProperties();
}
public function getLayout()
{
    $this->init();
    return $this->objPHPPresentation->getLayout();
}
public function getAllSlides()
{
    $this->init();
    return $this->objPHPPresentation->getAllSlides();
}
public function getSlide($index = 0)
{
    $this->init();
    return $this->objPHPPresentation->getSlide($index);
}
public function getAllMasterSlides()
{
    $this->init();
    return $this->objPHPPresentation->getAllMasterSlides();
}
public function setDrawingHashTable(HashTable $hashTable)
{
    $this->oHashTable = $hashTable;
    return $this;
}
public function getDrawingHashTable()
{
    return $this->oHashTable;
}


/**
 * Get an array of all drawings
 *
 * @return \PhpOffice\PhpPresentation\Shape\AbstractDrawing[] All drawings in PhpPresentation
 * @throws \Exception
 */
protected function allDrawings()
{
    // Get an array of all drawings
    $aDrawings  = array();
    // Loop through PhpPresentation
    foreach (array_merge($this->getObjPHPPresentation()->getAllSlides(), $this->getObjPHPPresentation()->getAllMasterSlides()) as $oSlide) {
        $arrayReturn = $this->iterateCollection($oSlide->getShapeCollection()->getIterator());
        $aDrawings = array_merge($aDrawings, $arrayReturn);
    }

    return $aDrawings;
}
private function iterateCollection(\ArrayIterator $oIterator)
{
    $arrayReturn = array();
    if ($oIterator->count() <= 0) {
        return $arrayReturn;
    }

    while ($oIterator->valid()) {
        $oShape = $oIterator->current();
        if ($oShape instanceof AbstractDrawingAdapter) {
            $arrayReturn[] = $oShape;
        } elseif ($oShape instanceof Chart) {
            $arrayReturn[] = $oShape;
        } elseif ($oShape instanceof Group) {
            $arrayGroup = $this->iterateCollection($oShape->getShapeCollection()->getIterator());
            $arrayReturn = array_merge($arrayReturn, $arrayGroup);
        }
        $oIterator->next();
    }
    return $arrayReturn;
}

//把我封装好的创建多媒体文字,图片画线等内容也加上吧
/**
* 创建图片背景框架
* @param $oSlide 从此对象创建
* @param $width 创建的宽度cm
* @param $height 创建的高度cm
* @param $offsetX 距离左边距离单位为cm
* @param $offsetY 距离左边距离单位为cm
* @param array $shadow 数组包含(direction(角度如:45), distance(距离:2), alpha(透明度:0-100), color(颜色:ff000000), blur_radius(模糊:0))
* @param int $border 如果数字不为0 则创建此值大小的border
* @return $oSlide
*/
protected function shapeMediaFrame($oSlide, $width, $height, $offsetX, $offsetY, $shadow = array(), $border = 20)
{
$oShape = $oSlide->createRichTextShape();
$fill = new Fill();
$fill->setFillType(Fill::FILL_SOLID);
$oShape->setOffsetX(Drawing::centimetersToPixels($offsetX))
->setOffsetY(Drawing::centimetersToPixels($offsetY))
->setWidth(Drawing::centimetersToPixels($width))
->setHeight(Drawing::centimetersToPixels($height))
->setFill($fill);
if( ! empty($shadow)) {
$oShape->getShadow()->setVisible(true)->setDirection(isset($shadow['direction']) ? $shadow['direction'] : 45)
->setDistance(isset($shadow['distance']) ? $shadow['distance'] : 2)
->setAlpha(isset($shadow['alpha']) ? $shadow['alpha'] : 100)
->setColor(new Color(isset($shadow['color']) ? $shadow['color'] : 'ff808080'))
->setBlurRadius(isset($shadow['blur_radius']) ? $shadow['blur_radius'] : 0);
}
if( ! empty($border)) {
$oShape->getBorder()->setLineWidth($border);
}
return $oSlide;
}

/**
* 从URL地址中创建图片
* @param $oSlide 从此对象创建
* @param $imgurl 图片URL地址,这里是阿里云OSS的图片地址
* @param $width 创建的宽度cm
* @param $height 创建的高度cm
* @param $offsetX 距离左边距离单位为cm
* @param $offsetY 距离左边距离单位为cm
* @return $oSlide
*/
protected function shapeImgUrl($oSlide, $oss_file, $local_file, $width, $height, $offsetX, $offsetY)
{
if( empty($oss_file) && empty($local_file)) {
return $oSlide;
}
if( ! empty($oss_file)) {
$imgurl = get_img_url($oss_file, '', '', false, 'w_'.floor(Drawing::centimetersToPixels($width)).',h_'.floor(Drawing::centimetersToPixels($height)));
$filecontent = file_get_contents($imgurl);
if (!function_exists('getimagesizefromstring')) {
$uri = 'data://application/octet-stream;base64,' . base64_encode($filecontent);
$fileEx = getimagesize($uri);
} else {
$fileEx = getimagesizefromstring($filecontent);
}
$data_img_content = 'data:image/jpeg;base64,'.base64_encode($filecontent);
} else {
$file_contents = get_image_data('', $local_file, '', 'w_'.floor(Drawing::centimetersToPixels($width)).',h_'.floor(Drawing::centimetersToPixels($height)));
$fileEx = getimagesize($file_contents);
$data_img_content = $file_contents;
}
$fileWidth = $fileEx[0] > Drawing::centimetersToPixels($width) ? Drawing::centimetersToPixels($width) : $fileEx[0];
$fileHeight = $fileEx[1] > Drawing::centimetersToPixels($height) ? Drawing::centimetersToPixels($height) : $fileEx[1];
$topShape = new Base64();
//$topShape->setData($data_img_content, false)->setResizeProportional(false);
//$this->log->write_log_file('img', $oss_file .$local_file .' ' . md5($data_img_content));
$topShape->setData($data_img_content)->setResizeProportional(false);
if($offsetX == '50%') {
$offsetX = $this->objPHPPresentation->getLayout()->getCX(DocumentLayout::UNIT_CENTIMETER);
$topShape->setWidth($fileWidth)->setHeight($fileHeight)
->setOffsetX(Drawing::centimetersToPixels($offsetX) - $fileWidth/2 )
->setOffsetY(Drawing::centimetersToPixels($offsetY));
} else {
$topShape->setWidth($fileWidth)->setHeight($fileHeight)
->setOffsetX(Drawing::centimetersToPixels($offsetX + $width/2) - $fileWidth/2 )
->setOffsetY(Drawing::centimetersToPixels($offsetY + $height/2) - $fileHeight/2 );
}
$oSlide->addShape($topShape);
return $oSlide;
}

/**
* 从本地图片文件创建图片
* @param $oSlide 从此对象创建
* @param $filePath 文件的完整路径
* @param $width 创建的宽度cm
* @param $height 创建的高度cm
* @param $offsetX 距离左边距离单位为cm
* @param $offsetY 距离左边距离单位为cm
* @param array $fileInfo 图片的标题和描述 array('name'=>'', 'description'=>'')
* @return $oSlide
*/
protected function shapeImgSource($oSlide, $filePath, $width, $height, $offsetX, $offsetY, $fileInfo = array())
{
$oShape = $oSlide->createDrawingShape();
$oShape->setName(empty($fileInfo['name']) ? 'file name' : $fileInfo['name'])
->setDescription(empty($fileInfo['description']) ? 'file description' : $fileInfo['description'])
->setPath($filePath);
$oShape->setResizeProportional(false)->setHeight(Drawing::centimetersToPixels($height))->setWidth(Drawing::centimetersToPixels($width))
->setOffsetX(Drawing::centimetersToPixels($offsetX))->setOffsetY(Drawing::centimetersToPixels($offsetY));
return $oSlide;
}

/**
* 创建多媒体文字
* @param $oSlide 从此对象创建
* @param $text 需要创建的文字 可以是字符串或数组
* @param $width 创建的宽度cm
* @param $height 创建的高度cm
* @param $offsetX 距离左边距离单位为cm
* @param $offsetY 距离左边距离单位为cm
* @param array $font 样式array(size:10,bold:true/false, family:华文细黑, color:ff808080)
* @param array $shadow 数组包含(direction(角度如:45), distance(距离:2), alpha(透明度:0-100), color(颜色:ff000000), blur_radius(模糊:0))
*/
protected function shapeRichText($oSlide, $text, $width, $height, $offsetX, $offsetY, $lineSpacing = 100, $font = array(), $shadow = array())
{
if( empty($text)) return $oSlide;
$oShape = $oSlide->createRichTextShape();
$oShape->setOffsetX(Drawing::centimetersToPixels($offsetX))->setOffsetY(Drawing::centimetersToPixels($offsetY))->setWidth(Drawing::centimetersToPixels($width))->setHeight(Drawing::centimetersToPixels($height));
$oShape->getParagraph(0)->setLineSpacing($lineSpacing);
if( is_array($text)) {
$oTextRun = $oShape->createTextRun($text[0]);
} else {
$oTextRun = $oShape->createTextRun($text);
}
$oFont = new Font();
$oFont->setSize(empty($font['size']) ? 10 : $font['size'])
->setBold(isset($font['bold']) ? true : false)
->setName(isset($font['family']) ? $font['family'] : '华文细黑')
->setColor(new Color(empty($font['color']) ? 'ff4d4d4d' : $font['color']));
if( ! empty($font['h_align'])) {
$oShape->getParagraph(0)->getAlignment()->setHorizontal($font['h_align']);
}
if( ! empty($font['v_align'])) {
$oShape->getParagraph(0)->getAlignment()->setVertical($font['h_align']);
}
$oTextRun->setFont($oFont);
if( ! empty($shadow)) {
//标题阴影
$oShape->getShadow()->setVisible(true)
->setAlpha(isset($shadow['alpha']) ? $shadow['alpha'] : 100)
->setDistance(isset($shadow['distance']) ? $shadow['distance'] : 2)
->setDirection(isset($shadow['direction']) ? $shadow['direction'] : 45)
->setBlurRadius(isset($shadow['blur_radius']) ? $shadow['blur_radius'] : 0)
->setColor(new Color(isset($shadow['color']) ? $shadow['color'] : 'ff808080'));
}
if( is_array($text)) {
//第二行开始
foreach ($text as $key => $pContenet) {
if($key == 0) {
continue;
}
$pg = $oShape->createParagraph();
$pg->setLineSpacing($lineSpacing);
$footerContentRun = $pg->createTextRun($pContenet);
$footerContentRun->getFont()->setSize(empty($font['size']) ? 10 : $font['size'])
->setBold(isset($font['bold']) ? true : false)
->setName(isset($font['family']) ? $font['family'] : '华文细黑')
->setColor(new Color(empty($font['color']) ? 'ff4d4d4d' : $font['color']));
}
}
return $oShape;
}

/**
* 同一行内容 字的样式不同
* @param $oSlide 从此对象创建
* @param $text 需要创建的文字 可以是字符串或数组
* @param $width 创建的宽度cm
* @param $height 创建的高度cm
* @param $offsetX 距离左边距离单位为cm
* @param $offsetY 距离左边距离单位为cm
* @param int $lineSpacing
* @param array $font 字体样式如果text为数组,则这里对应二维数组 样式array(size:10,bold:true/false, family:华文细黑, color:ff808080)
* @param array $shadow 数组包含(direction(角度如:45), distance(距离:2), alpha(透明度:0-100), color(颜色:ff000000), blur_radius(模糊:0))
* @return $oSlide
*/
protected function shapeRichTextInline($oSlide, $text, $width, $height, $offsetX, $offsetY, $lineSpacing = 100, $font = array(), $shadow = array())
{
$oShape = $oSlide->createRichTextShape();
$oShape->setOffsetX(Drawing::centimetersToPixels($offsetX))
->setOffsetY(Drawing::centimetersToPixels($offsetY))
->setWidth(Drawing::centimetersToPixels($width))
->setHeight(Drawing::centimetersToPixels($height));
$oShape->getParagraph(0)->setLineSpacing($lineSpacing);
if( is_array($text)) {
foreach ($text as $k => $string) {
$oTextRun = $oShape->createTextRun($string);
if( ! empty($font[$k]) && is_array($font[$k]) ) {
$oTextRun->getFont()->setName(isset($font[$k]['family']) ? $font[$k]['family'] : '华文细黑')
->setSize(empty($font[$k]['size']) ? 10 : $font[$k]['size'])
->setColor(new Color(empty($font[$k]['color']) ? 'FFCC0000' : $font[$k]['color']));
} elseif ( is_array($font)) {
$oTextRun->getFont()->setName(isset($font['family']) ? $font['family'] : '华文细黑')
->setSize(empty($font['size']) ? 10 : $font['size'])
->setColor(new Color(empty($font['color']) ? 'FFCC0000' : $font['color']));
}
}
} else {
$oTextRun = $oShape->createTextRun($text);
$oTextRun->getFont()->setName(isset($font['family']) ? $font['family'] : '华文细黑')
->setSize(empty($font['size']) ? 10 : $font['size'])
->setColor(new Color(empty($font['color']) ? 'FFCC0000' : $font['color']));
}
if( ! empty($shadow)) {
$oShape->getShadow()->setVisible(true)
->setAlpha(isset($shadow['alpha']) ? $shadow['alpha'] : 100)
->setDistance(isset($shadow['distance']) ? $shadow['distance'] : 2)
->setDirection(isset($shadow['direction']) ? $shadow['direction'] : 45)
->setBlurRadius(isset($shadow['blur_radius']) ? $shadow['blur_radius'] : 0)
->setColor(new Color(isset($shadow['color']) ? $shadow['color'] : 'ff808080'));
}

return $oShape;
}

/**
* 画线
* @param $oSlide 从此对象创建
* @param $width 创建的宽度cm
* @param $height 创建的高度cm
* @param $offsetX 距离左边距离单位为cm
* @param $offsetY 距离左边距离单位为cm
* @param array $line 样式 array(color, line_style, line_width)
* @return $oSlide
*/
protected function shapeLine($oSlide, $width, $height, $offsetX, $offsetY, $line = array())
{
$lineShape = $oSlide->createLineShape(Drawing::centimetersToPixels($offsetX), Drawing::centimetersToPixels($offsetY), Drawing::centimetersToPixels($offsetX + $width), Drawing::centimetersToPixels($offsetY + $height));
$lineShape->getBorder()->setColor(new Color(empty($line['color']) ? 'ff808080' : $line['color']))->setLineStyle(isset($line['line_stype']) ? $line['line_stype'] : Border::LINE_SINGLE)->setLineWidth(isset($line['line_width']) ? $line['line_width'] : 0.75);
return $oSlide;
}

}
结束 重点:把PPTX文件看作一个压缩文件,读取压缩包中特定文件或添加特定文件,这样可以保证不把所有的内容全部加载到内存。 其实里面占比例最大的是图片,所以这里我们按每次100张幻灯片来处理,请根据实际情况分配批次。 难点:如何将已经存入到PhpPresentation中的内容写入到已存在的文件中,这里需要我们将PPTX文件中的子文件都了解清楚,以及研读系统中save的实现方式,将我们需要的内容单独处理掉。