phpword template dynamic block - php

I have a word template with this content
${line1}
${line1} template content value from sql is
${block_name}
${var1}
${block_name}
Using PhpWord TemplateProcessor, I am able to replace this. In TemplateProcessor.php, I have add
$replace = preg_replace('~\R~u', '</w:t><w:br/><w:t>', $replace);
in function setValue. This is because, the block should be multiline and have no space in order cloneblock to happen.
Then, I save as template2.docx and load again in new TemplateProcessor(). When I open the word file, it already display multiline. But, still cloneblock could not be achieved.
include "db.php";
require_once 'vendor/autoload.php';
use PhpOffice\PhpWord\TemplateProcessor;
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\PhpWord;
//1
$templateProcessor = new TemplateProcessor('template.docx');
//template content value
$templateContentValue=$stmt->fetchAll(PDO::FETCH_ASSOC);
$content =$templateContentValue[0]['contentVal'];
$templateProcessor->setValue('line', $content);
//save as template2.docx
$pathToSave ='template2.docx';
$templateProcessor->saveAs($pathToSave);
//2
$templateProcessor2 = new TemplateProcessor($pathToSave);
$replacements = array(
array('var1' => 'value1'),
array('var1' => 'value2'),
);
$templateProcessor2->cloneBlock('block_name', 0, true, false, $replacements);
$templateProcessor2->saveAs('output.docx');
Expected Output:
value1
value2

I think maybe you got a typo in the block syntax on last line
By referring to cloneBlock documentation, you are missing a slash(/) in you template content of ${line1}, the content value should be
${block_name}
${var1}
${/block_name} // last line of block syntax
In your first word template with content
${line1}
I see your code with
$templateProcessor->setValue('line', $content);
shouldn't it be ?
$templateProcessor->setValue('line1', $content);
So I assume it might just a typo when you're writing the post, which it is not the main issue here.
Another possibility that I can think of after testing using the suggestion from this post with multiline value is, it might due to the unrecognized syntax of newline by PHPWord in your replacement content of ${line1}.
I try with following for frist word template :
${line1}
${line2}
${line3}
Then replace multi values at once
$templateProcessor->setValues([
'line1' => '${block_name}',
'line2' => '${var1}',
'line3' => '${/block_name}'
]);
// same goes to your code starting at line ==> //save as template2.docx
The output is as expected, but I don't think this is what you preferred.
Hope it helps!

Related

How to replace a specific value in a hard-coded array inside of a php file?

