简体   繁体   中英

Replacing a string in an xml/plist file using PHP

I have two .plist files in a directory both of them look something like this:

<dict>
    <key>bundle-identifier</key>
    <string>TEXT</string>
    <key>bundle-version</key>
    <string>VESRION</string>
    <key>kind</key>
    <string>software</string>
    <key>subtitle</key>
    <string>TEXT</string>
    <key>title</key>
    <string>TEXT</string>
  </dict>

I was able to get the value/string of title with this:

$files = array();
foreach (glob("../../plists/*.plist") as $file) {
    $files[] = $file;
    //echo "$file";

    $fileContent = file_get_contents($file);
    $xml = simplexml_load_string($fileContent) or die("Error: Cannot create object");
    $resultNames = $xml->dict->array->dict->dict->string[4] . '<br /> <br />';
    //echo $resultNames;

}

Now I want to change the value/string of title for each of them with php, like if I changed the title value/string of one of them I don't want the other one to get effected by it, is it possible?

Not sure how your echo of the title worked, but in this code I've used XPath to find the <key> element which has the content title . It then fetches the value of the following <string> element.

To update it, you use a slight fudge in SimpleXML to make it set the value of the <string> element and then save the result XML back to the same file name....

$xml = simplexml_load_string($fileContent) or die("Error: Cannot create object");
// Find the correct string element from the preceding key for title
$titleString = $xml->xpath('//key[.="title"]/following-sibling::string')[0];

// Set new value
$titleString[0] = "new Title1";
// Save the file
$xml->asXML($file);

If you identify the content by the file name, then check $file , but if you can only identify it by the title, then you would need to check if this finds the title you are after.

$titleToUpdate = "Correct Title2";
$fileContent = file_get_contents($file);
$xml = simplexml_load_string($fileContent) or die("Error: Cannot create object");
$titleString = $xml->xpath('//key[.="title"]/following-sibling::string[.="'.$titleToUpdate.'"]');


// Check there is 1 matching item
if ( count($titleString) == 1 )  {
    $titleString[0][0] = "new Title12";
    $xml->asXML($file);
}

I'm not sure to understand everything but you can use DomDocument to change the values.

$doc = new DOMDocument;
$doc->load('your_xml_file.xml');

$elements = $doc->getElementsByTagName("string");

foreach ($elements as $element) {
   //$element->nodeName will be "string"
   $nodes = $element->childNodes;

   foreach ($nodes as $node) {
      $node->nodeValue = 'New value';
    }
}

$doc->save("newfile.xml")

Its not pretty but works.

$doc = new DOMDocument;
$doc->load('file_address');

$elements = $doc->documentElement;

$found = false;
foreach ($elements->childNodes AS $item) {
    if($found){
        $item->nodeValue = 'changed';
        $found = false;
    }else{
        if($item->nodeName == 'key' && $item->nodeValue == 'title'){
            $found = true;
        }
    }
}
$doc->saveXML();

Using Xpath with DOM is possible as well.

$titleToUpdate = 'Original Title';

$document = new DOMDocument();
// load from string, for file: $document->load($fileName);
$document->loadXML($xml);
$xpath = new DOMXpath($document);

$expression = '//dict/key[.="title"]/following-sibling::string[.="'.$titleToUpdate.'"]';

$modified = FALSE; 
foreach ($xpath->evaluate($expression) as $titleValueNode) {
    $titleValueNode->textContent = 'Changed Title';
    $modified = TRUE;
}

if ($modified) {
  // output string, to save file: $document->save($fileName);
  echo $document->saveXML();
}

Output:

<?xml version="1.0"?>
<dict>
    <key>bundle-identifier</key>
    <string>TEXT</string>
    <key>bundle-version</key>
    <string>VESRION</string>
    <key>kind</key>
    <string>software</string>
    <key>subtitle</key>
    <string>TEXT</string>
    <key>title</key>
    <string>Changed Title</string>
</dict>

This approach would change any matching item and just store that it modified one or more.

Now if you do need to lookup different files for that, I would suggest moving the action into a function.

function replaceTitle(string $fileName, string $title): bool {
    // the actual action from the example
    return $modified;
}

$files = new GlobIterator(__DIR__.'/path/*.plist');
foreach ($files as $file) {
    if (replaceTitle((string)$file, 'Original Title')) {
        echo "Modified: $file\n";
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM