Чего “правильный” путь состоит в том, чтобы выполнить итерации через массив в Ruby?

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

У вас в основном есть два варианта:

  1. Использование PDO (для любой поддерживаемый драйвер базы данных):
    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. Использование MySQLi (для MySQL):
    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

Если вы подключаетесь к база данных, отличная от MySQL, есть вторая опция, зависящая от драйвера, к которой вы можете обратиться (например, pg_prepare() и pg_execute() для PostgreSQL). PDO является универсальной опцией.

Правильная настройка соединения

Обратите внимание, что при использовании PDO для доступа к базе данных MySQL real подготовленные операторы не используются по умолчанию. Чтобы исправить это, вы должны отключить эмуляцию подготовленных операторов. Пример создания соединения с использованием PDO:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

В приведенном выше примере режим ошибки не является строго необходимым, но рекомендуется добавить его. Таким образом, сценарий не остановится с Fatal Error, когда что-то пойдет не так. И это дает разработчику шанс catch получить любую ошибку (ы), которые являются throw n как PDOException s.

Однако обязательной является первая setAttribute() строка, которая сообщает PDO об отключении эмулируемых подготовленных операторов и использует подготовленные операторы real . Это гарантирует, что оператор и значения не будут разбираться с PHP перед отправкой на сервер MySQL (предоставление возможности злоумышленнику возможности внедрить вредоносный SQL).

Хотя вы можете установить charset в варианты конструктора, важно отметить, что «более старые» версии PHP (& lt; 5.3.6) молча игнорировали параметр charset в DSN.

Объяснение

Случается, что оператор SQL, который вы передаете prepare, анализируется и компилируется сервером базы данных. Указав параметры (либо ?, либо именованный параметр, такой как :name в примере выше), вы указываете механизм базы данных, в который вы хотите включить фильтр. Затем, когда вы вызываете execute, подготовленный оператор объединяется со значениями параметров, которые вы указываете.

Важно то, что значения параметров объединены с компилируемым оператором, а не с строкой SQL. SQL-инъекция работает, обманывая сценарий, включая вредоносные строки, когда он создает SQL для отправки в базу данных. Поэтому, отправляя фактический SQL отдельно от параметров, вы ограничиваете риск того, что закончите то, чего не намеревались. Любые параметры, которые вы отправляете при использовании подготовленного оператора, будут обрабатываться только как строки (хотя механизм базы данных может сделать некоторую оптимизацию, поэтому, конечно, параметры могут также оказаться как числа). В приведенном выше примере, если переменная $name содержит 'Sarah'; DELETE FROM employees, результатом будет просто поиск строки "'Sarah'; DELETE FROM employees", и вы не получите пустую таблицу .

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

О, и поскольку вы спросили, как это сделать для вставки, вот пример (с использованием PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array('column' => $unsafeValue));

Могут ли подготовленные операторы использоваться для динамических запросов?

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

Для этих конкретных сценариев лучше всего использовать фильтр белого списка, который ограничивает возможные значения.

// Value whitelist
// $dir can only be 'DESC' otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}

327
задан Andrew Grimm 27 August 2011 в 16:30
поделиться

8 ответов

Это выполнит итерации через все элементы:

array = [1, 2, 3, 4, 5, 6]
array.each { |x| puts x }

Печать:

1
2
3
4
5
6

Это выполнит итерации через все элементы, дающие Вам значение и индекс:

array = ["A", "B", "C"]
array.each_with_index {|val, index| puts "#{val} => #{index}" }

Печать:

A => 0
B => 1
C => 2

я не совсем уверен от Вашего вопроса, какой Вы ищете.

543
ответ дан brittohalloran 23 November 2019 в 00:49
поделиться

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

  • each достаточно для многих использований, так как я не часто забочусь об индексах.
  • each_ with _index действия как Hash#each - Вы получаете значение и индекс.
  • each_index - просто индексы. Я часто не использую этого. Эквивалентный "length.times".
  • map другой способ выполнить итерации, полезный, когда Вы хотите преобразовать один массив в другого.
  • select итератор для использования, когда Вы хотите выбрать подмножество.
  • inject полезно для генерации сумм или продуктов или сбора единственного результата.

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