I use a config.php file that returns an array. Before releasing the project, I usually manually change the 'apiKey' value that I use while developing in the file. I forget to perform this replacement sometimes, so I'm looking for a programmatical way to find this in a string version of the file:
'apiKey' => '1234567890'
and replace with this:
'apiKey' => 'YourAPIKeyHere'
The development apiKey value, spaces, tabs and formatting are inconsistent (Developer/IDE specific), so I guess there are wildcards for that?
Then I can just make the change in my deployment script.
Edit to show sample of config.php (which will be read into a string, edited, then re-written as a file).
<?php
return array(
// Comments with instruction exist throughout the file. They must remain.
'apiKey' => 'ecuhi3647325fdv23tjVncweuYtYTv532r3',
...
);
Edit: **There are instructional comments in the config.php file that must remain. So re-writing a modified array will lose the comments, and that is undesirable.
Save the config file's text in a variable called $content.
Then call:
$content = preg_replace("~'apiKey'\s*=>\s*'\K[^']+~", 'YourAPIKeyHere', $content, 1);
Then overwrite the file with the updated variable.
http://php.net/manual/en/function.preg-replace.php
\s* means match zero or more whitespace characters.
\K means restart the match from this point.
[^']+ means match one or more non-single-quote character.
Regex101 Demo
PHP Demo
I assume that you have a config file such as;
return [
'dbname' = 'project',
'username' = 'root',
'password' = '123456',
.
.
.
'apiKey' => '1234567890',
]
So you can make a small helper method then you can use it before relasing your project..
function reset_config()
{
$file_path = "your/config/path";
$configs = require_once($file_path);
array_walk_recursive($configs, function (&$config, $key) {
$config = "your " . $key;
});
$string = var_export($configs,true);
$new_config_file = <<<HEAD
<?php
return $string;
HEAD;
file_put_contents($file_path, $new_config_file);
}
so all you need to use reset_config() function before relasing the project
You can use this simple RegEx to match any line containing a key between the apostrophes:
'apiKey' => '[^']+'
The [^']+ will find one or more characters between single quotes.
Just replace with your new line.
Edit:
Your replacement string would simply be:
'apiKey' => 'EnterYourAPIKeyHere'
I solved the issue by reading the file into an array and replacing the line with 'apiKey':
$array = file('app/config.php');
$string = "";
for($i = 0, $maxi = count($array); $i < $maxi; $i++)
{
if(strpos($array[$i],'apiKey')>0){
$string.=" 'apiKey' => 'YourAppAPIKeyHere',\r\n\r\n";
}else{
$string.=$array[$i];
}
}
It may not be the most elegant solution but it works. Until someone doesn't format their code right. For this reason, I would still like to use a RegEx that isolates the replacement to the required pattern. But RegEx is something I just don't get into, and there's other issues to resolve now.
Inspired by everyone who helped.
Feedback appreciated.
As i suggested you can use the PHP tokenizer extension functions to achieve your purpose
function replaceApiKey($configpath,$newKey='test',$newpath=''){
if(file_exists($configpath)&&is_readable($configpath)&&is_file($configpath))
$string = file_get_contents($configpath);
else
return false;
$tokens=token_get_all($string);
$start=false;
foreach($tokens as $key=>$token){
if(is_array($token)&&stripos($token[1],'apiKey')){
$start=true;
$tokens[$key]=$token[1];
continue;
}
if($start&&$token&&is_array($token)&&token_name($token[0])!=="T_COMMENT"&&token_name($token[0])!=="T_DOUBLE_ARROW"&&!ctype_space($token[1])){
$token[1]=$token[1][0].$newKey.$token[1][strlen($token[1])-1];
$start=false;
}
if(is_array($token)) $tokens[$key]=$token[1];
}
if(empty($newpath))
$newpath=$configpath;
if (file_put_contents($newpath, join('',$tokens)))
return true;
else
return false;}
This function take in parameter the config path,tokenize the content then search and replace the old apiKey by the new one and save the changes in the new path...

Remove unknown contents of a text file in PHP

Is there a way to remove contents (substring) of a text file in PHP.
I know the start and the end of the string to remove, but not everything in between the content
File (routes.php) I want to edit
I want to keep this text here
# [[[#-100] I also want to remove the comment line here
I want to remove this text here
# I also want to remove this line [#-100]]]
I want to keep this line too
So I want to remove the content block that starts with # [[[#-100] and ends with [#-100]]]
Im using codeignighter so I write my file like so
$ROUTE_FILE ='system/routes.php'
$output = file_get_contents($ROUTE_FILE);
$output = $output->remove_substring(...);
$this->write_file($ROUTE_FILE, $output);
Tested:
$mystring='this is # [[[#-100] not [#-100]]] this string you want';
echo remove_substring('# [[[#-100]','[#-100]]]',$mystring);
function remove_substring($head,$foot,$string){
$top=reset(explode($head,$string));
$bottom=end(explode($foot,$string));
return $top.$bottom;
}
output:
this is this string you want
Here's a version that does not return errors:
Tested:
$mystring='this is # [[[#-100] not [#-100]]] this string you want';
echo remove_substring('# [[[#-100]','[#-100]]]',$mystring);
function remove_substring($head,$foot,$string){
$top=explode($head,$string);
$top=reset($top);
$bottom=explode($foot,$string);
$bottom=end($bottom);
return $top.$bottom;
}
Activate DOTALL mode with `(?s)
Escape the [braces] so the engine doesn't confuse them for a character chass
Use .*? to match everything between the delimiters.
In php code:
$replaced = preg_replace('~(?s)# \[\[\[#-100\] Start of custom routes.*?# End of custom routes \[#-100\]\]\]~',
'', $yourinput);
See the output at the bottom of this demo.

Replace certain content in txt file and save it using PHP?

So I receive variable replace it in certain area in txt file and save it back. I get page number and according to it I get exploded data. Anyway I'll post a code below to make it more clear:
$pgnm = $_GET['page']; //This is the page number as I've said.
$conts = file_get_contents("content.txt");
the content of content.txt looks like this:
text1|text2|text3
I display this content in certain pages. For example on first page: text1, on second text2, etc.
Now i'm working on a form where I successfully change these. I get as I've said page number and text:
$text = "new text"; //this is the content which I want to be replaced instead of text2.
I make the content.txt file look like this after its saved: text1|new text|text2
So lets go on:
$exp = explode("|", $conts); //this explodes data into slashes.
$rep = str_replace($exp[$pgnm], $text, $conts);
file_put_contents("content.txt", $rep); // Saving file
All these above-mentioned operations work perfectly, but here's my problem now. This only works if content.txt has certain content, if it's empty it enters my new text and that's all: 'new text' and that's all. Maybe I want to add second page content 'new text2' and after I finish entering it and save I want the file to be displayed like this: new text|new text2. If the content of content.txt looks like this: 'new text|' str_replace doesn't replace empty string. So that's my another problem too.
I tried everything, but couldn't manage to anything about this two problems. Thank you in advance for your help!
Why don't you use your $exp array for building the content. I mean $exp contains all the blocks as an array() one by one. So you just change, or add new values to the array (no str_replace() needed). Then rebuild using implode('|',$exp);.
As for your code;
$exp = explode("|", $conts); //this explodes data into slashes.
$exp[$pgnm] = $text;
file_put_contents("content.txt", implode('|',$exp)); // Saving file
Instead of str_replace use this code:
$pgnm = 1;
$text = 'new text';
$conts = 'text1||text3';
$exp = explode('|', $conts);
$exp[$pgnm] = $text;
$rep = implode('|', $exp);
var_dump($rep); // string(20) "text1|new text|text3"

PHPWord how to add text break / new line while in a text run

How can I add a text break or go to the next line/row while in a text run? I tried to just do $section->addTextBreak(2); while in the text run but it just added the breaks to the section after the text run. I also tried $textrun->addTextBreak(2); but it gave me a fatal error. Any responses would be greatly appreciated.
The question was asked 3 years ago but I have the same problem and I found a solution. Maybe this can help new users of PHPWord.
To add a crlf in Word document the tag can help :
$section->addText('Some text <w:br/> another text in the line ');
I found the solution here : http://jeroen.is/phpword-line-breaks/
I'm afraid that this will not be possible with current version. I don't have deep understanding of this library, but from looking at the code, I found out that the textRun class consist only of addText and addLink methods.
But I also need this feature along with several others, so I'm going to write it myself and create a pull request to get it included in the next release (if there will be any).
Basically it can be done by modifying the textRun class, adding an addLineBreak method (similar way as it is in the section class) and then modify class Base.php to create proper elements in final document.
In Docx xml, those line brakes are similar to the html br tag, but previous text must be closed and reopened after using break like this:
<w:r>
<w:t>This is</w:t>
<w:br/>
<w:t xml:space="preserve"> a simple sentence.</w:t>
</w:r>
instead of simply doing
<w:r>
<w:t>This is<w:br /> a simple sentence</w:t>
</w:r>
So in base.php, you'll need to edit behavior to create this block of code.
Hope this was useful!
EDIT
I have figured out that implementing this is very simple. In textRun.php just add this method:
/**
* Add a TextBreak Element
*
* #param int $count
*/
public function addTextBreak($count = 1) {
for($i=1; $i<=$count; $i++) {
$this->_elementCollection[] = new PHPWord_Section_TextBreak();
}
}
and in Base.php in the _writeTextRun method at the end of this method add this condition:
elseif($element instanceof PHPWord_Section_TextBreak) {
$objWriter->writeElement('w:br');
}
You can have a text and add \n where ever you want to have line breaks, and do the rest like this:
$text = "foo\nbar\nfoobar";
$textlines = explode("\n", $text);
$textrun = $section->addTextRun();
$textrun->addText(array_shift($textlines));
foreach($textlines as $line) {
$textrun->addTextBreak();
// maybe twice if you want to seperate the text
// $textrun->addTextBreak(2);
$textrun->addText($line);
}
Try this one:
str_replace("\n", '</w:t><w:br/><w:t xml:space="preserve">', $yourData );
or this one
str_replace("\r\n", '</w:t><w:br/><w:t xml:space="preserve">', $yourData );
Adding a newline in phpword has bothered me, and I finnaly found solution, by accident, so here it is:
And this justifies the text.
$PHPWord->addParagraphStyle('pJustify', array('align' => 'both', 'spaceBefore' => 0, 'spaceAfter' => 0, 'spacing' => 0));
//add this style then append it to text below
$section->addText('something', 'textstyle', 'pJustify');
//the text behind this will be justified and will be in a new line, not in a new paragraph
$section->addText('behind', 'textstyle', 'pJustify');
This will output:
something
behind
It's easy: just start a new textrun, and before it add textbreak to the section, like this (tested with docx format):
$textrun = $section->addTextRun();
$textrun->addText('blah-blah', 'p_bold');
$section->addTextBreak(2);
$textrun = $section->addTextRun();
$textrun->addText('blah-blah-blah in new line ', 'p');
$textrun->addText('blah-blah', 'p_bold');
$textrun->addText('blah-blah', 'p');
$section->addTextBreak(2);
$textrun = $section->addTextRun();
$textrun->addText('blahblah', 'p');
I don't know if a PR from the accepted answer was incorporated or it was in fact already possible in 2013, but regardless, since 2015 if not earlier, the correct way to insert line breaks within a paragraph is indicated in the initial response to #553 on GitHub:
Create a TextRun for the paragraph;
Add line breaks to the
TextRun by calling the addTextBreak() method of the TextRun
itself.
Here's a function that will take care of this for a complete paragraph of text in a string containing literal line-breaks (CR-LF ["\r\n"], LF ["\n"] or CR ["\r"]):
/**
* #param \PhpOffice\PhpWord\Element\AbstractContainer $container
* E.g. a section or table cell.
* #param string $text String with literal line breaks as CR-LF, LF or CR.
* #param string|array|\PhpOffice\PhpWord\Style\Paragraph $paragraphStyle
* #param string|array|\PhpOffice\PhpWord\Style\Font $fontStyle
*
* #return \PhpOffice\PhpWord\Element\TextRun
*/
function addTextWithLineBreaks(
\PhpOffice\PhpWord\Element\AbstractContainer $container,
$text,
$fontStyle,
$paragraphStyle
) {
$textRun = $container->addTextRun($paragraphStyle);
foreach (preg_split('/(\\r\\n?+|\\n)/',
$text,
-1,
PREG_SPLIT_DELIM_CAPTURE
) as $i => $part) {
if ($i & 1) {
$textRun->addTextBreak(1, $fontStyle, $paragraphStyle);
} else {
$textRun->addText($part, $fontStyle, $paragraphStyle);
}
}
return $textRun;
}
PHPWord (0.14.0) currently discards line breaks when reading Word2007 documents – hopefully that will be fixed before 1.0 – but the output from the above is correct when opened in Word.
For correct work, you need to add a text block:
$string = strtr($string, [
"\n" => "</w:t>\n<w:br />\n<w:t xml:space=\"preserve\">"
]);
use PHP_EOL for line break it is the only solution for line break in PHPWord

Need to extract special tags and replace them based upon their contents using regular expression

I'm working on a simple templating system. Basically I'm setting it up such that a user would enter text populated with special tags of the form: <== variableName ==>
When the system would display the text it would search for all tags of the form mentioned and replace the variableName with its corresponding value from a database result.
I think this would require a regular expression but I'm really messed up in REGEX here. I'm using php btw.
Thanks for the help guys.
A rather quick and dirty hack here:
<?php
$teststring = "Hello <== tag ==>";
$values = array();
$values['tag'] = "world";
function replaceTag($name)
{
global $values;
return $values[$name];
}
echo preg_replace('/<== ([a-z]*) ==>/e','replaceTag(\'$1\')',$teststring);
Output:
Hello world
Simply place your 'variables' in the variable array and they will be replaced.
The e modifier to the regular expression tells it to eval the replacement, the [a-z] lets you name the "variables" using the characters a-z (you could use [a-z0-9] if you wanted to include numbers). Other than that its pretty much standard PHP.
Very useful - Pointed me to what I was looking for...
Replacing tags in a template e.g.
<<page_title>>, <<meta_description>>
with corresponding request variables e,g,
$_REQUEST['page_title'], $_REQUEST['meta_description'],
using a modified version of the code posted:
$html_output=preg_replace('/<<(\w+)>>/e', '$_REQUEST[\'$1\']', $template);
Easy to change this to replace template tags with values from a DB etc...
If you are doing a simple replace, then you don't need to use a regexp. You can just use str_replace() which is quicker.
(I'm assuming your '<== ' and ' ==>' are delimiting your template var and are replaced with your value?)
$subject = str_replace('<== '.$varName.' ==>', $varValue, $subject);
And to cycle through all your template vars...
$tplVars = array();
$tplVars['ONE'] = 'This is One';
$tplVars['TWO'] = 'This is Two';
// etc.
// $subject is your original document
foreach ($tplVars as $varName => $varValue) {
$subject = str_replace('<== '.$varName.' ==>', $varValue, $subject);
}

Categories