lib/php_assoc_map.ex

defmodule PhpAssocMap do
  alias PhpAssocMap.Exploder
  @doc """
  Parses an associative array string (or charlist) to a key-valued map. Both single and double quotes are supported.

  *Note*: If the string starts with `<?php return`, it'll be ignored

  ## Exemples

      iex> PhpAssocMap.to_map("['key' => ['another_key' => 'value']]")
      %{"key" => %{"another_key" => "value"}}

      iex> PhpAssocMap.to_map("<?php return ['key' => ['another_key' => 'value']];")
      %{"key" => %{"another_key" => "value"}}
  """
  @spec to_map(binary() | charlist()) :: any()
  def to_map(string) do
    string
    |> to_tuple
    |> convert_to_map()
  end

  defp convert_to_map(list) when is_list(list) do
    Enum.reduce(list, %{}, fn ({key, value}, acc) ->
      Map.put(acc, convert_to_map(key), convert_to_map(value))
    end)
  end
  defp convert_to_map(value) when is_binary(value), do: String.replace(value, "\\", "")
  defp convert_to_map(value), do: value

  @doc """
  Converts a map structure to an associative array string. The string key and value are single quoted

  The returned document will not be formatted yet. Use PhpAssocMap.Exploder.explode/1 or PhpAssocMap.Exploder.explode/2 to have it formatted.

  ## Exmples

      iex> PhpAssocMap.from_map(%{"key" => %{"another_key" => "value"}})
      "['key'=>['another_key'=>'value']]"
  """
  @spec from_map(map(), Exploder.spacing()) :: binary()
  def from_map(map, spacing \\ :none), do: Exploder.explode(map, spacing)

  @doc """
  Parses an associative array string (or charlist) to a key-valued keyword list. Both single and double quotes are supported.

  *Note*: If the string starts with `<?php return`, it'll be ignored

  ## Exemples

      iex> PhpAssocMap.to_tuple("['key' => ['another_key' => 'value']]")
      [{"key", [{"another_key", "value"}]}]

      iex> PhpAssocMap.to_tuple("<?php return ['key' => ['another_key' => 'value']];")
      [{"key", [{"another_key", "value"}]}]
  """
  @spec to_tuple(binary() | charlist()) :: [tuple()]
  def to_tuple(string), do: ast(string)

  @doc """
  Converts a keyword list structure to an associative array string. The string key and value are single quoted

  The returned document will not be formatted yet. Use PhpAssocMap.Exploder.explode/1 or PhpAssocMap.Exploder.explode/2 to have it formatted.

  ## Exmples

      iex> PhpAssocMap.from_tuple([{"key", [{"another_key", "value"}]}])
      "['key'=>['another_key'=>'value']]"
  """
  @spec from_tuple([tuple()], Exploder.spacing()) :: binary()
  def from_tuple(tuple, spacing \\ :none), do: Exploder.explode(tuple, spacing)

  @doc """
  Gets the document AST from leex and yecc parser. The ast is automatically obtain from to_tuple/1 and to_map/1

  ## Exmples

      iex> PhpAssocMap.ast("['key'=>['another_key'=>'value']]")
      [{"key", [{"another_key", "value"}]}]
  """
  @spec ast(charlist() | binary()) :: [tuple()]
  def ast(chars) when is_bitstring(chars), do: chars |> String.to_charlist() |> ast()
  def ast(string) do
    with {:ok, tokens, _} <- :php_lang.string(string),
      {:ok, ast} <- :php_parse.parse(tokens) do
      ast
    end
  end
end