82
ответ дан AShelly 23 November 2019 в 00:49
поделиться

Используйте each_with_index при необходимости в обоих.

ary.each_with_index { |val, idx| # ...
17
ответ дан J Cooper 23 November 2019 в 00:49
поделиться

Другие ответы очень хорошо, но я хотел указать на еще одну периферийную вещь: Массивы заказаны, тогда как Хеши не находятся в 1,8. (В Ruby 1.9 Хеши заказаны порядком вставки ключей.), Таким образом, не имело бы смысла до 1,9 выполнять итерации по Хешу тем же путем/последовательностью как Массивы, которые всегда имели определенное упорядочивание. Я не знаю то, что порядок по умолчанию для ассоциативных массивов PHP (по-видимому, мой Google fu не достаточно силен для понимания этого, любого), но я не знаю, как можно полагать, что регулярные массивы PHP и ассоциативные массивы PHP "то же" в этом контексте, так как порядок на ассоциативные массивы кажется неопределенным.

По сути, Ruby путь кажется более ясным и интуитивным мне.:)

12
ответ дан Pistos 23 November 2019 в 00:49
поделиться

Попытка сделать то же самое последовательно с массивами и хешами могло бы просто быть запахом кода, но, рискуя тем, что я был выпущенным под брендом codorous half-monkey-patcher при поиске последовательного поведения это добилось бы цели?:

class Hash
    def each_pairwise
        self.each { | x, y |
            yield [x, y]
        }
    end
end

class Array
    def each_pairwise
        self.each_with_index { | x, y |
            yield [y, x]
        }
    end
end

["a","b","c"].each_pairwise { |x,y|
    puts "#{x} => #{y}"
}

{"a" => "Aardvark","b" => "Bogle","c" => "Catastrophe"}.each_pairwise { |x,y|
    puts "#{x} => #{y}"
}
7
ответ дан Brent.Longborough 23 November 2019 в 00:49
поделиться

Если Вы используете счетный смешивание (как направляющие делают), можно сделать что-то подобное php перечисленному отрывку. Просто используйте each_slice метод и сгладьте хеш.

require 'enumerator' 

['a',1,'b',2].to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }

# is equivalent to...

{'a'=>1,'b'=>2}.to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }

Меньше исправления обезьяны требуется.

Однако это действительно вызывает проблемы, когда у Вас есть рекурсивный массив или хеш со значениями массива. В рубиновых 1,9 этих проблемах решен с параметром к сглаживать методу, который определяет, как глубоко рекурсивно вызвать.

# Ruby 1.8
[1,2,[1,2,3]].flatten
=> [1,2,1,2,3]

# Ruby 1.9
[1,2,[1,2,3]].flatten(0)
=> [1,2,[1,2,3]]

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

3
ответ дан maxhawkins 23 November 2019 в 00:49
поделиться

Я не говорю это Array -> |value,index| и Hash -> |key,value| весьма нормально (см. комментарий Horace Loeb), но я говорю, что существует нормальный способ ожидать это расположение.

Когда я имею дело с массивами, я фокусируюсь на элементах в массиве (не индекс, потому что индекс является переходным). Метод - каждый с индексом, т.е. each+index или |each, индекс |, или |value,index|. Это также согласовывается с индексом, просматриваемым как дополнительный аргумент, например, |value | эквивалентен |value, index=nil |, который согласовывается с |value, индекс |.

Когда я имею дело с хешами, я часто более фокусируюсь на ключах, чем значения, и я обычно имею дело с ключами и значениями в том порядке, также key => value или hash[key] = value.

Если Вы хотите ввод утки, то или явно используют определенный метод, поскольку Brent Longborough показал, или неявный метод, как maxhawkins показал.

Ruby - все о размещении языка для удовлетворения программисту, не о программисте, размещающем для удовлетворения языку. Поэтому существует столько путей. Существует столько способов думать о чем-то. В Ruby Вы выбираете самое близкое, и остальная часть кода обычно выпадает чрезвычайно аккуратно и кратко.

