Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template too slow after 100 record. #513

Closed
exproof opened this issue Mar 27, 2015 · 33 comments
Closed

Template too slow after 100 record. #513

exproof opened this issue Mar 27, 2015 · 33 comments

Comments

@exproof
Copy link

exproof commented Mar 27, 2015

Hi,
Thanks for your product.
I have problem with template clonerow.
I have approx 150 record and template create document approx 500 second. is it normal?

@ghost ghost self-assigned this Mar 27, 2015
@ghost ghost added the Consulting Request label Mar 27, 2015
@ghost
Copy link

ghost commented Mar 27, 2015

Hi, @exproof. Could you please give me the code you benchmark with?

@ghost ghost added the Template Processor label Mar 27, 2015
@exproof
Copy link
Author

exproof commented Mar 27, 2015

if you need full code and template.docx i can send via e-mail.

$Document->cloneRow('A01Sira', mysql_num_rows($Sorgu));
while ($Satir<mysql_num_rows($Sorgu))
{
//Sabit Alanlar Gidiyor
$Document->setValue('A01Sira#'.($Satir+1), ($Satir+1));
$Document->setValue('A01Urun#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"UrunAdi")));
$Document->setValue('A01Ilce#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"IlceAdi")));
$Document->setValue('A01Koy#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"KoyAdi")));
$Document->setValue('A01Mevki#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"BeldeAdi")));
$Document->setValue('A01Ada#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"Ada")));
$Document->setValue('A01Parsel#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"Parsel")));

$MusteriAdi=TemizGoster(mysql_result($Sorgu,$Satir,"MusteriAdi"));

switch ($Secenek)
{
    case 1:
        $Document->setValue('A01Uretici#'.($Satir+1), $MusteriAdi);
        if (strlen(trim(mysql_result($Sorgu,$Satir,"VergiNo")))>0)
        {
            $Document->setValue('A01Tc#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"VergiNo")));   
        }
        else
        {
            $Document->setValue('A01Tc#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"TcKimlikNo")));
        }
        $Document->setValue('A01Adres#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"MusteriAdresi")));
        $Document->setValue('A01Telefon#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"MusteriTelefon")));
    break;

    case 2:
        $Document->setValue('MusteriAdi',$MusteriAdi);
        $Document->setValue('MusteriAdresi',TemizGoster(mysql_result($Sorgu,$Satir,"MusteriAdresi")));
        $Document->setValue('MusteriVergiNo',TemizGoster(mysql_result($Sorgu,$Satir,"MusteriVergiNo")));
        $Document->setValue('A01Uretici#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"UreticiAdi")));
        $Document->setValue('A01Tc#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"VergiNo")));
        $Document->setValue('A01Adres#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"UreticiAdresi")));
        $Document->setValue('A01Telefon#'.($Satir+1), TemizGoster(mysql_result($Sorgu,$Satir,"UreticiTelefon")));
    break;  
}

$Satir++;

}

@ghost
Copy link

ghost commented Mar 29, 2015

I see that TemplateProcessor::cloneRow is called on the first line only. What is execution time of this statement?

@exproof
Copy link
Author

exproof commented Mar 29, 2015

Approx 10 minute ;) This query result 175 record. What is your suggestion. Must i use every line not only first line?

@ghost
Copy link

ghost commented Mar 30, 2015

Wow. That's terrible. Please, give me your template file. Share it with any file sharing service like Dropbox, Google Drive, etc. and post link here.

@exproof
Copy link
Author

exproof commented Mar 30, 2015

Code, Template and Result files

2015-03-30 7:23 GMT+03:00 Roman Syroeshko [email protected]:

Wow. That's terrible. Please, give me your template file. Share it with
any file sharing service like Dropbox, Google Drive, etc. and post link
here.


Reply to this email directly or view it on GitHub
#513 (comment).

@exproof
Copy link
Author

exproof commented Mar 30, 2015

i am newbie in github i reply your last post with attach files vie gmail. I hope you can take attach files. Thank for your support.

@exproof exproof closed this as completed Mar 30, 2015
@exproof exproof reopened this Mar 30, 2015
@ghost
Copy link

ghost commented Mar 30, 2015

Nope. No luck. Share it using your Google Drive.

@exproof
Copy link
Author

exproof commented Mar 30, 2015

3 files in drive. Code, Template and Query Result

@ghost
Copy link

ghost commented Mar 30, 2015

OK, I got them. I will let you know results of investigation. Not sure that it happens today, because I feel asleep. :-) I would say that it happens this week.

@exproof
Copy link
Author

exproof commented Mar 30, 2015

;) good sleep. Be relax. We have enough time. Thanks again for your support.

iPhone'umdan gönderildi

30 Mar 2015 tarihinde 13:09 saatinde, Roman Syroeshko [email protected] şunları yazdı:

OK, I got them. I will let you know results of investigation. Not sure that it happens today, because I feel asleep. :-) I would say that it happens this week.


Reply to this email directly or view it on GitHub.

@Yongyiw
Copy link

Yongyiw commented Apr 1, 2015

I faced the same problem before. My solution is using PHP function strpos() instead of preg_replace() in setValueForPart() in Template.php ( Notice that this file is deprecated 0.12.0. But you can find almost same content in TemplateProcessor.php).

I guess the reason why PHPWord do not use strpos() is because, for example, "${tag}" won't be "${tag}" in openXML but something like "<w:r><w:t>${tag/w:t/w:r<w:bookmarkStart w:id="0" w:name="_GoBack"/><w:bookmarkEnd w:id="0"/><w:r><w:t>}/w:t/w:r". I don't understand why it should be like this.

I know nothing about openXML, but my solution is "clean" the template to a new cleaned version, from something like "<w:r><w:t>${tag/w:t/w:r<w:bookmarkStart w:id="0" w:name="_GoBack"/> <w:bookmarkEnd w:id="0"/><w:r><w:t>}/w:t/w:r" to something like "${tag}". Then we are free to use strpos() to set values I guess.

My code is here: https://github.com/Yongyiw/PHPWord/blob/master/src/PhpWord/Template.php

@ghost
Copy link

ghost commented Apr 4, 2015

Hi @exproof,
I've just tried to reproduce the problem, and know what? It doesn't appear. I used the followed peace of code.

<?php

require_once __DIR__ . '/src/PhpWord/Autoloader.php';
\PhpOffice\PhpWord\Autoloader::register();

$phpWord = new \PhpOffice\PhpWord\PhpWord();
$document = $phpWord->loadTemplate('template513.docx');

$startTime = \microtime(true);
$document->cloneRow('A01Sira', 175);
$endTime = \round(\microtime(true) - $startTime, 2);

$formatter = new NumberFormatter(null, NumberFormatter::DECIMAL, null);
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
echo $endTime;

$document->saveAs('result513.docx');

It executes in 30 ms on my old Windows-based laptop. Output is available here.

I will risk and make an assumption that the problem is in mysql_num_rows function which you put as the second argument of TemplateProcessor::cloneRow() method call. Please, move mysql_num_rows out of TemplateProcessor::cloneRow() call and let me know it's new execution time.

@exproof
Copy link
Author

exproof commented Apr 5, 2015

Hi,
Thanks for your reply and sample. I haven't any problem that create of template. I have a problem after create change values. Add this sample lines your code

$document->cloneRow('A01Sira', 175); //your code

$Document->setValue('A01Uretici#100', 'Test100');
$Document->setValue('A01Uretici#105', 'test105');
$Document->setValue('A01Uretici#112', 'Test112');
$Document->setValue('A01Uretici#121', 'Test121');

you will see what is my problem about long process time. I add just 4 variables for change. Think this i should change approx ~1000 variables in a template ;)

@ghost
Copy link

ghost commented Apr 5, 2015

I got it. There are two issues here.

  1. Your template is broken and needs to be fixed.
  2. The code which fixes template is slow.

So, what we are going to do to get workaround.

  1. Fix your template. You should open it any word processor (e.g. LibreOffice Writer), copy one macro (e.g. ${A01Uretici}) and paste it into any plain text editor (I use Notepad++), then copy that macro from plain text editor and paste it back into the document. You have to repeat these steps for all macroes in the template. Don't forget to save the document after all changes. For LibreOffice 4.4.1.2 just resaving your document fixes all the errors. This way you may find faster.
  2. Remove first 7 lines from TemplateProcessor::setValueForPart method.

This should help.

@exproof
Copy link
Author

exproof commented Apr 7, 2015

Done. ;)
Solved. After Remove first 7 lines from ''TemplateProcessor::setValueForPart'' method.

You are great. I would like you to buy me a coffee on next russia trip.

@exproof exproof closed this as completed Apr 7, 2015
@ghost
Copy link

ghost commented Apr 8, 2015

No problem. You are always welcome. 😃

ghost pushed a commit that referenced this issue Apr 11, 2015
ghost pushed a commit that referenced this issue Apr 11, 2015
ghost pushed a commit that referenced this issue Apr 11, 2015
@nicoSWD
Copy link

nicoSWD commented May 29, 2015

I was facing the same issue. It would take over an hour to generate a table with 132 lines. Lost a couple of hours on this, and ended up replacing the first seven lines of setValueForPart() with this:

protected function setValueForPart($documentPartXML, $search, $replace, $limit)
{
    $callback = function ($match) {
        return strip_tags($match[0]);
    };

    $documentPartXML = preg_replace_callback('~\$\{([<>][^\}]+)\}~', $callback, $documentPartXML);
    // ...

It's much, much faster.

@ghost
Copy link

ghost commented May 30, 2015

Did you try the version from develop branch?

@nicoSWD
Copy link

nicoSWD commented Jun 2, 2015

I was using master, but I just gave develop a try, and it's working for me as well.

I changed fixBrokenMacros() slightly to this, however:

foreach ($matches[0] as $value) {
    $valueCleaned = strip_tags($value);
    $fixedDocumentPart = str_replace($value, $valueCleaned, $fixedDocumentPart);
}

... and the speed improved from 6.1988 seconds to 1.7881 (with 132 lines).

ghost pushed a commit that referenced this issue Jun 2, 2015
ghost pushed a commit that referenced this issue Jun 2, 2015
@ghost
Copy link

ghost commented Jun 2, 2015

Done. Could you, please, retest?

@arcsump
Copy link

arcsump commented Jul 11, 2015

I can confirm that nicoSWD's fix for setValueForPart is much faster.

@ghost
Copy link

ghost commented Jul 12, 2015

Did you try the version from develop branch?

@ghost ghost added this to the 0.12.1 milestone Aug 16, 2015
@ghost ghost added the Status: Responded label Aug 16, 2015
ghost pushed a commit that referenced this issue Aug 16, 2015
@OAFCROB
Copy link

OAFCROB commented Aug 17, 2015

We're expericing issue with high volumes of data for cloning tables. Our data is setup as arrays and we're foreaching through the data to set the value. Using the development branch is much better than master, but still too slow. With 106 records it's taking about 15 seconds but with 1800 records it's timing out even after increase the set_time_limit to 900 seconds.

If I alter the code in TemplateProcessor.php to the following, performance is improved, but I'm thinking there must be a reason why you're using preg_replace over str_replace.

protected function setValueForPart($documentPartXML, $search, $replace, $limit)
    {
        /*if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
            $search = '${' . $search . '}';
        }*/

        /*if (!String::isUTF8($replace)) {
            echo "here<br/><br/>";
            $replace = utf8_encode($replace);
        }*/

        $regExpDelim = '/';
        $escapedSearch = preg_quote($search, $regExpDelim);

        return str_replace($search, $replace, $documentPartXML, $limit);


        return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
    }

Had an additional thought, is it possible to pass in the $search and $replace values in as arrays rather than looping through them? So I would only be hitting setValue once rather have hundreds of times.

@ghost
Copy link

ghost commented Aug 18, 2015

With 106 records it's taking about 15 seconds but with 1800 records it's timing out even after increase the set_time_limit to 900 seconds.

Can you share your data sample and peace of your code?

@ghost ghost reopened this Aug 18, 2015
@ghost ghost removed the Status: Responded label Aug 18, 2015
@OAFCROB
Copy link

OAFCROB commented Aug 19, 2015

I can't directly share the code due to company policies etc... but I've tried mocked up a rough example below;

Firstly I'm creating an array of tags with the key being the searchable tag and the value being the replacment value.

This data contains standards tags, then in contains a line called "repeatLine" which is an array that contains the tags to be cloned, the count for number of clones and tags for each clone. To complicate matter further the each cloned row can have a sub row which is also an array done in the same way to allow me to clone a sub row of data.

So realistically I'm getting template processor to process more than 1806 records because there are the standard tags, which is probably about 50 -75 definable tags. Then there are two row clones which each contain roughly 10 - 30 tags to be clone with the latter having a sub row clone which also contains 20 tags and a photo tag, which contains the pathway to an image to be included into the template.

$tags = [
  '${tagName}' => 'Tag Name',
  '${tagDescription}' => 'Some description',
  'repeatLine' => [
      ['cloneRow' => '${rowLine}',
        'rowCount' => 1806,
         'tags' => [
            '${tag#1}' => 'Tag 1',
            '${subRepeat#1}' => [
                'cloneRow' => '${subRepeatLine#1}',
                'rowCount'  => 10,
                'tags' => [
                   '${subRepatTag#1#1}' => 'Sub Table 1'
                ]
            ],
         ]
      ]
  ]
];

The tags eventually get passed through to a method which loops through the data and sets it appropriately.

** Please note I had to fork the repo to get photos to go into the template, as there wasn't a way to do this with the template processor as far as I know.

$templateProcessor = new TemplateProcessor($modifiedFilename);
        foreach ($data as $tag => $replacement) {

            /** repeatLine indicates that we want to clone a row of data */
            if ($tag === 'repeatLine') {
                if (is_array($replacement)) {
                    foreach ($replacement as $rowData) {
                         /** Check that the repeatLine data is setup like expected */
                        if (isset($rowData['cloneData']) && isset($rowData['rowCount']) &&
                            isset($rowData['tags'])) {
                            $templateProcessor->cloneRow($rowData['cloneRow'], $rowData['rowCount']);
                            /** Loop through the row tags */
                            foreach ($rowData['tags'] as $rowTag => $rowReplacement) {
                                /** If the tag mentions photo then render out as a photo in the docx */
                                if (strpos(strtolower($rowTag), 'photo')) {
                                    $exploded = array_reverse(explode('/', $rowReplacement));
                                    $imgName = $exploded[0];
                                    $tempImgFile = false;
                                    /** If the image is not a PNG convert then recreate it as a PNG to be put in the
                                     *  docx. */
                                    if (exif_imagetype($rowReplacement) != IMAGETYPE_JPEG) {
                                        $imgName = explode('.', $imgName)[0].'.jpg';
                                        $tempImgFile = sys_get_temp_dir().'/'.$imgName;
                                        \Intervention\Image\Image::make($rowReplacement)->save($tempImgFile);
                                    }
                                    $templateProcessor->setImageValue($rowTag, $imgName, $rowReplacement);
                                    /** Remove any temporary images  */
                                    if ($tempImgFile) {
                                        unlink($tempImgFile);
                                    }
                                } else {
                                    /** If the data is an array then we're deal with another row which we want to
                                     *  clone. */
                                    if (is_array($rowReplacement)) {
                                        $templateProcessor->cloneRow(
                                            $rowReplacement['cloneRow'],
                                            $rowReplacement['rowCount']
                                        );
                                        /** Loop through the tags and output the data */
                                        foreach ($rowReplacement['tags'] as $subRowTag => $subRowReplacement) {
                                            $templateProcessor->setValue($subRowTag, $subRowReplacement);
                                        }
                                    } else {
                                        $templateProcessor->setValue($rowTag, $rowReplacement);
                                    }
                                }
                            }
                        }
                    }
                }
            } else {
                $templateProcessor->setValue($tag, $replacement);
            }
        }
        /** Save the template file to disk */
        $templateProcessor->saveAs($modifiedFilename);

I have managed to work my way around my issue in my forked repo, by changing setValueForPart to;

protected function setValueForPart($documentPartXML, $search, $replace, $limit)
    {
        return str_replace($search, $replace, $documentPartXML, $limit);
    }

I have restructured the data to reduce the number of foreach loops down. So I now only loop to do the rowCloning and to do the images. The rest of data is passed in one call to setValue by passing it in as two arrays, array of tags and an array of replacement values. The template processor now process my very large data set including photos within 20 minutes.

I hope that all makes sense, it is a complicated beast that I'm creating here with potential dynamic clone rows off a parent clone row.

@ghost
Copy link

ghost commented Aug 22, 2015

@OAFCROB, preg_replace function is used here, because it gives us ability of limiting number of replacements. str_replace function doesn't offer such functionality. Its 4th attribute has different meaning. It means "the number of replacements performed".

This feature was introduced in #52. I'll try to create some patch for that.

Thanks for the report!

ghost pushed a commit that referenced this issue Aug 30, 2015
ghost pushed a commit that referenced this issue Aug 30, 2015
@ghost ghost closed this as completed Aug 30, 2015
@ghost ghost added the Status: Responded label Aug 30, 2015
@OAFCROB
Copy link

OAFCROB commented Sep 1, 2015

Sorry, for the delayed response. I understand why using preg_replace could be useful for limiting the replacement fields however, it can be incredibly slow in comparison to str_replace. Have you pushed a patch that I can try?

@ghost
Copy link

ghost commented Sep 1, 2015

Yes, we released v0.12.1. Now TemplateProcessor uses str_replace if you call setValue without last parameter, and it uses preg_replace when you limit number of replacements.

Thanks for the feedback!

@OAFCROB
Copy link

OAFCROB commented Sep 2, 2015

I've just looked at the source code and I could see a potential issue with str_replace as you can pass in the find and replacement fields as arrays. The calls to substr and String::isUTF8 expect a direct string, so is it worth doing a sanity check before calling them?

I n my forked repo I took them out completly as I decided to pass in all my data as two arrays meaning I was only hitting this method once. Instead of foreach looping through the data calling setValue each time.

if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
            $search = '${' . $search . '}';
        }
        if (!String::isUTF8($replace)) {
            $replace = utf8_encode($replace);
        }
        // Note: we can't use the same function for both cases here, because of performance considerations.
        if (self::MAXIMUM_REPLACEMENTS_DEFAULT === $limit) {
            return str_replace($search, $replace, $documentPartXML);
        } else {
            $regExpDelim = '/';
            $escapedSearch = preg_quote($search, $regExpDelim);
            return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
        }

@ghost
Copy link

ghost commented Sep 10, 2015

Good idea, @OAFCROB. Will change the behavior in v0.13.0.

@moussaCamara
Copy link

First of all, thanks to the authors for this great project and sorry for my aproximate english.

I think there is a design problem that came up when treating great volumes of data (by the way, treating hundreeds or thusand of lines can be common in many tasks).

From my point of view the problem is that values add added after the rows have been cloned. The problem for me is that each time you add (clone) a row, the porcessing time grows two times:

  • There is one more search/replace operation to do.
  • The amount of XML data to treat for every search/replace gets bigger
    this means that if the number of rows you want to treat is n, the processing time will be womething like n^2 (n*n).
    using str_replace, or give search/replace values as arrays speeds up the thing a little, but the processing time will still grow by someting like n^2 (n*n)

the solution might be to do the search/replace while cloning, that way, the search / replace operations will be done only on the cloned part, not on the whole document. The processing time grows by n, not by n^2.
This way, it can generate thusands of rows in less than a second...

I modified TemplateProcessor::cloneRow so it can accept an array with values a argument and do the replacement work while cloning.


    public function cloneRow($search, $numberOfClones, $arRepl="", $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT)
    {
        if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
            $search = '${' . $search . '}';
        }

        $tagPos = strpos($this->tempDocumentMainPart, $search);
        if (!$tagPos) {
            throw new Exception("Can not clone row, template variable not found or variable contains markup.");
        }

        $rowStart = $this->findRowStart($tagPos);
        $rowEnd = $this->findRowEnd($tagPos);
        $xmlRow = $this->getSlice($rowStart, $rowEnd);

        // Check if there's a cell spanning multiple rows.
        if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
            // $extraRowStart = $rowEnd;
            $extraRowEnd = $rowEnd;
            while (true) {
                $extraRowStart = $this->findRowStart($extraRowEnd + 1);
                $extraRowEnd = $this->findRowEnd($extraRowEnd + 1);

                // If extraRowEnd is lower then 7, there was no next row found.
                if ($extraRowEnd < 7) {
                    break;
                }

                // If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
                $tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
                if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
                    !preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) {
                    break;
                }
                // This row was a spanned row, update $rowEnd and search for the next row.
                $rowEnd = $extraRowEnd;
            }
            $xmlRow = $this->getSlice($rowStart, $rowEnd);
        }

        $result = $this->getSlice(0, $rowStart);
        
        if(is_array($arRepl))
		for ($i = 1; $i <= $numberOfClones; $i++) {
		    $ar_search=array_keys($arRepl[$i]);
		    $ar_repl=array_values($arRepl[$i]);
		    
		    foreach ($ar_search as &$item) $item=self::ensureMacroCompleted($item);
		    foreach ($ar_repl as &$item) $item = self::ensureUtf8Encoded($item);
		   
		    $result .= $this->setValueForPart($ar_search, $ar_repl, $xmlRow, $limit);
		    //$result .= str_replace($ar_search, $ar_repl, $xmlRow);
		}
	else
		for ($i = 1; $i <= $numberOfClones; $i++) {
		    $result .= preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlRow);
		}
		
        $result .= $this->getSlice($rowEnd);

        $this->tempDocumentMainPart = $result;
    }
    

Just call it by passing an array of arrays as third parameter:

$array_row_values=array(
	1 => array("row1" => "value 1", "row2" => "value 2" /*etc...*/ ), //values for first line
	2 => array("row1" => "value 1", "row2" => "value 2" /*etc...*/ ), //values for second line
	3 => array("row1" => "value 1", "row2" => "value 2" /*etc...*/ ), //values for third line
	/* ect.. */
);

$templateProcessor->cloneRow('rowFieldName', 1000 /*or greater*/, $array_row_values);

and the replacement will be directly made while cloning.

fourth parameter (limit) is totaly optional.

if you call it the normal way (only two parameter), i will still beleive the normal way (just clone row, down't apply values)

I tested it with the following code (using the template form the sample 7)


echo date('H:i:s'), ' Creating new TemplateProcessor instance...';
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('Sample_07.docx');

// Variables on different parts of document
$templateProcessor->setValue('weekday', date('l'));            // On section/content
$templateProcessor->setValue('time', date('H:i'));             // On footer
$templateProcessor->setValue('serverName', realpath(__DIR__)); // On header

$nbRSimple=1000;
$nbRcomplexe=1000;


$arValues_ClonesSimples=array();
for ($i=1; $i<=$nbRSimple; $i++)
	$arValues_ClonesSimples[$i]=array(
		"rowValue" => rand(10000000, 99999999),
		"rowNumber" => $i
	);
	

// Simple table
$templateProcessor->cloneRow('rowValue', $nbRSimple, $arValues_ClonesSimples);

$arValues_ClonesComplexes=array();
for ($i=1; $i<=$nbRSimple; $i++)
	$arValues_ClonesComplexes[$i]=array(
		"userId" => rand(10000000, 99999999),
		"userFirstName" => rand(10000000, 99999999),
		"userName" => rand(10000000, 99999999),
		"userPhone" => rand(10000000, 99999999),
	);

$templateProcessor->cloneRow('userId', $nbRcomplexe, $arValues_ClonesComplexes);
	

echo date('H:i:s'), ' Saving the result document...';
$templateProcessor->saveAs('Sample_07_out.docx');

@moussaCamara moussaCamara unassigned ghost Jan 3, 2017
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

6 participants