1 апреля 2011 в 19:25

Задача при собеседовании на работу в один крупный шведский сайт

PHP*
Я — PHP-Developer, живу в Стокгольме. Недавно был на собеседовании в один большой шведский сайт (более миллиарда page views в месяц). Интервью проводили 2 программиста из этой фирмы. В определенном моменте, один из них достал листок бумаги и сказал, что предлагают мне решить небольшую задачку (тут же на бумаге, без компьютера). И что у меня есть 10 мин. Попросили так же комментировать каждый шаг.

Скажу сразу, что я ее не решил. Сначала все вроде просто, а потом… Так что, ушел со встречи не солоно хлебавши. С моей стороны она так и осталась нерешенной.

Зачем публикую это? Во-первых, может кому-то пригодится как хороший тест для нанимаемых разработчиков; во-вторых, кто-то, если встретит нечто подобное, будет уже полон знаний; в-третьих, может кто-нибудь поместит правильное решение в коментах?

Ниже — сама задача. Оставляю все в оригинале, как было.

PHP assignment
Write a function, read_conf($filename), that converts the configuration below into a multidimensional array.

The configuration is divided up in rows and each row is divided up by key and value. The key can be multidimensional, and can be from 1...N, in the example below we only have 4 levels, but the solution should be able to work even when adding another row with more key levels: eg. session.save.db.master.host=10.0.0.1

===config.txt===
id=www
session.timeout=120
session.server.0.host=127.0.0.1
session.server.0.port=1111
session.server.0.id=session1
session.server.1.host=127.0.0.1
session.server.1.port=1111
session.server.1.id=session2
image.width=640
image.height=480
image.watermark.small=wsmall.png
image.watermark.normal=wnormal.png

===code===
<?php
$res = read_conf(«config.txt»);
var_dump($res);
?>

===output===
array(3) {
["id"]=>strong(3) "www"
["session"]=>array(2) {
["timeout"]=>string(3) "120"
["server"]=>array(2) {
[0]=>
array(3) {
["host"]=>
string(9) "127.0.0.1"
["post"]=>
string(4) "1111"
["id"]=>
string(8) "session1"
}
[1]=>
array(3) {
["host"]=>
string(9) "127.0.0.1"
["port"]=>
string(4) "1111"
["id"]=>
string(8) "session2"
}
}
}
["image"]=>
array(3) {
["width"]=>
string(3) "640"
["height"]=>
string(3) "480"
["watermark"]=>
array(2) {
["small"]=>
string(10) "wsmall.png"
["normal"]=>
string(11) "wnormal.png"
}
}
}
@martyshka
карма
27,0
рейтинг 0,0
Похожие публикации
Самое читаемое Разработка

Комментарии (227)

  • +33
    • +20
      >_<
      Вы когда в кинотеатр кино пойдёте смотреть, помните: эти двое поссорятся, а вон того убьют последнего.
      • +25
        Дык не тыкайте, в чем проблема то?
      • +20
        Это разве спойлер…
        Вот это спойлер:

        Kevin Spacey spoiler
    • +13
      эмм)) не слишко ли много буков для такого тривиального решения? одна рекурсивная функция всегто
      Потратил минут 12 на написание.

      <?php
      
      function array_merge_recursive_distinct () {
        $arrays = func_get_args();
        $base = array_shift($arrays);
        if(!is_array($base)) $base = empty($base) ? array() : array($base);
        foreach($arrays as $append) {
          if(!is_array($append)) $append = array($append);
          foreach($append as $key => $value) {
            if(!array_key_exists($key, $base) and !is_numeric($key)) {
              $base[$key] = $append[$key];
              continue;
            }
            if(is_array($value) or is_array($base[$key])) {
              $base[$key] = array_merge_recursive_distinct($base[$key], $append[$key]);
            } else if(is_numeric($key)) {
              if(!in_array($value, $base)) $base[] = $value;
            } else {
              $base[$key] = $value;
            }
          }
        }
        return $base;
      }
      
      function read_conf($path) {
      	$output=array();
      	$file=fopen($path,'r');
      	while($row=fgets($file)) {
      		list($key,$value)=explode('=',$row);
      		$path=explode('.',$key);
      		$path=array_reverse($path); $tmp=$value;
      		foreach ($path as $v) {
      			$tmp=array($v=>$tmp);
      		}
      		$output=array_merge_recursive_distinct($output,$tmp);
      	}
      	fclose($file);
      	return $output;
      }
      
      
      $res = read_conf("conf.cnf");
      var_dump($res);
      ?>
      
      • 0
        function array_merge_recursive_distinct () {
        $output=array_merge_recursive_distinct($output,$tmp);

        12 минут на написание, а на тестирование?
        • +3
          включая тестирование. если не верите что работает проверьте ;)
          и пусть вас не смущает что в описании функции нету параметров.
          $arrays = func_get_args();

          эта строка используется для взятия аргументов функции. таким образом можно писать функции с произвольным числом аргументов. данная функция тому пример
          • 0
            my bad

            Единственное, не понятно зачем вы применили в данном конкретном случае, ведь количество аргументов у вас одинаковое во всех вызовах функции.
            • 0
              изначальная задумка, думалось что будет несколько аргументов… но в итоге получилось два
      • +1
        >list($key,$value)=explode('=',$row);
        Если в значении параметра встретиться знак "=", то есть шанс что ваша функция выдаст чушь.
        • +1
          кстати да, но решается заменой explode('.',$key); на explode('.',$key,2);
          • +3
            list($key,$value)=explode('=',$row, 2) + array('','');

            Позволит избежать «Notice: Undefined offset: 1» если explode вернет меньше чем 2 элемента. Также можно использовать для указания значения по умолчанию (например, null сделать) для настроек вида «param» (пропущено '=значение').
    • –2
      Не вводите людей в заблуждение!
      protected function _loadIniFile($filename) — внимательно читаем и видим, что больше двух уровней вложенности она не парсит
      • 0
        Вы невнимательны. Этот метод не парсит инишник, он отвечает за наследование секций.
    • +14
      govnokod.com/4288
      Потратил 6 минут, заработало с первого раза)
      • –5
        И вы будете полностью разочарованны. Я бы вас не взял работать в серьёзную компанию.

        Я понимаю, что вы не пишете сейчас тестовое задание, но именно так вы ведете себя на дедлайнах, когда нужно написать что-то быстро.

        Причина отказа: любой другой программист не сможет разобраться в вашей функции, потому что она ужасна.
        • –3
          А что вы товарища минусуете? Я, как человек, ведущий собеседования PHP-ников в проект с несколькими сотнями мильёнов динамических хитов в сутки, сразу бы послал его вон. Если с $k и $v ещё можно как-то жить, то $cc — это «досвиданься, спасибо за ваше время». И да, это несмотря на впечатляющее время выполнения задачи.
          • +2
            Подобное наименование переменных допустимо в функции такого размера.
        • +2
          Вы преувеличиваете. Или не видели по-настоящему ужасных функций.
          • +8
            Не преувеличивает. Нормальное именование должно быть в крови.
            • 0
              Преувеличивает. Вы явно не видели говнокод. Это вообще еще семечки.
          • +1
            Вы не сталкивались с ситуациями, когда вам нужно исправить ошибку в коде, когда второй программист заболел?

            Да, он написал эту функцию за 6 минут, но исправлять ошибки будете ещё час.
            • 0
              Я понимаю, о чём речь. Работал как-то в паре с программистом, который именовал переменные набором букв со случайно подвернувшихся клавиш, типа «jhg» или «uiop»; но, по счастью, я оттуда ушёл чуть раньше.

              И всё же в этом примере вполне понятная логика, так что не думаю, что может потребоваться час на дебаггинг.
        • +1
          Я другой программист, писал на PHP 4 года. Разобрался сразу же.
          Я не стал бы работать в вашей «серьезной» компании, поскольку вы явно недооцениваете кандидатов.
    • 0
      <?
      function parseline($conf, $line) {
        list($key, $val) = explode('=', $line, 2);
        $keypath = explode('.', $key);
        $cconf = &$conf;
        foreach($keypath as $k)
          $cconf = &$cconf[$k];
        $cconf = $val;
      }

      $conf = array();

      $configtext = file_get_contents('config.txt');
      foreach(explode("\n", $configtext) as $l) {
        $l = trim($l);
        parseline(&$conf, $l);
      }

      var_dump($conf);


      * This source code was highlighted with Source Code Highlighter.
    • –2
      Угу. 400ms на парсинг конфиг-файла весом в ~килобайт. Когда я нашел это в нашем проекте — повесил девелопера на фикусе прямо посреди офиса.
      • +5
        Ага, вишу я на фикусе и думаю:) ранняя оптимизация зло или нет…
        А потом врубили APC и полетели…
        • –1
          ахах, Андрей:)) Не заметил что это ты. Слушай, ну за это время пора бы и исцелиться от Zend'офилии:)
          • 0
            с «ооп головного мозга», это раз и навсегда ;)
  • +4
    • +1
      Функция split() уже deprecated, почему не explode()?
      • +1
        Перловые привычки, простите.
  • +2
    parse_ini_file + дополнительный процессинг точек (ака разбивка на массив)?

    Интересная задачка, но больно прикладная ИМХО
  • +5
    Ну если стояла задача написать именно полностью свое решение, а не применять функции парсинга ini-файлов, то можно было написать что-то вроде следующего:

    function addToDictionary($array, $keyArray, $value)
    {
        if(!isset($array[$keyArray[0]]))
        {
            $array[$keyArray[0]] = array();
        }
        
        if(count($keyArray) > 1)
        {   
            $partOfKey = $keyArray[0];
            unset($keyArray[0]);
            addToDictionary($array[$partOfKey], $keyArray, $value);
        }
        else
        {
            $array[$keyArray[0]] = $value;
        }
    }
    
    function read_conf($filename)
    {
        $result = array();
        $fp = fopen($filename, "r");
        while(!feof($fp))
        {
            $line = fgets($fp); //Считываем новую строку конфига
            $keyValueArray = split("=", $line); //Отделяем ключ от значения
            $keyArray = split(".", $keyValueArray[0]); //Отделяем части ключа
            addToDictionary($result, $keyArray, $keyValueArray[1]); //Добавляем в массив
        }
        fclose($fp);
        
        return result;
    }
    • +1
      Код не тестировал, возможно где-то допустил ошибку, не судите строго. В кратце алгоритм таков: считываем строку из конфига, разделяем ее на ключ и значение. Ключ также разделяем на куски. Также создаем функцию, которая рекурсивно (пока в массиве частей ключа больше 1 элемента) создает массивы по частям ключа. Когда кол-во элементов массива ключей становится равным 1, присваиваем переданное значение.
    • 0
      Сам же заметил некритическую ошибку:

      Первые 3 строки функции addToDictionary стоит внести в if(count($keyArray) > 1) блок
    • +1
      массив по ссылке надо передавать:
      function addToDictionary(& $array, $keyArray, $value)
      • –6
        Я хоть и совсем себя программистом не считаю (так, с CMS Drupal иногда играюсь), но эту ошибку тоже заметил…
      • –1
        Признаться, считал, что массивы по умолчанию ссылкой передаются, как например, в C#. Век живи — век учись.
        • –1
          По ссылке передаются только объекты. Точнее, они не совсем по ссылке, но работаешь с тем же объектом.
  • +70
    Вот как то вот так:
    function read_conf($filename) {
    foreach (parse_ini_file($filename) as $key=>$value) {
    $key = vsprintf('$result["%s"] = "%s";', array(
    str_replace('.', '"]["', $key),
    $value,
    ));
    eval($key);
    }
    return $result;
    }
    • 0
      Сорри, отбивка не получилась. Работает при любом уровне вложенности и код понятный
      • 0
        Кстати, тоже влепил реплейс точек в ключе с последующим eval. Хоть в общем случае eval и зло, но тут получается просто и понятно.
    • 0
      Красивое решение, нестандартный подход… Спасибо, запомнил на будущее.
    • +3
      Когда дочитал задание сразу про этот способ подумал. Прелесть интерпретируемых языков.
    • +9
      Тоже сразу об этом подумал, но только в качестве «на коленке для себя». На собеседовании такое не стал бы показывать. (Многие считают eval вселенским злом по умолчанию, и кстати не зря, так что многие работодатели забраковали бы подобное решение даже не посмотрев работает оно или нет.)
      • 0
        если других вариантов не было можно было и рискнуть же
        • +1
          Не знаю, у меня сразу возник вариант (в комметариях ниже был подобный вариант, по этому свой приводить не буду) с обычным file+foreach, а уже потом (как вариант «для извращенцев») возникла идея что можно также использовать и eval (но как мне кажется это вариант больше «для прикола» чем для реальной ситуации)
      • –2
        Нет смысла ИМХО идти к работодателю, который считает eval вселенским злом. Это либо человек с предрассудками, либо владелец бизнеса с плохой тенденцией к микроменеджменту. Если собеседуемый действительно подкованный человек, он сумеет найти подходящий аргумент и переубедить, почему именно его вариант правильный. Именно таких людей и ищут на senior+ должности — уверенных (но не самоуверенных) :)
        • +6
          заколебетесь переубеждать. ;) неожиданно вспомнится, что ключи могут содержать символы ], [ и $ (ни к то ж не утверждал обратного), а элементы с цифрами валидны только на третьем уровне.

          на практике это означает, что ваш код должен быть готов к отладке и изменению требований, а варианты с eval(), в данном смысле, самые неудобные. причины всем известны. т.е. если вы всерьёз собираетесь решать практические задачи таким вот изящным способом, то это будет расцениваться не иначе как, блин, саботаж, потому что поведение кода слабо предсказуемо, а его рефакторинг неоправданно дорог. нафик такой «специалист» нужен. если же — первоапрельская шутка, то хотя бы смайлик поставьте.
          • +4
            Поскольку дополнительных требований выдвинуто не было, определение валидности входных данных лягло на плечи разработчика непосредственно. Давайте понимать, что в структуре INI-файла, квадратные скобки (обычно) означают секцию.

            Поскольку общепринятого стандарта описания INI-файла нет — то разработчик вполне вправе предпологать, что исходные данные по задаче — максимальны по содержанию и требованию. Все остальное — защита «от дурака» (описано чуть ниже, в другом комментарии).

            ИМХО не стоит усложнять требования задачи самостоятельно. Оставьте это постановщикам той самой задачи. Задача же программиста — перенести бизнес-логику в код, описав ее и только ее максимально просто и понятно и обработав только те исключения, которые предпологаются контекстом задачи

            Никакой шутки — никакого смайла

            З.Ы для защиты от «запрещенных» символов — достаточно всего навсего дописать экранирование ключа. Но опять же, это выходит за рамки конкретной задачи. Если собеседующие люди укажут на те факторы, которые вы описали выше — они будут безусловно правы (заказчик всегда прав?), но это уже будет «change request», так как одна из составляющих бизнес-логики изменилась (формат данных). И это уже будет совершенно другая задача, пусть и связанная.

            Вы можете сказать, что можно было подобную задачу решить «наперед», просто подумав об этой возможности? Безусловно можно, любой мало-мальски толковый программист именно так и сделает! Но есть одно НО. Давайте представим, что мы описываем не простейшую функцию, а огромный и сложный кусок бизнес-логики. И добавление неописанных в ТЗ обработчиков поведения — затягивает рабочий процесс, релиз, не на несколько минут, а на дни, недели, месяцы? А потом выясняется, что этот функционал и вовсе не нужен, так как там всегда будет только точка и литералы, и ничего кроме? Понимаете, о чем я? :)
            • +2
              Нет уж, усложнять требования задачи самостоятельно нужно, в этом лежит основа безопасности любой программы. Короче говоря на «возможно всё что угодно, пока не доказано обратного». Если в спецификации не написано «там не может быть буквы зю», она там может быть, и никак не наоборот. Не редко на собеседовании хотят от вас именно такого мышления, и «Защита от запрещенных» символов тут не причём, это не защита (звучит как нечто опциональное) а часть скрипта точно так-же как тормоз часть автомобиля. Да можно ехать без тормозов и даже приехать в нужное место в нужное время, но называть автомобиль без тормоза хорошим автомобилем, я бы постеснялся.
            • 0
              В противном случае окажется что там будут не только точка и литералы через месяц после сдачи — придется брать время из новых проектов, которые по этой самой причине отстанут на месяц, на два.

              В вопросе срок/качество я выбираю качество. Поэтому я не профессионал :(
              • 0
                Ну и ничего страшного. Вам дали ТЗ, по нему вы написали код. Заказчик под принял, протестил, денег отдал — проект закрыли. Через месяц поменялись условия — открываем новый таск на доработку с новой таксой.
                Это, конечно, на словах и в идеале, и я понимаю, что на практике заказчик будет говорить: «ну как же так, это ж ежу понятно, что могут быть спецсимволы в ключах»
      • 0
        Со словами «как вариант, если надо быстро» — чень даже. А если еще добавить «в данном подходе eval будет злом только в том случае, если программист в конфиг ресь напишет»…
    • +1
      Люди, вы чего. Красивое решение? Прелесть интерпретируемых языков?

      ===config.txt===
      a = "\"; echo \"php>=5.3 required; Предлагаю назвать этот вид уязвимости PHP-injection\";\" "

      • +2
        В задаче ничего не сказано о том, что конфиг является опасным. Не стоит ИМХО придумывать самому себе задачи :) Если имеем дело с потенциально опасным конфигом — тогда условие задачи должно быть сформулировано соотвтствующим образом и в код должна быть добавлена логика валидации
        • +1
          Если вы выбрасываете свой код через 5 минут после написания — то безусловно, ваш вариант можно использовать. Набыдлокодил и забыл.
          Но если вы тешите себя надеждой что после вас еще кто-то будет с этим кодом работать, поддерживать и ре-использовать его — такие eval'ы даже в голову не должны приходить.
          Ничего личного.
          • +12
            Вы ненавидите eval потому, что… кто то сказал, что это плохо? PHP интерпретируемый язык, это все один сплошной eval, нет?

            Есть задача. Строгая, типизированная, задача. Поставленная программистами — программисту. Задача содержит в себе глубокий смысл — а именно, возможность понять, как мыслит человек, которых проходит собеседование. И поверьте — мне кажется, что ответ с eval вполне удовлетворил бы этих господ. Почему? Он (ответ) не стандартен, а значит человек который можут думать нестандартно — будет способен решать другие задачи быстро, дешево и изящно. Лично я бы хотел бы видеть как можно больше людей с нестандартным мышлением в своей команде. Почему? Для стандартных решений я умею пользоваться google.

            Задача. Черный ящик, со входом и выходом. Давайте попробуем в данные 10 минут решить самое главное требование — реализацию бизнес-логики приложения. А проверки на «присутствует ли физически файл конфига», «не пришел ли барабашка и не положил ли в конфиг беду» и прочие вещи, которые выходят за рамки конкретной задачи — будем решать по мере поступления новых требований, а не заниматься отсебятиной?

            Вот потому и имеем (к сожалению) в мире людей, которые занимаются всем чем угодно: красивостями, построением «защиты от дурака», стадным блюдением стандартов — всем, чем угодно, но только не самой задачей в полной мере ее объема до ее логического завершения.

            Знаете, в жизни каждого программиста происходит переломный момент, когда выражения вида «вселенское зло», «быдлокод» и пр. начинают вызывать добрую улыбку :) Вселенское зло не есть eval. Вселенское зло есть 30 строк дополнительного (читать избыточного) кода, созданного либо потому, что другого подхода к решению программист не видел (это лечится, приходит с опытом и все будет хорошо), либо же (второй вариант, клинический) — когда программист знает и отдает себе отчет, что это избыточный код, но… в его eval будут тыкать пальцами.

            Зло eval'a в параграфе выше преувеличено, на его примере показана практика чтения и поддержки стороннего кода, когда main-программист вместо того, чтобы написать коротко и изящно (при этом читаемо и понятно) — пишет операционную систему. Зло, это когда вместо одной регулярки — имеем 50 строк хлама. Зло — это когда простейшую функцию набиваем таким количеством хлама (может быть и нужного, безусловно, но только не на текущий момент времени), что функция просто не читаема.

            Зло — это преждевременная оптимизация велосипеда, который изобретать не нужно, но очень хочется.
            • +5
              eval — плохо понимается средой, плохо отлавливаются ошибки, не оптимизируется оптимизаторами, может стать причиной уязвимости и ещё куча-куча всего. Возможно, это неплохой инструмент иногда (крайне редко), но лучше убедить людей, что он плохой, чем стараться объяснить, когда его можно использовать.
              • –1
                Вот тут вот, в ваших словах, и скрыт глубокий смысл — эти самые убеждения, порой, доходят до абсурда. Программист просто не в состоянии порой объяснить, почему именно это плохо. Это просто — плохо. Плохо и все, так в интернете написано было да и друг, старый программист на Cobol рассказал.

                Безусловно, есть минусы. В реальном проекте нужно трижды подумать, прежде чем выбирать путь решения задачи (назад дороги не будет, как правило).

                Есть очень хорошая пословица: «Не уверен — не обгоняй». В нашем с вами деле — то же самое :)
              • +1
                Ещё он работает медленнее.
            • +1
              > Вы ненавидите eval потому, что… кто то сказал, что это плохо?
              Вы выше привели кусок кода который отлично показывает почему eval испльзовать не стоит, если не понимаешь чем это грозит.

              > Задача. Черный ящик, со входом и выходом.
              Да нивапрос. Вас попроси сделать вывод ности из базы — вы, наверное, напишете
              $news = mysql_query('SELECT * FROM news WHERE id=' . $_GET['news_id']);
              А чё — работает, условиям задачи отвечает.

              • +1
                > Да нивапрос. Вас попроси сделать вывод ности из базы — вы, наверное, напишете
                А вот тут уже флейм пошел (и причем здесь новости?). Простите, ухожу, не хочу
              • 0
                Вы правда не видите разницы? В первом случае конфиг, контролируемый админом, который и так имеет ПОЛНЫЙ доступ к машине. А во втором практически неконтролируемый параметр news_id и поэтому должны его проверять.
                • 0
                  > В первом случае конфиг, контролируемый админом
                  Это с чего вы взяли?
                  Давайте, попробуйте убедить меня что функция которая позволяет сделать eval пользовательского ввода (того самого которому никогда нельзя доверять, ага), которая завершает работу ВСЕЙ программы (не возвращает «false», не выбрасывает эксепшен, а падает с fatal error) если в значении параметра встречается \" — это «красивое решение» :)))
                  • 0
                    > > В первом случае конфиг, контролируемый админом
                    > Это с чего вы взяли?
                    Тут пример приведу — эта функция может использоваться для редактирования настроек форума на сайте. На большом сайте есть маленький форум, у которого есть отдельный админ. Всё что он может делать с форумом — редачить его настройки через ini-file. И тут привет — вы даёте ему полный доступ к eval'у чего угодно на сервере.

                    И ладно это вы знаете что это функция которую можно использовать только с сто раз проверенными безопасными конфигами. Но вот вы умерли, уволились и заболели, и с вашим сайтом работает кто-то другой. А вы ему такой подарок оставили — бомбу с часовым механизмом. Ни дай бог он решит где-то в другом месте эту функцию применить — опять приехали.

                    Повторюсь — для домашних страничек на коленке эту функцию можно использовать — написал и выкинул, через неделю уже и не вспомнишь чего там было. И боже упаси это потом поддерживать.
                    Но если у вас проект на много тысяч строк над которым вы работаете уже второй год и не помните с точностью до процессорной инструкции какая функция как работает — такое писать нельзя. Сами же потом на все грабли в коде и наступите.
                    • +1
                      >Тут пример приведу — эта функция может использоваться для редактирования настроек форума на сайте. На большом сайте есть маленький форум, у которого есть отдельный админ.

                      А еще эта функция может предназначаться для конфигурации космического корабля. Эти шведские программисты — они такие хитрецы!

                      З.Ы простите, мне кажется у вас паранойя, эвалофобия и пробелы в знании предметной области
                      • +1
                        ок :-)
                  • –1
                    > Давайте, попробуйте убедить меня что функция которая позволяет сделать eval

                    php.net/manual/en/function.eval.php

                    Return Values

                    eval() returns NULL unless return is called in the evaluated code, in which case the value passed to return is returned. If there is a parse error in the evaluated code, eval() returns FALSE and execution of the following code continues normally. It is not possible to catch a parse error in eval() using set_error_handler().
                    • 0
                      Ололо. Я, если что, уже 3 года как на php не пишу.
                      • 0
                        и то смог прочитать: In case of a fatal error in the evaluated code, the whole script exits.
                      • 0
                        >Ололо. Я, если что, уже 3 года как на php не пишу.

                        Так а что вы тогда делаете в этом топике?

                        З.Ы предположу, что функция eval всегда так работала
                  • 0
                    >Это с чего вы взяли?
                    Ну посмотрите наконец содержимое файла. Или bind адрес может каждый менять?
                    >того самого которому никогда нельзя доверять, ага
                    Защитное программирование головного мозга? :-) У каждой задачи есть границы, выход за которые неразумная трата средств.
      • 0
        addslashes/addcslashes никто не отменял
    • 0
      Использование eval — первое же решение о котором я подумал по прочтении задачи, но я его отринул (но я не говорю что не использую eval вообще) из-за особенностей отлова ошибочных ситуаций и даже отслеживания различного рода атак через конфиг (например если не делать проверок на символы в наименованиях параметра, туда можно поместить вызовы функций типа:
      ===config.txt===
      fwrite(fopen('test','x'),'test');//.=ha!


      P.S. Конечно достаточно указать точный формат (проверки регулярками), который должен исключить все возможные варианты,… обычно если проект простой и незначительный, такие проверки опускают временно… но 'нет ничего постоянного чем временное'. И часто эти проверки обычно слишком простые (например вместо исключения опасных символов указывается список допустимых) — что порождает проблемы использования не латинских символов (русские например) или еще какие случаи.

      Проблема может показаться надуманной, но я говорю про потенциально проблемные места. Сам же я реализовал примерно таким способом.

      (Точнее моя 'слепая' реализация была чуть покороче и я допустил две примитивные синтаксические ошибки :) )
  • –1
    Вот нечто похожее:
    Есть массив вида:

    $items = array(
    array(
    'id' => 12,
    'category_id' => 4,
    'color' => 'red',
    ),
    array(
    'id' => 13,
    'category_id' => 5,
    'color' => 'green'
    ),
    array(
    'id' => 14,
    'category_id' => 4,
    'color' => 'blue'
    ),
    array(
    'id' => 15,
    'category_id' => 6,
    'color' => 'blue'
    )
    );

    id товара
    category_id — id категории (трусы, очки, сигары)
    color — цвет
    … — могут быть ещё параметры (ширина, глубина, секс и т.п.)

    Надо преобразовывать этот массив в вид дерева (сгруппировать в дерево) на основе массива настройки:
    $template = array('color', 'category_id', 'id');
    или
    $template = array('category_id', 'color', 'id');
    или
    $template = array('sex', 'category_id', 'color', 'id')
    и т.п.

    Полей в $items может быть больше, количество полей в $template может быть тоже любым.
    $template = array('category_id', 'color', 'id');

    l2t($arr, $template);
    -->

    array
    --4 =>
    ----array
    ------'red' =>
    --------array
    ----------12 => null
    ------'blue' =>
    --------array
    ----------14 => null
    --5 =>
    ----array
    ------'green' =>
    --------array
    ----------13 => null
    --6 =>
    ----array
    ------'blue' =>
    --------array
    ----------15 => null
  • +3
    оу оу хватит ребята, сегодня пятница)))
    • +2
      Если бы автор сказал, что нашел на просторах сети очередную задачку, то даже и париться не стал бы. Но предисловие о том, что подобное задание дают на собеседовании в очень серьезной конторе, заставило напрячь извилины :)
  • +6
    У меня получилось вот так никуда не заглядывая.
    Самая хитрая часть — конечно же сделать уровни вложенности. Решил делать через ссылки.
    Если бы не вложенность, то решение в два раза короче 8))

    function read_conf($f){
        $lines = file($f);
        $result = array();
        foreach($lines as $line){
    	$line = trim($line);
    	if(false === strpos($line ,'=')) continue;
    	if(0 === strpos($line, '=')) continue; 
    	list($key, $value) = explode('=', $line);
    	$key = trim($key);
    	$value = trim($value);
    	$parts = explode('.', $key);
    	
    	$iterator = &$result;
    	foreach($parts as $part){
    	    if(isset($iterator[$part])){
    		$iterator = &$iterator[$part];
    	    } else {
    		$iterator[$part] = array();
    	    }
    	}
    	$iterator[$part] = $value;
    //	$result[$key] = $value;
        }
        return $result;
    }
    

    • 0
      Только одно но: для строки конфига session.server.0.id=session1 сперва для ключа 0 будет создан массив ($iterator[$part] = array();), а потом сразу будет заменен на значение ($iterator[$part] = $value;).
      • 0
        Это не страшно 8)
        ведь в задаче не сказано как поступать с a = 1
      • 0
        Это не страшно 8)
        ведь в задаче не сказано как поступать с
        a = 1
        a.b.c = 1
        a.b = 1

        Если без таких коллизий, то результат у меня должен быть верным
    • 0
      Также в цикле foreach если на каком-то уровне вложенности ключа, в массиве такого элемента нет, то вы его добавляете, но не двигаете итератор.
      • 0
        Да, пожалуй, вы — правы
        	    if(isset($iterator[$part])){
        		$iterator = &$iterator[$part];
        	    } else {
        		$iterator[$part] = array();
        	    }


        Стоит заменить на
        	    if(!isset($iterator[$part])){
        		$iterator[$part] = array();
        	    }
        	    $iterator = &$iterator[$part];
        


      • 0
        Во! Вот так должно быть!
        	$iterator = &$result;
        	foreach($parts as $part){
        	    if(!isset($iterator[$part])){
        		$iterator[$part] = array();
        	    }
        	    $iterator = &$iterator[$part];
        	}
        	$iterator = $value;
        
    • 0
      Может быть заменить
      if(false === strpos($line ,'=')) continue;
      if(0 === strpos($line, '=')) continue;

      на
      if( ! strpos($line ,'=')) continue;
      • 0
        Не, в таких случаях предпочитаю именно два условия, чтобы ни у кого не было подозрений, что здесь может быть ошибка. Два условия выражают две разные мысли. Одна о невалидных строках, вторая о комментариях 8) Просто мне показалось, что ==== code ==== — это комментарии такие 8)))
        • +1
          В любом случае strpos() стоило бы вызвать один раз.
          $eqpos = strpos($line, '=');
          if ($eqpos === false || $eqpos === 0) continue;

          Мысли выражены, лишнего вызова функции нет.
  • –6
    Плейахеад дот ком?

    Впрочем понятен подход шведов. Типичный снобизм. Считают раз эмигрант значит должен напрягаться больше а получать меньше.

    Таже фигня, что и с Сони Ериксон.

    Удачи,
    ignat
    • 0
      Ну почему сразу так, это же не пакистанцы, запрашивающие убежище и в поисках работы.
      Хотя мне не очень нравится способ проверки знаний в виде «а реши-ка ты это сейчас быстро», причем дают довольно комплексные задачки, где не мешало бы посидеть и хорошо подумать.
      Никто ведь в последующем не будет стоять над тобой и подгонять…

      P.S. Это случаем не Aftonbladet? Мне всегда кажется, что каждый уважающий себя швед пролистывает этот сайт хотя бы раз в день. :)

      • 0
        Не Alftonbladet :) Но размер сайта сопоставим.
    • +5
      задача элементарнейшая, а вы демонстрируете типичный снобизм эмигрантов. чуть что, сразу ущемление прав? ты бы хотел работать с человеком, который не может такую задачу решить?
      • –11
        Я на php не пишу. Лишь бы человек был хороший и на своем месте. Все можно освоить. К сожалению об этом забывают часто.

        Я бы хотел работать с хорошим человеком. И предпочитаю обращения: Вы, сеньера, сеньеро.

        Уадчи,
        ignat
        • 0
          Cеньеро, идите в лес.
          • 0
            К сожалению очень сильно страдает общий уровень культуры общения на Хабре. Я уже не говорю об образовании и сообразительности.

            Удачи,
            ignat
        • +12
          То что вы Игнат Уадчи — обязательно писать в каждом комменте? :)
          • –5
            Прав юмор, в основном, PHP-шники в своей массе даже до быдлокодеров не дотягивают :-), просто парни из аулов и рабочих кварталов без диплома (купленные не в счёт).
            Есть конечно исключения.
            И да этот топик доказал, чтобы интересоваться PHP большинству мозгов не надо совсем, мозги в этом случае мешают.

            Удачи и вам,
            ignat
            • +1
              Вы правы, среди PHP-шников очень немало «даже до быдлокодеров не дотягивают», причина проста, потому что с этого языка многие начинают (как раньше начинали с Паскаля или Бейсика, и упаси вас бог увидеть мою писанину на Бейсике) но как вы и сказали исключения есть.

              И именно чтоб не обижать этих «исключительных» людей (отдавших все нервы и всё время обучению любимой профессии) не стоит утверждать что PHP-шником может стать каждый. Говнокодером вероятно и может, однако это касается не только PHP, но и любого другого языка.

              Удачи вам, сеньеро.
  • +27
    У меня вот так сходу получилось:
    
    function read_conf($filename) {
    	$lines = file($filename);
    	$result = array();
    	foreach($lines as $line) {
    		$parts = explode('=', $line);
    		$path = explode('.',$parts[0]);
    		$r = &$result;
    		foreach($path as $q) {
    			$r = &$r[$q];
    		}
    		$r = $parts[1];
    		
    		
    	}
    	return $result;
    }
    
    $r = read_conf('config.txt');
    print_r($r);
    
    • +2
      Вместо:
      $parts = explode('=', $line);
      Нужно делать:
      $parts = explode('=', $line, 2);

      Ибо, если в значении будет встречен знак "=", то у вас будут глюки.
      • +5
        Ну тут как ставить задачу.
        Вообще код можно расширить проверками в 3-5 раз.
        И проверить есть ли такой файл, и правильность синтаксиса, и разные кодировки, и кеширование.
        А еще обернуть все это в ООП и добавить expception, и покрыть код тестами.

        Только вот зачем? Ведь это задачка на собеседовании на 10 мин и в даном случае нужно было просто показать что ты знаешь как оно должно работать, а вот всякие ситуации рассматривать это уже когда возьмут на работу)
        • +2
          Наверняка те, кто проводит собеседование, хотят так же узнать насколько человек способен отслеживать вероятные глюки в процессе написания кода, а не после его 105-го выполнения. Так что на мой взгляд учитывать всяческие нюансы полезно так же и на 10-минутных задачах на собеседовании.
          • +1
            Но и с вами не могу не согласится. Наверное все же лучший вариант написать самый елементарный код, что бы он работал.
            А дальше уже не спеша, вслух рассказать о потенциальных проблемах и как их решать.
            Тогда хорошее впечатление 100% гарантировано.
            • +1
              по-моему, кодить, а затем рассказывать о потенциальных проблемах этого кода — не самая лучшая стратегия для программиста. :)

              задачкой проверяется то, насколько соискатель вообще адекватен. ведь дальше с ним придется работать вместе и доверять решение практических задач.

              встречаются разные неудобные типажи. если претендент сразу бросается писать закорючки на php, — перед вами скорее «кодер». если начинает до упора мусолить постановку, значит — «формалист». ну и т.п.

              в общем случае, лучше начинать с уточнения требований. ведь через 10 минут может выясниться, что строк в конфиге бывает over 9K, а памяти в обрез, ключи содержат юникод, и кучу т.п. причин, которые превратят весь ваш труд в бесполезную трату времени (кстати, отличный способ троллить «кодеров» :))

              но без фанатизма — иначе за отведенное время вообще ничего не напишете. если сразу чувствуете, что не уложитесь, аргументированно запросите дополнительное время. или попробуйте договориться о том, чтобы сузить scope задачи. в любом случае, способность адекватно соотносить сложность задачи с собственными возможностями для программиста является большим плюсом.

              писанина на бумажке сильно отличается от привычного текстового редактора, т.к. не допускает вставок. для многих программистов это не привычно — нужно писать последовательно. но есть и плюсы — можно псевдокодить. короче, если подозреваете, что на собеседовании могут быть подобные упражнения, лучше потренируйтесь заранее, решив несколько головоломок из интернета именно на листочках.
    • +1
      Мда, у меня с налета понять работу с ссылками в вашем примере не получилось :) Может, разъяснитье немного принцип работы?
      • +3
        Конечно
        пусть есть конфиг
        server.name=main

        тогда
        $parts = array('server.name', 'main');
        $path = array('server','name');

        $r = &$result; // $r ==$result
        и в цикле foreach (по итерациям)
        $r = &$r['server'] // $r == $result['server']
        $r = &$r['name'] //$r == $result['server']['name']

        $r = $parts[1] // $result['server']['name'] = $parts[1]

        • 0
          Вспоминая определение ссылок в PHP — переменные a и b, если сказано $a = &$b, ссылаются на один и тот же участок памяти. Из-за этого и происходит изменение переменной $result, поскольку переменная $r начинает ссылаться на место в памяти, указаное индексом массива (кстати, удивило, что PHP никак не ругнулся на неопределеный по сути своей элемент в массиве), я верно понял?
          • 0
            Да верно. То есть после

          • 0
            Упс, случайно отправилось

            Да верно, то есть после
            $r = &$r['server']
            вызов $r['something'] полностью равносильно $result['server']['something']
            ну и т.д.
            • 0
              Интересует момент еще, почему после во время смены ссылки ($r = &$r[$q]) не разрывается старая ссылка с $r = &$result;? Чем это обусловлено?
              • 0
                сначала вычисляется правая часть выражения
                потом она присваивается в левую.

                это как $x = $x +1;
                • 0
                  Чему в данном случае равно «вычисление» — вот это я никак не могу понять.
                  • +2
                    $r = &$result// это я думаю понятно
                    теперь например идет такое присвоение
                    $r = &($r[$q]) //взял в скобки для понятности
                    вычисляем правую часть
                    &($r[$q]) == &($result[$q]) (замена из 1 строки)
                    присвоение
                    $r = &($result[$q])

                    повторяем все еще раз, итерация 2
                    $r = &($r[$t])
                    вычисляем правую часть
                    &($r[$t]) == &($result[$q][$t])
                    $r = &($result[$q][$t])
                    ну и так далее

                    если все так же не понимаете, учите c/c++, там с этим прийдется разобраться)
                    • 0
                      Спасибо большое, теперь, наконец, понял.
                      • 0
                        Пожалуйста, всегда рад помочь чем могу)
      • +2
        Весь секрет в том, что если вы присваиваете по ссылке значение неинициализированной переменной — она автоматически создается. Таким образом, если сделать $b = &$a['key']; $b = 'val', то будет автоматически создан массив $a = array('key' => 'val') (при условии, что такого массива еще нет). В приведенном примере автор просто последовательно проходит по всей иерархии (создавая массивы, если их еще нет) и потом присваивает значение последней ссылке. Красота в том, что $result при этом не меняется и все время содержит целиком массив с конфигом, а вся работа просходит при помощи ссылок.
    • 0
      По моему это — самое адекватное решение. Единственный момент —
      $r = &$r[$q];
      не нужно ли предварительно создать массив (прежде чем обращаться к его ключу)? Мне кажется текущая версия будет сыпать Warning-и.
      • 0
        Я тоже так сначала подумал. Но насколько я понимаю я получаю лишь ссылку на такое елемент, а не его значение, а значит все ок. А в конце, когда задаю само значение, цепочка всех массивов автоматически создается, тоже без варнингов.
        • +2
          Это я понимаю. Все-таки тут особенность PHP… В питоне такой код бы отвалился например в той строчке, где я выделил на второй итерации.

          Суть в том, что когда мы делаем
          ## $path=array('key1', 'key2');
          $r=&$result; #$r=array();
          $r=&$r[$q]; #$r=array('key1'=>NULL)
          $r=&$r[$q]; #тут мы обращаемся ключу 'key2' значения массива с ключом 'key1' т.е. NULL.
          PHP преобразовывает NULL к массиву автоматом при первом обращении к NULL как к массиву.

          Такое только в PHP срабатывает…

          php > $path=array('a', 'b', 'c');
          php > $res=array();
          php > $r=&$res;
          php > foreach($path as $q){
          php {     $r=&$r[$q];
          php {     var_dump($res);
          php { }
          array(1) {
            ["a"]=>
            &NULL
          }
          array(1) {
            ["a"]=>
            array(1) {
              ["b"]=>
              &NULL
            }
          }
          array(1) {
            ["a"]=>
            array(1) {
              ["b"]=>
              array(1) {
                ["c"]=>
                &NULL
              }
            }
          }
          php > 
          

          • 0
            Ухты) Буду знать. Хотя я не устал от php ожидать всяких вот таких приколов.
            Вот только как же портит php программиста, если это его первый язык. Не зря говорят, начинать обучение именно с c++/c
            • +1
              А что говорят про тех, кто начинал с машинных кодов, ассемблера и машины Поста?
              • +1
                Таких уважают)
        • 0
          «елементарный» выше конечно режет глаз, но с кем описок не бывает. Но когда в еще одном комменте пишете «елемент» это уже перебор! :)
          • 0
            Виноват! Просто русский язык никогда не учил, пора взяться за книжку :)
        • 0
          Даже в Strict-режиме никаких уорнингов?
          • 0
            
            error_reporting(E_ALL|E_STRICT|E_NOTICE);
            

            Все равно все чисто.
      • 0
        Чуть выше автор (за что ему огромное спасибо, жаль, не могу плюсануть) все подробно разъяснил в ветке, которую я начал :)
    • 0
      У меня точно такой же вариант получился!
  • 0
    моя версия. с проверкой что "=" так же может быть частью значения
    pastie.org/1744440
    • +2
      Почитайте доку по функции explode — и проверку мутить не придется. Достаточно один дополнительный параметр передать.
  • –1
    У меня получилось вот такое решение. На все потритил 6 минут. Из-за ограничений по времени, пришлось использовать eval(), что не очень хорошо. Правильнее было бы использовать рекурсию.

    1. <?php
    2.  
    3. function read_conf ($fn)
    4. {
    5.     $file = file($fn);
    6.     $config = array();
    7.  
    8.     foreach ($file as $line)
    9.     {
    10.         $line = explode("=", $line);
    11.         $key = $line[0];
    12.         unset($line[0]);
    13.         $value = trim(implode("=", $line));
    14.  
    15.         $key = explode(".", $key);
    16.         $str = '$config';
    17.         foreach ($key as $l)
    18.         {
    19.             $str .= "['$l']";
    20.         }
    21.         $str .= " = '$value';";
    22.         eval($str);
    23.     }
    24.  
    25.     return $config;
    26. }
    27.  
    28.  
    29. $res = read_conf("config.txt");
    30. var_dump($res);
    31.  
    • –13
      Рекурсия — зло!
      • +1
        Поспорьте об этом с Эрланг программистом
      • +8
        Рекурсия божественна!
      • +7
        Рекурсия рекурсивна!
  • +1
    output руками чтоли писали? Ох лол:)
    Поправьте, в первой стороке string, а не strong)
  • 0
    Поспорьте об этом с Эрланг программистом.
  • НЛО прилетело и опубликовало эту надпись здесь
  • НЛО прилетело и опубликовало эту надпись здесь
  • +6
    function read_conf($file_name) {
    parse_str(implode("&", array_map(function($line) {
    list($key, $value) = explode('=', $line);
    return preg_replace('/\.([^\.=]+)/i', '[\\1]', $key) .'='. $value;
    }, file($file_name, FILE_IGNORE_NEW_LINES))), $data);
    return $data;
    }
    • 0
      Кто короче?
    • +4
      Поправлено для знака равно в значении.
      function read_conf($file_name) {
        parse_str(implode("&", array_map(function($line) {
            return preg_replace('/\.([^\.=]+)/i', '[\\1]', substr($line, 0, strpos($line, '='))) . substr($line, strpos($line, '='));
        }, file($file_name, FILE_IGNORE_NEW_LINES))), $data);
        return $data;
      }
      • –1
        Красиво, чесслово.
        И не придерёшься, что eval используется, и рекурсий нет.
        Даже невозможно придраться к именам переменных.
        И коротко. И быстро.
    • 0
      Раз уж можно использовать eval, то
      function read_conf($file_name){
          return eval(preg_replace_callback('/(.+?)=(.+)/',function($m){return'$r["'.str_replace('.','"]["',$m[1]).'"]="'.$m[2].'";';},file_get_contents($file_name)).'return $r;');
      }
      • +12
        А зачем в три строчки? Можно ведь было в одну уместить;)
    • 0
      В плане производительности мне тоже кажется, разумнее преобразовать к формату, для которого есть готовый парсер (сишный, встроенный в php), ведь он работает быстрее кода на самом php.
  • 0
    Сначала подумал «ключи» точки заменить на ][ + пошаманить и заюзать евал. но потом подмал что это будет говнокод.

    получилось так:function read_conf($fname)
    {
    $result = array();
    $fp = fopen($fname, "r");
    while($line = fgets($fp))
    {
    if( trim($line)=='') continue;
    list( $keys, $value ) = explode("=", trim($line));
    $akeys = explode(".", $keys);
    $temp = $value;
    for($i=count($akeys)-1;$i>=0;$i--)
    $temp = array( $akeys[$i] => $temp);
    $result = array_merge_recursive($result, $temp);

    }
    return $result;
    }
  • +4
    извините за критику, но мне кажется это не сложная задачка, и «плохо Вам» если «С моей стороны она так и осталась нерешенной». Я надеюсь прийдя домой Вы таки ее решили? Иначе грош цена Вашей профессиональной деятельности.
    • 0
      Прежде чем заниматься решением таких задач следует задать себе вопрос — «Зачем?». Я, например, не вижу в этой задае никакого интереса и сложностей кроме «ручкой на бумаге», так зачем тратить на нее время? Это повысит скилл? Нет. Это принесет денег? Нет. Тогда Что!?
      • 0
        Это даст человеку понять _что именно_ на бумаге он не так сделал, это очень помогает. Решение задач, даже если они и просты, но которые с первого раза не решил, нужно обязательно решить потом, чтоб узнать что же ты не так сделал.
        • 0
          Ну, не знаю, не знаю… Есть задачи которые решать действительно надо, есть которые хочется, есть такие которые пока не решишь не будешь находить себе места, но эта, по моему, ни к одной из перечисленных категорий не относится. Алгоритм решения ясен, более того их несколько и все простые. Будет необходимость напишется, а значит не стоит и заморачиваться. Имхо.
          • 0
            Не в задаче дело, а в подходе. Если не сделал простую задачу на бумаге, значит что-то нетак. Нужно знать что именно не так, в этом дело.
  • +46
    function read_conf($path) {
    
    die('Не очень-то и хотелось у вас работать.');
    
    }
    
    
  • –1
    Тем, кто использует explode для разделения строк конфига на ключ-значение, не стоит забывать, что в значении также может быть знак равенства. Например:

    some.stupid.config=value=another

    В этом случае функцию следует применить как explode('=', $config_data, 2)
    • 0
      про это ничего не сказано, равно как и необходимость в проверке доступности файла и много чего другого. есть пример входных и выходных данных — все.
  • 0
    У меня такое решение получилось. Единственное — не обрабатывает значения в кавычках (кавычки идут как значение).
    <?php
    $res = read_conf('conf.txt');
    var_dump($res);

    function read_conf($conf)
    {
    $conf = file_get_contents($conf);
    $pattern = '/^((?:[^.=]+\.?)+)\s*=\s*(.*)$/mu';
    $res = array();
    preg_match_all($pattern,$conf,$res, PREG_SET_ORDER);
    $config = array();
    foreach($res as $match)
    {
    _set($config, $match[1], $match[2]);
    }
    return $config;
    }

    function _set(&$map, $keys, $value)
    {
    $keys = explode('.', $keys);
    $key = array_shift($keys);
    if($key && !isset($map[$key]))
    {
    $map[$key] = array();
    }
    if(empty($keys))
    {
    $map[$key] = $value;
    }
    else
    {
    $map[$key] = _set($map[$key], implode('.', $keys), $value);
    }
    return $map;
    }
    • 0
      С первым куском почти согласен, а вот рекурсивный вызов функции можно было избежать (экономия!!! хоть и по мелочам)

      1) регулярка /^ ([^\=]+) [\s]* [\=] [\s]* (.*) $/mxu
      2) создаем переменную $x которая будет указывать на &$config
      $x = &$config; (каждый новый муль-кей)
      и по мере разбора $keys
      $x = &$config[$key];
      если $config[$key] нет, но дальше его создаем… ;)
      • 0
        1) Зачем один символ задавать через символьный класс?
        2) А еще можно было избежать лишних explode/implode ) Просто сделал первое что пришло в голову. Все же ограничение в 10 минут.
        • 0
          > 1) Зачем один символ задавать через символьный класс?

          это скорее привычка, зато всегда понятно что это и мне так читать удобнее

          > 2) А еще можно было избежать лишних explode/implode ) Просто сделал первое что пришло в голову. Все же ограничение в 10 минут.

          а где сдась лишние explode/implode? здесь один explode для разбиения $keys по символу «точка». А насчет 10 мин. рекурсию функций в принципе отлаживать дольше чем просто цикл ;)
          хотя в данном случае рекурсия примитивная и не слишком замороченая…
          • 0
            Внутри рекурсивной функции:
            Сначала мы делаем explode, а потом при рекурсивном вызове делаем implode.
  • +4
    Я б такую задау на карандше решать не взялся. Не то чтобы не смогу, но настолько разбалован PhpStorm'ом, что лишний раз думать становится лениво и без дебагера уже никуда, по крайней мере в сжатые сроки :)
    • +6
      Да даже зачастую не помнишь точное написание некоторых не самых употребляемых функций или порядок параметров. Благо PHP в этом плане богат на варианты.
      • 0
        И это тоже одна из причин. Без мануала, гугла и удобной IDE я работать просто не могу. В смсле если жизнь заставит, то смогу, конечно, но очень и очень не хочу. Если у меня будет выбор между кодингом без хелпов, в блокноте, и разгрузкой вагонов я, пожалуй, пойду разгружать.
        • 0
          Им не нужно так ваше знание названий функций, как умение создавать в голове алгоритм решения. Дальше они смотрят, насколько быстро у вас это получается. И просят комментировать, чтобы понимать, вы тупите, потому что забыли название функции или потому что не знаете, что делать дальше.
          • +1
            Хм; а если я не могу думать и разговаривать одновременно — всё, плохой, негодный программист?
            • 0
              если вы не можете говорить и думать одновременно — значит вы говорите необдуманно — и тогда вы не только плохой программист… :)
              • 0
                Если я не могу говорить и думать одновременно — это не значит, что я говорю необдуманно. Это как раз значит, что я высказываю уже обдуманное ранее.
        • 0
          кодинг без хэлпов не страшен — компилятора/интерпретатора на бумажке все равно нету. Ошибок именований не высветит.
    • +2
      С другой стороны, я думаю, что задания писать в блокноте безошибочно рабочий код не было. И вряд ли бы пристали к имени функции или перепутанному порядку аргументов. Нужно было просто показать сам ход решения хотя бы.
    • НЛО прилетело и опубликовало эту надпись здесь
    • +2
      На собеседовании смело можно сначала описать решение в псевдокоде, главное чтобы подход был правильный.
      Если с листка функция сразу заработает — это, конечно, громадный плюс, но не стоит отчаиваться если синтаксис забыт, можно списать на волнение и на ту же зависимость от разных кодхэлперов. Зачастую работодателям очень сложно найти толковых сотрудников, а если они видят, что человек соображает, то могут взять и с поблажкой на дообучение.
      Выучить синтаксис — легко, научится мыслить — совсем другое.
  • 0
    Точно не засекал, но больше 10 минут. Специально делал не в IDE, а в блокнотике. Но с учётом того, что на собеседованиях всегда напряженная обстановка, то там бы делал дольше ;)

    function read_conf ( $confFile ) {
        $content = explode ( PHP_EOL, trim ( file_get_contents ( $confFile ) ) );
        array_walk ( $content,
                function(&$item) {
                    $item = sprintf ( "\$ar['%s'] = '%s';" . PHP_EOL,
                            str_replace ( '.', "']'",
                                    substr ( $item, 0, $pos = strpos ( $item, '=' ) ) ),
                                    substr ( $item, $pos + 1 ) );
                } );
        $content = implode ( null, $content );
        $fnc = create_function ( null, "{$content}; return \$ar;" );
        return $fnc ();
    }


    Либо более мизонтропичный вариант (но это уже подумать надо, как всё расипхать и самому не запутаться):
    function read_conf($confFile) {  
      $f=create_function(null,implode(null,array_map(function(&$x) {return sprintf("\$ar['%s'] = '%s';".PHP_EOL,str_replace('.',"']['",substr($x,0,$o=strpos($x,'='))),substr($x,$o+1));},explode(PHP_EOL,trim(file_get_contents($confFile))))).'return $ar;');return $f();
    }


    Кстати, ищу работу ;).
    • +3
      $content = explode ( PHP_EOL, trim ( file_get_contents ( $confFile ) ) );
      Чем вам так функция file() не угодила?
      • 0
        Вы правы, file() будет даже быстрее работать в этом случае.
        Просто то, чем редко пользуешься — забывается.
      • 0
        Кстати, при использовании file(), добавляется необходимость дополнительных проверок на пустую строку, что не совсем благоприятно для читаемости кода.
        • +1
          FILE_SKIP_EMPTY_LINES и никаких дополнительных проверок.
    • +5
      Вы уж извините, но если Вы действительно пишете как во втором варианте, то неудивительно что Вы ищете работу. Бедные ваши коллеги…
      • 0
        Нет, как во втором варианте я не пишу.
        • –1
          Господин или госпожа, влепивший или влепившая минус данному комментарию, а чем вы руководстовались? Зайдите на гитхаб и покажите мне примеры моего кода соответсвующие второму варианту, будьте добры.
  • +1
    function read_conf($filename)
    {
            $data = file_get_contents($filename);
            $rows = explode("\n",$data);
            $dict = array();
            foreach($rows as $row)
            {
                    if($row=='') continue;
                    list($key,$value) = explode('=',$row,2);
                    $key = explode('.',$key);
                    $addTo = &$dict;
                    while(count($key)>1)
                    {
                            $addTo = &$addTo[array_shift($key)];
                    }
                    $addTo[$key[0]] = $value;
            }
            return $dict;
    }
    
    • 0
      чуть выше почти такое же предложили habrahabr.ru/blogs/php/116686/#comment_3786697
      • 0
        да, сначала challenge accepted а потом уже топик читать стал, отличие только в array_shift вместо итерации по массиву
    • +2
      Аватарка позволяет в комментах не писать <?php, а сразу начинать с кода.
  • +2
    Автор. 5+ от души. не смог — признался.
    Сам не программер, но поступил бы так же. Держи пять!
  • –10
    function read_cond($filename) {
      $data = array();
      $ini = parse_ini_file($filename);
      array_walk($ini, "ini_callback", &$data);
    
      return $data;
    }
    
    function ini_callback($element, $key, $data) {
      if (strstr($key, ".")) {
        $k = explode(".", $key);
        if (count($k) == 2) $data[$k[0]][$k[1]] = $element;
        //if (count($k) == 3) $data[$k[0]][$k[1]][$k[2]] = $element;
        //.....
      } else {
        $data[$key] = $element;
      }
    }


    да в ini_callback можно красивее что нибудь придумать, хотя зависит от уровня вложенности :)
    • +10
      Поприветствуем нашего колегу из Индии!
      Скажите, а если вложенность будет стократной? А если тысячекратной? Вдруг этот конфиг… будет генерироваться прогой и нужна тысячекратная вложенность? Там банальный цикл, который позволяет хранить неограниченную вложенность.
      • 0
        Так я же не претендовал на идеальное решение, просто написал что в зависимости от задачи написал бы. А где может понадобится тысясекратная вложенность?
        • 0
          Та при чём тут это? Просто быдлокод, стыдно такое должно быть такое показывать.
          • 0
            Зато первое что пришло за 10 минут в голову быдлокодеру :)
  • 0
    Мне надавно тоже давали тестовое задание, правда на 4 часа. Задание тут: yavalek.blogspot.com/2011/02/blog-post_12.html
  • +2
    ну и от меня на python:
    def read_conf( filename ):
        lines = open( filename ).readlines()
        array = {}
        for i in lines:
            key, value = i.split('=')
            items = key.split( '.' )
    
            current = array
            for j, i in enumerate(items):
                if i in current:
                    current = current[i]
                else:
                    if j == len(items)-1:
                        current[i] = value
                    else:
                        current[i] = {}
                        current = current[i]
        return array
    
    print read_conf( "dialog.txt" )
    
    

    • +2
      а минусы то за что?
      не заработало? :)
      • +2
        На php — нет)
        • +4
          На php уже написали, мне кажется любопытно посмотреть как это выглядит на других языках.
          Не хотел никого обидеть :))
  • 0
    Я, как питонист, тоже не удержался решить эту задачу:
    def read_conf(filename):
        d = {};
        with open(filename, 'r') as file:
            for line in file:
                key, value = line.split('=',1)
                t = d
                for subkey in key.strip().split('.'):
                    if not t.has_key(subkey):
                        t.update({subkey: {}})
                        p = t
                    t = t[subkey]
                p.update({subkey:value.strip()})
        return d
    
    print read_conf('config.txt')
    


    Учитывает пробелы между элементами.
    • +1
      За точку с запятой не ругать — просто не заметил её.
    • +2
      А вот этот же алгоритм, но с учётом уже пустых строк, комментариев и c обработкой событий, когда значение переопределяется и когда не соблюдается вложенность. Также проверяется доступ к файлу.
      Этим уже можно пользоваться.
      def read_conf(filename):
          from os import access, R_OK
          if not access(filename, R_OK):
              raise IOError('Error: Cannot read the configuration file "' + filename + '".')
              return False
          d = {}
          with open(filename, 'r') as file:
              for linenumber, line in enumerate(file):
                  line = line.strip()
                  if not line or line[0] == '#' or line[0] == ';':
                      continue
                  line_sp = line.split('=',1)
                  if not (len(line_sp) == 2 and line_sp[1]):
                      raise IOError('Error: Cannot parse the configuration file "' + filename + '" at line ' + str(linenumber) + '.')
                      return False
                  key, value = line_sp
                  t = d
                  for subkey in key.strip().split('.'):
                      if not t.has_key(subkey) or t[subkey].__class__.__name__ != 'dict':
                          t.update({subkey: {}})
                      p = t
                      t = t[subkey]
                  if p[subkey]:
                      if type(p[subkey]) != type(value):
                          raise IOError('Error: Cannot assign a value to a node in the configuration file "' + filename + '" at line ' + str(linenumber) + '.')
                          return False
                      print 'Warning: Overriding previously defined value for key "' + key + '" at line ' + str(linenumber) + '.'
                  p.update({subkey:value.strip()})
          return d
      
      try:
          print read_conf('config.txt')
      except IOError, error:
          print 'Failed to read the configuration file:'
          print error
      
  • –1
    Спасибо, люблю такие задачки! :)

    Я справился приблизительно за 8-9 минут, причём всё заработало правильно с первого раза без дебага. А сколько времени ушло у вас?

  • +1
    Задачка на стрессоустойчивость. А дым в лицо собеседователи при этом не пускали, иголкой внезапно не кололи? А то ещё можно внезапно крикнуть соискателю в ухо: «Бу!» Какой же он специалист, если его смутят такие, обычные в программистской практике мелочи?
    • 0
      Бу кричать не надо — надо дать одну задачу, взять решение.
      Дать вторую задачу и в процессе решения второй задавать вопросы по решению первой задачи.
      И да. естественно с временными рамками.
  • 0
    А у меня вот такая бодяга вышла за 10 минут…
    <code>  
      $config = file_get_contents("./test-config.txt");
      $config_array = explode("\n", $config);
      sort($config_array);
      $result = array();
    
      foreach($config_array as $config_item) {
        if ($config_item != NULL) {
          $value = substr($config_item, strpos($config_item, "=") + 1, strlen($config_item));
          $key = substr($config_item, 0, strpos($config_item, "="));
          $key_arr = explode(".", $key);
         
          $tmp = &$result;
          for($i=0; $i < count($key_arr); $i++) {
            if ( !isset($tmp[$key_arr[$i]]) ) $tmp[$key_arr[$i]] = "";
            $tmp = &$tmp[$key_arr[$i]];
          }
          $tmp = $value;
        }
      }
    
      var_dump($result);
    </code>
    • 0
      Блин, сел за решил эту задачу практически идентичным способом. Очень удивился, когда нашел ваш код в комментариях.

      Вот мой:
      <?php

      function read_conf( $file )
      {
      $result = array();

      $file = file( $file ) or die('File not found!');

      foreach( $file as $line )
      {
      $eq_pos = strpos( $line, '=' );
      $key = trim( substr( $line, 0, $eq_pos ) );
      $value = trim( substr( $line, $eq_pos + 1 ) );

      $arr = & $result;

      $arr_keys = explode( '.', $key );

      foreach( $arr_keys as $i => $arr_key )
      {
      if( ! isset( $arr[$arr_key] ) )
      $arr[ $arr_key ] = ($i + 1 == count($arr_keys) ? $value : array());

      $arr = & $arr[ $arr_key ];
      }
      }

      return $result;
      }

      //read_conf('conf.ini');
      echo '
      ';
      print_r( read_conf('conf.ini') );
      
      ?></code>
      
  • 0
    function read_conf($file) {
    $lines = file($file);
    $result = array();
    foreach ($lines as &$line) {
    $line = trim($line);
    if (!empty($line)) continue;
    $lineParts = explode("=", $line);
    $path = explode(".", $lineParts[0]);
    $current = &$result;
    $pathSize = sizeof($path) - 1;
    for ($i = 0; $i <= $pathSize; $i++) {
    if ($i == $pathSize) {
    $current[$path[$i]] = $lineParts[1];
    } else {
    if (!isset($current[$path[$i]])) $current[$path[$i]] = array();
    $current = &$current[$path[$i]];
    }
    }
    unset($current);
    }
    return $result;
    }

  • +1
    Знакомый, не имеющий аккаунта, попросил запостить:

    Для интереса решил попробовать. Но, т.к. сам — рубист, то на ruby.

    Сначала сохранил исходный текст с данными в файл, открыл текстовый редактор, и засек время. Накодил, нажал SAVE, снова засек время — итого 4 минуты 46 секунд.

    Затем запустил проверить — все сработало как надо. Размер скрипта 433 байта:
    def read_cfg(fname)
       s = File.read(fname)
       rez = {}
       s.each_line do |l|
          l.strip!
          if l != ''
             key, value = l.split('=',2)
             key.strip!
             value.strip!
             keys = key.split('.')
             last_key = keys.pop
             c = rez
             if keys.length > 0
                keys.each do |k|
                   if c.has_key?(k)
                      c = c[k]
                   else
                      c[k] = {}
                      c = c[k]
                   end
                end
             end
             c[last_key] = value
          end
       end
       rez
    end
    
    cfg = read_cfg('cfg')
    p cfg

    Потом, если подумать, код можно подсократить (там пара-тройка строчек необзательные), но раз уж речь идет про лимит в 10 минут, то код выложил без рефакторинга.
    • +1
      А на питоне вообще достаточно сделать:

      from sys.interview.swedish import read_conf

      По мотивам xkcd.
      • –5
        Вот это правильный подход. Свой сервер на Питоне а ешё лучше на Обероне. Остальные PHPухи в этом топике, просто парни с улицы, в основном.

        Удачи,
        ignat
        • 0
          Скажите честно, вам доставляет удовольствие троллить? Хабр — форум, что вы к каждому своему комменту добавляете подпись?
          • –6
            Я не тролю. Я выражаю свое мнение. А вот ваша попытка обвинить меня в троле напоминает действие шестёрки, б… ди или как это там. Вам заплатили? Вы хотите чтоб Вас поломали? Я подписываюсь так, как считаю нужным. И высказываюсь там где вижу влияние денег и обман простых трудовых мужиков. А беспредельшиков и бл… дей я ненавижу.

            Удачи,
            ignat
            • 0
              Вам явно стоит принять успокоительного. Если мой коммент Вас оскорбил, то прошу извинить, т.к. не ставил такой цели. К чему угрозы? Причем тут влияние денег? Где вы видите обман простых трудовых мужиков? Где вы тут увидели беспредельщиков? Простите, но не улавливаю ни грамма логики.
              • –4
                По пунктам:

                Про влияние денег:
                Например: AmdY, 1 апреля 2011, 21:57 Пишет о плохих эмигрантах.

                Вместе с тем шведская www.playahead.se в 2007 году была куплена холдингом Метро. В то же время они интересовались своими конкурентами такими как Хабр, Лепрозорий и т.д.
                www.youtube.com/watch?v=cdfT01qBGXE
                Это тот самый холдинг что распространяет бесплатные газеты в метро.

                Таким образом подобные сайты пытаются рекламировать новинки индустрии, так как видимо уже куплены подобными Метро международными холдингами. Поэтому мое высказывание о правах эмигрантов идёт в разрез с политикой международного Холдинга.

                По поводу обмана простых мужиков:
                Возможно Вы виртуал или кукла НЛО или имеете денежные отношения с этим сайтом (Достаточно посмотреть кто вас пригласил и какие темы вы обсуждаете, например технологии MS).

                Аналогично по поводу манипуляций общественным мнением с помощью кармы. Практически возможность публиковать материал на этом сайте на прямую зависит от лояльности к международному холдингу или отдельным корпорациям и прочему ворью а не от технического уровня специалиста.

                Удачи,
                ignat

                P.S. Таблетки не принимаю, веду здоровый образ жизни, изучаю троичную логику. Ваши извинения я принимаю.
                • 0
                  Пригласили меня на данный ресурс, по всей видимости, администраторы ресурса (отсюда и надпись в профиле «приглашен НЛО») по время последних новогодних праздников за топик в песочнице (позже его переместили в блог .NET). Таким образом абсолютно никакого отношения к «Тематические медиа» не имею.
                  • –7
                    Сразу после моего детального сообщения о манипуляциях на Хабре, моя карма чудесным образом упала на 10 пунктов!

                    Удачи,
                    ignat
            • +3
              Слушай, ты реально уже задолбал этой своей подписью в каждом комментарии.
              В комментариях такое не принято. Если хочешь, чтобы все знали, как тебя зовут и какого ты года рождения, то напиши это в своём профиле. Кому станет интересно, тот зайдёт в твой профиль и посмотрит. Пора взрослеть.
              Удачи, ignat.
              • –6
                А Вы (так следует обращаться) видимо записная оппозиция?

                Удачи,
                ignat
                • +2
                  Чувак, ты 3.1415здец! %)
                  • –1
                    www.ocp.inf.ethz.ch/wiki/OCP/Downloads

                    Возможно, Вам это поможет быть более вежливым при разговоре с незнакомыми людьми. И осведомленным в современных подходах к программированию (RIA-платформы). Ну если не поможет, то я умываю руки.

                    Удачи и вам,
                    ignat
                    • 0
                      Ноги умыть не забудь и зубы почистить! ;)))))
                      • –2
                        Так, все ясно, будем продолжать хамить. Больше я на Вас не реагирую. Но на всякий случай скажу, изучение вышеуказанного материала поможет Вам в борьбе с Вашим невежеством, озвученным в 28 Ваших постах.

                        Удачи,
                        ignat
                        • +2
                          Чувак, извини, я ошибся. Ты не просто 3.1415здец, ты ФЕЕРИЧЕСКИЙ 3.1415ЗДЕЦ! Ибо я написал всего один пост и больше в этом кармадрочерстве участвовать на собираюсь! :))))))))))
  • 0
    Вот такой интеллектуальный и культурный уровень на хабре.
    И что характерно все такие сообразительные либо на PHP пишут, либо с Одессы, либо евреи, либо все вместе :-) И еше одно наблюдения всех этих двух или трех людей, которые не вежливые, пустили сюда в новый год через песочницу. Похоже большинству тут делать нечего. Удивительно, что Мицгол-вебмастер все еще бывает на Хабре.

    К сожалению это закон математики, если все время равняться на среднячков, то однозначно получаем очень быструю деградацию. Хабр уже не тот.

    Удачи,
    ignat
    • +3
      >>> закон математики
      Ваш комментарий мне почему-то напомнил анекдот…

      Биолог, инженер и математик попивают кофе в патио, и замечают, что на другой стороне улицы в дом зашли два человека.
      Через некоторое время дом покинуло уже три человека.
      Биолог: Двое спарились, размножились и дом покинуло трое.
      Инженер: Нет, просто наше первоначальное наблюдение содержит ошибку!
      Математик: Вы оба ошибаетесь. Нужно дождаться, пока в дом войдёт ещё один человек и тогда он снова будет пуст.
  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    В 2001 году мы писали интерпретатор на C++, artPublishing (вот тут немного про него http://habrahabr.ru/blogs/development/104191) мы такую штуку писали на сях.

    И при проектировании заложили разные нештатные ситуации, которые, на первый взгляд, приведенными решениями не обрабатываются. Впрочем, может и в исходном задании этого не было. Но для конфигурационных файлов — это правило.

    К примеру, должны нормально парситься такие файлы:

    [block ]
    ;asdasd=
    a="длинный текст
    с переносом на следующую строку #решетка работает,
    потому что в кавычках" # а это комментарий
    b=а тут пишем без кавычек, ясен перец, что должно в одну строку тогда #комментарий


    Потом нужно сразу понять, что делать с квотингом кавычек и «решеток». Что делать с ОЧЕНЬ ДЛИННЫМИ СТРОКАМИ.
    Что делать с несколькими одинаковыми блоками.
    Что делать с несколькими одинаковыми переменными.
    в случае a=3,54 трактовать ли правую часть как число или как строку?
    Что делать, если в файле стали присутствовать странные символы
    Что делать, если знака = вообще нет
    Что делать, если скобка квадратная не закрыта
    ну и много всего такого.
  • 0
    Без проверок и прочего я бы написал так

    function read_conf($fileName)
    {
    $result = array();
    $fp = fopen($fileName, 'r');

    while ($line = fgets($fp)) {
    list($key, $value) = explode('=', $line);
    $keyParts = explode('.', $key);
    setValueFromKeyParts($result, $keyParts, $value);
    }

    return $result;
    }

    function setValueFromKeyParts(&$result, &$parts, $value)
    {
    $part = array_shift($parts);

    if (count($parts) == 0) {
    $result[$part] = $value;
    } else {
    if (!isset($result[$part])) {
    $result[$part] = array();
    }
    setValueFromKeyParts($result[$part], $parts, $value);
    }
    }
  • 0
    ===config.txt===
    id=www
    id.2=www2
    session.timeout=120
    session.timeout.old=200
    session.timeout.next=500
    session.server.0=open
    session.server.0.host=127.0.0.1
    session.server.0.port=1111
    session.server.0.id=session1
    session.server.0=closed
    session.server.1.host=127.0.0.1
    session.server.1.port=1111
    session.server.1.id=session2
    image=picture.pic
    image.width=640
    image.height=480
    image.watermark=yes
    image.watermark.small=wsmall.png
    image.watermark.normal=wnormal.png
    ===
    ;-)

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.