Что касается исходного вопроса, "Чего “правильный” путь состоит в том, чтобы выполнить итерации через массив в Ruby?", ну, в общем, я думаю, что базовый путь (т.е. без мощного синтаксического сахарного или объектно-ориентированного питания) состоит в том, чтобы сделать:

for index in 0 ... array.size
  puts "array[#{index}] = #{array[index].inspect}"
end

Но Ruby - все о мощном синтаксическом сахарном и объектно-ориентированном питании, но так или иначе вот эквивалент для хешей, и ключи могут быть заказаны или нет:

for key in hash.keys.sort
  puts "hash[#{key.inspect}] = #{hash[key].inspect}"
end

Так, мой ответ, "“правильный” способ выполнить итерации через массив в Ruby зависит от Вас (т.е. программист или команда программистов) и проект".. Чем лучший Ruby программист делает, тем лучший выбор (который синтаксическое питание и/или который объектно-ориентированный подход). Лучший Ruby программист продолжает искать больше путей.


Теперь, я хочу задать другой вопрос, "Чего “правильный” путь состоит в том, чтобы выполнить итерации через Диапазон в Ruby назад?"! (Этот вопрос состоит в том, как я достиг этой страницы.)

Хорошо сделать (для форвардов):

(1..10).each{|i| puts "i=#{i}" }

но мне не нравится делать (для назад):

(1..10).to_a.reverse.each{|i| puts "i=#{i}" }

Ну, я на самом деле не возражаю делать так слишком много, но когда я преподаю движение назад, я хочу показать моим студентам хорошую симметрию (т.е. с минимальным различием, например, только добавлением реверса или шага-1, но не изменяя ничто больше). Можно сделать (для симметрии):

(a=*1..10).each{|i| puts "i=#{i}" }

и

(a=*1..10).reverse.each{|i| puts "i=#{i}" }

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

(*1..10).each{|i| puts "i=#{i}" }
(*1..10).reverse.each{|i| puts "i=#{i}" }
#
(1..10).step(1){|i| puts "i=#{i}" }
(1..10).step(-1){|i| puts "i=#{i}" }
#
(1..10).each{|i| puts "i=#{i}" }
(10..1).each{|i| puts "i=#{i}" }   # I don't want this though.  It's dangerous

Вы могли в конечном счете сделать

class Range

  def each_reverse(&block)
    self.to_a.reverse.each(&block)
  end

end

но я хочу преподавать чистый Ruby, а не объектно-ориентированные подходы (просто все же). Я хотел бы выполнить итерации назад:

  • не создавая массив (рассматривают 0.. 1000000000)
  • работа для любого Диапазона (например, Строки, не просто Целые числа)
  • не используя дополнительного объектно-ориентированного питания (т.е. никакая модификация класса)

Я полагаю, что это невозможно, не определяя a pred метод, что означает изменять класс Диапазона для использования его. Если бы можно сделать, это сообщило мне, иначе подтверждение невозможности ценилось бы, хотя это было бы неутешительно. Возможно, Ruby 1.9 обращается к этому.

(Спасибо за внимание в чтении этого.)

59
ответ дан 23 November 2019 в 00:49
поделиться

Я пытался создать меню (в Camping и Markaby ), используя хэш.

Каждый элемент состоит из двух элементов: метка меню и URL , поэтому хэш казался правильным, но URL-адрес '/' для 'Home' всегда появлялся последним (как и следовало ожидать от хеша), поэтому пункты меню отображались в неправильном порядке.

Использование массива с each_slice выполняет свою работу:

['Home', '/', 'Page two', 'two', 'Test', 'test'].each_slice(2) do|label,link|
   li {a label, :href => link}
end

Добавление дополнительных значений для каждого пункта меню (например, как имя CSS ID ) просто означает увеличение значения среза. Итак, как хэш, но с группами, состоящими из любого количества элементов. Идеально.

Итак, это просто благодарность за непреднамеренный намек на решение!

Очевидно, но стоит отметить: я предлагаю проверить, делится ли длина массива на значение среза.

5
ответ дан 23 November 2019 в 00:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: