Search code examples
phparraysparsingyamlpecl

Parsing YAML file with PHP does not dump manipulated array back into correct YAML format


I've made a program that loads a YAML file into a PHP array, using yaml_parse_file(), where I then manipulate the array using this function:

function removeElementWithValue($array, $key, $value){
     foreach($array as $subKey => $subArray){
          if($subArray[$key] == $value){
               unset($array[$subKey]);
          }
     }
     return $array;
}

Then I attempt to dump the new array back into the YAML file using yaml_emit()

Here's the problem: The program does not dump the array into the YAML file using the proper YAML formatting. The only time it will dump the array into the proper YAML format is if my function only removes the last element in the array. If I use the function to remove any other element from the array, it will dump back to the YAML file like:

---
1:
  ID: 1
  startX: 258.000000
  endX: 276.000000
  startZ: 271.000000
  endZ: 290.000000
  price: 38000.000000
  owner: KeithDavisRatio
  level: world
  invitee: []
  expires: ~
...

When it should dump back like

- ID: 1
  startX: 258.000000
  endX: 276.000000
  startZ: 271.000000
  endZ: 290.000000
  price: 38000.000000
  owner: KeithDavisRatio
  level: world
  invitee: []
  expires: ~
...

The Question: How do I properly manipulate the array so that yaml_emit() dumps the new array into the file with the correct YAML formatting? Thanks in advance for your help.

Full program:

#Loads Land.yml into PHP Array
$file = '/Users/keithdavis/MineCraft/pocketmine-mp/plugins/EconomyLand/Land.yml';
$array = yaml_parse_file($file);

echo "Loaded into PHP: \n";
print_r($array);

echo "Would you like to
\n1: Delete all land claims by owner?
\n2: Delete all land claims except owner?\n";
$answer = readline("\nType 1 or 2: ");

#Answer 1
if ($answer == 1){
  $ownerName = readline("What is the owner's name?:  ");

  #Searches land array for owner
  if(array_search($ownerName, array_column($array, 'owner')) !== false ){
      echo "...Found $ownerName \n";

      #Removes owner from array
      $array = removeElementWithValue($array, 'owner', $ownerName);
      echo "...Removed claims owned by $ownerName \n";

      #Sends data back to Land.yml
      $landAfterDump = yaml_emit($array);
      file_put_contents($file, $landAfterDump);
      echo "YAML Data dumped back: \n";
      echo $landAfterDump;

  }else{
      echo 'Did not find ' . $ownerName . "\n";
  }
#Answer 2
}elseif ($answer == 2){
  $ownerName = readline("What is the owner's name?:  ");

  #Searches land array for owner
  if(array_search($ownerName, array_column($array, 'owner')) !== false ){
      echo "...Found claims by $ownerName \n";

      #Removes from array except owner
      $array = removeElementWithValueExcept($array, 'owner', $ownerName);
      echo "...Removed claims not owned by $ownerName \n";


      #Sends data back to Land.yml
      $landAfterDump = yaml_emit($array);
      file_put_contents($file, $landAfterDump);
      echo "YAML Data dumped back: \n";
      echo $landAfterDump;
  }
  else {
        echo 'Did not find ' . $ownerName . "\n";
  }
}else {
  echo "Input not recognized. Please run program again.\n";
}


#Remove from array
function removeElementWithValue($array, $key, $value){
     foreach($array as $subKey => $subArray){
          if($subArray[$key] == $value){
               unset($array[$subKey]);
          }
     }
     return $array;
}

#Remove from array except
function removeElementWithValueExcept($array, $key, $value){
     foreach($array as $subKey => $subArray){
          if($subArray[$key] !== $value){
               unset($array[$subKey]);
          }
     }
     return $array;
} <?php

#Loads Land.yml into PHP Array
$file = '/Users/keithdavis/MineCraft/pocketmine-mp/plugins/EconomyLand/Land.yml';
$array = yaml_parse_file($file);

echo "Loaded into PHP: \n";
print_r($array);

echo "Would you like to
\n1: Delete all land claims by owner?
\n2: Delete all land claims except owner?\n";
$answer = readline("\nType 1 or 2: ");

#Answer 1
if ($answer == 1){
  $ownerName = readline("What is the owner's name?:  ");

  #Searches land array for owner
  if(array_search($ownerName, array_column($array, 'owner')) !== false ){
      echo "...Found $ownerName \n";

      #Removes owner from array
      $array = removeElementWithValue($array, 'owner', $ownerName);
      echo "...Removed claims owned by $ownerName \n";

      #Sends data back to Land.yml
      $landAfterDump = yaml_emit($array);
      file_put_contents($file, $landAfterDump);
      echo "YAML Data dumped back: \n";
      echo $landAfterDump;

  }else{
      echo 'Did not find ' . $ownerName . "\n";
  }
#Answer 2
}elseif ($answer == 2){
  $ownerName = readline("What is the owner's name?:  ");

  #Searches land array for owner
  if(array_search($ownerName, array_column($array, 'owner')) !== false ){
      echo "...Found claims by $ownerName \n";

      #Removes from array except owner
      $array = removeElementWithValueExcept($array, 'owner', $ownerName);
      echo "...Removed claims not owned by $ownerName \n";


      #Sends data back to Land.yml
      $landAfterDump = yaml_emit($array);
      file_put_contents($file, $landAfterDump);
      echo "YAML Data dumped back: \n";
      echo $landAfterDump;
  }
  else {
        echo 'Did not find ' . $ownerName . "\n";
  }
}else {
  echo "Input not recognized. Please run program again.\n";
}


#Remove from array
function removeElementWithValue($array, $key, $value){
     foreach($array as $subKey => $subArray){
          if($subArray[$key] == $value){
               unset($array[$subKey]);
          }
     }
     return $array;
}

#Remove from array except
function removeElementWithValueExcept($array, $key, $value){
     foreach($array as $subKey => $subArray){
          if($subArray[$key] !== $value){
               unset($array[$subKey]);
          }
     }
     return $array;
}

Land.yml file:

---
- ID: 0
  startX: 260.000000
  endX: 276.000000
  startZ: 244.000000
  endZ: 256.000000
  price: 22100.000000
  owner: BlockBach
  level: world
  invitee: []
  expires: ~
- ID: 1
  startX: 258.000000
  endX: 276.000000
  startZ: 271.000000
  endZ: 290.000000
  price: 38000.000000
  owner: KeithDavisRatio
  level: world
  invitee: []
  expires: ~
- ID: 2
  startX: 353.000000
  endX: 364.000000
  startZ: 218.000000
  endZ: 253.000000
  price: 43200.000000
  owner: TestDummy1
  level: world
  invitee: []
  expires: ~
- ID: 3
  startX: 317.000000
  endX: 336.000000
  startZ: 302.000000
  endZ: 314.000000
  price: 26000.000000
  owner: TestDummy1
  level: world
  invitee: []
  expires: ~
- ID: 4
  startX: 203.000000
  endX: 223.000000
  startZ: 312.000000
  endZ: 352.000000
  price: 86100.000000
  owner: Elytra1
  level: world
  invitee: []
  expires: ~
- ID: 5
  startX: 157.000000
  endX: 174.000000
  startZ: 318.000000
  endZ: 334.000000
  price: 30600.000000
  owner: Elytra1
  level: world
  invitee: []
  expires: ~
- ID: 6
  startX: 121.000000
  endX: 128.000000
  startZ: 286.000000
  endZ: 299.000000
  price: 11200.000000
  owner: Elytra1
  level: world
  invitee: []
  expires: ~
- ID: 7
  startX: 226.000000
  endX: 244.000000
  startZ: 214.000000
  endZ: 225.000000
  price: 22800.000000
  owner: Haxley
  level: world
  invitee: []
  expires: ~
- ID: 8
  startX: 193.000000
  endX: 222.000000
  startZ: 141.000000
  endZ: 172.000000
  price: 96000.000000
  owner: Haxley
  level: world
  invitee: []
  expires: ~
- ID: 9
  startX: 171.000000
  endX: 199.000000
  startZ: 45.000000
  endZ: 80.000000
  price: 104400.000000
  owner: Haxley
  level: world
  invitee: []
  expires: ~
...

Solution

  • When you modify your array, you're removing elements without reindexing. Then yaml_emit gets an array with a missing key, so it will try to make that weird syntax. I'm not aware if this is a bug on the library or the desired behavior.

    To get the output you want, change these line (2 occurrences):

    // Before
    $landAfterDump = yaml_emit($array);
    
    // After
    $landAfterDump = yaml_emit(array_values($array));