diff --git a/.travis.yml b/.travis.yml index 8ed7a45f..f322426d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,7 +37,7 @@ before_script: script: ## PHP_CodeSniffer - - phpcs --standard=PSR2 -n src/ + - phpcs --standard=PSR2 -n src/ --ignore=src/PhpWord/Shared/PCLZip - phpcs --standard=PSR2 -n tests/ ## PHP Copy/Paste Detector #- php phpcpd.phar --verbose src/ diff --git a/CHANGELOG.md b/CHANGELOG.md index f97c4c07..c785dbf4 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This is the changelog between releases of PHPWord. Releases are listed in revers - Font: Add `bgColor` to font style to define background using HEX color - @jcarignan GH-168 - Table: Add `exactHeight` to row style to define whether row height should be exact or atLeast - @jcarignan GH-168 - Element: New `CheckBox` element for sections and table cells - @ozilion GH-156 +- Settings: Ability to use PCLZip as alternative to ZipArchive - @bskrtich @ivanlanin GH-106 GH-140 GH-185 ### Bugfixes diff --git a/docs/general.rst b/docs/general.rst index 9b25551a..1c810722 100644 --- a/docs/general.rst +++ b/docs/general.rst @@ -7,7 +7,8 @@ Basic example ------------- The following is a basic example of the PHPWord library. More examples -are provided in the `samples folder `__. +are provided in the `samples +folder `__. .. code-block:: php @@ -52,6 +53,42 @@ are provided in the `samples folder save('helloWorld.rtf'); +Settings +-------- + +The ``PhpOffice\PhpWord\Settings`` class provides some options that will +affect the behavior of PHPWord. Below are the options. + +XML Writer compatibility +~~~~~~~~~~~~~~~~~~~~~~~~ + +This option sets +```XMLWriter::setIndent`` `__ +and +```XMLWriter::setIndentString`` `__. +The default value of this option is ``true`` (compatible), which is +`required for OpenOffice `__ to +render OOXML document correctly. You can set this option to ``false`` +during development to make the resulting XML file easier to read. + +.. code-block:: php + + PhpOffice\PhpWord\Settings::setCompatibility(false); + +Zip class +~~~~~~~~~ + +By default, PHPWord uses PHP +`ZipArchive `__ to read or write +ZIP compressed archive and the files inside them. If you can't have +ZipArchive installed on your server, you can use pure PHP library +alternative, `PCLZip `__, which +included with PHPWord. + +.. code-block:: php + + PhpOffice\PhpWord\Settings::setZipClass(PhpOffice\PhpWord\Settings::PCLZIP); + Default font ------------ @@ -105,3 +142,4 @@ points to twips. $sectionStyle->setMarginLeft(\PhpOffice\PhpWord\Shared\Font::inchSizeToTwips(.5)); // 2 cm right margin $sectionStyle->setMarginRight(\PhpOffice\PhpWord\Shared\Font::centimeterSizeToTwips(2)); + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 05b08442..f77b9ce3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,6 +16,9 @@ ./src + + ./src/PhpWord/Shared/PCLZip + \ No newline at end of file diff --git a/src/PhpWord/DocumentProperties.php b/src/PhpWord/DocumentProperties.php index 5e982646..63ff0275 100644 --- a/src/PhpWord/DocumentProperties.php +++ b/src/PhpWord/DocumentProperties.php @@ -39,14 +39,14 @@ class DocumentProperties /** * Created * - * @var datetime + * @var datetime|int */ private $_created; /** * Modified * - * @var datetime + * @var datetime|int */ private $_modified; @@ -102,7 +102,7 @@ class DocumentProperties /** * Custom Properties * - * @var string + * @var array */ private $_customProperties = array(); @@ -542,26 +542,21 @@ class DocumentProperties case 'ui8': // 8-Byte Unsigned Integer case 'uint': // Unsigned Integer return self::PROPERTY_TYPE_INTEGER; - break; case 'r4': // 4-Byte Real Number case 'r8': // 8-Byte Real Number case 'decimal': // Decimal return self::PROPERTY_TYPE_FLOAT; - break; case 'empty': // Empty case 'null': // Null case 'lpstr': // LPSTR case 'lpwstr': // LPWSTR case 'bstr': // Basic String return self::PROPERTY_TYPE_STRING; - break; case 'date': // Date and Time case 'filetime': // File Time return self::PROPERTY_TYPE_DATE; - break; case 'bool': // Boolean return self::PROPERTY_TYPE_BOOLEAN; - break; case 'cy': // Currency case 'error': // Error Status Code case 'vector': // Vector @@ -576,7 +571,6 @@ class DocumentProperties case 'clsid': // Class ID case 'cf': // Clipboard Data return self::PROPERTY_TYPE_UNKNOWN; - break; } return self::PROPERTY_TYPE_UNKNOWN; } diff --git a/src/PhpWord/Reader/Word2007.php b/src/PhpWord/Reader/Word2007.php index 78720237..9161d162 100644 --- a/src/PhpWord/Reader/Word2007.php +++ b/src/PhpWord/Reader/Word2007.php @@ -10,6 +10,7 @@ namespace PhpOffice\PhpWord\Reader; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\DocumentProperties; use PhpOffice\PhpWord\Exceptions\Exception; @@ -34,7 +35,8 @@ class Word2007 extends Reader implements IReader $return = false; // Load file - $zip = new \ZipArchive(); + $zipClass = Settings::getZipClass(); + $zip = new $zipClass(); if ($zip->open($pFilename) === true) { // check if it is an OOXML archive $rels = simplexml_load_string($this->getFromZipArchive($zip, "_rels/.rels")); @@ -59,7 +61,7 @@ class Word2007 extends Reader implements IReader /** * Get zip content * - * @param \ZipArchive $archive + * @param mixed $archive * @param string $fileName * @param bool $removeNamespace * @return mixed @@ -101,7 +103,8 @@ class Word2007 extends Reader implements IReader // Initialisations $word = new PhpWord(); - $zip = new \ZipArchive(); + $zipClass = Settings::getZipClass(); + $zip = new $zipClass(); $zip->open($pFilename); // Read properties and documents diff --git a/src/PhpWord/Section.php b/src/PhpWord/Section.php index e350f7f8..090ca236 100644 --- a/src/PhpWord/Section.php +++ b/src/PhpWord/Section.php @@ -17,7 +17,6 @@ use PhpOffice\PhpWord\Section\Link; use PhpOffice\PhpWord\Section\ListItem; use PhpOffice\PhpWord\Section\Object; use PhpOffice\PhpWord\Section\PageBreak; -use PhpOffice\PhpWord\Section\Settings; use PhpOffice\PhpWord\Section\Table; use PhpOffice\PhpWord\Section\Text; use PhpOffice\PhpWord\Section\TextBreak; @@ -76,7 +75,7 @@ class Section public function __construct($sectionCount, $settings = null) { $this->_sectionCount = $sectionCount; - $this->_settings = new Settings(); + $this->_settings = new \PhpOffice\PhpWord\Section\Settings(); $this->setSettings($settings); } diff --git a/src/PhpWord/Section/CheckBox.php b/src/PhpWord/Section/CheckBox.php index 00f75e54..5e101d9f 100644 --- a/src/PhpWord/Section/CheckBox.php +++ b/src/PhpWord/Section/CheckBox.php @@ -34,14 +34,14 @@ class CheckBox /** * Text style * - * @var Font + * @var string|Font */ private $fontStyle; /** * Paragraph style * - * @var Paragraph + * @var string|Paragraph */ private $paragraphStyle; @@ -50,8 +50,8 @@ class CheckBox * * @param string $name * @param string $text - * @param Font $fontStyle - * @param Paragraph $paragraphStyle + * @param mixed $fontStyle + * @param mixed $paragraphStyle */ public function __construct($name = null, $text = null, $fontStyle = null, $paragraphStyle = null) { @@ -66,9 +66,9 @@ class CheckBox /** * Set Text style * - * @param Font $style - * @param Paragraph $paragraphStyle - * @return Font + * @param mixed $style + * @param mixed $paragraphStyle + * @return string|Font */ public function setFontStyle($style = null, $paragraphStyle = null) { @@ -90,7 +90,7 @@ class CheckBox /** * Get Text style * - * @return Font + * @return string|Font */ public function getFontStyle() { @@ -100,8 +100,8 @@ class CheckBox /** * Set Paragraph style * - * @param Paragraph $style - * @return Paragraph + * @param mixed $style + * @return string|Paragraph */ public function setParagraphStyle($style = null) { @@ -121,7 +121,7 @@ class CheckBox /** * Get Paragraph style * - * @return Paragraph + * @return string|Paragraph */ public function getParagraphStyle() { diff --git a/src/PhpWord/Section/Footer/PreserveText.php b/src/PhpWord/Section/Footer/PreserveText.php index 579ca271..6fbd7bba 100644 --- a/src/PhpWord/Section/Footer/PreserveText.php +++ b/src/PhpWord/Section/Footer/PreserveText.php @@ -27,14 +27,14 @@ class PreserveText /** * Text style * - * @var \PhpOffice\PhpWord\Style\Font + * @var string|Font */ private $_styleFont; /** * Paragraph style * - * @var \PhpOffice\PhpWord\Style\Paragraph + * @var string|Paragraph */ private $_styleParagraph; @@ -45,7 +45,7 @@ class PreserveText * @param string $text * @param mixed $styleFont * @param mixed $styleParagraph - * @return PHPWord_Section_Footer_PreserveText + * @return $this */ public function __construct($text = null, $styleFont = null, $styleParagraph = null) { @@ -88,7 +88,7 @@ class PreserveText /** * Get Text style * - * @return \PhpOffice\PhpWord\Style\Font + * @return string|Font */ public function getFontStyle() { @@ -98,7 +98,7 @@ class PreserveText /** * Get Paragraph style * - * @return \PhpOffice\PhpWord\Style\Paragraph + * @return string|Paragraph */ public function getParagraphStyle() { diff --git a/src/PhpWord/Section/Link.php b/src/PhpWord/Section/Link.php index 39b1c56d..240d0445 100644 --- a/src/PhpWord/Section/Link.php +++ b/src/PhpWord/Section/Link.php @@ -41,14 +41,14 @@ class Link /** * Link style * - * @var \PhpOffice\PhpWord\Style\Font + * @var string|Font */ private $_styleFont; /** * Paragraph style * - * @var \PhpOffice\PhpWord\Style\Paragraph + * @var string|Paragraph */ private $_styleParagraph; @@ -140,7 +140,7 @@ class Link /** * Get Text style * - * @return \PhpOffice\PhpWord\Style\Font + * @return string|Font */ public function getFontStyle() { @@ -150,7 +150,7 @@ class Link /** * Get Paragraph style * - * @return \PhpOffice\PhpWord\Style\Paragraph + * @return string|Paragraph */ public function getParagraphStyle() { diff --git a/src/PhpWord/Section/Text.php b/src/PhpWord/Section/Text.php index a99945fe..4e228cda 100644 --- a/src/PhpWord/Section/Text.php +++ b/src/PhpWord/Section/Text.php @@ -27,14 +27,14 @@ class Text /** * Text style * - * @var \PhpOffice\PhpWord\Style\Font + * @var string|Font */ private $fontStyle; /** * Paragraph style * - * @var \PhpOffice\PhpWord\Style\Paragraph + * @var string|Paragraph */ private $paragraphStyle; @@ -42,8 +42,8 @@ class Text * Create a new Text Element * * @param string $text - * @param null|array|\PhpOffice\PhpWord\Style\Font $fontStyle - * @param null|array|\PhpOffice\PhpWord\Style\Paragraph $paragraphStyle + * @param mixed $fontStyle + * @param mixed $paragraphStyle */ public function __construct($text = null, $fontStyle = null, $paragraphStyle = null) { @@ -55,9 +55,9 @@ class Text /** * Set Text style * - * @param null|array|\PhpOffice\PhpWord\Style\Font $style - * @param null|array|\PhpOffice\PhpWord\Style\Paragraph $paragraphStyle - * @return \PhpOffice\PhpWord\Style\Font + * @param string|array|Font $style + * @param string|array|Paragraph $paragraphStyle + * @return string|Font */ public function setFontStyle($style = null, $paragraphStyle = null) { @@ -79,7 +79,7 @@ class Text /** * Get Text style * - * @return \PhpOffice\PhpWord\Style\Font + * @return string|Font */ public function getFontStyle() { @@ -89,8 +89,8 @@ class Text /** * Set Paragraph style * - * @param null|array|\PhpOffice\PhpWord\Style\Paragraph $style - * @return null|\PhpOffice\PhpWord\Style\Paragraph + * @param string|array|Paragraph $style + * @return string|Paragraph */ public function setParagraphStyle($style = null) { @@ -110,7 +110,7 @@ class Text /** * Get Paragraph style * - * @return \PhpOffice\PhpWord\Style\Paragraph + * @return string|Paragraph */ public function getParagraphStyle() { diff --git a/src/PhpWord/Section/TextBreak.php b/src/PhpWord/Section/TextBreak.php index 97f6deca..4df4c780 100755 --- a/src/PhpWord/Section/TextBreak.php +++ b/src/PhpWord/Section/TextBreak.php @@ -20,14 +20,14 @@ class TextBreak /** * Paragraph style * - * @var \PhpOffice\PhpWord\Style\Pagaraph + * @var string|Paragraph */ private $paragraphStyle = null; /** * Text style * - * @var \PhpOffice\PhpWord\Style\Font + * @var string|Font */ private $fontStyle = null; @@ -50,9 +50,9 @@ class TextBreak /** * Set Text style * - * @param null|array|\PhpOffice\PhpWord\Style\Font $style - * @param null|array|\PhpOffice\PhpWord\Style\Paragraph $paragraphStyle - * @return \PhpOffice\PhpWord\Style\Font + * @param mixed $style + * @param mixed $paragraphStyle + * @return string|Font */ public function setFontStyle($style = null, $paragraphStyle = null) { @@ -72,7 +72,7 @@ class TextBreak /** * Get Text style * - * @return \PhpOffice\PhpWord\Style\Font + * @return string|Font */ public function getFontStyle() { @@ -82,8 +82,8 @@ class TextBreak /** * Set Paragraph style * - * @param null|array|\PhpOffice\PhpWord\Style\Paragraph $style - * @return null|\PhpOffice\PhpWord\Style\Paragraph + * @param string|array|Paragraph $style + * @return string|Paragraph */ public function setParagraphStyle($style = null) { @@ -101,7 +101,7 @@ class TextBreak /** * Get Paragraph style * - * @return \PhpOffice\PhpWord\Style\Paragraph + * @return string|Paragraph */ public function getParagraphStyle() { diff --git a/src/PhpWord/Section/TextRun.php b/src/PhpWord/Section/TextRun.php index 497f6f45..bcb5173a 100755 --- a/src/PhpWord/Section/TextRun.php +++ b/src/PhpWord/Section/TextRun.php @@ -12,6 +12,7 @@ namespace PhpOffice\PhpWord\Section; use PhpOffice\PhpWord\Exceptions\InvalidImageException; use PhpOffice\PhpWord\Media; use PhpOffice\PhpWord\Shared\String; +use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; /** @@ -22,7 +23,7 @@ class TextRun /** * Paragraph style * - * @var \PhpOffice\PhpWord\Style\Paragraph + * @var Paragraph */ private $_styleParagraph; @@ -123,8 +124,8 @@ class TextRun * Add TextBreak * * @param int $count - * @param null|string|array|\PhpOffice\PhpWord\Style\Font $fontStyle - * @param null|string|array|\PhpOffice\PhpWord\Style\Paragraph $paragraphStyle + * @param mixed $fontStyle + * @param mixed $paragraphStyle */ public function addTextBreak($count = 1, $fontStyle = null, $paragraphStyle = null) { @@ -161,7 +162,7 @@ class TextRun /** * Get Paragraph style * - * @return \PhpOffice\PhpWord\Style\Paragraph + * @return string|Paragraph */ public function getParagraphStyle() { diff --git a/src/PhpWord/Settings.php b/src/PhpWord/Settings.php index 6a648651..0fb8bad0 100644 --- a/src/PhpWord/Settings.php +++ b/src/PhpWord/Settings.php @@ -14,6 +14,10 @@ namespace PhpOffice\PhpWord; */ class Settings { + /** Available Zip library classes */ + const PCLZIP = 'PhpOffice\\PhpWord\\Shared\\ZipArchive'; + const ZIPARCHIVE = 'ZipArchive'; + /** * Compatibility option for XMLWriter * @@ -21,6 +25,15 @@ class Settings */ private static $_xmlWriterCompatibility = true; + /** + * Name of the class used for Zip file management + * e.g. + * ZipArchive + * + * @var string + */ + private static $_zipClass = self::ZIPARCHIVE; + /** * Set the compatibility option used by the XMLWriter * @@ -45,4 +58,34 @@ class Settings { return self::$_xmlWriterCompatibility; } + + /** + * Set the Zip handler Class that PHPWord should use for Zip file management (PCLZip or ZipArchive) + * + * @param string $zipClass The Zip handler class that PHPWord should use for Zip file management + * e.g. Settings::PCLZip or Settings::ZipArchive + * @return boolean Success or failure + */ + public static function setZipClass($zipClass) + { + if (($zipClass === self::PCLZIP) || + ($zipClass === self::ZIPARCHIVE)) { + self::$_zipClass = $zipClass; + return true; + } + return false; + } // function setZipClass() + + /** + * Return the name of the Zip handler Class that PHPWord is configured to use (PCLZip or ZipArchive) + * or Zip file management + * + * @return string Name of the Zip handler Class that PHPWord is configured to use + * for Zip file management + * e.g. Settings::PCLZip or Settings::ZipArchive + */ + public static function getZipClass() + { + return self::$_zipClass; + } // function getZipClass() } diff --git a/src/PhpWord/Shared/PCLZip/pclzip.lib.php b/src/PhpWord/Shared/PCLZip/pclzip.lib.php new file mode 100644 index 00000000..4e2a496f --- /dev/null +++ b/src/PhpWord/Shared/PCLZip/pclzip.lib.php @@ -0,0 +1,5691 @@ +zipname = $p_zipname; + $this->zip_fd = 0; + $this->magic_quotes_status = -1; + + // ----- Return + return; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // create($p_filelist, $p_add_dir="", $p_remove_dir="") + // create($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two different synopsis. The first one is historical. + // This method creates a Zip Archive. The Zip file is created in the + // filesystem. The files and directories indicated in $p_filelist + // are added in the archive. See the parameters description for the + // supported format of $p_filelist. + // When a directory is in the list, the directory and its content is added + // in the archive. + // In this synopsis, the function takes an optional variable list of + // options. See bellow the supported options. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function create($p_filelist) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove from the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } + else if ($v_size > 2) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Invalid number / type of arguments"); + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); + + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + } + + // ----- The list is a list of string names + else { + $v_string_list = $p_filelist; + } + } + + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + } + + // ----- Invalid variable type for $p_filelist + else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); + return 0; + } + + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + if ($v_string != '') { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } + else { + } + } + } + + // ----- For each file in the list check the attributes + $v_supported_attributes + = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' + ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' + ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' + ,PCLZIP_ATT_FILE_MTIME => 'optional' + ,PCLZIP_ATT_FILE_CONTENT => 'optional' + ,PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, + $v_filedescr_list[], + $v_options, + $v_supported_attributes); + if ($v_result != 1) { + return 0; + } + } + + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Call the create fct + $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Return + return $p_result_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // add($p_filelist, $p_add_dir="", $p_remove_dir="") + // add($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two synopsis. The first one is historical. + // This methods add the list of files in an existing archive. + // If a file with the same name already exists, it is added at the end of the + // archive, the first one is still present. + // If the archive does not exist, it is created. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_OPT_ADD_COMMENT : + // PCLZIP_OPT_PREPEND_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function add($p_filelist) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_ADD_COMMENT => 'optional', + PCLZIP_OPT_PREPEND_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); + + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + } + + // ----- The list is a list of string names + else { + $v_string_list = $p_filelist; + } + } + + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + } + + // ----- Invalid variable type for $p_filelist + else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist"); + return 0; + } + + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } + } + + // ----- For each file in the list check the attributes + $v_supported_attributes + = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' + ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' + ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' + ,PCLZIP_ATT_FILE_MTIME => 'optional' + ,PCLZIP_ATT_FILE_CONTENT => 'optional' + ,PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, + $v_filedescr_list[], + $v_options, + $v_supported_attributes); + if ($v_result != 1) { + return 0; + } + } + + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Call the create fct + $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Return + return $p_result_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : listContent() + // Description : + // This public method, gives the list of the files and directories, with their + // properties. + // The properties of each entries in the list are (used also in other functions) : + // filename : Name of the file. For a create or add action it is the filename + // given by the user. For an extract function it is the filename + // of the extracted file. + // stored_filename : Name of the file / directory stored in the archive. + // size : Size of the stored file. + // compressed_size : Size of the file's data compressed in the archive + // (without the headers overhead) + // mtime : Last known modification date of the file (UNIX timestamp) + // comment : Comment associated with the file + // folder : true | false + // index : index of the file in the archive + // status : status of the action (depending of the action) : + // Values are : + // ok : OK ! + // filtered : the file / dir is not extracted (filtered by user) + // already_a_directory : the file can not be extracted because a + // directory with the same name already exists + // write_protected : the file can not be extracted because a file + // with the same name already exists and is + // write protected + // newer_exist : the file was not extracted because a newer file exists + // path_creation_fail : the file is not extracted because the folder + // does not exist and can not be created + // write_error : the file was not extracted because there was a + // error while writing the file + // read_error : the file was not extracted because there was a error + // while reading the file + // invalid_header : the file was not extracted because of an archive + // format error (bad file header) + // Note that each time a method can continue operating when there + // is an action error on a file, the error is only logged in the file status. + // Return Values : + // 0 on an unrecoverable failure, + // The list of the files in the archive. + // -------------------------------------------------------------------------------- + function listContent() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Call the extracting fct + $p_list = array(); + if (($v_result = $this->privList($p_list)) != 1) + { + unset($p_list); + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // extract($p_path="./", $p_remove_path="") + // extract([$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method extract all the files / directories from the archive to the + // folder indicated in $p_path. + // If you want to ignore the 'root' part of path of the memorized files + // you can indicate this in the optional $p_remove_path parameter. + // By default, if a newer file with the same name already exists, the + // file is not extracted. + // + // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions + // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append + // at the end of the path value of PCLZIP_OPT_PATH. + // Parameters : + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 or a negative value on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function extract() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); +// $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional' + ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' + ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Trace + + // ----- Call the extracting fct + $p_list = array(); + $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, + $v_remove_all_path, $v_options); + if ($v_result < 1) { + unset($p_list); + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + + // -------------------------------------------------------------------------------- + // Function : + // extractByIndex($p_index, $p_path="./", $p_remove_path="") + // extractByIndex($p_index, [$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method is doing a partial extract of the archive. + // The extracted files or folders are identified by their index in the + // archive (from 0 to n). + // Note that if the index identify a folder, only the folder entry is + // extracted, not all the files included in the archive. + // Parameters : + // $p_index : A single index (integer) or a string of indexes of files to + // extract. The form of the string is "0,4-6,8-12" with only numbers + // and '-' for range or ',' to separate ranges. No spaces or ';' + // are allowed. + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and + // not as files. + // The resulting content is in a new field 'content' in the file + // structure. + // This option must be used alone (any other options are ignored). + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + //function extractByIndex($p_index, options...) + function extractByIndex($p_index) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); +// $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional' + ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' + ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + } + else { + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Trace + + // ----- Trick + // Here I want to reuse extractByRule(), so I need to parse the $p_index + // with privParseOptions() + $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index); + $v_options_trick = array(); + $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, + array (PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + return 0; + } + $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Call the extracting fct + if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) { + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // delete([$p_option, $p_option_value, ...]) + // Description : + // This method removes files from the archive. + // If no parameters are given, then all the archive is emptied. + // Parameters : + // None or optional arguments. + // Options : + // PCLZIP_OPT_BY_INDEX : + // PCLZIP_OPT_BY_NAME : + // PCLZIP_OPT_BY_EREG : + // PCLZIP_OPT_BY_PREG : + // Return Values : + // 0 on failure, + // The list of the files which are still present in the archive. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function delete() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Call the delete fct + $v_list = array(); + if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) { + $this->privSwapBackMagicQuotes(); + unset($v_list); + return(0); + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : deleteByIndex() + // Description : + // ***** Deprecated ***** + // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. + // -------------------------------------------------------------------------------- + function deleteByIndex($p_index) + { + + $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : properties() + // Description : + // This method gives the properties of the archive. + // The properties are : + // nb : Number of files in the archive + // comment : Comment associated with the archive file + // status : not_exist, ok + // Parameters : + // None + // Return Values : + // 0 on failure, + // An array with the archive properties. + // -------------------------------------------------------------------------------- + function properties() + { + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + $this->privSwapBackMagicQuotes(); + return(0); + } + + // ----- Default properties + $v_prop = array(); + $v_prop['comment'] = ''; + $v_prop['nb'] = 0; + $v_prop['status'] = 'not_exist'; + + // ----- Look if file exists + if (@is_file($this->zipname)) + { + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + + // ----- Return + return 0; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privSwapBackMagicQuotes(); + return 0; + } + + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Set the user attributes + $v_prop['comment'] = $v_central_dir['comment']; + $v_prop['nb'] = $v_central_dir['entries']; + $v_prop['status'] = 'ok'; + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_prop; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : duplicate() + // Description : + // This method creates an archive by copying the content of an other one. If + // the archive already exist, it is replaced by the new one without any warning. + // Parameters : + // $p_archive : The filename of a valid archive, or + // a valid PclZip object. + // Return Values : + // 1 on success. + // 0 or a negative value on error (error code). + // -------------------------------------------------------------------------------- + function duplicate($p_archive) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the $p_archive is a PclZip object + if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) + { + + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive->zipname); + } + + // ----- Look if the $p_archive is a string (so a filename) + else if (is_string($p_archive)) + { + + // ----- Check that $p_archive is a valid zip file + // TBC : Should also check the archive format + if (!is_file($p_archive)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); + $v_result = PCLZIP_ERR_MISSING_FILE; + } + else { + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive); + } + } + + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : merge() + // Description : + // This method merge the $p_archive_to_add archive at the end of the current + // one ($this). + // If the archive ($this) does not exist, the merge becomes a duplicate. + // If the $p_archive_to_add archive does not exist, the merge is a success. + // Parameters : + // $p_archive_to_add : It can be directly the filename of a valid zip archive, + // or a PclZip object archive. + // Return Values : + // 1 on success, + // 0 or negative values on error (see below). + // -------------------------------------------------------------------------------- + function merge($p_archive_to_add) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Look if the $p_archive_to_add is a PclZip object + if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) + { + + // ----- Merge the archive + $v_result = $this->privMerge($p_archive_to_add); + } + + // ----- Look if the $p_archive_to_add is a string (so a filename) + else if (is_string($p_archive_to_add)) + { + + // ----- Create a temporary archive + $v_object_archive = new PclZip($p_archive_to_add); + + // ----- Merge the archive + $v_result = $this->privMerge($v_object_archive); + } + + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + + + // -------------------------------------------------------------------------------- + // Function : errorCode() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorCode() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorCode()); + } + else { + return($this->error_code); + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorName() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorName($p_with_code=false) + { + $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', + PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', + PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', + PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', + PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', + PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', + PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', + PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', + PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', + PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', + PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', + PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', + PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', + PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', + PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', + PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', + PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE', + PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', + PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION' + ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE' + ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION' + ); + + if (isset($v_name[$this->error_code])) { + $v_value = $v_name[$this->error_code]; + } + else { + $v_value = 'NoName'; + } + + if ($p_with_code) { + return($v_value.' ('.$this->error_code.')'); + } + else { + return($v_value); + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorInfo() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorInfo($p_full=false) + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorString()); + } + else { + if ($p_full) { + return($this->errorName(true)." : ".$this->error_string); + } + else { + return($this->error_string." [code ".$this->error_code."]"); + } + } + } + // -------------------------------------------------------------------------------- + + +// -------------------------------------------------------------------------------- +// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** +// ***** ***** +// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** +// -------------------------------------------------------------------------------- + + + + // -------------------------------------------------------------------------------- + // Function : privCheckFormat() + // Description : + // This method check that the archive exists and is a valid zip archive. + // Several level of check exists. (futur) + // Parameters : + // $p_level : Level of check. Default 0. + // 0 : Check the first bytes (magic codes) (default value)) + // 1 : 0 + Check the central directory (futur) + // 2 : 1 + Check each file header (futur) + // Return Values : + // true on success, + // false on error, the error code is set. + // -------------------------------------------------------------------------------- + function privCheckFormat($p_level=0) + { + $v_result = true; + + // ----- Reset the file system cache + clearstatcache(); + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the file exits + if (!is_file($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'"); + return(false); + } + + // ----- Check that the file is readeable + if (!is_readable($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'"); + return(false); + } + + // ----- Check the magic code + // TBC + + // ----- Check the central header + // TBC + + // ----- Check each file header + // TBC + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privParseOptions() + // Description : + // This internal methods reads the variable list of arguments ($p_options_list, + // $p_size) and generate an array with the options and values ($v_result_list). + // $v_requested_options contains the options that can be present and those that + // must be present. + // $v_requested_options is an array, with the option value as key, and 'optional', + // or 'mandatory' as value. + // Parameters : + // See above. + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false) + { + $v_result=1; + + // ----- Read the options + $i=0; + while ($i<$p_size) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$p_options_list[$i]])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for next option + switch ($p_options_list[$i]) { + // ----- Look for options that request a path value + case PCLZIP_OPT_PATH : + case PCLZIP_OPT_REMOVE_PATH : + case PCLZIP_OPT_ADD_PATH : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_THRESHOLD : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + return PclZip::errorCode(); + } + + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + return PclZip::errorCode(); + } + + // ----- Check the value + $v_value = $p_options_list[$i+1]; + if ((!is_integer($v_value)) || ($v_value<0)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + return PclZip::errorCode(); + } + + // ----- Get the value (and convert it in bytes) + $v_result_list[$p_options_list[$i]] = $v_value*1048576; + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_ON : + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_TEMP_FILE_OFF : + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'"); + return PclZip::errorCode(); + } + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if ( is_string($p_options_list[$i+1]) + && ($p_options_list[$i+1] != '')) { + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); + $i++; + } + else { + } + break; + + // ----- Look for options that request an array of string for value + case PCLZIP_OPT_BY_NAME : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an EREG or PREG expression + case PCLZIP_OPT_BY_EREG : + // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG + // to PCLZIP_OPT_BY_PREG + $p_options_list[$i] = PCLZIP_OPT_BY_PREG; + case PCLZIP_OPT_BY_PREG : + //case PCLZIP_OPT_CRYPT : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that takes a string + case PCLZIP_OPT_COMMENT : + case PCLZIP_OPT_ADD_COMMENT : + case PCLZIP_OPT_PREPEND_COMMENT : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, + "Missing parameter value for option '" + .PclZipUtilOptionText($p_options_list[$i]) + ."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, + "Wrong parameter value for option '" + .PclZipUtilOptionText($p_options_list[$i]) + ."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an array of index + case PCLZIP_OPT_BY_INDEX : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_work_list = array(); + if (is_string($p_options_list[$i+1])) { + + // ----- Remove spaces + $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', ''); + + // ----- Parse items + $v_work_list = explode(",", $p_options_list[$i+1]); + } + else if (is_integer($p_options_list[$i+1])) { + $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + $v_work_list = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Reduce the index list + // each index item in the list must be a couple with a start and + // an end value : [0,3], [5-5], [8-10], ... + // ----- Check the format of each item + $v_sort_flag=false; + $v_sort_value=0; + for ($j=0; $j= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + $i++; + break; + + // ----- Look for options that request a call-back + case PCLZIP_CB_PRE_EXTRACT : + case PCLZIP_CB_POST_EXTRACT : + case PCLZIP_CB_PRE_ADD : + case PCLZIP_CB_POST_ADD : + /* for futur use + case PCLZIP_CB_PRE_DELETE : + case PCLZIP_CB_POST_DELETE : + case PCLZIP_CB_PRE_LIST : + case PCLZIP_CB_POST_LIST : + */ + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_function_name = $p_options_list[$i+1]; + + // ----- Check that the value is a valid existing function + if (!function_exists($v_function_name)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Set the attribute + $v_result_list[$p_options_list[$i]] = $v_function_name; + $i++; + break; + + default : + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Unknown parameter '" + .$p_options_list[$i]."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Next options + $i++; + } + + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($v_result_list[$key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); + + // ----- Return + return PclZip::errorCode(); + } + } + } + } + + // ----- Look for default values + if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { + + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privOptionDefaultThreshold() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privOptionDefaultThreshold(&$p_options) + { + $v_result=1; + + if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { + return $v_result; + } + + // ----- Get 'memory_limit' configuration value + $v_memory_limit = ini_get('memory_limit'); + $v_memory_limit = trim($v_memory_limit); + $last = strtolower(substr($v_memory_limit, -1)); + + if($last == 'g') + //$v_memory_limit = $v_memory_limit*1024*1024*1024; + $v_memory_limit = $v_memory_limit*1073741824; + if($last == 'm') + //$v_memory_limit = $v_memory_limit*1024*1024; + $v_memory_limit = $v_memory_limit*1048576; + if($last == 'k') + $v_memory_limit = $v_memory_limit*1024; + + $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO); + + + // ----- Sanity check : No threshold if value lower than 1M + if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { + unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privFileDescrParseAtt() + // Description : + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false) + { + $v_result=1; + + // ----- For each file in the list check the attributes + foreach ($p_file_list as $v_key => $v_value) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$v_key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for attribute + switch ($v_key) { + case PCLZIP_ATT_FILE_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['filename'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + break; + + case PCLZIP_ATT_FILE_NEW_SHORT_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_short_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + break; + + case PCLZIP_ATT_FILE_NEW_FULL_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_full_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + break; + + // ----- Look for options that takes a string + case PCLZIP_ATT_FILE_COMMENT : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['comment'] = $v_value; + break; + + case PCLZIP_ATT_FILE_MTIME : + if (!is_integer($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['mtime'] = $v_value; + break; + + case PCLZIP_ATT_FILE_CONTENT : + $p_filedescr['content'] = $v_value; + break; + + default : + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Unknown parameter '".$v_key."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($p_file_list[$key])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); + return PclZip::errorCode(); + } + } + } + } + + // end foreach + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privFileDescrExpand() + // Description : + // This method look for each item of the list to see if its a file, a folder + // or a string to be added as file. For any other type of files (link, other) + // just ignore the item. + // Then prepare the information that will be stored for that file. + // When its a folder, expand the folder with all the files that are in that + // folder (recursively). + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privFileDescrExpand(&$p_filedescr_list, &$p_options) + { + $v_result=1; + + // ----- Create a result list + $v_result_list = array(); + + // ----- Look each entry + for ($i=0; $iprivCalculateStoredFilename($v_descr, $p_options); + + // ----- Add the descriptor in result list + $v_result_list[sizeof($v_result_list)] = $v_descr; + + // ----- Look for folder + if ($v_descr['type'] == 'folder') { + // ----- List of items in folder + $v_dirlist_descr = array(); + $v_dirlist_nb = 0; + if ($v_folder_handler = @opendir($v_descr['filename'])) { + while (($v_item_handler = @readdir($v_folder_handler)) !== false) { + + // ----- Skip '.' and '..' + if (($v_item_handler == '.') || ($v_item_handler == '..')) { + continue; + } + + // ----- Compose the full filename + $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler; + + // ----- Look for different stored filename + // Because the name of the folder was changed, the name of the + // files/sub-folders also change + if (($v_descr['stored_filename'] != $v_descr['filename']) + && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { + if ($v_descr['stored_filename'] != '') { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler; + } + else { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; + } + } + + $v_dirlist_nb++; + } + + @closedir($v_folder_handler); + } + else { + // TBC : unable to open folder in read mode + } + + // ----- Expand each element of the list + if ($v_dirlist_nb != 0) { + // ----- Expand + if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { + return $v_result; + } + + // ----- Concat the resulting list + $v_result_list = array_merge($v_result_list, $v_dirlist_descr); + } + else { + } + + // ----- Free local array + unset($v_dirlist_descr); + } + } + + // ----- Get the result list + $p_filedescr_list = $v_result_list; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCreate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privCreate($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the file in write mode + if (($v_result = $this->privOpenFd('wb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Add the list of files + $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options); + + // ----- Close + $this->privCloseFd(); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAdd() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAdd($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Look if the archive exists or is empty + if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) + { + + // ----- Do a create + $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options); + + // ----- Return + return $v_result; + } + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) + { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Create the Central Dir files header + for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = $v_central_dir['comment']; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) { + $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Close + $this->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privOpenFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privOpenFd($p_mode) + { + $v_result=1; + + // ----- Look if already open + if ($this->zip_fd != 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCloseFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privCloseFd() + { + $v_result=1; + + if ($this->zip_fd != 0) + @fclose($this->zip_fd); + $this->zip_fd = 0; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddList() + // Description : + // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is + // different from the real path of the file. This is usefull if you want to have PclTar + // running in any directory, and memorize relative path from an other directory. + // Parameters : + // $p_list : An array containing the file or directory names to add in the tar + // $p_result_list : list of added files with their properties (specially the status field) + // $p_add_dir : Path to add in the filename path archived + // $p_remove_dir : Path to remove in the filename path archived + // Return Values : + // -------------------------------------------------------------------------------- +// function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + function privAddList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Create the Central Dir files header + for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileList() + // Description : + // Parameters : + // $p_filedescr_list : An array containing the file description + // or directory names to add in the zip + // $p_result_list : list of added files with their properties (specially the status field) + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_header = array(); + + // ----- Recuperate the current number of elt in list + $v_nb = sizeof($p_result_list); + + // ----- Loop on the files + for ($j=0; ($jprivAddFile($p_filedescr_list[$j], $v_header, + $p_options); + if ($v_result != 1) { + return $v_result; + } + + // ----- Store the file infos + $p_result_list[$v_nb++] = $v_header; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFile($p_filedescr, &$p_header, &$p_options) + { + $v_result=1; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + // TBC : Already done in the fileAtt check ... ? + if ($p_filename == "") { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for a stored different filename + /* TBC : Removed + if (isset($p_filedescr['stored_filename'])) { + $v_stored_filename = $p_filedescr['stored_filename']; + } + else { + $v_stored_filename = $p_filedescr['stored_filename']; + } + */ + + // ----- Set the file properties + clearstatcache(); + $p_header['version'] = 20; + $p_header['version_extracted'] = 10; + $p_header['flag'] = 0; + $p_header['compression'] = 0; + $p_header['crc'] = 0; + $p_header['compressed_size'] = 0; + $p_header['filename_len'] = strlen($p_filename); + $p_header['extra_len'] = 0; + $p_header['disk'] = 0; + $p_header['internal'] = 0; + $p_header['offset'] = 0; + $p_header['filename'] = $p_filename; +// TBC : Removed $p_header['stored_filename'] = $v_stored_filename; + $p_header['stored_filename'] = $p_filedescr['stored_filename']; + $p_header['extra'] = ''; + $p_header['status'] = 'ok'; + $p_header['index'] = -1; + + // ----- Look for regular file + if ($p_filedescr['type']=='file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = filesize($p_filename); + } + + // ----- Look for regular folder + else if ($p_filedescr['type']=='folder') { + $p_header['external'] = 0x00000010; + $p_header['mtime'] = filemtime($p_filename); + $p_header['size'] = filesize($p_filename); + } + + // ----- Look for virtual file + else if ($p_filedescr['type'] == 'virtual_file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = strlen($p_filedescr['content']); + } + + + // ----- Look for filetime + if (isset($p_filedescr['mtime'])) { + $p_header['mtime'] = $p_filedescr['mtime']; + } + else if ($p_filedescr['type'] == 'virtual_file') { + $p_header['mtime'] = time(); + } + else { + $p_header['mtime'] = filemtime($p_filename); + } + + // ------ Look for file comment + if (isset($p_filedescr['comment'])) { + $p_header['comment_len'] = strlen($p_filedescr['comment']); + $p_header['comment'] = $p_filedescr['comment']; + } + else { + $p_header['comment_len'] = 0; + $p_header['comment'] = ''; + } + + // ----- Look for pre-add callback + if (isset($p_options[PCLZIP_CB_PRE_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_header['status'] = "skipped"; + $v_result = 1; + } + + // ----- Update the informations + // Only some fields can be modified + if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { + $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); + } + } + + // ----- Look for empty stored filename + if ($p_header['stored_filename'] == "") { + $p_header['status'] = "filtered"; + } + + // ----- Check the path length + if (strlen($p_header['stored_filename']) > 0xFF) { + $p_header['status'] = 'filename_too_long'; + } + + // ----- Look if no error, or file not skipped + if ($p_header['status'] == 'ok') { + + // ----- Look for a file + if ($p_filedescr['type'] == 'file') { + // ----- Look for using temporary file to zip + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) + || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) { + $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + } + + // ----- Use "in memory" zip algo + else { + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + return PclZip::errorCode(); + } + + // ----- Read the file content + $v_content = @fread($v_file, $p_header['size']); + + // ----- Close the file + @fclose($v_file); + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + } + + // ----- Look for normal compression + else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + + } + + } + + // ----- Look for a virtual file (a file from string) + else if ($p_filedescr['type'] == 'virtual_file') { + + $v_content = $p_filedescr['content']; + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + } + + // ----- Look for normal compression + else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + } + + // ----- Look for a directory + else if ($p_filedescr['type'] == 'folder') { + // ----- Look for directory last '/' + if (@substr($p_header['stored_filename'], -1) != '/') { + $p_header['stored_filename'] .= '/'; + } + + // ----- Set the file properties + $p_header['size'] = 0; + //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked + $p_header['external'] = 0x00000010; // Value for a folder : to be checked + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) + { + return $v_result; + } + } + } + + // ----- Look for post-add callback + if (isset($p_options[PCLZIP_CB_POST_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Ignored + $v_result = 1; + } + + // ----- Update the informations + // Nothing can be modified + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) + { + $v_result=PCLZIP_ERR_NO_ERROR; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + return PclZip::errorCode(); + } + + // ----- Creates a compressed temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; + if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = filesize($p_filename); + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @gzputs($v_file_compressed, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file); + @gzclose($v_file_compressed); + + // ----- Check the minimum file size + if (filesize($v_gzip_temp_name) < 18) { + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes'); + return PclZip::errorCode(); + } + + // ----- Extract the compressed attributes + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + // ----- Read the gzip file header + $v_binary_data = @fread($v_file_compressed, 10); + $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data); + + // ----- Check some parameters + $v_data_header['os'] = bin2hex($v_data_header['os']); + + // ----- Read the gzip file footer + @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8); + $v_binary_data = @fread($v_file_compressed, 8); + $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data); + + // ----- Set the attributes + $p_header['compression'] = ord($v_data_header['cm']); + //$p_header['mtime'] = $v_data_header['mtime']; + $p_header['crc'] = $v_data_footer['crc']; + $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18; + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + return $v_result; + } + + // ----- Add the compressed data + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) + { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + fseek($v_file_compressed, 10); + $v_size = $p_header['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file_compressed, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Unlink the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCalculateStoredFilename() + // Description : + // Based on file descriptor properties and global options, this method + // calculate the filename that will be stored in the archive. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privCalculateStoredFilename(&$p_filedescr, &$p_options) + { + $v_result=1; + + // ----- Working variables + $p_filename = $p_filedescr['filename']; + if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { + $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH]; + } + else { + $p_add_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) { + $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH]; + } + else { + $p_remove_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + else { + $p_remove_all_dir = 0; + } + + + // ----- Look for full name change + if (isset($p_filedescr['new_full_name'])) { + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); + } + + // ----- Look for path and/or short name change + else { + + // ----- Look for short name change + // Its when we cahnge just the filename but not the path + if (isset($p_filedescr['new_short_name'])) { + $v_path_info = pathinfo($p_filename); + $v_dir = ''; + if ($v_path_info['dirname'] != '') { + $v_dir = $v_path_info['dirname'].'/'; + } + $v_stored_filename = $v_dir.$p_filedescr['new_short_name']; + } + else { + // ----- Calculate the stored filename + $v_stored_filename = $p_filename; + } + + // ----- Look for all path to remove + if ($p_remove_all_dir) { + $v_stored_filename = basename($p_filename); + } + // ----- Look for partial path remove + else if ($p_remove_dir != "") { + if (substr($p_remove_dir, -1) != '/') + $p_remove_dir .= "/"; + + if ( (substr($p_filename, 0, 2) == "./") + || (substr($p_remove_dir, 0, 2) == "./")) { + + if ( (substr($p_filename, 0, 2) == "./") + && (substr($p_remove_dir, 0, 2) != "./")) { + $p_remove_dir = "./".$p_remove_dir; + } + if ( (substr($p_filename, 0, 2) != "./") + && (substr($p_remove_dir, 0, 2) == "./")) { + $p_remove_dir = substr($p_remove_dir, 2); + } + } + + $v_compare = PclZipUtilPathInclusion($p_remove_dir, + $v_stored_filename); + if ($v_compare > 0) { + if ($v_compare == 2) { + $v_stored_filename = ""; + } + else { + $v_stored_filename = substr($v_stored_filename, + strlen($p_remove_dir)); + } + } + } + + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); + + // ----- Look for path to add + if ($p_add_dir != "") { + if (substr($p_add_dir, -1) == "/") + $v_stored_filename = $p_add_dir.$v_stored_filename; + else + $v_stored_filename = $p_add_dir."/".$v_stored_filename; + } + } + + // ----- Filename (reduce the path of stored name) + $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); + $p_filedescr['stored_filename'] = $v_stored_filename; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteFileHeader(&$p_header) + { + $v_result=1; + + // ----- Store the offset position of the file + $p_header['offset'] = ftell($this->zip_fd); + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + // ----- Packed data + $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, + $p_header['version_extracted'], $p_header['flag'], + $p_header['compression'], $v_mtime, $v_mdate, + $p_header['crc'], $p_header['compressed_size'], + $p_header['size'], + strlen($p_header['stored_filename']), + $p_header['extra_len']); + + // ----- Write the first 148 bytes of the header in the archive + fputs($this->zip_fd, $v_binary_data, 30); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralFileHeader(&$p_header) + { + $v_result=1; + + // TBC + //for(reset($p_header); $key = key($p_header); next($p_header)) { + //} + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + + // ----- Packed data + $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, + $p_header['version'], $p_header['version_extracted'], + $p_header['flag'], $p_header['compression'], + $v_mtime, $v_mdate, $p_header['crc'], + $p_header['compressed_size'], $p_header['size'], + strlen($p_header['stored_filename']), + $p_header['extra_len'], $p_header['comment_len'], + $p_header['disk'], $p_header['internal'], + $p_header['external'], $p_header['offset']); + + // ----- Write the 42 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 46); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + if ($p_header['comment_len'] != 0) + { + fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) + { + $v_result=1; + + // ----- Packed data + $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, + $p_nb_entries, $p_size, + $p_offset, strlen($p_comment)); + + // ----- Write the 22 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 22); + + // ----- Write the variable fields + if (strlen($p_comment) != 0) + { + fputs($this->zip_fd, $p_comment, strlen($p_comment)); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privList() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privList(&$p_list) + { + $v_result=1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Go to beginning of Central Dir + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_central_dir['offset'])) + { + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + for ($i=0; $i<$v_central_dir['entries']; $i++) + { + // ----- Read the file header + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + $v_header['index'] = $i; + + // ----- Get the only interesting attributes + $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); + unset($v_header); + } + + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privConvertHeader2FileInfo() + // Description : + // This function takes the file informations from the central directory + // entries and extract the interesting parameters that will be given back. + // The resulting file infos are set in the array $p_info + // $p_info['filename'] : Filename with full path. Given by user (add), + // extracted in the filesystem (extract). + // $p_info['stored_filename'] : Stored filename in the archive. + // $p_info['size'] = Size of the file. + // $p_info['compressed_size'] = Compressed size of the file. + // $p_info['mtime'] = Last modification date of the file. + // $p_info['comment'] = Comment associated with the file. + // $p_info['folder'] = true/false : indicates if the entry is a folder or not. + // $p_info['status'] = status of the action on the file. + // $p_info['crc'] = CRC of the file content. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privConvertHeader2FileInfo($p_header, &$p_info) + { + $v_result=1; + + // ----- Get the interesting attributes + $v_temp_path = PclZipUtilPathReduction($p_header['filename']); + $p_info['filename'] = $v_temp_path; + $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']); + $p_info['stored_filename'] = $v_temp_path; + $p_info['size'] = $p_header['size']; + $p_info['compressed_size'] = $p_header['compressed_size']; + $p_info['mtime'] = $p_header['mtime']; + $p_info['comment'] = $p_header['comment']; + $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); + $p_info['index'] = $p_header['index']; + $p_info['status'] = $p_header['status']; + $p_info['crc'] = $p_header['crc']; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractByRule() + // Description : + // Extract a file or directory depending of rules (by index, by name, ...) + // Parameters : + // $p_file_list : An array where will be placed the properties of each + // extracted file + // $p_path : Path to add while writing the extracted files + // $p_remove_path : Path to remove (from the file memorized path) while writing the + // extracted files. If the path does not match the file path, + // the file is extracted with its memorized path. + // $p_remove_path does not apply to 'list' mode. + // $p_path and $p_remove_path are commulative. + // Return Values : + // 1 on success,0 or less on error (see error code list) + // -------------------------------------------------------------------------------- + function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result=1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check the path + if ( ($p_path == "") + || ( (substr($p_path, 0, 1) != "/") + && (substr($p_path, 0, 3) != "../") + && (substr($p_path,1,2)!=":/"))) + $p_path = "./".$p_path; + + // ----- Reduce the path last (and duplicated) '/' + if (($p_path != "./") && ($p_path != "/")) + { + // ----- Look for the path end '/' + while (substr($p_path, -1) == "/") + { + $p_path = substr($p_path, 0, strlen($p_path)-1); + } + } + + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) + { + $p_remove_path .= '/'; + } + $p_remove_path_size = strlen($p_remove_path); + + // ----- Open the zip file + if (($v_result = $this->privOpenFd('rb')) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + + // ----- Read each entry + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + + // ----- Read next Central dir entry + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Store the index + $v_header['index'] = $i; + + // ----- Store the file position + $v_pos_entry = ftell($this->zip_fd); + + // ----- Look for the specific extract rules + $v_extract = false; + + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_extract = true; + } + } + // ----- Look for a filename + elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_extract = true; + } + } + } + + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { + $v_extract = true; + } + } + */ + + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { + $v_extract = true; + } + } + + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_extract = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j+1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + break; + } + } + } + + // ----- Look for no rule, which means extract all the archive + else { + $v_extract = true; + } + + // ----- Check compression method + if ( ($v_extract) + && ( ($v_header['compression'] != 8) + && ($v_header['compression'] != 0))) { + $v_header['status'] = 'unsupported_compression'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, + "Filename '".$v_header['stored_filename']."' is " + ."compressed by an unsupported compression " + ."method (".$v_header['compression'].") "); + + return PclZip::errorCode(); + } + } + + // ----- Check encrypted files + if (($v_extract) && (($v_header['flag'] & 1) == 1)) { + $v_header['status'] = 'unsupported_encryption'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, + "Unsupported encryption for " + ." filename '".$v_header['stored_filename'] + ."'"); + + return PclZip::errorCode(); + } + } + + // ----- Look for real extraction + if (($v_extract) && ($v_header['status'] != 'ok')) { + $v_result = $this->privConvertHeader2FileInfo($v_header, + $p_file_list[$v_nb_extracted++]); + if ($v_result != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + $v_extract = false; + } + + // ----- Look for real extraction + if ($v_extract) + { + + // ----- Go to the file position + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_header['offset'])) + { + // ----- Close the zip file + $this->privCloseFd(); + + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for extraction as string + if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { + + $v_string = ''; + + // ----- Extracting the file + $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Set the file content + $p_file_list[$v_nb_extracted]['content'] = $v_string; + + // ----- Next extracted file + $v_nb_extracted++; + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + // ----- Look for extraction in standard output + elseif ( (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) + && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) { + // ----- Extracting the file in standard output + $v_result1 = $this->privExtractFileInOutput($v_header, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + // ----- Look for normal extraction + else { + // ----- Extracting the file + $v_result1 = $this->privExtractFile($v_header, + $p_path, $p_remove_path, + $p_remove_all_path, + $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + } + } + + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFile() + // Description : + // Parameters : + // Return Values : + // + // 1 : ... ? + // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback + // -------------------------------------------------------------------------------- + function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result=1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for all path to remove + if ($p_remove_all_path == true) { + // ----- Look for folder entry that not need to be extracted + if (($p_entry['external']&0x00000010)==0x00000010) { + + $p_entry['status'] = "filtered"; + + return $v_result; + } + + // ----- Get the basename of the path + $p_entry['filename'] = basename($p_entry['filename']); + } + + // ----- Look for path to remove + else if ($p_remove_path != "") + { + if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) + { + + // ----- Change the file status + $p_entry['status'] = "filtered"; + + // ----- Return + return $v_result; + } + + $p_remove_path_size = strlen($p_remove_path); + if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) + { + + // ----- Remove the path + $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); + + } + } + + // ----- Add the path + if ($p_path != '') { + $p_entry['filename'] = $p_path."/".$p_entry['filename']; + } + + // ----- Check a base_dir_restriction + if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { + $v_inclusion + = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], + $p_entry['filename']); + if ($v_inclusion == 0) { + + PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, + "Filename '".$p_entry['filename']."' is " + ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION"); + + return PclZip::errorCode(); + } + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Look for specific actions while the file exist + if (file_exists($p_entry['filename'])) + { + + // ----- Look if file is a directory + if (is_dir($p_entry['filename'])) + { + + // ----- Change the file status + $p_entry['status'] = "already_a_directory"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, + "Filename '".$p_entry['filename']."' is " + ."already used by an existing directory"); + + return PclZip::errorCode(); + } + } + // ----- Look if file is write protected + else if (!is_writeable($p_entry['filename'])) + { + + // ----- Change the file status + $p_entry['status'] = "write_protected"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, + "Filename '".$p_entry['filename']."' exists " + ."and is write protected"); + + return PclZip::errorCode(); + } + } + + // ----- Look if the extracted file is older + else if (filemtime($p_entry['filename']) > $p_entry['mtime']) + { + // ----- Change the file status + if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER])) + && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) { + } + else { + $p_entry['status'] = "newer_exist"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, + "Newer version of '".$p_entry['filename']."' exists " + ."and option PCLZIP_OPT_REPLACE_NEWER is not selected"); + + return PclZip::errorCode(); + } + } + } + else { + } + } + + // ----- Check the directory availability and create it if necessary + else { + if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) + $v_dir_to_check = $p_entry['filename']; + else if (!strstr($p_entry['filename'], "/")) + $v_dir_to_check = ""; + else + $v_dir_to_check = dirname($p_entry['filename']); + + if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { + + // ----- Change the file status + $p_entry['status'] = "path_creation_fail"; + + // ----- Return + //return $v_result; + $v_result = 1; + } + } + } + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) + { + // ----- Look for not compressed file + if ($p_entry['compression'] == 0) { + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) + { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + // ----- Return + return $v_result; + } + + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + /* Try to speed up the code + $v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_binary_data, $v_read_size); + */ + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Closing the destination file + fclose($v_dest_file); + + // ----- Change the file mtime + touch($p_entry['filename'], $p_entry['mtime']); + + + } + else { + // ----- TBC + // Need to be finished + if (($p_entry['flag'] & 1) == 1) { + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.'); + return PclZip::errorCode(); + } + + + // ----- Look for using temporary file to unzip + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) + || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) { + $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + } + + // ----- Look for extract in memory + else { + + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = @gzinflate($v_buffer); + unset($v_buffer); + if ($v_file_content === FALSE) { + + // ----- Change the file status + // TBC + $p_entry['status'] = "error"; + + return $v_result; + } + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + return $v_result; + } + + // ----- Write the uncompressed data + @fwrite($v_dest_file, $v_file_content, $p_entry['size']); + unset($v_file_content); + + // ----- Closing the destination file + @fclose($v_dest_file); + + } + + // ----- Change the file mtime + @touch($p_entry['filename'], $p_entry['mtime']); + } + + // ----- Look for chmod option + if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { + + // ----- Change the mode of the file + @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); + } + + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileUsingTempFile(&$p_entry, &$p_options) + { + $v_result=1; + + // ----- Creates a temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; + if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); + return PclZip::errorCode(); + } + + + // ----- Write gz file format header + $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3)); + @fwrite($v_dest_file, $v_binary_data, 10); + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Write gz file format footer + $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']); + @fwrite($v_dest_file, $v_binary_data, 8); + + // ----- Close the temporary file + @fclose($v_dest_file); + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + $p_entry['status'] = "write_error"; + return $v_result; + } + + // ----- Open the temporary gz file + if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) { + @fclose($v_dest_file); + $p_entry['status'] = "read_error"; + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($v_src_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + @fclose($v_dest_file); + @gzclose($v_src_file); + + // ----- Delete the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileInOutput() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileInOutput(&$p_entry, &$p_options) + { + $v_result=1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) { + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + // ----- Trace + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) { + // ----- Look for not compressed file + if ($p_entry['compressed_size'] == $p_entry['size']) { + + // ----- Read the file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Send the file to the output + echo $v_buffer; + unset($v_buffer); + } + else { + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = gzinflate($v_buffer); + unset($v_buffer); + + // ----- Send the file to the output + echo $v_file_content; + unset($v_file_content); + } + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileAsString() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileAsString(&$p_entry, &$p_string, &$p_options) + { + $v_result=1; + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) { + // ----- Look for not compressed file + // if ($p_entry['compressed_size'] == $p_entry['size']) + if ($p_entry['compression'] == 0) { + + // ----- Reading the file + $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); + } + else { + + // ----- Reading the file + $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + if (($p_string = @gzinflate($v_data)) === FALSE) { + // TBC + } + } + + // ----- Trace + } + else { + // TBC : error : can not extract a folder in a string + } + + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Swap the content to header + $v_local_header['content'] = $p_string; + $p_string = ''; + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Swap back the content to header + $p_string = $v_local_header['content']; + unset($v_local_header['content']); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadFileHeader(&$p_header) + { + $v_result=1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x04034b50) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 26); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 26) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); + + // ----- Get filename + $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); + + // ----- Get extra_fields + if ($v_data['extra_len'] != 0) { + $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); + } + else { + $p_header['extra'] = ''; + } + + // ----- Extract properties + $p_header['version_extracted'] = $v_data['version']; + $p_header['compression'] = $v_data['compression']; + $p_header['size'] = $v_data['size']; + $p_header['compressed_size'] = $v_data['compressed_size']; + $p_header['crc'] = $v_data['crc']; + $p_header['flag'] = $v_data['flag']; + $p_header['filename_len'] = $v_data['filename_len']; + + // ----- Recuperate date in UNIX format + $p_header['mdate'] = $v_data['mdate']; + $p_header['mtime'] = $v_data['mtime']; + if ($p_header['mdate'] && $p_header['mtime']) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } + else + { + $p_header['mtime'] = time(); + } + + // TBC + //for(reset($v_data); $key = key($v_data); next($v_data)) { + //} + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set the status field + $p_header['status'] = "ok"; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadCentralFileHeader(&$p_header) + { + $v_result=1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x02014b50) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 42); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 42) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); + + // ----- Get filename + if ($p_header['filename_len'] != 0) + $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); + else + $p_header['filename'] = ''; + + // ----- Get extra + if ($p_header['extra_len'] != 0) + $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); + else + $p_header['extra'] = ''; + + // ----- Get comment + if ($p_header['comment_len'] != 0) + $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); + else + $p_header['comment'] = ''; + + // ----- Extract properties + + // ----- Recuperate date in UNIX format + //if ($p_header['mdate'] && $p_header['mtime']) + // TBC : bug : this was ignoring time with 0/0/0 + if (1) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } + else + { + $p_header['mtime'] = time(); + } + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set default status to ok + $p_header['status'] = 'ok'; + + // ----- Look if it is a directory + if (substr($p_header['filename'], -1) == '/') { + //$p_header['external'] = 0x41FF0010; + $p_header['external'] = 0x00000010; + } + + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCheckFileHeaders() + // Description : + // Parameters : + // Return Values : + // 1 on success, + // 0 on error; + // -------------------------------------------------------------------------------- + function privCheckFileHeaders(&$p_local_header, &$p_central_header) + { + $v_result=1; + + // ----- Check the static values + // TBC + if ($p_local_header['filename'] != $p_central_header['filename']) { + } + if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) { + } + if ($p_local_header['flag'] != $p_central_header['flag']) { + } + if ($p_local_header['compression'] != $p_central_header['compression']) { + } + if ($p_local_header['mtime'] != $p_central_header['mtime']) { + } + if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { + } + + // ----- Look for flag bit 3 + if (($p_local_header['flag'] & 8) == 8) { + $p_local_header['size'] = $p_central_header['size']; + $p_local_header['compressed_size'] = $p_central_header['compressed_size']; + $p_local_header['crc'] = $p_central_header['crc']; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadEndCentralDir() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadEndCentralDir(&$p_central_dir) + { + $v_result=1; + + // ----- Go to the end of the zip file + $v_size = filesize($this->zipname); + @fseek($this->zip_fd, $v_size); + if (@ftell($this->zip_fd) != $v_size) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- First try : look if this is an archive with no commentaries (most of the time) + // in this case the end of central dir is at 22 bytes of the file end + $v_found = 0; + if ($v_size > 26) { + @fseek($this->zip_fd, $v_size-22); + if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read for bytes + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = @unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] == 0x06054b50) { + $v_found = 1; + } + + $v_pos = ftell($this->zip_fd); + } + + // ----- Go back to the maximum possible size of the Central Dir End Record + if (!$v_found) { + $v_maximum_size = 65557; // 0xFFFF + 22; + if ($v_maximum_size > $v_size) + $v_maximum_size = $v_size; + @fseek($this->zip_fd, $v_size-$v_maximum_size); + if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read byte per byte in order to find the signature + $v_pos = ftell($this->zip_fd); + $v_bytes = 0x00000000; + while ($v_pos < $v_size) + { + // ----- Read a byte + $v_byte = @fread($this->zip_fd, 1); + + // ----- Add the byte + //$v_bytes = ($v_bytes << 8) | Ord($v_byte); + // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number + // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. + $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); + + // ----- Compare the bytes + if ($v_bytes == 0x504b0506) + { + $v_pos++; + break; + } + + $v_pos++; + } + + // ----- Look if not found end of central dir + if ($v_pos == $v_size) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Read the first 18 bytes of the header + $v_binary_data = fread($this->zip_fd, 18); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 18) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); + + // ----- Check the global size + if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { + + // ----- Removed in release 2.2 see readme file + // The check of the file size is a little too strict. + // Some bugs where found when a zip is encrypted/decrypted with 'crypt'. + // While decrypted, zip has training 0 bytes + if (0) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, + 'The central dir is not at the end of the archive.' + .' Some trailing bytes exists after the archive.'); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Get comment + if ($v_data['comment_size'] != 0) { + $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); + } + else + $p_central_dir['comment'] = ''; + + $p_central_dir['entries'] = $v_data['entries']; + $p_central_dir['disk_entries'] = $v_data['disk_entries']; + $p_central_dir['offset'] = $v_data['offset']; + $p_central_dir['size'] = $v_data['size']; + $p_central_dir['disk'] = $v_data['disk']; + $p_central_dir['disk_start'] = $v_data['disk_start']; + + // TBC + //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { + //} + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDeleteByRule() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDeleteByRule(&$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Scan all the files + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + $v_header_list = array(); + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + + // ----- Read the file header + $v_header_list[$v_nb_extracted] = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + + return $v_result; + } + + + // ----- Store the index + $v_header_list[$v_nb_extracted]['index'] = $i; + + // ----- Look for the specific extract rules + $v_found = false; + + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } + elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */ + && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } + } + // ----- Look for a filename + elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_found = true; + } + } + } + + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + } + */ + + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + } + + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_found = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j+1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + break; + } + } + } + else { + $v_found = true; + } + + // ----- Look for deletion + if ($v_found) + { + unset($v_header_list[$v_nb_extracted]); + } + else + { + $v_nb_extracted++; + } + } + + // ----- Look if something need to be deleted + if ($v_nb_extracted > 0) { + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Creates a temporary zip archive + $v_temp_zip = new PclZip($v_zip_temp_name); + + // ----- Open the temporary zip file in write mode + if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Look which file need to be kept + for ($i=0; $izip_fd); + if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_local_header = array(); + if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Check that local file header is same as central file header + if ($this->privCheckFileHeaders($v_local_header, + $v_header_list[$i]) != 1) { + // TBC + } + unset($v_local_header); + + // ----- Write the file header + if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Read/write the data block + if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_temp_zip->zip_fd); + + // ----- Re-Create the Central Dir files header + for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Transform the header to a 'usable' info + $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { + // ----- Reset the file list + unset($v_header_list); + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Close + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Destroy the temporary archive + unset($v_temp_zip); + } + + // ----- Remove every files : reset the file + else if ($v_central_dir['entries'] != 0) { + $this->privCloseFd(); + + if (($v_result = $this->privOpenFd('wb')) != 1) { + return $v_result; + } + + if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) { + return $v_result; + } + + $this->privCloseFd(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDirCheck() + // Description : + // Check if a directory exists, if not it creates it and all the parents directory + // which may be useful. + // Parameters : + // $p_dir : Directory path to check. + // Return Values : + // 1 : OK + // -1 : Unable to create directory + // -------------------------------------------------------------------------------- + function privDirCheck($p_dir, $p_is_dir=false) + { + $v_result = 1; + + + // ----- Remove the final '/' + if (($p_is_dir) && (substr($p_dir, -1)=='/')) + { + $p_dir = substr($p_dir, 0, strlen($p_dir)-1); + } + + // ----- Check the directory availability + if ((is_dir($p_dir)) || ($p_dir == "")) + { + return 1; + } + + // ----- Extract parent directory + $p_parent_dir = dirname($p_dir); + + // ----- Just a check + if ($p_parent_dir != $p_dir) + { + // ----- Look for parent directory + if ($p_parent_dir != "") + { + if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) + { + return $v_result; + } + } + } + + // ----- Create the directory + if (!@mkdir($p_dir, 0777)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privMerge() + // Description : + // If $p_archive_to_add does not exist, the function exit with a success result. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privMerge(&$p_archive_to_add) + { + $v_result=1; + + // ----- Look if the archive_to_add exists + if (!is_file($p_archive_to_add->zipname)) + { + + // ----- Nothing to merge, so merge is a success + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Look if the archive exists + if (!is_file($this->zipname)) + { + + // ----- Do a duplicate + $v_result = $this->privDuplicate($p_archive_to_add->zipname); + + // ----- Return + return $v_result; + } + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Open the archive_to_add file + if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1) + { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir_to_add = array(); + if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + return $v_result; + } + + // ----- Go to beginning of File + @rewind($p_archive_to_add->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the files from the archive_to_add into the temporary file + $v_size = $v_central_dir_to_add['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_zip_temp_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the block of file headers from the archive_to_add + $v_size = $v_central_dir_to_add['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Merge the file comments + $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment']; + + // ----- Calculate the size of the (new) central header + $v_size = @ftell($v_zip_temp_fd)-$v_offset; + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive fd + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + @fclose($v_zip_temp_fd); + $this->zip_fd = null; + + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Close + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDuplicate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDuplicate($p_archive_filename) + { + $v_result=1; + + // ----- Look if the $p_archive_filename exists + if (!is_file($p_archive_filename)) + { + + // ----- Nothing to duplicate, so duplicate is a success. + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('wb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) + { + $this->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = filesize($p_archive_filename); + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close + $this->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privErrorLog() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorLog($p_error_code=0, $p_error_string='') + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclError($p_error_code, $p_error_string); + } + else { + $this->error_code = $p_error_code; + $this->error_string = $p_error_string; + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privErrorReset() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorReset() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclErrorReset(); + } + else { + $this->error_code = 0; + $this->error_string = ''; + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDisableMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDisableMagicQuotes() + { + $v_result=1; + + // ----- Look if function exists + if ( (!function_exists("get_magic_quotes_runtime")) + || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } + + // ----- Look if already done + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Get and memorize the magic_quote value + $this->magic_quotes_status = @get_magic_quotes_runtime(); + + // ----- Disable magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime(0); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privSwapBackMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privSwapBackMagicQuotes() + { + $v_result=1; + + // ----- Look if function exists + if ( (!function_exists("get_magic_quotes_runtime")) + || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } + + // ----- Look if something to do + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Swap back magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime($this->magic_quotes_status); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + } + // End of class + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathReduction() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilPathReduction($p_dir) + { + $v_result = ""; + + // ----- Look for not empty path + if ($p_dir != "") { + // ----- Explode path by directory names + $v_list = explode("/", $p_dir); + + // ----- Study directories from last to first + $v_skip = 0; + for ($i=sizeof($v_list)-1; $i>=0; $i--) { + // ----- Look for current path + if ($v_list[$i] == ".") { + // ----- Ignore this directory + // Should be the first $i=0, but no check is done + } + else if ($v_list[$i] == "..") { + $v_skip++; + } + else if ($v_list[$i] == "") { + // ----- First '/' i.e. root slash + if ($i == 0) { + $v_result = "/".$v_result; + if ($v_skip > 0) { + // ----- It is an invalid path, so the path is not modified + // TBC + $v_result = $p_dir; + $v_skip = 0; + } + } + // ----- Last '/' i.e. indicates a directory + else if ($i == (sizeof($v_list)-1)) { + $v_result = $v_list[$i]; + } + // ----- Double '/' inside the path + else { + // ----- Ignore only the double '//' in path, + // but not the first and last '/' + } + } + else { + // ----- Look for item to skip + if ($v_skip > 0) { + $v_skip--; + } + else { + $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:""); + } + } + } + + // ----- Look for skip + if ($v_skip > 0) { + while ($v_skip > 0) { + $v_result = '../'.$v_result; + $v_skip--; + } + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathInclusion() + // Description : + // This function indicates if the path $p_path is under the $p_dir tree. Or, + // said in an other way, if the file or sub-dir $p_path is inside the dir + // $p_dir. + // The function indicates also if the path is exactly the same as the dir. + // This function supports path with duplicated '/' like '//', but does not + // support '.' or '..' statements. + // Parameters : + // Return Values : + // 0 if $p_path is not inside directory $p_dir + // 1 if $p_path is inside directory $p_dir + // 2 if $p_path is exactly the same as $p_dir + // -------------------------------------------------------------------------------- + function PclZipUtilPathInclusion($p_dir, $p_path) + { + $v_result = 1; + + // ----- Look for path beginning by ./ + if ( ($p_dir == '.') + || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) { + $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1); + } + if ( ($p_path == '.') + || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) { + $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1); + } + + // ----- Explode dir and path by directory separator + $v_list_dir = explode("/", $p_dir); + $v_list_dir_size = sizeof($v_list_dir); + $v_list_path = explode("/", $p_path); + $v_list_path_size = sizeof($v_list_path); + + // ----- Study directories paths + $i = 0; + $j = 0; + while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { + + // ----- Look for empty dir (path reduction) + if ($v_list_dir[$i] == '') { + $i++; + continue; + } + if ($v_list_path[$j] == '') { + $j++; + continue; + } + + // ----- Compare the items + if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { + $v_result = 0; + } + + // ----- Next items + $i++; + $j++; + } + + // ----- Look if everything seems to be the same + if ($v_result) { + // ----- Skip all the empty items + while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; + while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; + + if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { + // ----- There are exactly the same + $v_result = 2; + } + else if ($i < $v_list_dir_size) { + // ----- The path is shorter than the dir + $v_result = 0; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilCopyBlock() + // Description : + // Parameters : + // $p_mode : read/write compression mode + // 0 : src & dest normal + // 1 : src gzip, dest normal + // 2 : src normal, dest gzip + // 3 : src & dest gzip + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0) + { + $v_result = 1; + + if ($p_mode==0) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==1) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==2) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==3) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilRename() + // Description : + // This function tries to do a simple rename() function. If it fails, it + // tries to copy the $p_src file in a new $p_dest file and then unlink the + // first one. + // Parameters : + // $p_src : Old filename + // $p_dest : New filename + // Return Values : + // 1 on success, 0 on failure. + // -------------------------------------------------------------------------------- + function PclZipUtilRename($p_src, $p_dest) + { + $v_result = 1; + + // ----- Try to rename the files + if (!@rename($p_src, $p_dest)) { + + // ----- Try to copy & unlink the src + if (!@copy($p_src, $p_dest)) { + $v_result = 0; + } + else if (!@unlink($p_src)) { + $v_result = 0; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilOptionText() + // Description : + // Translate option value in text. Mainly for debug purpose. + // Parameters : + // $p_option : the option value. + // Return Values : + // The option text value. + // -------------------------------------------------------------------------------- + function PclZipUtilOptionText($p_option) + { + + $v_list = get_defined_constants(); + for (reset($v_list); $v_key = key($v_list); next($v_list)) { + $v_prefix = substr($v_key, 0, 10); + if (( ($v_prefix == 'PCLZIP_OPT') + || ($v_prefix == 'PCLZIP_CB_') + || ($v_prefix == 'PCLZIP_ATT')) + && ($v_list[$v_key] == $p_option)) { + return $v_key; + } + } + + $v_result = 'Unknown'; + + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilTranslateWinPath() + // Description : + // Translate windows path by replacing '\' by '/' and optionally removing + // drive letter. + // Parameters : + // $p_path : path to translate. + // $p_remove_disk_letter : true | false + // Return Values : + // The path translated. + // -------------------------------------------------------------------------------- + function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true) + { + if (stristr(php_uname(), 'windows')) { + // ----- Look for potential disk letter + if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { + $p_path = substr($p_path, $v_position+1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } + } + return $p_path; + } + // -------------------------------------------------------------------------------- diff --git a/src/PhpWord/Shared/ZipArchive.php b/src/PhpWord/Shared/ZipArchive.php new file mode 100644 index 00000000..ae22357a --- /dev/null +++ b/src/PhpWord/Shared/ZipArchive.php @@ -0,0 +1,204 @@ +_tempDir = sys_get_temp_dir(); + $this->_zip = new \PclZip($fileName); + + return true; + } + + /** + * Close this zip archive + * + * @codeCoverageIgnore + */ + public function close() + { + } + + /** + * Add a new file to the zip archive. + * + * @param string $filename Directory/Name of the file to add to the zip archive + * @param string $localname Directory/Name of the file added to the zip + */ + public function addFile($filename, $localname = null) + { + $filename = realpath($filename); + $filenameParts = pathinfo($filename); + $localnameParts = pathinfo($localname); + + // To Rename the file while adding it to the zip we + // need to create a temp file with the correct name + if ($filenameParts['basename'] != $localnameParts['basename']) { + $temppath = $this->_tempDir . '/' . $localnameParts['basename']; + copy($filename, $temppath); + $filename = $temppath; + $filenameParts = pathinfo($temppath); + } + + $res = $this->_zip->add( + $filename, + PCLZIP_OPT_REMOVE_PATH, + $filenameParts['dirname'], + PCLZIP_OPT_ADD_PATH, + $localnameParts["dirname"] + ); + + if ($res == 0) { + throw new Exception("Error zipping files : " . $this->_zip->errorInfo(true)); + return false; + } + + return true; + } + + /** + * Add a new file to the zip archive from a string of raw data. + * + * @param string $localname Directory/Name of the file to add to the zip archive + * @param string $contents String of data to add to the zip archive + */ + public function addFromString($localname, $contents) + { + $filenameParts = pathinfo($localname); + + // Write $contents to a temp file + $handle = fopen($this->_tempDir . '/' . $filenameParts["basename"], "wb"); + fwrite($handle, $contents); + fclose($handle); + + // Add temp file to zip + $res = $this->_zip->add( + $this->_tempDir . '/' . $filenameParts["basename"], + PCLZIP_OPT_REMOVE_PATH, + $this->_tempDir, + PCLZIP_OPT_ADD_PATH, + $filenameParts["dirname"] + ); + if ($res == 0) { + throw new Exception("Error zipping files : " . $this->_zip->errorInfo(true)); + return false; + } + + // Remove temp file + unlink($this->_tempDir . '/' . $filenameParts["basename"]); + + return true; + } + + /** + * Find if given fileName exist in archive (Emulate ZipArchive locateName()) + * + * @param string $fileName Filename for the file in zip archive + * @return boolean + */ + public function locateName($fileName) + { + $list = $this->_zip->listContent(); + $listCount = count($list); + $list_index = -1; + for ($i = 0; $i < $listCount; ++$i) { + if (strtolower($list[$i]["filename"]) == strtolower($fileName) || + strtolower($list[$i]["stored_filename"]) == strtolower($fileName)) { + $list_index = $i; + break; + } + } + + return ($list_index > -1); + } + + /** + * Extract file from archive by given fileName (Emulate ZipArchive getFromName()) + * + * @param string $fileName Filename for the file in zip archive + * @return string $contents File string contents + */ + public function getFromName($fileName) + { + $list = $this->_zip->listContent(); + $listCount = count($list); + $list_index = -1; + for ($i = 0; $i < $listCount; ++$i) { + if (strtolower($list[$i]["filename"]) == strtolower($fileName) || + strtolower($list[$i]["stored_filename"]) == strtolower($fileName)) { + $list_index = $i; + break; + } + } + + $extracted = ""; + if ($list_index != -1) { + $extracted = $this->_zip->extractByIndex($list_index, PCLZIP_OPT_EXTRACT_AS_STRING); + } else { + $filename = substr($fileName, 1); + $list_index = -1; + for ($i = 0; $i < $listCount; ++$i) { + if (strtolower($list[$i]["filename"]) == strtolower($fileName) || + strtolower($list[$i]["stored_filename"]) == strtolower($fileName)) { + $list_index = $i; + break; + } + } + $extracted = $this->_zip->extractByIndex($list_index, PCLZIP_OPT_EXTRACT_AS_STRING); + } + if ((is_array($extracted)) && ($extracted != 0)) { + $contents = $extracted[0]["content"]; + } + + return $contents; + } +} diff --git a/src/PhpWord/Shared/ZipStreamWrapper.php b/src/PhpWord/Shared/ZipStreamWrapper.php index b9eb5f32..87e17983 100644 --- a/src/PhpWord/Shared/ZipStreamWrapper.php +++ b/src/PhpWord/Shared/ZipStreamWrapper.php @@ -10,6 +10,7 @@ namespace PhpOffice\PhpWord\Shared; use PhpOffice\PhpWord\Exceptions\Exception; +use PhpOffice\PhpWord\Settings; /** * Zip stream wrapper @@ -90,7 +91,8 @@ class ZipStreamWrapper } // Open archive - $this->_archive = new \ZipArchive(); + $zipClass = Settings::getZipClass(); + $this->_archive = new $zipClass(); $this->_archive->open($url['host']); $this->_fileNameInArchive = $url['fragment']; diff --git a/src/PhpWord/Style/Font.php b/src/PhpWord/Style/Font.php index 3d235343..d1f5353a 100644 --- a/src/PhpWord/Style/Font.php +++ b/src/PhpWord/Style/Font.php @@ -479,8 +479,8 @@ class Font /** * Set background color * - * @param string $pValue - * @return PHPWord_Style_Font + * @param string $pValue + * @return $this */ public function setBgColor($pValue = null) { diff --git a/src/PhpWord/Style/Row.php b/src/PhpWord/Style/Row.php index 74b4d966..2b714024 100644 --- a/src/PhpWord/Style/Row.php +++ b/src/PhpWord/Style/Row.php @@ -57,7 +57,7 @@ class Row * Set tblHeader * * @param boolean $pValue - * @return PHPWord_Style_Row + * @return $this */ public function setTblHeader($pValue = false) { @@ -82,7 +82,7 @@ class Row * Set cantSplit * * @param boolean $pValue - * @return PHPWord_Style_Row + * @return $this */ public function setCantSplit($pValue = false) { @@ -107,7 +107,7 @@ class Row * Set exactHeight * * @param bool $pValue - * @return PHPWord_Style_Row + * @return $this */ public function setExactHeight($pValue = false) { diff --git a/src/PhpWord/TOC.php b/src/PhpWord/TOC.php index e6a24234..bd1c3f38 100644 --- a/src/PhpWord/TOC.php +++ b/src/PhpWord/TOC.php @@ -10,6 +10,7 @@ namespace PhpOffice\PhpWord; use PhpOffice\PhpWord\Style\Font; +use PhpOffice\PhpWord\Style\TOC as TOCStyle; /** * Table of contents @@ -26,14 +27,14 @@ class TOC /** * TOC style * - * @var PhpOffice\PhpWord\Style\TOC + * @var TOCStyle */ private static $_styleTOC; /** * Font style * - * @var PhpOffice\PhpWord\Style\Font|array|string + * @var Font|array|string */ private static $_styleFont; @@ -60,7 +61,7 @@ class TOC */ public function __construct($styleFont = null, $styleTOC = null) { - self::$_styleTOC = new \PhpOffice\PhpWord\Style\TOC(); + self::$_styleTOC = new TOCStyle(); if (!is_null($styleTOC) && is_array($styleTOC)) { foreach ($styleTOC as $key => $value) { @@ -122,7 +123,7 @@ class TOC /** * Get TOC Style * - * @return \PhpOffice\PhpWord\Style\TOC + * @return TOCStyle */ public static function getStyleTOC() { @@ -132,7 +133,7 @@ class TOC /** * Get Font Style * - * @return \PhpOffice\PhpWord\Style\Font + * @return Font */ public static function getStyleFont() { diff --git a/src/PhpWord/Template.php b/src/PhpWord/Template.php index d952678e..f54d729e 100644 --- a/src/PhpWord/Template.php +++ b/src/PhpWord/Template.php @@ -10,6 +10,7 @@ namespace PhpOffice\PhpWord; use PhpOffice\PhpWord\Exceptions\Exception; +use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Shared\String; /** @@ -20,7 +21,7 @@ class Template /** * ZipArchive object * - * @var \ZipArchive + * @var mixed */ private $_objZip; @@ -57,7 +58,8 @@ class Template throw new Exception("Could not copy the template from {$strFilename} to {$this->_tempFileName}."); } - $this->_objZip = new \ZipArchive(); + $zipClass = Settings::getZipClass(); + $this->_objZip = new $zipClass(); $this->_objZip->open($this->_tempFileName); $this->_documentXML = $this->_objZip->getFromName('word/document.xml'); diff --git a/src/PhpWord/Writer/ODText.php b/src/PhpWord/Writer/ODText.php index 8c58df60..7ff31bf0 100755 --- a/src/PhpWord/Writer/ODText.php +++ b/src/PhpWord/Writer/ODText.php @@ -12,6 +12,7 @@ namespace PhpOffice\PhpWord\Writer; use PhpOffice\PhpWord\Exceptions\Exception; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\HashTable; +use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Writer\ODText\Content; use PhpOffice\PhpWord\Writer\ODText\Manifest; use PhpOffice\PhpWord\Writer\ODText\Meta; @@ -26,13 +27,13 @@ class ODText extends Writer implements IWriter /** * Private unique PHPWord_Worksheet_BaseDrawing HashTable * - * @var \PhpOffice\PhpWord\HashTable + * @var HashTable */ private $drawingHashTable; /** * Create new ODText writer - * @param \PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord */ public function __construct(PhpWord $phpWord = null) { @@ -57,7 +58,7 @@ class ODText extends Writer implements IWriter * Save PhpWord to file * * @param string $pFilename - * @throws \PhpOffice\PhpWord\Exceptions\Exception + * @throws Exception */ public function save($pFilename = null) { @@ -65,11 +66,23 @@ class ODText extends Writer implements IWriter $pFilename = $this->getTempFile($pFilename); // Create new ZIP file and open it for writing - $objZip = new \ZipArchive(); + $zipClass = Settings::getZipClass(); + $objZip = new $zipClass(); + + // Retrieve OVERWRITE and CREATE constants from the instantiated zip class + // This method of accessing constant values from a dynamic class should work with all appropriate versions of PHP + $ro = new \ReflectionObject($objZip); + $zipOverWrite = $ro->getConstant('OVERWRITE'); + $zipCreate = $ro->getConstant('CREATE'); + + // Remove any existing file + if (file_exists($pFilename)) { + unlink($pFilename); + } // Try opening the ZIP file - if ($objZip->open($pFilename, \ZipArchive::OVERWRITE) !== true) { - if ($objZip->open($pFilename, \ZipArchive::CREATE) !== true) { + if ($objZip->open($pFilename, $zipOverWrite) !== true) { + if ($objZip->open($pFilename, $zipCreate) !== true) { throw new Exception("Could not open " . $pFilename . " for writing."); } } @@ -101,7 +114,8 @@ class ODText extends Writer implements IWriter $imagePath = substr($imagePath, 6); $imagePathSplitted = explode('#', $imagePath); - $imageZip = new \ZipArchive(); + $zipClass = Settings::getZipClass(); + $imageZip = new $zipClass(); $imageZip->open($imagePathSplitted[0]); $imageContents = $imageZip->getFromName($imagePathSplitted[1]); $imageZip->close(); @@ -139,7 +153,7 @@ class ODText extends Writer implements IWriter /** * Get PHPWord_Worksheet_BaseDrawing HashTable * - * @return \PhpOffice\PhpWord\HashTable + * @return HashTable */ public function getDrawingHashTable() { diff --git a/src/PhpWord/Writer/ODText/Content.php b/src/PhpWord/Writer/ODText/Content.php index 4e75068d..2e9a61e6 100644 --- a/src/PhpWord/Writer/ODText/Content.php +++ b/src/PhpWord/Writer/ODText/Content.php @@ -35,7 +35,7 @@ class Content extends WriterPart /** * Write content file to XML format * - * @param \PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord * @return string XML Output */ public function writeContent(PhpWord $phpWord = null) @@ -126,7 +126,7 @@ class Content extends WriterPart $numFonts = 0; if (count($styles) > 0) { foreach ($styles as $styleName => $style) { - // PhpOffice\PhpWord\Style\Font + // Font if ($style instanceof Font) { $numFonts++; $name = $style->getName(); @@ -158,7 +158,7 @@ class Content extends WriterPart if (preg_match('#^T[0-9]+$#', $styleName) != 0 || preg_match('#^P[0-9]+$#', $styleName) != 0 ) { - // PhpOffice\PhpWord\Style\Font + // Font if ($style instanceof Font) { $xmlWriter->startElement('style:style'); $xmlWriter->writeAttribute('style:name', $styleName); @@ -244,11 +244,11 @@ class Content extends WriterPart foreach ($_elements as $element) { if ($element instanceof Text) { - $this->_writeText($xmlWriter, $element); + $this->writeText($xmlWriter, $element); } elseif ($element instanceof TextRun) { - $this->_writeTextRun($xmlWriter, $element); + $this->writeTextRun($xmlWriter, $element); } elseif ($element instanceof TextBreak) { - $this->_writeTextBreak($xmlWriter); + $this->writeTextBreak($xmlWriter); } elseif ($element instanceof Link) { $this->writeUnsupportedElement($xmlWriter, 'Link'); } elseif ($element instanceof Title) { @@ -271,9 +271,9 @@ class Content extends WriterPart } if ($pSection == $countSections) { - $this->_writeEndSection($xmlWriter, $section); + $this->writeEndSection($xmlWriter, $section); } else { - $this->_writeSection($xmlWriter, $section); + $this->writeSection($xmlWriter, $section); } } } @@ -288,11 +288,11 @@ class Content extends WriterPart /** * Write text * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param \PhpOffice\PhpWord\Section\Text $text + * @param XMLWriter $xmlWriter + * @param Text $text * @param bool $withoutP */ - protected function _writeText(XMLWriter $xmlWriter, Text $text, $withoutP = false) + protected function writeText(XMLWriter $xmlWriter, Text $text, $withoutP = false) { $styleFont = $text->getFontStyle(); $styleParagraph = $text->getParagraphStyle(); @@ -336,18 +336,18 @@ class Content extends WriterPart /** * Write TextRun section * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param \PhpOffice\PhpWord\Section\TextRun $textrun + * @param XMLWriter $xmlWriter + * @param TextRun $textrun * @todo Enable all other section types */ - protected function _writeTextRun(XMLWriter $xmlWriter, TextRun $textrun) + protected function writeTextRun(XMLWriter $xmlWriter, TextRun $textrun) { $elements = $textrun->getElements(); $xmlWriter->startElement('text:p'); if (count($elements) > 0) { foreach ($elements as $element) { if ($element instanceof Text) { - $this->_writeText($xmlWriter, $element, true); + $this->writeText($xmlWriter, $element, true); } } } @@ -357,9 +357,9 @@ class Content extends WriterPart /** * Write TextBreak * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param XMLWriter $xmlWriter */ - protected function _writeTextBreak(XMLWriter $xmlWriter = null) + protected function writeTextBreak(XMLWriter $xmlWriter = null) { $xmlWriter->startElement('text:p'); $xmlWriter->writeAttribute('text:style-name', 'Standard'); @@ -370,20 +370,20 @@ class Content extends WriterPart /** * Write end section * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section $section + * @param XMLWriter $xmlWriter + * @param Section $section */ - private function _writeEndSection(XMLWriter $xmlWriter = null, Section $section = null) + private function writeEndSection(XMLWriter $xmlWriter = null, Section $section = null) { } /** * Write section * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section $section + * @param XMLWriter $xmlWriter + * @param Section $section */ - private function _writeSection(XMLWriter $xmlWriter = null, Section $section = null) + private function writeSection(XMLWriter $xmlWriter = null, Section $section = null) { } // @codeCoverageIgnoreEnd @@ -391,7 +391,7 @@ class Content extends WriterPart /** * Write unsupported element * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param XMLWriter $xmlWriter * @param string $element */ private function writeUnsupportedElement($xmlWriter, $element) diff --git a/src/PhpWord/Writer/ODText/Manifest.php b/src/PhpWord/Writer/ODText/Manifest.php index cb92ddcc..31168e3d 100755 --- a/src/PhpWord/Writer/ODText/Manifest.php +++ b/src/PhpWord/Writer/ODText/Manifest.php @@ -21,7 +21,7 @@ class Manifest extends WriterPart /** * Write Manifest file to XML format * - * @param \PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord * @return string XML Output */ public function writeManifest(PhpWord $phpWord = null) @@ -64,7 +64,7 @@ class Manifest extends WriterPart for ($i = 0; $i < $this->getParentWriter()->getDrawingHashTable()->count(); ++$i) { if ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof PHPWord_Shape_Drawing) { $extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getExtension()); - $mimeType = $this->_getImageMimeType($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getPath()); + $mimeType = $this->getImageMimeType($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getPath()); $xmlWriter->startElement('manifest:file-entry'); $xmlWriter->writeAttribute('manifest:media-type', $mimeType); @@ -97,9 +97,9 @@ class Manifest extends WriterPart * * @param string $pFile Filename * @return string Mime Type - * @throws \PhpOffice\PhpWord\Exceptions\Exception + * @throws Exception */ - private function _getImageMimeType($pFile = '') + private function getImageMimeType($pFile = '') { if (file_exists($pFile)) { $image = getimagesize($pFile); diff --git a/src/PhpWord/Writer/ODText/Meta.php b/src/PhpWord/Writer/ODText/Meta.php index fdd0ec7c..51647173 100644 --- a/src/PhpWord/Writer/ODText/Meta.php +++ b/src/PhpWord/Writer/ODText/Meta.php @@ -20,7 +20,7 @@ class Meta extends WriterPart /** * Write Meta file to XML format * - * @param \PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord * @return string XML Output */ public function writeMeta(PhpWord $phpWord = null) diff --git a/src/PhpWord/Writer/ODText/Mimetype.php b/src/PhpWord/Writer/ODText/Mimetype.php index 0d4e598e..1e713f2c 100644 --- a/src/PhpWord/Writer/ODText/Mimetype.php +++ b/src/PhpWord/Writer/ODText/Mimetype.php @@ -19,7 +19,7 @@ class Mimetype extends WriterPart /** * Write Mimetype to Text format * - * @param \PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord * @return string Text Output */ public function writeMimetype(PhpWord $phpWord = null) diff --git a/src/PhpWord/Writer/ODText/Styles.php b/src/PhpWord/Writer/ODText/Styles.php index 6a91a7e2..4ae937e0 100644 --- a/src/PhpWord/Writer/ODText/Styles.php +++ b/src/PhpWord/Writer/ODText/Styles.php @@ -24,7 +24,7 @@ class Styles extends WriterPart /** * Write Styles file to XML format * - * @param \PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord * @return string XML Output */ public function writeStyles(PhpWord $phpWord = null) @@ -73,7 +73,7 @@ class Styles extends WriterPart $numFonts = 0; if (count($styles) > 0) { foreach ($styles as $styleName => $style) { - // PhpOffice\PhpWord\Style\Font + // Font if ($style instanceof Font) { $numFonts++; $name = $style->getName(); @@ -144,7 +144,7 @@ class Styles extends WriterPart if (preg_match('#^T[0-9]+$#', $styleName) == 0 && preg_match('#^P[0-9]+$#', $styleName) == 0 ) { - // PhpOffice\PhpWord\Style\Font + // Font if ($style instanceof Font) { // style:style $xmlWriter->startElement('style:style'); @@ -168,7 +168,7 @@ class Styles extends WriterPart $xmlWriter->endElement(); $xmlWriter->endElement(); } elseif ($style instanceof Paragraph) { - // PhpOffice\PhpWord\Style\Paragraph + // Paragraph // style:style $xmlWriter->startElement('style:style'); $xmlWriter->writeAttribute('style:name', $styleName); @@ -183,7 +183,7 @@ class Styles extends WriterPart $xmlWriter->endElement(); } elseif ($style instanceof Table) { - // PhpOffice\PhpWord\Style\Table + // Table } } } diff --git a/src/PhpWord/Writer/RTF.php b/src/PhpWord/Writer/RTF.php index d7fc7101..e37e53b3 100755 --- a/src/PhpWord/Writer/RTF.php +++ b/src/PhpWord/Writer/RTF.php @@ -36,7 +36,7 @@ class RTF extends Writer implements IWriter /** * Private unique PHPWord_Worksheet_BaseDrawing HashTable * - * @var \PhpOffice\PhpWord\HashTable + * @var HashTable */ private $drawingHashTable; @@ -63,7 +63,7 @@ class RTF extends Writer implements IWriter /** * Create new RTF writer - * @param \PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord */ public function __construct(PhpWord $phpWord = null) { @@ -78,7 +78,7 @@ class RTF extends Writer implements IWriter * Save PhpWord to file * * @param string $pFilename - * @throws \PhpOffice\PhpWord\Exceptions\Exception + * @throws Exception */ public function save($pFilename = null) { @@ -98,7 +98,7 @@ class RTF extends Writer implements IWriter /** * Get PHPWord_Worksheet_BaseDrawing HashTable * - * @return \PhpOffice\PhpWord\HashTable + * @return HashTable */ public function getDrawingHashTable() { @@ -179,10 +179,9 @@ class RTF extends Writer implements IWriter // Browse styles $styles = Style::getStyles(); - $numPStyles = 0; if (count($styles) > 0) { foreach ($styles as $styleName => $style) { - // PhpOffice\PhpWord\Style\Font + // Font if ($style instanceof Font) { if (in_array($style->getName(), $arrFonts) == false) { $arrFonts[] = $style->getName(); @@ -232,7 +231,6 @@ class RTF extends Writer implements IWriter // Browse styles $styles = Style::getStyles(); - $numPStyles = 0; if (count($styles) > 0) { foreach ($styles as $styleName => $style) { // Font diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php index 6ceed4fb..687bf8d2 100755 --- a/src/PhpWord/Writer/Word2007.php +++ b/src/PhpWord/Writer/Word2007.php @@ -13,6 +13,7 @@ use PhpOffice\PhpWord\Exceptions\Exception; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\Footnote; use PhpOffice\PhpWord\Media; +use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Writer\Word2007\ContentTypes; use PhpOffice\PhpWord\Writer\Word2007\DocProps; use PhpOffice\PhpWord\Writer\Word2007\Document; @@ -34,19 +35,19 @@ class Word2007 extends Writer implements IWriter * * @var array */ - private $_imageTypes = array(); + private $imageTypes = array(); /** * Types of objects * * @var array */ - private $_objectTypes = array(); + private $objectTypes = array(); /** * Create new Word2007 writer * - * @param PhpOffice\PhpWord\PhpWord + * @param PhpWord */ public function __construct(PhpWord $phpWord = null) { @@ -80,11 +81,23 @@ class Word2007 extends Writer implements IWriter $pFilename = $this->getTempFile($pFilename); // Create new ZIP file and open it for writing - $objZip = new \ZipArchive(); + $zipClass = Settings::getZipClass(); + $objZip = new $zipClass(); + + // Retrieve OVERWRITE and CREATE constants from the instantiated zip class + // This method of accessing constant values from a dynamic class should work with all appropriate versions of PHP + $ro = new \ReflectionObject($objZip); + $zipOverWrite = $ro->getConstant('OVERWRITE'); + $zipCreate = $ro->getConstant('CREATE'); + + // Remove any existing file + if (file_exists($pFilename)) { + unlink($pFilename); + } // Try opening the ZIP file - if ($objZip->open($pFilename, \ZipArchive::OVERWRITE) !== true) { - if ($objZip->open($pFilename, \ZipArchive::CREATE) !== true) { + if ($objZip->open($pFilename, $zipOverWrite) !== true) { + if ($objZip->open($pFilename, $zipCreate) !== true) { throw new Exception("Could not open " . $pFilename . " for writing."); } } @@ -167,8 +180,8 @@ class Word2007 extends Writer implements IWriter $objZip->addFromString( '[Content_Types].xml', $this->getWriterPart('contenttypes')->writeContentTypes( - $this->_imageTypes, - $this->_objectTypes, + $this->imageTypes, + $this->objectTypes, $_cHdrs, $footers ) @@ -235,12 +248,12 @@ class Word2007 extends Writer implements IWriter if ($imageExtension === 'jpeg') { $imageExtension = 'jpg'; } - if (!in_array($imageType, $this->_imageTypes)) { - $this->_imageTypes[$imageExtension] = $imageType; + if (!in_array($imageType, $this->imageTypes)) { + $this->imageTypes[$imageExtension] = $imageType; } } else { - if (!in_array($extension, $this->_objectTypes)) { - $this->_objectTypes[] = $extension; + if (!in_array($extension, $this->objectTypes)) { + $this->objectTypes[] = $extension; } } } diff --git a/src/PhpWord/Writer/Word2007/Base.php b/src/PhpWord/Writer/Word2007/Base.php index 644bf2a2..3acf6c1d 100644 --- a/src/PhpWord/Writer/Word2007/Base.php +++ b/src/PhpWord/Writer/Word2007/Base.php @@ -10,23 +10,25 @@ namespace PhpOffice\PhpWord\Writer\Word2007; use PhpOffice\PhpWord\PhpWord; -use PhpOffice\PhpWord\Section\Footer\PreserveText; -use PhpOffice\PhpWord\Section\Footnote; -use PhpOffice\PhpWord\Section\Image; -use PhpOffice\PhpWord\Section\Link; -use PhpOffice\PhpWord\Section\ListItem; -use PhpOffice\PhpWord\Section\Object; -use PhpOffice\PhpWord\Section\Table; use PhpOffice\PhpWord\Section\Text; -use PhpOffice\PhpWord\Section\TextBreak; use PhpOffice\PhpWord\Section\TextRun; +use PhpOffice\PhpWord\Section\Link; use PhpOffice\PhpWord\Section\Title; +use PhpOffice\PhpWord\Section\Footer\PreserveText; +use PhpOffice\PhpWord\Section\TextBreak; +use PhpOffice\PhpWord\Section\ListItem; +use PhpOffice\PhpWord\Section\Table; +use PhpOffice\PhpWord\Section\Image; +use PhpOffice\PhpWord\Section\Object; +use PhpOffice\PhpWord\Section\Footnote; use PhpOffice\PhpWord\Section\CheckBox; use PhpOffice\PhpWord\Shared\String; use PhpOffice\PhpWord\Shared\XMLWriter; -use PhpOffice\PhpWord\Style\Cell; -use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; +use PhpOffice\PhpWord\Style\Font; +use PhpOffice\PhpWord\Style\Cell; +use PhpOffice\PhpWord\Style\Table as TableStyle; +use PhpOffice\PhpWord\Style\Image as ImageStyle; /** * Word2007 base part writer @@ -38,55 +40,31 @@ class Base extends WriterPart /** * Write text element * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section\Text $text + * @param XMLWriter $xmlWriter + * @param Text $text * @param boolean $withoutP */ - protected function _writeText(XMLWriter $xmlWriter, Text $text, $withoutP = false) - { + protected function writeText( + XMLWriter $xmlWriter, + Text $text, + $withoutP = false + ) { $styleFont = $text->getFontStyle(); - - $sfIsObject = ($styleFont instanceof Font) ? true : false; - - if (!$withoutP) { - $xmlWriter->startElement('w:p'); - - $styleParagraph = $text->getParagraphStyle(); - $spIsObject = ($styleParagraph instanceof Paragraph) ? true : false; - - if ($spIsObject) { - $this->_writeParagraphStyle($xmlWriter, $styleParagraph); - } elseif (!$spIsObject && !is_null($styleParagraph)) { - $xmlWriter->startElement('w:pPr'); - $xmlWriter->startElement('w:pStyle'); - $xmlWriter->writeAttribute('w:val', $styleParagraph); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - } - + $styleParagraph = $text->getParagraphStyle(); $strText = htmlspecialchars($text->getText()); $strText = String::controlCharacterPHP2OOXML($strText); - $xmlWriter->startElement('w:r'); - - if ($sfIsObject) { - $this->_writeTextStyle($xmlWriter, $styleFont); - } elseif (!$sfIsObject && !is_null($styleFont)) { - $xmlWriter->startElement('w:rPr'); - $xmlWriter->startElement('w:rStyle'); - $xmlWriter->writeAttribute('w:val', $styleFont); - $xmlWriter->endElement(); - $xmlWriter->endElement(); + if (!$withoutP) { + $xmlWriter->startElement('w:p'); + $this->writeInlineParagraphStyle($xmlWriter, $styleParagraph); } - + $xmlWriter->startElement('w:r'); + $this->writeInlineFontStyle($xmlWriter, $styleFont); $xmlWriter->startElement('w:t'); - $xmlWriter->writeAttribute('xml:space', 'preserve'); // needed because of drawing spaces before and after text + $xmlWriter->writeAttribute('xml:space', 'preserve'); $xmlWriter->writeRaw($strText); $xmlWriter->endElement(); - $xmlWriter->endElement(); // w:r - if (!$withoutP) { $xmlWriter->endElement(); // w:p } @@ -95,55 +73,693 @@ class Base extends WriterPart /** * Write textrun element * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section $section + * @param XMLWriter $xmlWriter + * @param TextRun $textrun */ - protected function _writeTextRun(XMLWriter $xmlWriter, TextRun $textrun) - { + protected function writeTextRun( + XMLWriter $xmlWriter, + TextRun $textrun + ) { $elements = $textrun->getElements(); $styleParagraph = $textrun->getParagraphStyle(); - - $spIsObject = ($styleParagraph instanceof Paragraph) ? true : false; - $xmlWriter->startElement('w:p'); - - if ($spIsObject) { - $this->_writeParagraphStyle($xmlWriter, $styleParagraph); - } elseif (!$spIsObject && !is_null($styleParagraph)) { - $xmlWriter->startElement('w:pPr'); - $xmlWriter->startElement('w:pStyle'); - $xmlWriter->writeAttribute('w:val', $styleParagraph); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - + $this->writeInlineParagraphStyle($xmlWriter, $styleParagraph); if (count($elements) > 0) { foreach ($elements as $element) { if ($element instanceof Text) { - $this->_writeText($xmlWriter, $element, true); + $this->writeText($xmlWriter, $element, true); } elseif ($element instanceof Link) { - $this->_writeLink($xmlWriter, $element, true); + $this->writeLink($xmlWriter, $element, true); } elseif ($element instanceof Image) { - $this->_writeImage($xmlWriter, $element, true); + $this->writeImage($xmlWriter, $element, true); } elseif ($element instanceof Footnote) { - $this->_writeFootnote($xmlWriter, $element, true); + $this->writeFootnote($xmlWriter, $element, true); } elseif ($element instanceof TextBreak) { $xmlWriter->writeElement('w:br'); } } } + $xmlWriter->endElement(); // w:p + } + + /** + * Write link element + * + * @param XMLWriter $xmlWriter + * @param Link $link + * @param boolean $withoutP + */ + protected function writeLink( + XMLWriter $xmlWriter, + Link $link, + $withoutP = false + ) { + $rID = $link->getRelationId(); + $linkName = $link->getLinkName(); + if (is_null($linkName)) { + $linkName = $link->getLinkSrc(); + } + $styleFont = $link->getFontStyle(); + $styleParagraph = $link->getParagraphStyle(); + + if (!$withoutP) { + $xmlWriter->startElement('w:p'); + $this->writeInlineParagraphStyle($xmlWriter, $styleParagraph); + } + $xmlWriter->startElement('w:hyperlink'); + $xmlWriter->writeAttribute('r:id', 'rId' . $rID); + $xmlWriter->writeAttribute('w:history', '1'); + $xmlWriter->startElement('w:r'); + $this->writeInlineFontStyle($xmlWriter, $styleFont); + $xmlWriter->startElement('w:t'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); // needed because of drawing spaces before and after text + $xmlWriter->writeRaw($linkName); + $xmlWriter->endElement(); // w:t + $xmlWriter->endElement(); // w:r + $xmlWriter->endElement(); // w:hyperlink + if (!$withoutP) { + $xmlWriter->endElement(); // w:p + } + } + + /** + * Write title element + * + * @param XMLWriter $xmlWriter + * @param Title $title + */ + protected function writeTitle(XMLWriter $xmlWriter, Title $title) + { + $text = htmlspecialchars($title->getText()); + $text = String::controlCharacterPHP2OOXML($text); + $anchor = $title->getAnchor(); + $bookmarkId = $title->getBookmarkId(); + $style = $title->getStyle(); + + $xmlWriter->startElement('w:p'); + + if (!empty($style)) { + $xmlWriter->startElement('w:pPr'); + $xmlWriter->startElement('w:pStyle'); + $xmlWriter->writeAttribute('w:val', $style); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + } + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'end'); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + $xmlWriter->startElement('w:bookmarkStart'); + $xmlWriter->writeAttribute('w:id', $bookmarkId); + $xmlWriter->writeAttribute('w:name', $anchor); + $xmlWriter->endElement(); + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:t'); + $xmlWriter->writeRaw($text); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + $xmlWriter->startElement('w:bookmarkEnd'); + $xmlWriter->writeAttribute('w:id', $bookmarkId); + $xmlWriter->endElement(); $xmlWriter->endElement(); } + /** + * Write preserve text element + * + * @param XMLWriter $xmlWriter + * @param PreserveText $textrun + */ + protected function writePreserveText( + XMLWriter $xmlWriter, + PreserveText $textrun + ) { + $styleFont = $textrun->getFontStyle(); + $styleParagraph = $textrun->getParagraphStyle(); + + $arrText = $textrun->getText(); + if (!is_array($arrText)) { + $arrText = array($arrText); + } + + $xmlWriter->startElement('w:p'); + $this->writeInlineParagraphStyle($xmlWriter, $styleParagraph); + foreach ($arrText as $text) { + if (substr($text, 0, 1) == '{') { + $text = substr($text, 1, -1); + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'begin'); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + $xmlWriter->startElement('w:r'); + $this->writeInlineFontStyle($xmlWriter, $styleFont); + $xmlWriter->startElement('w:instrText'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $xmlWriter->writeRaw($text); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'separate'); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'end'); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + } else { + $text = htmlspecialchars($text); + $text = String::controlCharacterPHP2OOXML($text); + + $xmlWriter->startElement('w:r'); + $this->writeInlineFontStyle($xmlWriter, $styleFont); + $xmlWriter->startElement('w:t'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $xmlWriter->writeRaw($text); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + } + } + + $xmlWriter->endElement(); // p + } + + /** + * Write text break element + * + * @param XMLWriter $xmlWriter + * @param TextBreak $element + */ + protected function writeTextBreak($xmlWriter, TextBreak $element = null) + { + $hasStyle = false; + $styleFont = null; + $styleParagraph = null; + if (!is_null($element)) { + $styleFont = $element->getFontStyle(); + $styleParagraph = $element->getParagraphStyle(); + $hasStyle = !is_null($styleFont) || !is_null($styleParagraph); + } + if ($hasStyle) { + $xmlWriter->startElement('w:p'); + $this->writeInlineParagraphStyle($xmlWriter, $styleParagraph); + if (!is_null($styleFont)) { + $xmlWriter->startElement('w:pPr'); + $this->writeInlineFontStyle($xmlWriter, $styleFont); + $xmlWriter->endElement(); // w:pPr + } + $xmlWriter->endElement(); // w:p + } else { + // Null element. No paragraph nor font style + $xmlWriter->writeElement('w:p', null); + } + } + + /** + * Write list item element + * + * @param XMLWriter $xmlWriter + * @param ListItem $listItem + */ + protected function writeListItem(XMLWriter $xmlWriter, ListItem $listItem) + { + $textObject = $listItem->getTextObject(); + $depth = $listItem->getDepth(); + $listType = $listItem->getStyle()->getListType(); + $styleParagraph = $textObject->getParagraphStyle(); + + $xmlWriter->startElement('w:p'); + $xmlWriter->startElement('w:pPr'); + $this->writeInlineParagraphStyle($xmlWriter, $styleParagraph, true); + $xmlWriter->startElement('w:numPr'); + $xmlWriter->startElement('w:ilvl'); + $xmlWriter->writeAttribute('w:val', $depth); + $xmlWriter->endElement(); // w:ilvl + $xmlWriter->startElement('w:numId'); + $xmlWriter->writeAttribute('w:val', $listType); + $xmlWriter->endElement(); // w:numId + $xmlWriter->endElement(); // w:numPr + $xmlWriter->endElement(); // w:pPr + $this->writeText($xmlWriter, $textObject, true); + $xmlWriter->endElement(); // w:p + } + + /** + * Write footnote reference element + * + * @param XMLWriter $xmlWriter + * @param Table $table + */ + protected function writeTable(XMLWriter $xmlWriter, Table $table) + { + $_rows = $table->getRows(); + $_cRows = count($_rows); + + if ($_cRows > 0) { + $xmlWriter->startElement('w:tbl'); + + // Table grid + $cellWidths = array(); + for ($i = 0; $i < $_cRows; $i++) { + $row = $_rows[$i]; + $cells = $row->getCells(); + if (count($cells) <= count($cellWidths)) { + continue; + } + $cellWidths = array(); + foreach ($cells as $cell) { + $cellWidths[] = $cell->getWidth(); + } + } + $xmlWriter->startElement('w:tblGrid'); + foreach ($cellWidths as $width) { + $xmlWriter->startElement('w:gridCol'); + if (!is_null($width)) { + $xmlWriter->writeAttribute('w:w', $width); + $xmlWriter->writeAttribute('w:type', 'dxa'); + } + $xmlWriter->endElement(); + } + $xmlWriter->endElement(); // w:tblGrid + + // Table style + $tblStyle = $table->getStyle(); + $tblWidth = $table->getWidth(); + if ($tblStyle instanceof TableStyle) { + $this->writeTableStyle($xmlWriter, $tblStyle, false); + } else { + if (!empty($tblStyle)) { + $xmlWriter->startElement('w:tblPr'); + $xmlWriter->startElement('w:tblStyle'); + $xmlWriter->writeAttribute('w:val', $tblStyle); + $xmlWriter->endElement(); + if (!is_null($tblWidth)) { + $xmlWriter->startElement('w:tblW'); + $xmlWriter->writeAttribute('w:w', $tblWidth); + $xmlWriter->writeAttribute('w:type', 'pct'); + $xmlWriter->endElement(); + } + $xmlWriter->endElement(); + } + } + + // Table rows + for ($i = 0; $i < $_cRows; $i++) { + $row = $_rows[$i]; + $height = $row->getHeight(); + $rowStyle = $row->getStyle(); + $tblHeader = $rowStyle->getTblHeader(); + $cantSplit = $rowStyle->getCantSplit(); + $exactHeight = $rowStyle->getExactHeight(); + + $xmlWriter->startElement('w:tr'); + + if (!is_null($height) || !is_null($tblHeader) || !is_null($cantSplit)) { + $xmlWriter->startElement('w:trPr'); + if (!is_null($height)) { + $xmlWriter->startElement('w:trHeight'); + $xmlWriter->writeAttribute('w:val', $height); + $xmlWriter->writeAttribute('w:hRule', ($exactHeight ? 'exact' : 'atLeast')); + $xmlWriter->endElement(); + } + if ($tblHeader) { + $xmlWriter->startElement('w:tblHeader'); + $xmlWriter->writeAttribute('w:val', '1'); + $xmlWriter->endElement(); + } + if ($cantSplit) { + $xmlWriter->startElement('w:cantSplit'); + $xmlWriter->writeAttribute('w:val', '1'); + $xmlWriter->endElement(); + } + $xmlWriter->endElement(); + } + + foreach ($row->getCells() as $cell) { + $xmlWriter->startElement('w:tc'); + + $cellStyle = $cell->getStyle(); + $width = $cell->getWidth(); + + $xmlWriter->startElement('w:tcPr'); + $xmlWriter->startElement('w:tcW'); + $xmlWriter->writeAttribute('w:w', $width); + $xmlWriter->writeAttribute('w:type', 'dxa'); + $xmlWriter->endElement(); + + if ($cellStyle instanceof Cell) { + $this->writeCellStyle($xmlWriter, $cellStyle); + } + + $xmlWriter->endElement(); + + $_elements = $cell->getElements(); + if (count($_elements) > 0) { + foreach ($_elements as $element) { + if ($element instanceof Text) { + $this->writeText($xmlWriter, $element); + } elseif ($element instanceof TextRun) { + $this->writeTextRun($xmlWriter, $element); + } elseif ($element instanceof Link) { + $this->writeLink($xmlWriter, $element); + } elseif ($element instanceof TextBreak) { + $this->writeTextBreak($xmlWriter, $element); + } elseif ($element instanceof ListItem) { + $this->writeListItem($xmlWriter, $element); + } elseif ($element instanceof Image) { + $this->writeImage($xmlWriter, $element); + } elseif ($element instanceof Object) { + $this->writeObject($xmlWriter, $element); + } elseif ($element instanceof PreserveText) { + $this->writePreserveText($xmlWriter, $element); + } elseif ($element instanceof CheckBox) { + $this->writeCheckBox($xmlWriter, $element); + } + } + } else { + $this->writeTextBreak($xmlWriter); + } + + $xmlWriter->endElement(); + } + $xmlWriter->endElement(); + } + $xmlWriter->endElement(); + } + } + + /** + * Write image element + * + * @param XMLWriter $xmlWriter + * @param Image $image + * @param boolean $withoutP + */ + protected function writeImage( + XMLWriter $xmlWriter, + Image $image, + $withoutP = false + ) { + $rId = $image->getRelationId(); + + $style = $image->getStyle(); + $width = $style->getWidth(); + $height = $style->getHeight(); + $align = $style->getAlign(); + $marginTop = $style->getMarginTop(); + $marginLeft = $style->getMarginLeft(); + $wrappingStyle = $style->getWrappingStyle(); + + if (!$withoutP) { + $xmlWriter->startElement('w:p'); + + if (!is_null($align)) { + $xmlWriter->startElement('w:pPr'); + $xmlWriter->startElement('w:jc'); + $xmlWriter->writeAttribute('w:val', $align); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + } + } + + $xmlWriter->startElement('w:r'); + + $xmlWriter->startElement('w:pict'); + + $xmlWriter->startElement('v:shape'); + $xmlWriter->writeAttribute('type', '#_x0000_t75'); + + $imgStyle = ''; + if (null !== $width) { + $imgStyle .= 'width:' . $width . 'px;'; + } + if (null !== $height) { + $imgStyle .= 'height:' . $height . 'px;'; + } + if (null !== $marginTop) { + $imgStyle .= 'margin-top:' . $marginTop . 'in;'; + } + if (null !== $marginLeft) { + $imgStyle .= 'margin-left:' . $marginLeft . 'in;'; + } + + switch ($wrappingStyle) { + case ImageStyle::WRAPPING_STYLE_BEHIND: + $imgStyle .= 'position:absolute;z-index:-251658752;'; + break; + case ImageStyle::WRAPPING_STYLE_SQUARE: + $imgStyle .= 'position:absolute;z-index:251659264;mso-position-horizontal:absolute;mso-position-vertical:absolute;'; + break; + case ImageStyle::WRAPPING_STYLE_TIGHT: + $imgStyle .= 'position:absolute;z-index:251659264;mso-wrap-edited:f;mso-position-horizontal:absolute;mso-position-vertical:absolute'; + break; + case ImageStyle::WRAPPING_STYLE_INFRONT: + $imgStyle .= 'position:absolute;zz-index:251659264;mso-position-horizontal:absolute;mso-position-vertical:absolute;'; + break; + } + + $xmlWriter->writeAttribute('style', $imgStyle); + + $xmlWriter->startElement('v:imagedata'); + $xmlWriter->writeAttribute('r:id', 'rId' . $rId); + $xmlWriter->writeAttribute('o:title', ''); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + $xmlWriter->endElement(); + + $xmlWriter->endElement(); + + if (!$withoutP) { + $xmlWriter->endElement(); // w:p + } + } + + /** + * Write watermark element + * + * @param XMLWriter $xmlWriter + * @param Image $image + */ + protected function writeWatermark(XMLWriter $xmlWriter, Image $image) + { + $rId = $image->getRelationId(); + + $style = $image->getStyle(); + $width = $style->getWidth(); + $height = $style->getHeight(); + $marginLeft = $style->getMarginLeft(); + $marginTop = $style->getMarginTop(); + + $xmlWriter->startElement('w:p'); + + $xmlWriter->startElement('w:r'); + + $xmlWriter->startElement('w:pict'); + + $xmlWriter->startElement('v:shape'); + $xmlWriter->writeAttribute('type', '#_x0000_t75'); + + $strStyle = 'position:absolute;'; + $strStyle .= ' width:' . $width . 'px;'; + $strStyle .= ' height:' . $height . 'px;'; + if (!is_null($marginTop)) { + $strStyle .= ' margin-top:' . $marginTop . 'px;'; + } + if (!is_null($marginLeft)) { + $strStyle .= ' margin-left:' . $marginLeft . 'px;'; + } + + $xmlWriter->writeAttribute('style', $strStyle); + + $xmlWriter->startElement('v:imagedata'); + $xmlWriter->writeAttribute('r:id', 'rId' . $rId); + $xmlWriter->writeAttribute('o:title', ''); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + $xmlWriter->endElement(); + + $xmlWriter->endElement(); + + $xmlWriter->endElement(); + } + + /** + * Write object element + * + * @param XMLWriter $xmlWriter + * @param Object $object + */ + protected function writeObject(XMLWriter $xmlWriter, Object $object) + { + $rIdObject = $object->getRelationId(); + $rIdImage = $object->getImageRelationId(); + $shapeId = md5($rIdObject . '_' . $rIdImage); + $objectId = $object->getObjectId(); + $style = $object->getStyle(); + $align = $style->getAlign(); + + $xmlWriter->startElement('w:p'); + if (!is_null($align)) { + $xmlWriter->startElement('w:pPr'); + $xmlWriter->startElement('w:jc'); + $xmlWriter->writeAttribute('w:val', $align); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + } + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:object'); + $xmlWriter->writeAttribute('w:dxaOrig', '249'); + $xmlWriter->writeAttribute('w:dyaOrig', '160'); + $xmlWriter->startElement('v:shape'); + $xmlWriter->writeAttribute('id', $shapeId); + $xmlWriter->writeAttribute('type', '#_x0000_t75'); + $xmlWriter->writeAttribute('style', 'width:104px;height:67px'); + $xmlWriter->writeAttribute('o:ole', ''); + $xmlWriter->startElement('v:imagedata'); + $xmlWriter->writeAttribute('r:id', 'rId' . $rIdImage); + $xmlWriter->writeAttribute('o:title', ''); + $xmlWriter->endElement(); // v:imagedata + $xmlWriter->endElement(); // v:shape + $xmlWriter->startElement('o:OLEObject'); + $xmlWriter->writeAttribute('Type', 'Embed'); + $xmlWriter->writeAttribute('ProgID', 'Package'); + $xmlWriter->writeAttribute('ShapeID', $shapeId); + $xmlWriter->writeAttribute('DrawAspect', 'Icon'); + $xmlWriter->writeAttribute('ObjectID', '_' . $objectId); + $xmlWriter->writeAttribute('r:id', 'rId' . $rIdObject); + $xmlWriter->endElement(); // o:OLEObject + $xmlWriter->endElement(); // w:object + $xmlWriter->endElement(); // w:r + $xmlWriter->endElement(); // w:p + } + + /** + * Write footnote element which links to the actual content in footnotes.xml + * + * @param XMLWriter $xmlWriter + * @param Footnote $footnote + * @param boolean $withoutP + */ + protected function writeFootnote( + XMLWriter $xmlWriter, + Footnote $footnote, + $withoutP = false + ) { + if (!$withoutP) { + $xmlWriter->startElement('w:p'); + } + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:rPr'); + $xmlWriter->startElement('w:rStyle'); + $xmlWriter->writeAttribute('w:val', 'FootnoteReference'); + $xmlWriter->endElement(); // w:rStyle + $xmlWriter->endElement(); // w:rPr + $xmlWriter->startElement('w:footnoteReference'); + $xmlWriter->writeAttribute('w:id', $footnote->getReferenceId()); + $xmlWriter->endElement(); // w:footnoteReference + $xmlWriter->endElement(); // w:r + if (!$withoutP) { + $xmlWriter->endElement(); // w:p + } + } + + /** + * Write CheckBox + * + * @param boolean $withoutP + * @param boolean $checkState + */ + protected function writeCheckBox( + XMLWriter $xmlWriter, + CheckBox $checkbox, + $withoutP = false, + $checkState = false + ) { + $name = htmlspecialchars($checkbox->getName()); + $name = String::controlCharacterPHP2OOXML($name); + $text = htmlspecialchars($checkbox->getText()); + $text = String::controlCharacterPHP2OOXML($text); + $styleFont = $checkbox->getFontStyle(); + $styleParagraph = $checkbox->getParagraphStyle(); + + if (!$withoutP) { + $xmlWriter->startElement('w:p'); + $this->writeInlineParagraphStyle($xmlWriter, $styleParagraph); + } + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'begin'); + $xmlWriter->startElement('w:ffData'); + $xmlWriter->startElement('w:name'); + $xmlWriter->writeAttribute('w:val', $name); + $xmlWriter->endElement(); //w:name + $xmlWriter->writeAttribute('w:enabled', ''); + $xmlWriter->startElement('w:calcOnExit'); + $xmlWriter->writeAttribute('w:val', '0'); + $xmlWriter->endElement(); //w:calcOnExit + $xmlWriter->startElement('w:checkBox'); + $xmlWriter->writeAttribute('w:sizeAuto', ''); + $xmlWriter->startElement('w:default'); + $xmlWriter->writeAttribute('w:val', ($checkState ? '1' : '0')); + $xmlWriter->endElement(); //w:default + $xmlWriter->endElement(); //w:checkBox + $xmlWriter->endElement(); // w:ffData + $xmlWriter->endElement(); // w:fldChar + $xmlWriter->endElement(); // w:r + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:instrText'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $xmlWriter->writeRaw(' FORMCHECKBOX '); + $xmlWriter->endElement();// w:instrText + $xmlWriter->endElement(); // w:r + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'seperate'); + $xmlWriter->endElement();// w:fldChar + $xmlWriter->endElement(); // w:r + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'end'); + $xmlWriter->endElement();// w:fldChar + $xmlWriter->endElement(); // w:r + + $xmlWriter->startElement('w:r'); + $this->writeInlineFontStyle($xmlWriter, $styleFont); + $xmlWriter->startElement('w:t'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $xmlWriter->writeRaw($text); + $xmlWriter->endElement(); // w:t + $xmlWriter->endElement(); // w:r + + if (!$withoutP) { + $xmlWriter->endElement(); // w:p + } + } + /** * Write paragraph style * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param \PhpOffice\PhpWord\Style\Paragraph $style + * @param XMLWriter $xmlWriter + * @param Paragraph $style * @param bool $withoutPPR */ - protected function _writeParagraphStyle( + protected function writeParagraphStyle( XMLWriter $xmlWriter, Paragraph $style, $withoutPPR = false @@ -240,173 +856,12 @@ class Base extends WriterPart } /** - * Write link element + * Write font style * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section\Link $link - * @param boolean $withoutP + * @param XMLWriter $xmlWriter + * @param Font $style */ - protected function _writeLink(XMLWriter $xmlWriter, Link $link, $withoutP = false) - { - $rID = $link->getRelationId(); - $linkName = $link->getLinkName(); - if (is_null($linkName)) { - $linkName = $link->getLinkSrc(); - } - - $styleFont = $link->getFontStyle(); - $sfIsObject = ($styleFont instanceof Font) ? true : false; - - if (!$withoutP) { - $xmlWriter->startElement('w:p'); - - $styleParagraph = $link->getParagraphStyle(); - $spIsObject = ($styleParagraph instanceof Paragraph) ? true : false; - - if ($spIsObject) { - $this->_writeParagraphStyle($xmlWriter, $styleParagraph); - } elseif (!$spIsObject && !is_null($styleParagraph)) { - $xmlWriter->startElement('w:pPr'); - $xmlWriter->startElement('w:pStyle'); - $xmlWriter->writeAttribute('w:val', $styleParagraph); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - } - - $xmlWriter->startElement('w:hyperlink'); - $xmlWriter->writeAttribute('r:id', 'rId' . $rID); - $xmlWriter->writeAttribute('w:history', '1'); - - $xmlWriter->startElement('w:r'); - if ($sfIsObject) { - $this->_writeTextStyle($xmlWriter, $styleFont); - } elseif (!$sfIsObject && !is_null($styleFont)) { - $xmlWriter->startElement('w:rPr'); - $xmlWriter->startElement('w:rStyle'); - $xmlWriter->writeAttribute('w:val', $styleFont); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - - $xmlWriter->startElement('w:t'); - $xmlWriter->writeAttribute('xml:space', 'preserve'); // needed because of drawing spaces before and after text - $xmlWriter->writeRaw($linkName); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - - if (!$withoutP) { - $xmlWriter->endElement(); // w:p - } - } - - /** - * Write preserve text element - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section\TextRun $textrun - */ - protected function _writePreserveText(XMLWriter $xmlWriter, PreserveText $textrun) - { - $styleFont = $textrun->getFontStyle(); - $styleParagraph = $textrun->getParagraphStyle(); - - $sfIsObject = ($styleFont instanceof Font) ? true : false; - $spIsObject = ($styleParagraph instanceof Paragraph) ? true : false; - - $arrText = $textrun->getText(); - if (!is_array($arrText)) { - $arrText = array($arrText); - } - - $xmlWriter->startElement('w:p'); - - if ($spIsObject) { - $this->_writeParagraphStyle($xmlWriter, $styleParagraph); - } elseif (!$spIsObject && !is_null($styleParagraph)) { - $xmlWriter->startElement('w:pPr'); - $xmlWriter->startElement('w:pStyle'); - $xmlWriter->writeAttribute('w:val', $styleParagraph); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - - foreach ($arrText as $text) { - - if (substr($text, 0, 1) == '{') { - $text = substr($text, 1, -1); - - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:fldChar'); - $xmlWriter->writeAttribute('w:fldCharType', 'begin'); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $xmlWriter->startElement('w:r'); - - if ($sfIsObject) { - $this->_writeTextStyle($xmlWriter, $styleFont); - } elseif (!$sfIsObject && !is_null($styleFont)) { - $xmlWriter->startElement('w:rPr'); - $xmlWriter->startElement('w:rStyle'); - $xmlWriter->writeAttribute('w:val', $styleFont); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - - $xmlWriter->startElement('w:instrText'); - $xmlWriter->writeAttribute('xml:space', 'preserve'); - $xmlWriter->writeRaw($text); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:fldChar'); - $xmlWriter->writeAttribute('w:fldCharType', 'separate'); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:fldChar'); - $xmlWriter->writeAttribute('w:fldCharType', 'end'); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } else { - $text = htmlspecialchars($text); - $text = String::controlCharacterPHP2OOXML($text); - - $xmlWriter->startElement('w:r'); - - if ($sfIsObject) { - $this->_writeTextStyle($xmlWriter, $styleFont); - } elseif (!$sfIsObject && !is_null($styleFont)) { - $xmlWriter->startElement('w:rPr'); - $xmlWriter->startElement('w:rStyle'); - $xmlWriter->writeAttribute('w:val', $styleFont); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - - $xmlWriter->startElement('w:t'); - $xmlWriter->writeAttribute('xml:space', 'preserve'); - $xmlWriter->writeRaw($text); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - } - - $xmlWriter->endElement(); // p - } - - /** - * Write footnote reference element - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section $section - */ - protected function _writeTextStyle(XMLWriter $xmlWriter, Font $style) + protected function writeFontStyle(XMLWriter $xmlWriter, Font $style) { $font = $style->getName(); $bold = $style->getBold(); @@ -504,209 +959,16 @@ class Base extends WriterPart $xmlWriter->endElement(); } - /** - * Write text break element - * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param \PhpOffice\PhpWord\Section\TextBreak $element - */ - protected function _writeTextBreak($xmlWriter, $element = null) - { - $hasStyle = false; - if (!is_null($element)) { - $fontStyle = $element->getFontStyle(); - $sfIsObject = ($fontStyle instanceof Font) ? true : false; - $paragraphStyle = $element->getParagraphStyle(); - $spIsObject = ($paragraphStyle instanceof Paragraph) ? true : false; - $hasStyle = !is_null($fontStyle) || !is_null($paragraphStyle); - } - if ($hasStyle) { - // Paragraph style - $xmlWriter->startElement('w:p'); - if ($spIsObject) { - $this->_writeParagraphStyle($xmlWriter, $paragraphStyle); - } elseif (!$spIsObject && !is_null($paragraphStyle)) { - $xmlWriter->startElement('w:pPr'); - $xmlWriter->startElement('w:pStyle'); - $xmlWriter->writeAttribute('w:val', $paragraphStyle); - $xmlWriter->endElement(); // w:pStyle - $xmlWriter->endElement(); // w:pPr - } - // Font style - if (!is_null($fontStyle)) { - $xmlWriter->startElement('w:pPr'); - if ($sfIsObject) { - $this->_writeTextStyle($xmlWriter, $fontStyle); - } elseif (!$sfIsObject && !is_null($fontStyle)) { - $xmlWriter->startElement('w:rPr'); - $xmlWriter->startElement('w:rStyle'); - $xmlWriter->writeAttribute('w:val', $fontStyle); - $xmlWriter->endElement(); // w:rStyle - $xmlWriter->endElement(); // w:rPr - } - $xmlWriter->endElement(); // w:pPr - } - $xmlWriter->endElement(); // w:p - } else { - // Null element. No paragraph nor font style - $xmlWriter->writeElement('w:p', null); - } - } - - /** - * Write footnote reference element - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section\Table $table - */ - protected function _writeTable(XMLWriter $xmlWriter, Table $table) - { - $_rows = $table->getRows(); - $_cRows = count($_rows); - - if ($_cRows > 0) { - $xmlWriter->startElement('w:tbl'); - - // Table grid - $cellWidths = array(); - for ($i = 0; $i < $_cRows; $i++) { - $row = $_rows[$i]; - $cells = $row->getCells(); - if (count($cells) <= count($cellWidths)) { - continue; - } - $cellWidths = array(); - foreach ($cells as $cell) { - $cellWidths[] = $cell->getWidth(); - } - } - $xmlWriter->startElement('w:tblGrid'); - foreach ($cellWidths as $width) { - $xmlWriter->startElement('w:gridCol'); - if (!is_null($width)) { - $xmlWriter->writeAttribute('w:w', $width); - $xmlWriter->writeAttribute('w:type', 'dxa'); - } - $xmlWriter->endElement(); - } - $xmlWriter->endElement(); // w:tblGrid - - // Table style - $tblStyle = $table->getStyle(); - $tblWidth = $table->getWidth(); - if ($tblStyle instanceof \PhpOffice\PhpWord\Style\Table) { - $this->_writeTableStyle($xmlWriter, $tblStyle, false); - } else { - if (!empty($tblStyle)) { - $xmlWriter->startElement('w:tblPr'); - $xmlWriter->startElement('w:tblStyle'); - $xmlWriter->writeAttribute('w:val', $tblStyle); - $xmlWriter->endElement(); - if (!is_null($tblWidth)) { - $xmlWriter->startElement('w:tblW'); - $xmlWriter->writeAttribute('w:w', $tblWidth); - $xmlWriter->writeAttribute('w:type', 'pct'); - $xmlWriter->endElement(); - } - $xmlWriter->endElement(); - } - } - - // Table rows - for ($i = 0; $i < $_cRows; $i++) { - $row = $_rows[$i]; - $height = $row->getHeight(); - $rowStyle = $row->getStyle(); - $tblHeader = $rowStyle->getTblHeader(); - $cantSplit = $rowStyle->getCantSplit(); - $exactHeight = $rowStyle->getExactHeight(); - - $xmlWriter->startElement('w:tr'); - - if (!is_null($height) || !is_null($tblHeader) || !is_null($cantSplit)) { - $xmlWriter->startElement('w:trPr'); - if (!is_null($height)) { - $xmlWriter->startElement('w:trHeight'); - $xmlWriter->writeAttribute('w:val', $height); - $xmlWriter->writeAttribute('w:hRule', ($exactHeight ? 'exact' : 'atLeast')); - $xmlWriter->endElement(); - } - if ($tblHeader) { - $xmlWriter->startElement('w:tblHeader'); - $xmlWriter->writeAttribute('w:val', '1'); - $xmlWriter->endElement(); - } - if ($cantSplit) { - $xmlWriter->startElement('w:cantSplit'); - $xmlWriter->writeAttribute('w:val', '1'); - $xmlWriter->endElement(); - } - $xmlWriter->endElement(); - } - - foreach ($row->getCells() as $cell) { - $xmlWriter->startElement('w:tc'); - - $cellStyle = $cell->getStyle(); - $width = $cell->getWidth(); - - $xmlWriter->startElement('w:tcPr'); - $xmlWriter->startElement('w:tcW'); - $xmlWriter->writeAttribute('w:w', $width); - $xmlWriter->writeAttribute('w:type', 'dxa'); - $xmlWriter->endElement(); - - if ($cellStyle instanceof Cell) { - $this->_writeCellStyle($xmlWriter, $cellStyle); - } - - $xmlWriter->endElement(); - - $_elements = $cell->getElements(); - if (count($_elements) > 0) { - foreach ($_elements as $element) { - if ($element instanceof Text) { - $this->_writeText($xmlWriter, $element); - } elseif ($element instanceof TextRun) { - $this->_writeTextRun($xmlWriter, $element); - } elseif ($element instanceof Link) { - $this->_writeLink($xmlWriter, $element); - } elseif ($element instanceof TextBreak) { - $this->_writeTextBreak($xmlWriter, $element); - } elseif ($element instanceof ListItem) { - $this->_writeListItem($xmlWriter, $element); - } elseif ($element instanceof Image) { - $this->_writeImage($xmlWriter, $element); - } elseif ($element instanceof Object) { - $this->_writeObject($xmlWriter, $element); - } elseif ($element instanceof PreserveText) { - $this->_writePreserveText($xmlWriter, $element); - } elseif ($element instanceof CheckBox) { - $this->_writeCheckBox($xmlWriter, $element); - } - } - } else { - $this->_writeTextBreak($xmlWriter); - } - - $xmlWriter->endElement(); - } - $xmlWriter->endElement(); - } - $xmlWriter->endElement(); - } - } - /** * Write table style * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Style\Table $style + * @param XMLWriter $xmlWriter + * @param TableStyle $style * @param boolean $isFullStyle */ - protected function _writeTableStyle( + protected function writeTableStyle( XMLWriter $xmlWriter, - \PhpOffice\PhpWord\Style\Table $style, + TableStyle $style, $isFullStyle = true ) { $bgColor = $style->getBgColor(); @@ -822,7 +1084,7 @@ class Base extends WriterPart // First Row $firstRow = $style->getFirstRow(); if (!is_null($firstRow)) { - $this->_writeRowStyle($xmlWriter, 'firstRow', $firstRow); + $this->writeRowStyle($xmlWriter, 'firstRow', $firstRow); } } } @@ -830,14 +1092,14 @@ class Base extends WriterPart /** * Write row style * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param XMLWriter $xmlWriter * @param string $type - * @param PhpOffice\PhpWord\Style\Table $style + * @param TableStyle $style */ - protected function _writeRowStyle( + protected function writeRowStyle( XMLWriter $xmlWriter, $type, - \PhpOffice\PhpWord\Style\Table $style + TableStyle $style ) { $brdSz = $style->getBorderSize(); $brdCol = $style->getBorderColor(); @@ -847,7 +1109,6 @@ class Base extends WriterPart $bLeft = (!is_null($brdSz[1])) ? true : false; $bRight = (!is_null($brdSz[2])) ? true : false; $bBottom = (!is_null($brdSz[3])) ? true : false; - $borders = ($bTop || $bLeft || $bRight || $bBottom) ? true : false; $xmlWriter->startElement('w:tblStylePr'); $xmlWriter->writeAttribute('w:type', $type); @@ -900,10 +1161,10 @@ class Base extends WriterPart /** * Write footnote reference element * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Style\Cell $style + * @param XMLWriter $xmlWriter + * @param Cell $style */ - protected function _writeCellStyle(XMLWriter $xmlWriter, Cell $style = null) + protected function writeCellStyle(XMLWriter $xmlWriter, Cell $style) { $bgColor = $style->getBgColor(); $valign = $style->getVAlign(); @@ -1007,303 +1268,91 @@ class Base extends WriterPart } /** - * Write image element + * Write individual rels entry * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param mixed $image - * @param boolean $withoutP + * @param XMLWriter $xmlWriter + * @param int $pId Relationship ID + * @param string $pType Relationship type + * @param string $pTarget Relationship target + * @param string $pTargetMode Relationship target mode */ - protected function _writeImage(XMLWriter $xmlWriter, $image, $withoutP = false) - { - $rId = $image->getRelationId(); - - $style = $image->getStyle(); - $width = $style->getWidth(); - $height = $style->getHeight(); - $align = $style->getAlign(); - $marginTop = $style->getMarginTop(); - $marginLeft = $style->getMarginLeft(); - $wrappingStyle = $style->getWrappingStyle(); - - if (!$withoutP) { - $xmlWriter->startElement('w:p'); - - if (!is_null($align)) { - $xmlWriter->startElement('w:pPr'); - $xmlWriter->startElement('w:jc'); - $xmlWriter->writeAttribute('w:val', $align); - $xmlWriter->endElement(); - $xmlWriter->endElement(); + protected function writeRelationship( + XMLWriter $xmlWriter, + $pId = 1, + $pType = '', + $pTarget = '', + $pTargetMode = '' + ) { + if ($pType != '' && $pTarget != '') { + if (strpos($pId, 'rId') === false) { + $pId = 'rId' . $pId; } - } - $xmlWriter->startElement('w:r'); + // Write relationship + $xmlWriter->startElement('Relationship'); + $xmlWriter->writeAttribute('Id', $pId); + $xmlWriter->writeAttribute('Type', $pType); + $xmlWriter->writeAttribute('Target', $pTarget); - $xmlWriter->startElement('w:pict'); + if ($pTargetMode != '') { + $xmlWriter->writeAttribute('TargetMode', $pTargetMode); + } - $xmlWriter->startElement('v:shape'); - $xmlWriter->writeAttribute('type', '#_x0000_t75'); - - $imgStyle = ''; - if (null !== $width) { - $imgStyle .= 'width:' . $width . 'px;'; - } - if (null !== $height) { - $imgStyle .= 'height:' . $height . 'px;'; - } - if (null !== $marginTop) { - $imgStyle .= 'margin-top:' . $marginTop . 'in;'; - } - if (null !== $marginLeft) { - $imgStyle .= 'margin-left:' . $marginLeft . 'in;'; - } - - switch ($wrappingStyle) { - case \PhpOffice\PhpWord\Style\Image::WRAPPING_STYLE_BEHIND: - $imgStyle .= 'position:absolute;z-index:-251658752;'; - break; - case \PhpOffice\PhpWord\Style\Image::WRAPPING_STYLE_SQUARE: - $imgStyle .= 'position:absolute;z-index:251659264;mso-position-horizontal:absolute;mso-position-vertical:absolute;'; - break; - case \PhpOffice\PhpWord\Style\Image::WRAPPING_STYLE_TIGHT: - $imgStyle .= 'position:absolute;z-index:251659264;mso-wrap-edited:f;mso-position-horizontal:absolute;mso-position-vertical:absolute'; - break; - case \PhpOffice\PhpWord\Style\Image::WRAPPING_STYLE_INFRONT: - $imgStyle .= 'position:absolute;zz-index:251659264;mso-position-horizontal:absolute;mso-position-vertical:absolute;'; - break; - } - - $xmlWriter->writeAttribute('style', $imgStyle); - - $xmlWriter->startElement('v:imagedata'); - $xmlWriter->writeAttribute('r:id', 'rId' . $rId); - $xmlWriter->writeAttribute('o:title', ''); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - - if (!$withoutP) { - $xmlWriter->endElement(); // w:p - } - } - - /** - * Write footnote reference element - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param mixed $image - */ - protected function _writeWatermark(XMLWriter $xmlWriter, $image) - { - $rId = $image->getRelationId(); - - $style = $image->getStyle(); - $width = $style->getWidth(); - $height = $style->getHeight(); - $marginLeft = $style->getMarginLeft(); - $marginTop = $style->getMarginTop(); - - $xmlWriter->startElement('w:p'); - - $xmlWriter->startElement('w:r'); - - $xmlWriter->startElement('w:pict'); - - $xmlWriter->startElement('v:shape'); - $xmlWriter->writeAttribute('type', '#_x0000_t75'); - - $strStyle = 'position:absolute;'; - $strStyle .= ' width:' . $width . 'px;'; - $strStyle .= ' height:' . $height . 'px;'; - if (!is_null($marginTop)) { - $strStyle .= ' margin-top:' . $marginTop . 'px;'; - } - if (!is_null($marginLeft)) { - $strStyle .= ' margin-left:' . $marginLeft . 'px;'; - } - - $xmlWriter->writeAttribute('style', $strStyle); - - $xmlWriter->startElement('v:imagedata'); - $xmlWriter->writeAttribute('r:id', 'rId' . $rId); - $xmlWriter->writeAttribute('o:title', ''); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - } - - /** - * Write title element - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section\Title $title - */ - protected function _writeTitle(XMLWriter $xmlWriter, Title $title) - { - $text = htmlspecialchars($title->getText()); - $text = String::controlCharacterPHP2OOXML($text); - $anchor = $title->getAnchor(); - $bookmarkId = $title->getBookmarkId(); - $style = $title->getStyle(); - - $xmlWriter->startElement('w:p'); - - if (!empty($style)) { - $xmlWriter->startElement('w:pPr'); - $xmlWriter->startElement('w:pStyle'); - $xmlWriter->writeAttribute('w:val', $style); $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:fldChar'); - $xmlWriter->writeAttribute('w:fldCharType', 'end'); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $xmlWriter->startElement('w:bookmarkStart'); - $xmlWriter->writeAttribute('w:id', $bookmarkId); - $xmlWriter->writeAttribute('w:name', $anchor); - $xmlWriter->endElement(); - - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:t'); - $xmlWriter->writeRaw($text); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $xmlWriter->startElement('w:bookmarkEnd'); - $xmlWriter->writeAttribute('w:id', $bookmarkId); - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - } - - /** - * Write footnote element which links to the actual content in footnotes.xml - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section\Footnote $footnote - * @param boolean $withoutP - */ - protected function _writeFootnote(XMLWriter $xmlWriter, Footnote $footnote, $withoutP = false) - { - if (!$withoutP) { - $xmlWriter->startElement('w:p'); - } - - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:rPr'); - $xmlWriter->startElement('w:rStyle'); - $xmlWriter->writeAttribute('w:val', 'FootnoteReference'); - $xmlWriter->endElement(); // w:rStyle - $xmlWriter->endElement(); // w:rPr - $xmlWriter->startElement('w:footnoteReference'); - $xmlWriter->writeAttribute('w:id', $footnote->getReferenceId()); - $xmlWriter->endElement(); // w:footnoteReference - - $xmlWriter->endElement(); // w:r - - if (!$withoutP) { - $xmlWriter->endElement(); // w:p + } else { + throw new Exception("Invalid parameters passed."); } } /** - * Write CheckBox + * Write inline paragraph style * - * @param boolean $withoutP - * @param boolean $checkState + * @param XMLWriter $xmlWriter + * @param Paragraph|string $styleParagraph + * @param boolean $withoutPPR */ - protected function _writeCheckBox(XMLWriter $xmlWriter, CheckBox $checkbox, $withoutP = false, $checkState = false) - { - $name = htmlspecialchars($checkbox->getName()); - $name = String::controlCharacterPHP2OOXML($name); - $text = htmlspecialchars($checkbox->getText()); - $text = String::controlCharacterPHP2OOXML($text); - - $styleFont = $checkbox->getFontStyle(); - $sfIsObject = ($styleFont instanceof Font) ? true : false; - if (!$withoutP) { - $xmlWriter->startElement('w:p'); - $styleParagraph = $checkbox->getParagraphStyle(); - $spIsObject = ($styleParagraph instanceof Paragraph) ? true : false; - if ($spIsObject) { - $this->_writeParagraphStyle($xmlWriter, $styleParagraph); - } elseif (!$spIsObject && !is_null($styleParagraph)) { - $xmlWriter->startElement('w:pPr'); + protected function writeInlineParagraphStyle( + XMLWriter $xmlWriter, + $styleParagraph = null, + $withoutPPR = false + ) { + if ($styleParagraph instanceof Paragraph) { + $this->writeParagraphStyle($xmlWriter, $styleParagraph, $withoutPPR); + } else { + if (!is_null($styleParagraph)) { + if (!$withoutPPR) { + $xmlWriter->startElement('w:pPr'); + } $xmlWriter->startElement('w:pStyle'); $xmlWriter->writeAttribute('w:val', $styleParagraph); $xmlWriter->endElement(); + if (!$withoutPPR) { + $xmlWriter->endElement(); + } + } + } + } + + /** + * Write inline font style + * + * @param XMLWriter $xmlWriter + * @param Font|string $styleFont + */ + protected function writeInlineFontStyle( + XMLWriter $xmlWriter, + $styleFont = null + ) { + if ($styleFont instanceof Font) { + $this->writeFontStyle($xmlWriter, $styleFont); + } else { + if (!is_null($styleFont)) { + $xmlWriter->startElement('w:rPr'); + $xmlWriter->startElement('w:rStyle'); + $xmlWriter->writeAttribute('w:val', $styleFont); + $xmlWriter->endElement(); $xmlWriter->endElement(); } } - - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:fldChar'); - $xmlWriter->writeAttribute('w:fldCharType', 'begin'); - $xmlWriter->startElement('w:ffData'); - $xmlWriter->startElement('w:name'); - $xmlWriter->writeAttribute('w:val', $name); - $xmlWriter->endElement(); //w:name - $xmlWriter->writeAttribute('w:enabled', ''); - $xmlWriter->startElement('w:calcOnExit'); - $xmlWriter->writeAttribute('w:val', '0'); - $xmlWriter->endElement(); //w:calcOnExit - $xmlWriter->startElement('w:checkBox'); - $xmlWriter->writeAttribute('w:sizeAuto', ''); - $xmlWriter->startElement('w:default'); - $xmlWriter->writeAttribute('w:val', ($checkState ? '1' : '0')); - $xmlWriter->endElement(); //w:default - $xmlWriter->endElement(); //w:checkBox - $xmlWriter->endElement(); // w:ffData - $xmlWriter->endElement(); // w:fldChar - $xmlWriter->endElement(); // w:r - - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:instrText'); - $xmlWriter->writeAttribute('xml:space', 'preserve'); - $xmlWriter->writeRaw(' FORMCHECKBOX '); - $xmlWriter->endElement();// w:instrText - $xmlWriter->endElement(); // w:r - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:fldChar'); - $xmlWriter->writeAttribute('w:fldCharType', 'seperate'); - $xmlWriter->endElement();// w:fldChar - $xmlWriter->endElement(); // w:r - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:fldChar'); - $xmlWriter->writeAttribute('w:fldCharType', 'end'); - $xmlWriter->endElement();// w:fldChar - $xmlWriter->endElement(); // w:r - - $xmlWriter->startElement('w:r'); - if ($sfIsObject) { - $this->_writeTextStyle($xmlWriter, $styleFont); - } elseif (!$sfIsObject && !is_null($styleFont)) { - $xmlWriter->startElement('w:rPr'); - $xmlWriter->startElement('w:rStyle'); - $xmlWriter->writeAttribute('w:val', $styleFont); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - $xmlWriter->startElement('w:t'); - $xmlWriter->writeAttribute('xml:space', 'preserve'); - $xmlWriter->writeRaw($text); - $xmlWriter->endElement(); // w:t - $xmlWriter->endElement(); // w:r - - if (!$withoutP) { - $xmlWriter->endElement(); // w:p - } } } diff --git a/src/PhpWord/Writer/Word2007/ContentTypes.php b/src/PhpWord/Writer/Word2007/ContentTypes.php index 9f8c3fb2..65c75384 100755 --- a/src/PhpWord/Writer/Word2007/ContentTypes.php +++ b/src/PhpWord/Writer/Word2007/ContentTypes.php @@ -19,12 +19,12 @@ class ContentTypes extends WriterPart { /** * Write [Content_Types].xml - * @param array $_imageTypes - * @param array $_objectTypes + * @param array $imageTypes + * @param array $objectTypes * @param int $_cHdrs * @param array $footers */ - public function writeContentTypes($_imageTypes, $_objectTypes, $_cHdrs, $footers) + public function writeContentTypes($imageTypes, $objectTypes, $_cHdrs, $footers) { // Create XML writer $xmlWriter = $this->getXmlWriter(); @@ -37,27 +37,27 @@ class ContentTypes extends WriterPart $xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/content-types'); // Rels - $this->_writeDefaultContentType( + $this->writeDefaultContentType( $xmlWriter, 'rels', 'application/vnd.openxmlformats-package.relationships+xml' ); // XML - $this->_writeDefaultContentType( + $this->writeDefaultContentType( $xmlWriter, 'xml', 'application/xml' ); // Add media content-types - foreach ($_imageTypes as $key => $value) { - $this->_writeDefaultContentType($xmlWriter, $key, $value); + foreach ($imageTypes as $key => $value) { + $this->writeDefaultContentType($xmlWriter, $key, $value); } // Add embedding content-types - if (count($_objectTypes) > 0) { - $this->_writeDefaultContentType( + if (count($objectTypes) > 0) { + $this->writeDefaultContentType( $xmlWriter, 'bin', 'application/vnd.openxmlformats-officedocument.oleObject' @@ -65,76 +65,76 @@ class ContentTypes extends WriterPart } // DocProps - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/docProps/app.xml', 'application/vnd.openxmlformats-officedocument.extended-properties+xml' ); - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/docProps/core.xml', 'application/vnd.openxmlformats-package.core-properties+xml' ); // Document - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/document.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml' ); // Styles - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/styles.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml' ); // Numbering - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/numbering.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml' ); // Settings - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/settings.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml' ); // Theme1 - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/theme/theme1.xml', 'application/vnd.openxmlformats-officedocument.theme+xml' ); // WebSettings - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/webSettings.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml' ); // Font Table - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/fontTable.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml' ); // Footnotes - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/footnotes.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml' ); for ($i = 1; $i <= $_cHdrs; $i++) { - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/header' . $i . '.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml' @@ -143,7 +143,7 @@ class ContentTypes extends WriterPart for ($i = 1; $i <= count($footers); $i++) { if (!is_null($footers[$i])) { - $this->_writeOverrideContentType( + $this->writeOverrideContentType( $xmlWriter, '/word/footer' . $i . '.xml', 'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml' @@ -158,32 +158,15 @@ class ContentTypes extends WriterPart return $xmlWriter->getData(); } - /** - * Get image mime type - * - * @param string $pFile Filename - * @return string Mime Type - * @throws \PhpOffice\PhpWord\Exceptions\Exception - */ - private function _getImageMimeType($pFile = '') - { - if (file_exists($pFile)) { - $image = getimagesize($pFile); - return image_type_to_mime_type($image[2]); - } else { - throw new Exception("File $pFile does not exist"); - } - } - /** * Write Default XML element * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter XML Writer + * @param XMLWriter $xmlWriter XML Writer * @param string $pPartname Part name * @param string $pContentType Content type - * @throws \PhpOffice\PhpWord\Exceptions\Exception + * @throws Exception */ - private function _writeDefaultContentType(XMLWriter $xmlWriter = null, $pPartname = '', $pContentType = '') + private function writeDefaultContentType(XMLWriter $xmlWriter = null, $pPartname = '', $pContentType = '') { if ($pPartname != '' && $pContentType != '') { // Write content type @@ -199,12 +182,12 @@ class ContentTypes extends WriterPart /** * Write Override XML element * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param XMLWriter $xmlWriter * @param string $pPartname Part name * @param string $pContentType Content type - * @throws \PhpOffice\PhpWord\Exceptions\Exception + * @throws Exception */ - private function _writeOverrideContentType(XMLWriter $xmlWriter = null, $pPartname = '', $pContentType = '') + private function writeOverrideContentType(XMLWriter $xmlWriter = null, $pPartname = '', $pContentType = '') { if ($pPartname != '' && $pContentType != '') { // Write content type @@ -216,4 +199,21 @@ class ContentTypes extends WriterPart throw new Exception("Invalid parameters passed."); } } + + /** + * Get image mime type + * + * @param string $pFile Filename + * @return string Mime Type + * @throws Exception + */ + private function getImageMimeType($pFile = '') + { + if (file_exists($pFile)) { + $image = getimagesize($pFile); + return image_type_to_mime_type($image[2]); + } else { + throw new Exception("File $pFile does not exist"); + } + } } diff --git a/src/PhpWord/Writer/Word2007/DocProps.php b/src/PhpWord/Writer/Word2007/DocProps.php index 591dfc0e..92c2fab8 100644 --- a/src/PhpWord/Writer/Word2007/DocProps.php +++ b/src/PhpWord/Writer/Word2007/DocProps.php @@ -110,7 +110,7 @@ class DocProps extends WriterPart /** * Write docProps/core.xml * - * @param PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord */ public function writeDocPropsCore(PhpWord $phpWord = null) { diff --git a/src/PhpWord/Writer/Word2007/Document.php b/src/PhpWord/Writer/Word2007/Document.php index 585050df..03184bd1 100644 --- a/src/PhpWord/Writer/Word2007/Document.php +++ b/src/PhpWord/Writer/Word2007/Document.php @@ -36,7 +36,7 @@ class Document extends Base /** * Write word/document.xml * - * @param PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord */ public function writeDocument(PhpWord $phpWord = null) { @@ -72,38 +72,38 @@ class Document extends Base $_elements = $section->getElements(); foreach ($_elements as $element) { if ($element instanceof Text) { - $this->_writeText($xmlWriter, $element); + $this->writeText($xmlWriter, $element); } elseif ($element instanceof TextRun) { - $this->_writeTextRun($xmlWriter, $element); + $this->writeTextRun($xmlWriter, $element); } elseif ($element instanceof Link) { - $this->_writeLink($xmlWriter, $element); + $this->writeLink($xmlWriter, $element); } elseif ($element instanceof Title) { - $this->_writeTitle($xmlWriter, $element); + $this->writeTitle($xmlWriter, $element); } elseif ($element instanceof TextBreak) { - $this->_writeTextBreak($xmlWriter, $element); + $this->writeTextBreak($xmlWriter, $element); } elseif ($element instanceof PageBreak) { - $this->_writePageBreak($xmlWriter); + $this->writePageBreak($xmlWriter); } elseif ($element instanceof Table) { - $this->_writeTable($xmlWriter, $element); + $this->writeTable($xmlWriter, $element); } elseif ($element instanceof ListItem) { - $this->_writeListItem($xmlWriter, $element); + $this->writeListItem($xmlWriter, $element); } elseif ($element instanceof Image) { - $this->_writeImage($xmlWriter, $element); + $this->writeImage($xmlWriter, $element); } elseif ($element instanceof Object) { - $this->_writeObject($xmlWriter, $element); + $this->writeObject($xmlWriter, $element); } elseif ($element instanceof TOC) { - $this->_writeTOC($xmlWriter); + $this->writeTOC($xmlWriter); } elseif ($element instanceof Footnote) { - $this->_writeFootnote($xmlWriter, $element); + $this->writeFootnote($xmlWriter, $element); } elseif ($element instanceof CheckBox) { - $this->_writeCheckBox($xmlWriter, $element); + $this->writeCheckBox($xmlWriter, $element); } } if ($pSection == $countSections) { - $this->_writeEndSection($xmlWriter, $section); + $this->writeEndSection($xmlWriter, $section); } else { - $this->_writeSection($xmlWriter, $section); + $this->writeSection($xmlWriter, $section); } } } @@ -118,14 +118,14 @@ class Document extends Base /** * Write begin section * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section $section + * @param XMLWriter $xmlWriter + * @param Section $section */ - private function _writeSection(XMLWriter $xmlWriter, Section $section) + private function writeSection(XMLWriter $xmlWriter, Section $section) { $xmlWriter->startElement('w:p'); $xmlWriter->startElement('w:pPr'); - $this->_writeEndSection($xmlWriter, $section, 3); + $this->writeEndSection($xmlWriter, $section, 3); $xmlWriter->endElement(); $xmlWriter->endElement(); } @@ -133,10 +133,10 @@ class Document extends Base /** * Write end section * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section $section + * @param XMLWriter $xmlWriter + * @param Section $section */ - private function _writeEndSection(XMLWriter $xmlWriter, Section $section) + private function writeEndSection(XMLWriter $xmlWriter, Section $section) { $settings = $section->getSettings(); $_headers = $section->getHeaders(); @@ -272,9 +272,9 @@ class Document extends Base /** * Write page break element * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param XMLWriter $xmlWriter */ - private function _writePageBreak(XMLWriter $xmlWriter) + private function writePageBreak(XMLWriter $xmlWriter) { $xmlWriter->startElement('w:p'); $xmlWriter->startElement('w:r'); @@ -285,122 +285,12 @@ class Document extends Base $xmlWriter->endElement(); } - /** - * Write list item element - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section\ListItem $listItem - */ - public function _writeListItem(XMLWriter $xmlWriter, ListItem $listItem) - { - $textObject = $listItem->getTextObject(); - $text = $textObject->getText(); - $styleParagraph = $textObject->getParagraphStyle(); - $SpIsObject = ($styleParagraph instanceof Paragraph) ? true : false; - - $depth = $listItem->getDepth(); - $listType = $listItem->getStyle()->getListType(); - - $xmlWriter->startElement('w:p'); - $xmlWriter->startElement('w:pPr'); - - if ($SpIsObject) { - $this->_writeParagraphStyle($xmlWriter, $styleParagraph, true); - } elseif (!$SpIsObject && !is_null($styleParagraph)) { - $xmlWriter->startElement('w:pStyle'); - $xmlWriter->writeAttribute('w:val', $styleParagraph); - $xmlWriter->endElement(); - } - - $xmlWriter->startElement('w:numPr'); - - $xmlWriter->startElement('w:ilvl'); - $xmlWriter->writeAttribute('w:val', $depth); - $xmlWriter->endElement(); - - $xmlWriter->startElement('w:numId'); - $xmlWriter->writeAttribute('w:val', $listType); - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $this->_writeText($xmlWriter, $textObject, true); - - $xmlWriter->endElement(); - } - - /** - * Write object element - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section\Object $object - */ - protected function _writeObject(XMLWriter $xmlWriter, Object $object) - { - $rIdObject = $object->getRelationId(); - $rIdImage = $object->getImageRelationId(); - $shapeId = md5($rIdObject . '_' . $rIdImage); - - $objectId = $object->getObjectId(); - - $style = $object->getStyle(); - $width = $style->getWidth(); - $height = $style->getHeight(); - $align = $style->getAlign(); - - - $xmlWriter->startElement('w:p'); - - if (!is_null($align)) { - $xmlWriter->startElement('w:pPr'); - $xmlWriter->startElement('w:jc'); - $xmlWriter->writeAttribute('w:val', $align); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } - - $xmlWriter->startElement('w:r'); - - $xmlWriter->startElement('w:object'); - $xmlWriter->writeAttribute('w:dxaOrig', '249'); - $xmlWriter->writeAttribute('w:dyaOrig', '160'); - - $xmlWriter->startElement('v:shape'); - $xmlWriter->writeAttribute('id', $shapeId); - $xmlWriter->writeAttribute('type', '#_x0000_t75'); - $xmlWriter->writeAttribute('style', 'width:104px;height:67px'); - $xmlWriter->writeAttribute('o:ole', ''); - - $xmlWriter->startElement('v:imagedata'); - $xmlWriter->writeAttribute('r:id', 'rId' . $rIdImage); - $xmlWriter->writeAttribute('o:title', ''); - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - - $xmlWriter->startElement('o:OLEObject'); - $xmlWriter->writeAttribute('Type', 'Embed'); - $xmlWriter->writeAttribute('ProgID', 'Package'); - $xmlWriter->writeAttribute('ShapeID', $shapeId); - $xmlWriter->writeAttribute('DrawAspect', 'Icon'); - $xmlWriter->writeAttribute('ObjectID', '_' . $objectId); - $xmlWriter->writeAttribute('r:id', 'rId' . $rIdObject); - $xmlWriter->endElement(); - - $xmlWriter->endElement(); - - $xmlWriter->endElement(); // w:r - - $xmlWriter->endElement(); // w:p - } - /** * Write TOC element * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param XMLWriter $xmlWriter */ - private function _writeTOC(XMLWriter $xmlWriter) + private function writeTOC(XMLWriter $xmlWriter) { $titles = TOC::getTitles(); $styleFont = TOC::getStyleFont(); @@ -421,7 +311,7 @@ class Document extends Base $xmlWriter->startElement('w:pPr'); if ($isObject && !is_null($styleFont->getParagraphStyle())) { - $this->_writeParagraphStyle($xmlWriter, $styleFont->getParagraphStyle()); + $this->writeParagraphStyle($xmlWriter, $styleFont->getParagraphStyle()); } if ($indent > 0) { @@ -479,7 +369,7 @@ class Document extends Base $xmlWriter->startElement('w:r'); if ($isObject) { - $this->_writeTextStyle($xmlWriter, $styleFont); + $this->writeFontStyle($xmlWriter, $styleFont); } $xmlWriter->startElement('w:t'); diff --git a/src/PhpWord/Writer/Word2007/DocumentRels.php b/src/PhpWord/Writer/Word2007/DocumentRels.php index 20a014f2..53a5ded5 100755 --- a/src/PhpWord/Writer/Word2007/DocumentRels.php +++ b/src/PhpWord/Writer/Word2007/DocumentRels.php @@ -15,7 +15,7 @@ use PhpOffice\PhpWord\Shared\XMLWriter; /** * Word2007 document rels part writer */ -class DocumentRels extends WriterPart +class DocumentRels extends Base { /** * Write word/_rels/document.xml.rels @@ -35,7 +35,7 @@ class DocumentRels extends WriterPart $xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); // Relationship word/document.xml - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles', @@ -43,7 +43,7 @@ class DocumentRels extends WriterPart ); // Relationship word/numbering.xml - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, 2, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering', @@ -51,7 +51,7 @@ class DocumentRels extends WriterPart ); // Relationship word/settings.xml - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, 3, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings', @@ -59,7 +59,7 @@ class DocumentRels extends WriterPart ); // Relationship word/settings.xml - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, 4, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', @@ -67,7 +67,7 @@ class DocumentRels extends WriterPart ); // Relationship word/settings.xml - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, 5, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings', @@ -75,7 +75,7 @@ class DocumentRels extends WriterPart ); // Relationship word/settings.xml - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, 6, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable', @@ -89,7 +89,7 @@ class DocumentRels extends WriterPart $relationId = $relation['rID']; $targetMode = ($relationType == 'hyperlink') ? 'External' : ''; - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, $relationId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/' . $relationType, @@ -128,7 +128,7 @@ class DocumentRels extends WriterPart $relationName = $relation['target']; $relationId = $relation['rID']; - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, $relationId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/' . $relationType, @@ -142,36 +142,4 @@ class DocumentRels extends WriterPart // Return return $xmlWriter->getData(); } - - /** - * Write individual rels entry - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param int $pId Relationship ID - * @param string $pType Relationship type - * @param string $pTarget Relationship target - * @param string $pTargetMode Relationship target mode - */ - private function _writeRelationship(XMLWriter $xmlWriter = null, $pId = 1, $pType = '', $pTarget = '', $pTargetMode = '') - { - if ($pType != '' && $pTarget != '') { - if (strpos($pId, 'rId') === false) { - $pId = 'rId' . $pId; - } - - // Write relationship - $xmlWriter->startElement('Relationship'); - $xmlWriter->writeAttribute('Id', $pId); - $xmlWriter->writeAttribute('Type', $pType); - $xmlWriter->writeAttribute('Target', $pTarget); - - if ($pTargetMode != '') { - $xmlWriter->writeAttribute('TargetMode', $pTargetMode); - } - - $xmlWriter->endElement(); - } else { - throw new Exception("Invalid parameters passed."); - } - } } diff --git a/src/PhpWord/Writer/Word2007/Footer.php b/src/PhpWord/Writer/Word2007/Footer.php index d4f68826..4a3b97f6 100644 --- a/src/PhpWord/Writer/Word2007/Footer.php +++ b/src/PhpWord/Writer/Word2007/Footer.php @@ -15,6 +15,7 @@ use PhpOffice\PhpWord\Section\Table; use PhpOffice\PhpWord\Section\Text; use PhpOffice\PhpWord\Section\TextBreak; use PhpOffice\PhpWord\Section\TextRun; +use PhpOffice\PhpWord\Section\Footer as FooterElement; use PhpOffice\PhpWord\Shared\XMLWriter; /** @@ -25,9 +26,9 @@ class Footer extends Base /** * Write word/footnotes.xml * - * @param PhpOffice\PhpWord\Section\Footer $footer + * @param FooterElement $footer */ - public function writeFooter(\PhpOffice\PhpWord\Section\Footer $footer) + public function writeFooter(FooterElement $footer) { // Create XML writer $xmlWriter = $this->getXmlWriter(); @@ -50,17 +51,17 @@ class Footer extends Base foreach ($_elements as $element) { if ($element instanceof Text) { - $this->_writeText($xmlWriter, $element); + $this->writeText($xmlWriter, $element); } elseif ($element instanceof TextRun) { - $this->_writeTextRun($xmlWriter, $element); + $this->writeTextRun($xmlWriter, $element); } elseif ($element instanceof TextBreak) { - $this->_writeTextBreak($xmlWriter, $element); + $this->writeTextBreak($xmlWriter, $element); } elseif ($element instanceof Table) { - $this->_writeTable($xmlWriter, $element); + $this->writeTable($xmlWriter, $element); } elseif ($element instanceof Image) { - $this->_writeImage($xmlWriter, $element); + $this->writeImage($xmlWriter, $element); } elseif ($element instanceof PreserveText) { - $this->_writePreserveText($xmlWriter, $element); + $this->writePreserveText($xmlWriter, $element); } } diff --git a/src/PhpWord/Writer/Word2007/Footnotes.php b/src/PhpWord/Writer/Word2007/Footnotes.php index d69e0f0c..ce4aba60 100644 --- a/src/PhpWord/Writer/Word2007/Footnotes.php +++ b/src/PhpWord/Writer/Word2007/Footnotes.php @@ -77,26 +77,18 @@ class Footnotes extends Base /** * Write footnote content, overrides method in parent class * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param PhpOffice\PhpWord\Section\Footnote $footnote + * @param XMLWriter $xmlWriter + * @param Footnote $footnote + * @param boolean $withoutP */ - private function writeFootnote(XMLWriter $xmlWriter, Footnote $footnote) + protected function writeFootnote(XMLWriter $xmlWriter, Footnote $footnote, $withoutP = false) { $xmlWriter->startElement('w:footnote'); $xmlWriter->writeAttribute('w:id', $footnote->getReferenceId()); $xmlWriter->startElement('w:p'); // Paragraph style - $paragraphStyle = $footnote->getParagraphStyle(); - $spIsObject = ($paragraphStyle instanceof Paragraph) ? true : false; - if ($spIsObject) { - $this->_writeParagraphStyle($xmlWriter, $paragraphStyle); - } elseif (!$spIsObject && !is_null($paragraphStyle)) { - $xmlWriter->startElement('w:pPr'); - $xmlWriter->startElement('w:pStyle'); - $xmlWriter->writeAttribute('w:val', $paragraphStyle); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - } + $styleParagraph = $footnote->getParagraphStyle(); + $this->writeInlineParagraphStyle($xmlWriter, $styleParagraph); // Reference symbol $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:rPr'); @@ -118,9 +110,9 @@ class Footnotes extends Base if (count($elements) > 0) { foreach ($elements as $element) { if ($element instanceof Text) { - $this->_writeText($xmlWriter, $element, true); + $this->writeText($xmlWriter, $element, true); } elseif ($element instanceof Link) { - $this->_writeLink($xmlWriter, $element, true); + $this->writeLink($xmlWriter, $element, true); } elseif ($element instanceof TextBreak) { $xmlWriter->writeElement('w:br'); } diff --git a/src/PhpWord/Writer/Word2007/FootnotesRels.php b/src/PhpWord/Writer/Word2007/FootnotesRels.php index f9e5c015..d0665de3 100644 --- a/src/PhpWord/Writer/Word2007/FootnotesRels.php +++ b/src/PhpWord/Writer/Word2007/FootnotesRels.php @@ -15,7 +15,7 @@ use PhpOffice\PhpWord\Shared\XMLWriter; /** * Word2007 footnotes rel part writer */ -class FootnotesRels extends WriterPart +class FootnotesRels extends Base { /** * Write word/_rels/footnotes.xml.rels @@ -41,7 +41,7 @@ class FootnotesRels extends WriterPart $relationId = $relation['rID']; $targetMode = ($relationType == 'hyperlink') ? 'External' : ''; - $this->_writeRelationship($xmlWriter, $relationId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/' . $relationType, $relationName, $targetMode); + $this->writeRelationship($xmlWriter, $relationId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/' . $relationType, $relationName, $targetMode); } $xmlWriter->endElement(); @@ -49,36 +49,4 @@ class FootnotesRels extends WriterPart // Return return $xmlWriter->getData(); } - - /** - * Write individual rels entry - * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param int $pId Relationship ID - * @param string $pType Relationship type - * @param string $pTarget Relationship target - * @param string $pTargetMode Relationship target mode - */ - private function _writeRelationship(XMLWriter $xmlWriter = null, $pId = 1, $pType = '', $pTarget = '', $pTargetMode = '') - { - if ($pType != '' && $pTarget != '') { - if (strpos($pId, 'rId') === false) { - $pId = 'rId' . $pId; - } - - // Write relationship - $xmlWriter->startElement('Relationship'); - $xmlWriter->writeAttribute('Id', $pId); - $xmlWriter->writeAttribute('Type', $pType); - $xmlWriter->writeAttribute('Target', $pTarget); - - if ($pTargetMode != '') { - $xmlWriter->writeAttribute('TargetMode', $pTargetMode); - } - - $xmlWriter->endElement(); - } else { - throw new Exception("Invalid parameters passed."); - } - } } diff --git a/src/PhpWord/Writer/Word2007/Header.php b/src/PhpWord/Writer/Word2007/Header.php index 50d77e98..03757693 100644 --- a/src/PhpWord/Writer/Word2007/Header.php +++ b/src/PhpWord/Writer/Word2007/Header.php @@ -15,6 +15,7 @@ use PhpOffice\PhpWord\Section\Table; use PhpOffice\PhpWord\Section\Text; use PhpOffice\PhpWord\Section\TextBreak; use PhpOffice\PhpWord\Section\TextRun; +use PhpOffice\PhpWord\Section\Header as HeaderElement; use PhpOffice\PhpWord\Shared\XMLWriter; /** @@ -25,9 +26,9 @@ class Header extends Base /** * Write word/headerx.xml * - * @param PhpOffice\PhpWord\Section\Header $header + * @param HeaderElement $header */ - public function writeHeader(\PhpOffice\PhpWord\Section\Header $header) + public function writeHeader(HeaderElement $header) { // Create XML writer $xmlWriter = $this->getXmlWriter(); @@ -51,21 +52,21 @@ class Header extends Base foreach ($_elements as $element) { if ($element instanceof Text) { - $this->_writeText($xmlWriter, $element); + $this->writeText($xmlWriter, $element); } elseif ($element instanceof TextRun) { - $this->_writeTextRun($xmlWriter, $element); + $this->writeTextRun($xmlWriter, $element); } elseif ($element instanceof TextBreak) { - $this->_writeTextBreak($xmlWriter, $element); + $this->writeTextBreak($xmlWriter, $element); } elseif ($element instanceof Table) { - $this->_writeTable($xmlWriter, $element); + $this->writeTable($xmlWriter, $element); } elseif ($element instanceof Image) { if (!$element->getIsWatermark()) { - $this->_writeImage($xmlWriter, $element); + $this->writeImage($xmlWriter, $element); } else { - $this->_writeWatermark($xmlWriter, $element); + $this->writeWatermark($xmlWriter, $element); } } elseif ($element instanceof PreserveText) { - $this->_writePreserveText($xmlWriter, $element); + $this->writePreserveText($xmlWriter, $element); } } diff --git a/src/PhpWord/Writer/Word2007/Rels.php b/src/PhpWord/Writer/Word2007/Rels.php index 19ce58b0..3e41033f 100755 --- a/src/PhpWord/Writer/Word2007/Rels.php +++ b/src/PhpWord/Writer/Word2007/Rels.php @@ -16,12 +16,12 @@ use PhpOffice\PhpWord\Shared\XMLWriter; /** * Word2007 rels part writer */ -class Rels extends WriterPart +class Rels extends Base { /** * Write _rels/.rels * - * @param PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord */ public function writeRelationships(PhpWord $phpWord = null) { @@ -38,7 +38,7 @@ class Rels extends WriterPart $relationId = 1; // Relationship word/document.xml - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, $relationId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument', @@ -46,7 +46,7 @@ class Rels extends WriterPart ); // Relationship docProps/core.xml - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, ++$relationId, 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties', @@ -54,7 +54,7 @@ class Rels extends WriterPart ); // Relationship docProps/app.xml - $this->_writeRelationship( + $this->writeRelationship( $xmlWriter, ++$relationId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties', @@ -65,37 +65,4 @@ class Rels extends WriterPart return $xmlWriter->getData(); } - - /** - * Write Override content type - * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param int $pId Relationship ID. rId will be prepended! - * @param string $pType Relationship type - * @param string $pTarget Relationship target - * @param string $pTargetMode Relationship target mode - * @throws \PhpOffice\PhpWord\Exceptions\Exception - */ - private function _writeRelationship(XMLWriter $xmlWriter = null, $pId = 1, $pType = '', $pTarget = '', $pTargetMode = '') - { - if ($pType != '' && $pTarget != '') { - if (strpos($pId, 'rId') === false) { - $pId = 'rId' . $pId; - } - - // Write relationship - $xmlWriter->startElement('Relationship'); - $xmlWriter->writeAttribute('Id', $pId); - $xmlWriter->writeAttribute('Type', $pType); - $xmlWriter->writeAttribute('Target', $pTarget); - - if ($pTargetMode != '') { - $xmlWriter->writeAttribute('TargetMode', $pTargetMode); - } - - $xmlWriter->endElement(); - } else { - throw new Exception("Invalid parameters passed."); - } - } } diff --git a/src/PhpWord/Writer/Word2007/Styles.php b/src/PhpWord/Writer/Word2007/Styles.php index 1c7c05e2..c7b35faf 100644 --- a/src/PhpWord/Writer/Word2007/Styles.php +++ b/src/PhpWord/Writer/Word2007/Styles.php @@ -14,28 +14,20 @@ use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; +use PhpOffice\PhpWord\Style\Table; /** * Word2007 styles part writer */ class Styles extends Base { - /** - * PhpWord object - * - * @var PhpWord - */ - private $phpWord; - /** * Write word/styles.xml * - * @param PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord */ public function writeStyles(PhpWord $phpWord = null) { - $this->phpWord = $phpWord; - // Create XML writer $xmlWriter = $this->getXmlWriter(); @@ -52,7 +44,7 @@ class Styles extends Base ); // Write default styles $styles = Style::getStyles(); - $this->writeDefaultStyles($xmlWriter, $styles); + $this->writeDefaultStyles($xmlWriter, $phpWord, $styles); // Write other styles if (count($styles) > 0) { foreach ($styles as $styleName => $style) { @@ -94,10 +86,10 @@ class Styles extends Base $xmlWriter->startElement('w:basedOn'); $xmlWriter->writeAttribute('w:val', 'Normal'); $xmlWriter->endElement(); - $this->_writeParagraphStyle($xmlWriter, $paragraphStyle); + $this->writeParagraphStyle($xmlWriter, $paragraphStyle); } - $this->_writeTextStyle($xmlWriter, $style); + $this->writeFontStyle($xmlWriter, $style); $xmlWriter->endElement(); @@ -127,10 +119,10 @@ class Styles extends Base $xmlWriter->endElement(); } - $this->_writeParagraphStyle($xmlWriter, $style); + $this->writeParagraphStyle($xmlWriter, $style); $xmlWriter->endElement(); - } elseif ($style instanceof \PhpOffice\PhpWord\Style\Table) { + } elseif ($style instanceof Table) { $xmlWriter->startElement('w:style'); $xmlWriter->writeAttribute('w:type', 'table'); $xmlWriter->writeAttribute('w:customStyle', '1'); @@ -144,7 +136,7 @@ class Styles extends Base $xmlWriter->writeAttribute('w:val', '99'); $xmlWriter->endElement(); - $this->_writeTableStyle($xmlWriter, $style); + $this->writeTableStyle($xmlWriter, $style); $xmlWriter->endElement(); // w:style } @@ -160,13 +152,13 @@ class Styles extends Base /** * Write default font and other default styles * - * @param PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param XMLWriter $xmlWriter * @param array $styles */ - private function writeDefaultStyles(XMLWriter $xmlWriter, $styles) + private function writeDefaultStyles(XMLWriter $xmlWriter, PhpWord $phpWord, $styles) { - $fontName = $this->phpWord->getDefaultFontName(); - $fontSize = $this->phpWord->getDefaultFontSize(); + $fontName = $phpWord->getDefaultFontName(); + $fontSize = $phpWord->getDefaultFontSize(); // Default font $xmlWriter->startElement('w:docDefaults'); @@ -197,7 +189,7 @@ class Styles extends Base $xmlWriter->writeAttribute('w:val', 'Normal'); $xmlWriter->endElement(); // w:name if (array_key_exists('Normal', $styles)) { - $this->_writeParagraphStyle($xmlWriter, $styles['Normal']); + $this->writeParagraphStyle($xmlWriter, $styles['Normal']); } $xmlWriter->endElement(); // w:style diff --git a/src/PhpWord/Writer/Writer.php b/src/PhpWord/Writer/Writer.php index eeba55ca..bc749738 100644 --- a/src/PhpWord/Writer/Writer.php +++ b/src/PhpWord/Writer/Writer.php @@ -22,7 +22,7 @@ abstract class Writer implements IWriter /** * PHPWord object * - * @var PhpOffice\PhpWord\PhpWord + * @var PhpWord */ protected $phpWord = null; diff --git a/tests/PhpWord/Tests/Shared/ZipArchiveTest.php b/tests/PhpWord/Tests/Shared/ZipArchiveTest.php new file mode 100644 index 00000000..ba47ade1 --- /dev/null +++ b/tests/PhpWord/Tests/Shared/ZipArchiveTest.php @@ -0,0 +1,38 @@ +open($zipFile); + $object->addFile($existingFile, 'xls/new.xls'); + $object->addFromString('content/string.txt', 'Test'); + + $this->assertTrue($object->locateName('xls/new.xls')); + $this->assertEquals('Test', $object->getFromName('content/string.txt')); + + unlink($zipFile); + } +}